DEV Community

Cover image for Make Button By Floating Window
Rasukarusan
Rasukarusan

Posted on

Make Button By Floating Window

Let's make a button by floating window!

I made a button using floating window. You can push the button by ENTER.

How do I make it look like a button?

screenshot_ 2021-02-14 20.56.45

  • Prepare two windows, front and back.
  • Make the color of the back a little darker than the color of the front.
  • Lower the position of the front so that it covers the back when it is pressed.

When ENTER is pressed, just move it back down and it will appear to be pressed.

Code

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' }

  " back window
  let back_config = deepcopy(config)
  let back_config.row += 1
  let back_win = s:create_window(back_config, 'Normal:BackColor')

  " front window
  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 fullscreen mode Exit fullscreen mode

Bonus: Fire bullets with ENTER

And you can move by 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' }

  " back window
  let back_config = deepcopy(config)
  let back_config.row += 1
  let back_win = s:create_window(back_config, 'Normal:BackColor')

  " front window
  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()
Enter fullscreen mode Exit fullscreen mode

Have a happy vim life!

Top comments (0)