2024-02-19 17:02:19 +01:00

758 lines
20 KiB
VimL

"======================================================================
"
" window.vim -
"
" Created by skywind on 2021/12/08
" Last Modified: 2021/12/08 23:45
"
"======================================================================
" vim: set ts=4 sw=4 tw=78 noet :
"----------------------------------------------------------------------
" window class
"----------------------------------------------------------------------
let s:window = {}
let s:window.w = 1 " window width
let s:window.h = 1 " window height
let s:window.x = 1 " column starting from 0
let s:window.y = 1 " row starting from 0
let s:window.z = 40 " priority
let s:window.winid = -1 " window id
let s:window.dirty = 0 " need update buffer ?
let s:window.text = [] " text lines
let s:window.bid = -1 " allocated buffer id
let s:window.hide = 0 " visibility
let s:window.mode = 0 " mode: 0/created, 1/closed
let s:window.opts = {} " creation options
let s:window.info = {} " init environment
let s:window.quit = 0 " closed by button ? (vim only)
"----------------------------------------------------------------------
" internal
"----------------------------------------------------------------------
let s:has_nvim = g:quickui#core#has_nvim
let s:has_nvim_060 = g:quickui#core#has_nvim_060
"----------------------------------------------------------------------
" prepare opts
"----------------------------------------------------------------------
function! s:window.__prepare_opts(textlist, opts)
let opts = deepcopy(a:opts)
let opts.x = get(a:opts, 'x', 1)
let opts.y = get(a:opts, 'y', 1)
let opts.z = get(a:opts, 'z', 40)
let opts.w = get(a:opts, 'w', 1)
let opts.h = get(a:opts, 'h', -1)
let opts.hide = get(a:opts, 'hide', 0)
let opts.wrap = get(a:opts, 'wrap', 0)
let opts.color = get(a:opts, 'color', 'QuickBG')
let opts.border = get(a:opts, 'border', 0)
let self.opts = opts
let self.bid = quickui#core#buffer_alloc()
let self.dirty = 1
let self.x = opts.x
let self.y = opts.y
let self.z = opts.z
let self.w = (opts.w < 1)? 1 : (opts.w)
let self.h = (opts.h < 1)? 1 : (opts.h)
let self.hide = opts.hide
let self.mode = 0
if has_key(a:opts, 'padding')
let self.opts.padding = a:opts.padding
else
let self.opts.padding = [0,0,0,0]
endif
let pad = self.opts.padding
let info = self.info
let info.tw = self.w + pad[1] + pad[3]
let info.th = self.h + pad[0] + pad[2]
let sum_pad = pad[0] + pad[1] + pad[2] + pad[3]
let info.has_padding = (sum_pad > 0)? 1 : 0
let border = quickui#core#border_auto(self.opts.border)
let info.has_border = (self.opts.border > 0)? 1 : 0
if info.has_border != 0
let info.tw += 2
let info.th += 2
endif
" echom info
call self.set_text(a:textlist)
if opts.h < 0
let opts.h = len(self.text)
endif
let cmd = []
if has_key(opts, 'tabstop')
let cmd += ['setl tabstop=' . get(opts, 'tabstop', 4)]
endif
if has_key(opts, 'list')
let cmd += [(opts.list)? 'setl list' : 'setl nolist']
else
let cmd += ['setl nolist']
endif
if get(opts, 'number', 0) != 0
let cmd += ['setl number']
else
let cmd += ['setl nonumber']
endif
let cmd += ['setl scrolloff=0']
let cmd += ['setl signcolumn=no']
if has_key(opts, 'syntax')
let cmd += ['set ft=' . fnameescape(opts.syntax)]
endif
if has_key(opts, 'cursorline')
let need = (opts.cursorline)? 'cursorline' : 'nocursorlin'
let cmd += ['setl ' . need]
if exists('+cursorlineopt')
let cmd += ['setl cursorlineopt=both']
endif
else
let cmd += ['setl nocursorline']
endif
let cmd += ['setl nocursorcolumn nospell']
let cmd += [opts.wrap? 'setl wrap' : 'setl nowrap']
if has_key(opts, 'command')
let command = opts.command
if type(command) == type([])
let cmd += command
else
let cmd += [''. command]
endif
endif
let info.cmd = cmd
let info.pending_cmd = []
let info.border_winid = -1
let info.border_bid = -1
endfunc
"----------------------------------------------------------------------
" win filter
"----------------------------------------------------------------------
function! s:popup_filter(winid, key)
let local = quickui#core#popup_local(a:winid)
let hwnd = local.window_hwnd
endfunc
"----------------------------------------------------------------------
" exited
"----------------------------------------------------------------------
function! s:popup_exit(winid, code)
let local = quickui#core#popup_local(a:winid)
let hwnd = local.window_hwnd
call quickui#core#popup_clear(a:winid)
let hwnd.quit = 1
let hwnd.winid = -1
endfunc
"----------------------------------------------------------------------
" create window in vim
"----------------------------------------------------------------------
function! s:window.__vim_create()
let opts = {"hidden":1, "pos": 'topleft'}
let opts.hidden = 1
let opts.wrap = self.opts.wrap
let opts.minwidth = self.w
let opts.maxwidth = self.w
let opts.minheight = self.h
let opts.maxheight = self.h
let opts.col = self.x + 1
let opts.line = self.y + 1
let opts.mapping = 0
let opts.fixed = (opts.wrap == 0)? 1 : 0
let opts.cursorline = get(self.opts, 'cursorline', 0)
let opts.drag = get(self.opts, 'drag', 0)
let opts.scrollbar = 0
let opts.zindex = self.z + 1
if get(self.opts, 'button', 0) != 0
let opts.close = 'button'
endif
let self.winid = popup_create(self.bid, opts)
let winid = self.winid
let local = quickui#core#popup_local(winid)
let local.window_hwnd = self
let init = []
let init += ['setlocal nonumber signcolumn=no scrolloff=0']
call quickui#core#win_execute(winid, init, 1)
let opts = {}
let opts.filter = function('s:popup_filter')
let opts.callback = function('s:popup_exit')
let opts.highlight = self.opts.color
let border = quickui#core#border_auto(self.opts.border)
if self.info.has_border
let opts.borderchars = border
let opts.border = [1,1,1,1,1,1,1,1,1]
let bc = get(self.opts, 'bordercolor', 'QuickBorder')
let opts.borderhighlight = [bc, bc, bc, bc]
if has_key(self.opts, 'title')
let opts.title = self.opts.title
endif
endif
if has_key(self.opts, 'padding')
let opts.padding = self.opts.padding
endif
call setwinvar(winid, '&wincolor', self.opts.color)
call popup_setoptions(winid, opts)
call quickui#core#win_execute(winid, self.info.cmd)
let pc = self.info.pending_cmd
if len(pc) > 0
call quickui#core#win_execute(winid, pc)
let self.info.pending_cmd = []
endif
let self.mode = 1
if get(self.opts, 'center', 0) != 0
call self.center()
endif
if self.hide == 0
call popup_show(winid)
endif
endfunc
"----------------------------------------------------------------------
" create window in nvim
"----------------------------------------------------------------------
function! s:window.__nvim_create()
let opts = {'focusable':0, 'style':'minimal', 'relative':'editor'}
let opts.row = self.y
let opts.col = self.x
let opts.width = self.w
let opts.height = self.h
let opts.focusable = get(self.opts, 'focusable', 0)
if s:has_nvim_060
let opts.noautocmd = 1
let opts.zindex = self.z + 1
endif
let info = self.info
let info.nvim_opts = opts
let info.sim_border = 0
let info.off_x = 0
let info.off_y = 0
let pad = self.opts.padding
if info.has_border
let info.sim_border = 1
let info.off_x = 1
let info.off_y = 1
if info.has_padding
let info.sim_border = 1
let info.off_x += pad[3]
let info.off_y += pad[0]
endif
endif
if info.has_border
let tw = info.tw
let th = info.th
let opts.col += info.off_x
let opts.row += info.off_y
let t = get(self.opts, 'title', '')
let b = get(self.opts, 'button', 0)
let border = self.opts.border
let back = quickui#utils#make_border(tw - 2, th - 2, border, t, b)
let info.border_bid = quickui#core#buffer_alloc()
call quickui#core#buffer_update(info.border_bid, back)
let op = {'relative':'editor', 'focusable':0, 'style':'minimal'}
let op.focusable = get(self.opts, 'focusable', 0)
let op.width = tw
let op.height = th
let op.col = self.x
let op.row = self.y
if s:has_nvim_060
let op.noautocmd = 1
let op.zindex = self.z
endif
let info.border_opts = op
let init = []
let init += ['setl tabstop=' . get(self.opts, 'tabstop', 4)]
let init += ['setl signcolumn=no scrolloff=0 nowrap nonumber']
let init += ['setl nocursorline nolist']
if exists('+cursorlineopt')
let init += ['setl cursorlineopt=both']
endif
let info.border_init = init
endif
let self.mode = 1
if get(self.opts, 'center', 0) != 0
call self.center()
endif
if self.hide == 0
call self.__nvim_show()
endif
endfunc
"----------------------------------------------------------------------
" nvim - show window
"----------------------------------------------------------------------
function! s:window.__nvim_show()
if self.mode == 0
return
elseif self.winid >= 0
return
endif
call self.move(self.x, self.y)
let info = self.info
let winid = nvim_open_win(self.bid, 0, info.nvim_opts)
let self.winid = winid
let color = self.opts.color
call quickui#core#win_execute(winid, info.cmd)
if len(info.pending_cmd) > 0
call quickui#core#win_execute(winid, info.pending_cmd)
let info.pending_cmd = []
endif
call nvim_win_set_option(self.winid, 'winhl', 'Normal:'. color)
if info.has_border
let bwid = nvim_open_win(info.border_bid, 0, info.border_opts)
let info.border_winid = bwid
call quickui#core#win_execute(bwid, info.border_init)
call nvim_win_set_option(bwid, 'winhl', 'Normal:'. color)
endif
let self.hide = 0
endfunc
"----------------------------------------------------------------------
" nvim - hide window
"----------------------------------------------------------------------
function! s:window.__nvim_hide()
if self.mode == 0
return
elseif self.winid < 0
return
endif
let info = self.info
if info.border_winid >= 0
call nvim_win_close(info.border_winid, 1)
let info.border_winid = -1
endif
if self.winid >= 0
call nvim_win_close(self.winid, 1)
let self.winid = -1
endif
let self.hide = 1
endfunc
"----------------------------------------------------------------------
" open window
"----------------------------------------------------------------------
function! s:window.open(textlist, opts)
call self.close()
call self.__prepare_opts(a:textlist, a:opts)
if s:has_nvim == 0
call self.__vim_create()
else
call self.__nvim_create()
endif
let self.mode = 1
endfunc
"----------------------------------------------------------------------
" close window
"----------------------------------------------------------------------
function! s:window.close()
if self.winid >= 0
if s:has_nvim == 0
call popup_close(self.winid)
else
call nvim_win_close(self.winid, 1)
if self.info.border_winid >= 0
call nvim_win_close(self.info.border_winid, 1)
let self.info.border_winid = -1
endif
endif
endif
let self.winid = -1
if self.bid >= 0
call quickui#core#buffer_free(self.bid)
let self.bid = -1
endif
if has_key(self.info, 'border_bid')
if self.info.border_bid >= 0
call quickui#core#buffer_free(self.info.border_bid)
let self.info.border_bid = -1
endif
endif
let self.hide = 0
let self.mode = 0
endfunc
"----------------------------------------------------------------------
" show the window
"----------------------------------------------------------------------
function! s:window.show(show)
if self.mode == 0
return
elseif s:has_nvim == 0
if a:show == 0
if self.winid >= 0
call popup_hide(self.winid)
endif
else
if self.winid >= 0
call popup_show(self.winid)
endif
endif
else
if a:show == 0
call self.__nvim_hide()
else
call self.__nvim_show()
endif
endif
let self.hide = (a:show == 0)? 1 : 0
endfunc
"----------------------------------------------------------------------
" move window
"----------------------------------------------------------------------
function! s:window.__move(x, y)
let self.x = a:x
let self.y = a:y
if self.mode == 0
return
elseif s:has_nvim == 0
if self.winid >= 0
let opts = {}
let opts.col = self.x + 1
let opts.line = self.y + 1
call popup_move(self.winid, opts)
endif
else
let info = self.info
let opts = info.nvim_opts
let opts.col = self.x + info.off_x
let opts.row = self.y + info.off_y
if info.has_border != 0
let opts = info.border_opts
let opts.col = self.x
let opts.row = self.y
endif
if self.winid >= 0
let op = {'relative':'editor'}
let op.col = info.nvim_opts.col
let op.row = info.nvim_opts.row
call nvim_win_set_config(self.winid, op)
endif
if info.has_border != 0
if info.border_winid >= 0
let op = {'relative':'editor'}
let op.col = info.border_opts.col
let op.row = info.border_opts.row
call nvim_win_set_config(info.border_winid, op)
endif
endif
endif
endfunc
"----------------------------------------------------------------------
" actual move
"----------------------------------------------------------------------
function! s:window.move(x, y)
let x = a:x
let y = a:y
let w = self.info.tw
let h = self.info.th
let sw = &columns
let sh = &lines
let x = (x + w > sw)? (sw - w) : x
let y = (y + h > sh)? (sh - h) : y
let x = (x < 0)? 0 : x
let y = (y < 0)? 0 : y
" unsilent echom ['move', x, a:x, self.opts.x]
call self.__move(x, y)
endfunc
"----------------------------------------------------------------------
" center window
"----------------------------------------------------------------------
function! s:window.center(...)
let w = self.w
let h = self.h
let style = (a:0 < 1)? 0 : (a:1)
if self.mode != 0
let w = self.info.tw
let h = self.info.th
endif
let x = (&columns - w) / 2
if style == 0
let height = &lines - &cmdheight - 1
let middle = height * 38 / 100
let y = middle - (h + 1) / 2
let y = (y < 0)? 0 : y
else
let y = (&lines - h) / 2
let limit1 = (&lines - 2) * 80 / 100
let limit2 = (&lines - 2)
if h + 8 < limit1
let y = (limit1 - h) / 2
else
let y = (limit2 - h) / 2
endif
endif
call self.move(x, y)
endfunc
"----------------------------------------------------------------------
" resize
"----------------------------------------------------------------------
function! s:window.resize(w, h)
let self.w = a:w
let self.h = a:h
let info = self.info
if self.mode == 0
let info.tw = self.w
let info.th = self.h
return
endif
let pad = self.opts.padding
let info.tw = self.w + pad[1] + pad[3]
let info.th = self.h + pad[0] + pad[2]
let info.tw += (info.has_border? 2 : 0)
let info.th += (info.has_border? 2 : 0)
if self.winid < 0
return
endif
if s:has_nvim == 0
let opts = {}
let opts.minwidth = self.w
let opts.maxwidth = self.w
let opts.minheight = self.h
let opts.maxheight = self.h
call popup_move(self.winid, opts)
else
let opts = info.nvim_opts
let opts.width = self.w
let opts.height = self.h
if info.has_border
let opts = info.border_opts
let opts.width = info.tw
let opts.height = info.th
let t = get(self.opts, 'title', '')
let b = self.opts.border
let tw = info.tw
let th = info.th
let btn = get(self.opts, 'button', 0)
let back = quickui#utils#make_border(tw - 2, th - 2, b, t, btn)
call quickui#core#buffer_update(info.border_bid, back)
endif
if self.winid >= 0
let op = {'width':self.w, 'height':self.h}
call nvim_win_set_config(self.winid, op)
if info.has_border
if info.border_winid >= 0
let op = {'width':info.tw, 'height':info.th}
call nvim_win_set_config(info.border_winid, op)
endif
endif
endif
endif
endfunc
"----------------------------------------------------------------------
" execute commands
"----------------------------------------------------------------------
function! s:window.execute(cmdlist)
if type(a:cmdlist) == v:t_string
let cmd = split(a:cmdlist, '\n')
else
let cmd = a:cmdlist
endif
let winid = self.winid
if winid >= 0
let pc = self.info.pending_cmd
if len(pc) > 0
call quickui#core#win_execute(winid, pc)
let self.info.pending_cmd = []
endif
if len(cmd) > 0
call quickui#core#win_execute(winid, cmd)
endif
else
if !has_key(self.info, 'pending_cmd')
let self.info.pending_cmd = cmd
else
let self.info.pending_cmd += cmd
endif
endif
endfunc
"----------------------------------------------------------------------
" update text in buffer
"----------------------------------------------------------------------
function! s:window.update()
if self.bid >= 0
call quickui#core#buffer_update(self.bid, self.text)
endif
endfunc
"----------------------------------------------------------------------
" set content
"----------------------------------------------------------------------
function! s:window.set_text(textlist)
if type(a:textlist) == v:t_list
let textlist = deepcopy(a:textlist)
else
let textlist = split(a:textlist, '\n', 1)
endif
let self.text = textlist
call self.update()
endfunc
"----------------------------------------------------------------------
" set line
"----------------------------------------------------------------------
function! s:window.set_line(index, text, ...)
let require = a:index + 1
let refresh = (a:0 < 1)? 1 : (a:1)
let index = a:index
let update = 0
if index < 0
return
elseif len(self.text) < require
let self.text += repeat([''], require - len(self.text))
let update = 1
endif
let self.text[a:index] = a:text
if update != 0
call self.update()
elseif refresh != 0
let bid = self.bid
if bid >= 0
call setbufvar(bid, '&modifiable', 1)
call setbufline(bid, index + 1, [a:text])
call setbufvar(bid, '&modified', 0)
endif
endif
endfunc
"----------------------------------------------------------------------
" get line
"----------------------------------------------------------------------
function! s:window.get_line(index)
if a:index >= len(self.text)
return ''
endif
return self.text[a:index]
endfunc
"----------------------------------------------------------------------
" syntax begin
"----------------------------------------------------------------------
function! s:window.syntax_begin(...)
let info = self.info
let info.syntax_cmd = ['syn clear']
let info.syntax_mod = (a:0 < 1)? 1 : (a:1)
endfunc
"----------------------------------------------------------------------
" flush commands
"----------------------------------------------------------------------
function! s:window.syntax_end()
let info = self.info
if has_key(info, 'syntax_cmd') != 0
if len(info.syntax_cmd) > 0
call self.execute(info.syntax_cmd)
let info.syntax_cmd = []
endif
endif
endfunc
"----------------------------------------------------------------------
" calculate region
"----------------------------------------------------------------------
function! s:window.syntax_region(color, x1, y1, x2, y2)
let info = self.info
if a:y1 == a:y2 && a:x1 >= a:x2
return
elseif has_key(info, 'syntax_cmd') != 0
let x1 = a:x1 + 1
let y1 = a:y1 + 1
let x2 = a:x2 + 1
let y2 = a:y2 + 1
let cc = a:color
let mm = info.syntax_mod
let cmd = quickui#core#high_region(cc, y1, x1, y2, x2, mm)
let info.syntax_cmd += [cmd]
" echom cmd
endif
endfunc
"----------------------------------------------------------------------
" click window
"----------------------------------------------------------------------
function! s:window.mouse_click()
let winid = self.winid
let retval = {'x':-1, 'y':-1}
if g:quickui#core#has_nvim == 0
let pos = getmousepos()
if pos.winid != winid
return retval
endif
if self.info.has_border == 0
let retval.x = pos.column - 1
let retval.y = pos.line - 1
else
let retval.x = pos.column - 2
let retval.y = pos.line - 2
endif
else
if v:mouse_winid != winid
return retval
endif
if self.info.has_border == 0
let retval.x = v:mouse_col - 1
let retval.y = v:mouse_lnum - 1
else
let retval.x = v:mouse_col - 2
let retval.y = v:mouse_lnum - 2
endif
endif
return retval
endfunc
"----------------------------------------------------------------------
" refresh redraw
"----------------------------------------------------------------------
function! s:window.refresh()
let winid = self.winid
if g:quickui#core#has_nvim == 0
if winid >= 0
call popup_setoptions(winid, {})
endif
else
endif
redraw
endfunc
"----------------------------------------------------------------------
" constructor
"----------------------------------------------------------------------
function! quickui#window#new()
let obj = deepcopy(s:window)
return obj
endfunc