2604,vim可定制对话框

原文

Vim里使用真正的可自定义对话框

作者:韦易笑

你有没有试过在Vim里搭一个表单?不是那种简单的单行提示,而是一个真正的有文本输入框,单选按钮,复选框,下拉列表和确认按钮,全部集中在一个窗口里的表单?

如果你试过,你应该知道有多痛苦.

原方式有多难受

Vim本地给你的工具,只有input()做单行输入,和inputlist()做列表选择,就这些了.

想要问用户要一个项名?简单:

cpp 复制代码
let name = input("Project name: ")

再加一个邮箱:

cpp 复制代码
let name = input("Project name: ")
let email = input("Email: ")

两次阻塞式的提示.用户填完项名按回车,然后再填邮箱.填错了项名?对不起,回不去.没有可视化布局,没法同时看到两个字段.

再加个语言选择:

cpp 复制代码
let name = input("Project name: ")
let email = input("Email: ")
let lang = inputlist(["1. Python", "2. Go", "3. Rust"])

三个字段,三次独立的交互.这不是表单,这是审讯.

用户选完语言后想改项名?从头来过.

如果还需要一个"是否初化git仓库"的复选框呢?Vim没有inputcheck()该东西,你只能再搞一个input("初化git?(y/n):"),然后自己解析答案.

这样,不能扩展.

更好的方式:vimquickuiDialog

vimquickui是一个为Vim和NeoVim设计的TUI组件库,提供菜单,列表框,文本框等控件,全部用纯VimScript实现,不依赖任何外部工具.

在1.5.0版本中,它新增了一套数据驱动的对话框系统:按一组字典声明控件,quickui在一个弹窗中渲染它们;

用户完成操作后,按一个字典返回给你所有值.

不需要+神算,不需要Lua,不需要外部依赖,纯VimScript搞定.

安装

用vimplug:

cpp 复制代码
Plug skywind3000/vimquickui"

或用Vim内置包管理:

cpp 复制代码
cd ~/.vim/pack/vendor/start && git clone https://github.com/skywind3000/vim-quickui

可选设置统一边框:

cpp 复制代码
let g:quickui_border_style = 2

好了,没有构建步骤,没有依赖.

你的第一个对话框

来简单设置对话框,如下放置进一个函数里:

cpp 复制代码
function! MySettings()
    let items = [
        \ {'type': 'label', 'text': 'Settings:'},
        \ {'type': 'input', 'name': 'name', 'prompt': 'Name:',
        \  'value': 'test'},
        \ {'type': 'radio', 'name': 'choice', 'prompt': 'Pick:',
        \  'items': ['A', 'B', 'C']},
        \ {'type': 'check', 'name': 'flag',
        \  'text': 'Enable Feature'},
        \ {'type': 'button', 'name': 'confirm',
        \  'items': [' &OK ', ' &Cancel ']},
        \ ]
    let result = quickui#dialog#open(items, {'title': 'Settings'})
    echo result
endfunc

执行:callMySettings()效果如下:略.

一个真正的对话框,在Vim里,带多个控件.

逐行解释一下:

1,label,顶部的静态文本,不可得焦

2,input,带提示标签和默认值的文本输入框

3,radio,单选按钮组,只能选一个

4,check,复选框,可切换开关

5,button,底部的按钮行

可用制表符和ShiftTab在控件间切换焦点,在输入框里直接输入,按空格切换复选框或选择单选项,按回车或点击按钮确认.

在结果字典里返回所有的值.

怎么退出?

对话框关闭后,你需要知道两件事:用户是确认了还是取消了?如果确认了,是按了按钮还是在输入框里按了回车?
返回值有两个关键字段:

1,button_index,按了哪个按钮(0开始),取消时为-1.

2,button,按钮的名字,如果是在非按钮上按回车或取消则为"".

这是在每个对话框里都会用到的判断模式:

cpp 复制代码
let r = quickui#dialog#open(items, opts)
if r.button_index == -1
  " 用户按了 ESC,CtrlC 或`关闭按钮`"
  echo "Cancelled"
elseif r.button == ""
  " 用户在输入框/单选/复选上按了回车"
  echo "Confirmed (Enter): name=" . r.name
else
  " 用户点击了按钮"
  echo "Button pressed: " . r.button . " #" . r.button_index
endif

几个要点:

1,button_index从0开始.第一个按钮返回0,第二个返回1,等.

2,用按钮区分回车和按钮点击.当button_index为0时,检查r.button:如果为"",说明是在非按钮上按了回车;如果非空,说明是点击了第一个按钮.

3,取消时仍返回值.即使按了ESC,r.name等字段仍包含用户在取消前输入的内容.下次重新打开对话框时可恢复状态.

一般,只需要这样判断:

cpp 复制代码
let r = quickui#dialog#open(items, opts)
if r.button_index >= 0 && r.button != ""
  " 用户点击了按钮,处理`返回值`"
  echo "Name: " . r.name
endif

或如果有好和取消两个按钮:

cpp 复制代码
" " &OK " 是按钮 0," &Cancel " 是按钮 1"
if r.button_index == 0 && r.button != ""
  echo "Accepted: " . r.name
endif

一个实战示例

一个更贴近真实插件的:一个包含所有控件类型的"新建项":

cpp 复制代码
function! NewProject()
    let items = [
        \ {'type': 'label', 'text': 'Create New Project:'},
        \ {'type': 'input', 'name': 'project_name', 'prompt': 'Project:'},
        \ {'type': 'input', 'name': 'email', 'prompt': 'Email:'},
        \ {'type': 'dropdown', 'name': 'language', 'prompt': 'Language:',
        \  'items': ['Python', 'JavaScript', 'Go', 'Rust', 'C++'],
        \  'value': 0},
        \ {'type': 'dropdown', 'name': 'build', 'prompt': 'Build:',
        \  'items': ['Make', 'CMake', 'Cargo', 'npm', 'pip'],
        \  'value': 0},
        \ {'type': 'radio', 'name': 'license', 'prompt': 'License:',
        \  'items': ['&MIT', '&Apache', '&GPL', '&Proprietary'],
        \  'value': 0},
        \ {'type': 'check', 'name': 'git_init',
        \  'text': 'Initialize git repo', 'value': 1},
        \ {'type': 'check', 'name': 'ci',
        \  'text': 'Add CI config'},
        \ {'type': 'button', 'name': 'confirm',
        \  'items': [' &Create ', '  Cancel  ']},
        \ ]

    let opts = {'title': 'New Project', 'w': 50, 'focus': 'project_name'}
    let result = quickui#dialog#open(items, opts)

    " 检查用户是否点击了 "Create" 按钮(按钮 0)"
    if result.button_index == 0 && result.button != ''
        let languages = ['Python', 'JavaScript', 'Go', 'Rust', 'C++']
        let builds = ['Make', 'CMake', 'Cargo', 'npm', 'pip']

        echo 'Project:  ' . result.project_name
        echo 'Email:    ' . result.email
        echo 'Language: ' . languages[result.language]
        echo 'Build:    ' . builds[result.build]
        echo 'License:  ' . result.license
        echo 'Git:      ' . (result.git_init ? 'yes' : 'no')
        echo 'CI:       ' . (result.ci ? 'yes' : 'no')
    else
        echo 'Cancelled'
    endif
endfunc

效果截图:略.

该例展示了几个要点:

1,下拉列表控件(dropdown)按一个折叠的选择框显示.按回车或空格弹出选项列表供选择.返回值是0开始的索引,需要自己映射回文本.

2,分隔线(separator)在复选框和按钮间画一条水平线,替代了控件间的默认空行,保持布局整洁.

3,opts.focus把初始焦点设置到project_name输入框,用户打开对话框就能立刻开始输入.

4,提示文本自动对齐.注意Project:,Email:,Language:,Build:和License:这些标签都是左对齐的,它们后面的控件开始位置在同一列.

5,QuickUI会自动计算最长的提示文本,补齐其余的.

6,热键标记.按钮,单选和复选文本中的&标记了热键符.比如&创建,&Create让C变成热键,在对话框的任何地方(不在输入框中时)按C就可触发该按钮.

单选组的&MIT,&Apache同理.

使用技巧

分享几个我在开发对话过程中总结的经验:

1,先设好opts.w.如果不设宽度,QuickUI会自动计算.对简单对话框没问题,但对有多个字段,显式设置一个宽度(比如50)可让布局更一致.

2,用"值"设默认值.每个控件都支持值字段.输入框接收串,单选/下拉/复选框接收数字.预填好默认值可让用户少打几个字.

3,复选框不需要提示标签.和输入框,单选不同,复选框的文本自身就是标签,不加提示看着更自然.如果想让它和其他有提示的控件对齐,也可加"提示"字段.

4,给按钮行命名.如果只有一行按钮,默认名字"按钮"就够了.但如果有两行按钮(比如"Apply/Reset"和"OK/Cancel"),要给它们不同名,这样才能区分用户点的是哪一行.

更多进阶特性

这里只覆盖了基础用法.对话框系统还有更多能力:

1,输入历史,输入框可历史字段在多次调用间共享历史记录,按Ctrl+Up/Ctrl+Down浏览

2,垂直单选,选项文本较长时,单选组会自动切换为垂直布局

3,表单验证,opts.validator设置一个回调函数,在对话框关闭前验证字段值.

4,支持鼠标,点击任何控件即可得焦,切换或激活.

5,自定义颜色和边框,配合你的Vim配色方案
完整参考可查看vimquickui仓库中的DialogGuide.

不只是对话框

vimquickui不仅是对话框.它还提供:

1,顶部菜单栏,屏幕顶部的下拉菜单,Borland/TurboC++风格

2,右键菜单,光标附近弹的环境菜单

3,列表框,带搜索功能的可滚动栏表

4,文本框,在弹窗中显示文本

5,预览窗口,在光标附近预览文件内容

6,输入框,简单的单行输入(比完整对话框更轻量)

7,终端,在弹窗中运行壳命令

全部是纯VimScript实现,全部同时支持Vim和NeoVim.

相关推荐
zpjing~.~1 天前
Vim 高效批量替换实战:从 /dev/ 替换为 /dev-ai/
人工智能·编辑器·vim
幸运小圣4 天前
Buffer 新手入门【Node.js】
node.js·编辑器·vim
lzx_0024 天前
Linux 开发工具(二) 简单认识一下编辑器 —— vim
linux·编辑器·vim
此生决int5 天前
Linux系列(04)——基础开发工具(上)软件包管理器、vim、gcc/g++
linux·服务器·vim
geats人山人海6 天前
linux 2 基本操作与vim
linux·运维·vim
星恒随风10 天前
Linux开发工具详解(一):软件包管理、Vim、GCC/G++、Makefile与进度条实战
linux·运维·笔记·学习·vim
开开心心就好11 天前
超市定时播音软件,免费版支持循环播放
智能手机·ffmpeg·ocr·word·vim·音视频·visual studio
ZhangJun9512 天前
Linux常用命令笔记,中高级运维高频指令,并附上vim常见操作
linux·运维·centos·云计算·vim
Pointer Pursuit14 天前
编辑器Vim
linux·编辑器·vim
阿钱真强道21 天前
06 嵌入式操作系统 | vim 与软件管理:apt / 源码编译
ubuntu·vim·嵌入式·apt·源码编译