"====================================================================== " " 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