Floating Windowをボタンにしないなんてもったいない!
Floating Windowを使って立体的なボタンを作ってみた。ENTERでボタンを押せる。
ボタンに見せるにはどうするか
- frontとbackの2枚のウィンドウを用意
- backの色をfrontの色よりちょっと暗く
- 押されたときにbackを覆い隠すようにfrontの位置を下げる
これでボタンになる。Floating Windowを2枚用意して、ENTERが押されたら位置を下げて戻せば押したように見える。
ソース
let g:button_window = {} hi FrontColor guibg=#F27200 hi BackColor guibg=#AC5D24 function! s:center(str) let width = nvim_win_get_width(0) let shift = floor(width/2) - floor(strdisplaywidth(a:str)/2) return repeat(' ', float2nr(shift)) . a:str endfunction function! s:remove_button() abort let front_win = nvim_get_current_win() let back_win = g:button_window[front_win] call nvim_win_close(back_win, v:true) call nvim_win_close(front_win, v:true) call remove(g:button_window, front_win) endfunction function! s:create_window(config, ...) abort let hi_group = a:1 let transparency = get(a:, '2', 0) let buf = nvim_create_buf(v:false, v:true) let win = nvim_open_win(buf, v:true, a:config) if hi_group != '' call nvim_win_set_option(win, 'winhighlight', hi_group) call nvim_win_set_option(win, 'winblend', transparency) call nvim_win_set_config(win, a:config) endif return win endfunction function! s:push() abort let front_win = nvim_get_current_win() let back_win = g:button_window[front_win] let config = nvim_win_get_config(front_win) let config.row += 1 call nvim_win_set_config(front_win, config) sleep 100ms redraw let config.row -= 1 call nvim_win_set_config(front_win, config) endfunction function! s:main() abort let row = 20 let col = 20 let width = 20 let height = 3 let config = { 'relative': 'editor', 'row': row, 'col': col, 'width': width, 'height': height, 'anchor': 'NW', 'style': 'minimal' } " 影の部分 let back_config = deepcopy(config) let back_config.row += 1 let back_win = s:create_window(back_config, 'Normal:BackColor') " 前面の部分 let front_win = s:create_window(config, 'Normal:FrontColor') call setline(2, s:center('Button')) call cursor(2, 0) nnoremap <buffer><nowait><silent> :q :call <SID>remove_button() nnoremap <CR> :call <SID>push()<CR> " 影と前面のウィンドウを紐付け let g:button_window[front_win] = back_win endfunction call s:main()
せっかくなのでENTERで弾を発射できるようにする
ENTERでボタンPUSH&弾発射
ついでにhjklで移動できるようにもした。
let g:button_window = {} hi FrontColor guibg=#F27200 hi BackColor guibg=#AC5D24 function! s:center(str) let width = nvim_win_get_width(0) let shift = floor(width/2) - floor(strdisplaywidth(a:str)/2) return repeat(' ', float2nr(shift)) . a:str endfunction function! s:move(direction, value) let front_win = nvim_get_current_win() let back_win = g:button_window[front_win] for id in [front_win, back_win] let config = nvim_win_get_config(id) if a:direction == 'x' let config.col += a:value else let config.row += a:value endif call nvim_win_set_config(id, config) endfor endfunction function! s:remove_button() abort let front_win = nvim_get_current_win() let back_win = g:button_window[front_win] call nvim_win_close(back_win, v:true) call nvim_win_close(front_win, v:true) call remove(g:button_window, front_win) endfunction function! s:create_window(config, ...) abort let hi_group = a:1 let transparency = get(a:, '2', 0) let buf = nvim_create_buf(v:false, v:true) let win = nvim_open_win(buf, v:true, a:config) if hi_group != '' call nvim_win_set_option(win, 'winhighlight', hi_group) call nvim_win_set_option(win, 'winblend', transparency) call nvim_win_set_config(win, a:config) endif return win endfunction function! s:fire() abort let front_win = nvim_get_current_win() let conf = nvim_win_get_config(front_win) let row = conf.row + 1 let col = conf.col + conf.width let width = 2 let height = 1 let config = { 'relative': 'editor', 'row': row, 'col': col, 'width': width, 'height': height, 'anchor': 'NW', 'style': 'minimal', } let ballet = s:create_window(config, 'Normal:FrontColor') for i in range(1, 30) let config.col += 1 call nvim_win_set_config(ballet, config) redraw sleep 10ms endfor call nvim_win_close(ballet, v:true) endfunction function! s:push() abort let front_win = nvim_get_current_win() let back_win = g:button_window[front_win] let config = nvim_win_get_config(front_win) let config.row += 1 call nvim_win_set_config(front_win, config) sleep 100ms redraw let config.row -= 1 call nvim_win_set_config(front_win, config) call s:fire() endfunction function! s:main() abort let row = 20 let col = 20 let width = 20 let height = 3 let config = { 'relative': 'editor', 'row': row, 'col': col, 'width': width, 'height': height, 'anchor': 'NW', 'style': 'minimal' } " 影の部分 let back_config = deepcopy(config) let back_config.row += 1 let back_win = s:create_window(back_config, 'Normal:BackColor') " 前面の部分 let front_win = s:create_window(config, 'Normal:FrontColor') call setline(2, s:center('Button')) call cursor(2, 0) nnoremap <buffer><nowait><silent> l :call <SID>move('x', 2)<CR> nnoremap <buffer><nowait><silent> h :call <SID>move('x', -2)<CR> nnoremap <buffer><nowait><silent> j :call <SID>move('y', 2)<CR> nnoremap <buffer><nowait><silent> k :call <SID>move('y', -2)<CR> nnoremap <buffer><nowait><silent> :q :call <SID>remove_button() nnoremap <CR> :call <SID>push()<CR> " 影と前面のウィンドウを紐付け let g:button_window[front_win] = back_win endfunction call s:main()
終わり
いまのところ全く実用性はないけど、どこかで使うときが来ると思う。たぶん。