├── .gitignore ├── LICENSE ├── README.md ├── autoload ├── rabbit_ui.vim └── rabbit_ui │ ├── components │ ├── choices.vim │ ├── gridview.vim │ ├── messagebox.vim │ └── panel.vim │ ├── helper.vim │ └── keymap.vim ├── choices.png ├── doc └── rabbit_ui.txt ├── gridview.png ├── messagebox.png └── panel.png /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 rbtnn 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | vim-rabbit-ui 3 | ============= 4 | 5 | This is Rich UI Vim script Library. 6 | Please see `./doc/rabbit_ui.txt`. 7 | 8 | 9 | 1. MessageBox 10 | ------------- 11 | 12 | ![](https://raw.github.com/rbtnn/rabbit-ui.vim/master/messagebox.png) 13 | 14 | 15 | 2. Choices 16 | ---------- 17 | 18 | ![](https://raw.github.com/rbtnn/rabbit-ui.vim/master/choices.png) 19 | 20 | 21 | 3. Panel 22 | -------- 23 | 24 | ![](https://raw.github.com/rbtnn/rabbit-ui.vim/master/panel.png) 25 | 26 | 27 | 4. GridView 28 | ----------- 29 | 30 | ![](https://raw.github.com/rbtnn/rabbit-ui.vim/master/gridview.png) 31 | 32 | 33 | -------------------------------------------------------------------------------- /autoload/rabbit_ui.vim: -------------------------------------------------------------------------------- 1 | 2 | scriptencoding utf-8 3 | 4 | function! s:getSID() 5 | return matchstr(expand(''), '\d\+_\zegetSID$') 6 | endfunction 7 | let s:SID = s:getSID() 8 | 9 | let s:rabbit_ui = { 10 | \ 'status' : 'redraw', 11 | \ 'active_window_id' : '', 12 | \ 'context_list' : {}, 13 | \ } 14 | 15 | function! s:rabbit_ui.add(component_name, arguments, ...) 16 | let config = ( 0 < a:0 ) ? (type(a:1) is type({}) ? a:1 : {}) : {} 17 | let windowstatus = ( 1 < a:0 ) ? (type(a:2) is type({}) ? a:2 : {}) : {} 18 | let config['keymap'] = get(config, 'keymap', {}) 19 | let default_keymap = rabbit_ui#components#{a:component_name}#get_default_keymap() 20 | call extend(config['keymap'], default_keymap, 'keep') 21 | let context = { 22 | \ 'component_name' : a:component_name, 23 | \ 'arguments' : a:arguments, 24 | \ 'config' : config, 25 | \ 'windowstatus' : windowstatus, 26 | \ } 27 | call rabbit_ui#components#{a:component_name}#init(context) 28 | let id = string('x' . len(self['context_list'])) 29 | let self['context_list'][id] = context 30 | if empty(self['active_window_id']) 31 | let self['active_window_id'] = id 32 | endif 33 | return id 34 | endfunction 35 | function! s:rabbit_ui.remove(id) 36 | call rabbit_ui#helper#clear_matches(self.get_context(a:id)) 37 | call remove(self['context_list'], a:id) 38 | endfunction 39 | function! s:rabbit_ui.get_context(id) 40 | return get(self['context_list'], a:id, {}) 41 | endfunction 42 | function! s:rabbit_ui.get_active_context() 43 | return self.get_context(self['active_window_id']) 44 | endfunction 45 | 46 | function! s:rabbit_ui.enter() 47 | let self['status'] = 'break' 48 | endfunction 49 | function! s:rabbit_ui.quit_window() 50 | let prev = self['active_window_id'] 51 | call self.focus_next_window() 52 | call self.remove(prev) 53 | " call self.layout1() 54 | let self['status'] = 'continue' 55 | endfunction 56 | function! s:rabbit_ui.focus_next_window() 57 | let xs = keys(self['context_list']) 58 | let idx = index(xs, self['active_window_id']) 59 | if -1 is idx 60 | let self['active_window_id'] = get(xs, 0, '') 61 | else 62 | for i in range(1, len(xs)) 63 | let x = xs[(idx + i) % len(xs)] 64 | if ! rabbit_ui#helper#windowstatus(self['context_list'][x], 'nonactivate') 65 | let self['active_window_id'] = x 66 | break 67 | endif 68 | endfor 69 | endif 70 | endfunction 71 | 72 | function! s:rabbit_ui.enumelator() 73 | let xs = [] 74 | for x in sort(keys(self['context_list'])) 75 | let xs += [ self['context_list'][x] ] 76 | endfor 77 | return xs 78 | endfunction 79 | function! s:rabbit_ui.layout1() 80 | let xs = self.enumelator() 81 | let size = len(xs) 82 | let width = { 'start' : &columns * 1 / 4, 'last' : &columns * 3 / 4 } 83 | let height = { 'start' : &lines * 1 / 4, 'last' : &lines * 3 / 4 } 84 | 85 | let splited_col_size = 1 86 | while splited_col_size * splited_col_size < size 87 | let splited_col_size += 1 88 | endwhile 89 | 90 | let splited_row_size = 1 91 | while splited_row_size * splited_col_size < size 92 | let splited_row_size += 1 93 | endwhile 94 | 95 | for row in range(0, splited_row_size - 1) 96 | for col in range(0, splited_col_size - 1) 97 | let index = row * splited_col_size + col 98 | if index < size 99 | let context = xs[index] 100 | let config = context['config'] 101 | 102 | let box_height = (height.last - height.start) / splited_row_size 103 | let config['box_top'] = height.start + row * (box_height + 1) 104 | let config['box_bottom'] = config['box_top'] + (box_height - 1) 105 | 106 | let box_width = (width.last - width.start) / splited_col_size 107 | let config['box_left'] = width.start + col * (box_width + 1) 108 | let config['box_right'] = config['box_left'] + (box_width - 1) 109 | 110 | call rabbit_ui#components#{context['component_name']}#init(context) 111 | endif 112 | endfor 113 | endfor 114 | endfunction 115 | function! s:rabbit_ui.exec() 116 | let saved_laststatus = &laststatus 117 | let saved_statusline = &statusline 118 | let saved_hlsearch = &hlsearch 119 | let saved_currtabindex = tabpagenr() 120 | let saved_titlestring = &titlestring 121 | let rtn_value = '' 122 | try 123 | let background_lines = [] 124 | for line in repeat([''], 100) 125 | " getline(line('w0'), line('w0') + &lines) + repeat([''], &lines) 126 | let background_lines += [ 127 | \ join(map(split(line,'\zs'), 'strdisplaywidth(v:val) isnot 1 ? ".." : v:val'), '') 128 | \ ] 129 | endfor 130 | 131 | tabnew 132 | normal gg 133 | 134 | setlocal nowrap 135 | setlocal nolist 136 | setlocal nospell 137 | setlocal nonumber 138 | setlocal norelativenumber 139 | setlocal nohlsearch 140 | setlocal laststatus=2 141 | setlocal buftype=nofile nobuflisted noswapfile bufhidden=hide 142 | let &l:statusline = ' ' 143 | let &l:filetype = rabbit_ui#helper#id() 144 | let &l:titlestring = printf('[%s]', rabbit_ui#helper#id()) 145 | 146 | redraw 147 | 148 | if exists(':VimConsoleOpen') 149 | " VimConsoleOpen 150 | endif 151 | 152 | let c_nr = '' 153 | while 1 154 | if len(filter(deepcopy(self.enumelator()), '! rabbit_ui#helper#windowstatus(v:val, "nonactivate")')) is 0 155 | break 156 | endif 157 | 158 | " key event 159 | let active_context = self.get_active_context() 160 | if has_key(active_context['config']['keymap'], c_nr) 161 | call call(active_context['config']['keymap'][c_nr], [self]) 162 | endif 163 | let status = self['status'] 164 | if status is 'break' 165 | break 166 | elseif status is 'continue' 167 | let c_nr = '' 168 | let self['status'] = 'redraw' 169 | continue 170 | endif 171 | 172 | " clear highlight 173 | for context in self.enumelator() 174 | call rabbit_ui#helper#clear_matches(context) 175 | endfor 176 | 177 | for context in self.enumelator() 178 | let context['windowstatus']['focused'] = 0 179 | endfor 180 | let active_context = self.get_active_context() 181 | let active_context['windowstatus']['focused'] = 1 182 | 183 | 184 | " redraw 185 | let lines = deepcopy(background_lines) 186 | 187 | for context in self.enumelator() 188 | call rabbit_ui#components#{context['component_name']}#redraw(lines, context) 189 | endfor 190 | 191 | % delete _ 192 | silent! put=lines 193 | 1 delete _ 194 | 195 | redraw 196 | 197 | " input 198 | let c_nr = getchar() 199 | endwhile 200 | finally 201 | " clear highlight 202 | for context in self.enumelator() 203 | call rabbit_ui#helper#clear_matches(context) 204 | endfor 205 | " 206 | tabclose 207 | let &l:laststatus = saved_laststatus 208 | let &l:statusline = saved_statusline 209 | let &l:hlsearch = saved_hlsearch 210 | let &l:titlestring = saved_titlestring 211 | execute 'tabnext' . saved_currtabindex 212 | redraw 213 | endtry 214 | 215 | return self.enumelator() 216 | endfunction 217 | 218 | function! rabbit_ui#new() 219 | return deepcopy(s:rabbit_ui) 220 | endfunction 221 | 222 | function! rabbit_ui#messagebox(title, text, ...) 223 | let config = ( 0 < a:0 ) ? (type(a:1) is type({}) ? a:1 : {}) : {} 224 | let r = rabbit_ui#new() 225 | let id = r.add('messagebox', [(a:title), (a:text)], config) 226 | let context_list = r.exec() 227 | if empty(context_list) 228 | return {} 229 | else 230 | return { 'value' : [] } 231 | endif 232 | endfunction 233 | function! rabbit_ui#choices(title, items, ...) 234 | let config = ( 0 < a:0 ) ? (type(a:1) is type({}) ? a:1 : {}) : {} 235 | let r = rabbit_ui#new() 236 | let id = r.add('choices', [(a:title), (a:items)], config) 237 | let context_list = r.exec() 238 | if empty(context_list) 239 | return {} 240 | else 241 | return { 'value' : context_list[0]['config']['index'] } 242 | endif 243 | endfunction 244 | function! rabbit_ui#panel(title_and_items_list, ...) 245 | let config = ( 0 < a:0 ) ? (type(a:1) is type({}) ? a:1 : {}) : {} 246 | let r = rabbit_ui#new() 247 | let id = r.add('panel', [(a:title_and_items_list)], config) 248 | let context_list = r.exec() 249 | if empty(context_list) 250 | return {} 251 | else 252 | let item_index = context_list[0]['config']['item_index'] 253 | let text_items = context_list[0]['config']['text_items'] 254 | let xs = [] 255 | for key in sort(keys(item_index)) 256 | let xs += [[item_index[key], text_items[key]]] 257 | endfor 258 | return { 'value' : xs } 259 | endif 260 | endfunction 261 | function! rabbit_ui#gridview(data, ...) 262 | let config = ( 0 < a:0 ) ? (type(a:1) is type({}) ? a:1 : {}) : {} 263 | let r = rabbit_ui#new() 264 | let id = r.add('gridview', [(a:data)], config) 265 | let context_list = r.exec() 266 | if empty(context_list) 267 | return {} 268 | else 269 | return { 'value' : context_list[0]['config']['data'] } 270 | endif 271 | endfunction 272 | 273 | function! rabbit_ui#exec_components(context_list) 274 | let r = rabbit_ui#new() 275 | for context in a:context_list 276 | let id = r.add(context['component_name'], context['arguments']) 277 | endfor 278 | return a:context_list 279 | endfunction 280 | 281 | function! s:hoge() 282 | let r = rabbit_ui#new() 283 | let id = r.add('choices', ['hi',[1,2,3]]) 284 | let id = r.add('gridview', [[[1,2,3]]]) 285 | " call r.remove(id) 286 | let id = r.add('messagebox', ['hi','hoge'], {}, { 'nonactivate' : 1 }) 287 | let id = r.add('messagebox', ['hi','hoge'], {}, { 'nonactivate' : 0 }) 288 | call r.layout1() 289 | call r.exec() 290 | endfunction 291 | " call s:hoge() 292 | 293 | " echo rabbit_ui#messagebox('','') 294 | " echo rabbit_ui#choices('',[0,1,2]) 295 | " echo rabbit_ui#gridview([[0,1,2]]) 296 | 297 | -------------------------------------------------------------------------------- /autoload/rabbit_ui/components/choices.vim: -------------------------------------------------------------------------------- 1 | 2 | function! s:getSID() 3 | return matchstr(expand(''), '\d\+_\zegetSID$') 4 | endfunction 5 | let s:SID = s:getSID() 6 | 7 | 8 | function! rabbit_ui#components#choices#init(context) 9 | let context = a:context 10 | call rabbit_ui#helper#set_common_configs(context['config']) 11 | 12 | let context['config']['index'] = 0 13 | let context['config']['display_offset'] = 0 14 | let context['config']['display_start'] = 0 15 | let context['config']['display_last'] = context['config']['box_bottom'] - context['config']['box_top'] - 1 16 | let context['config']['title'] = rabbit_ui#helper#smart_split(context['arguments'][0], context['config']['box_width'])[0] 17 | let context['config']['text_items'] = map(deepcopy(context['arguments'][1]), 'rabbit_ui#helper#smart_split(v:val, context["config"]["box_width"])[0]') 18 | endfunction 19 | function! rabbit_ui#components#choices#redraw(lines, context) 20 | let config = a:context['config'] 21 | let focused = rabbit_ui#helper#windowstatus(a:context, 'focused') 22 | let nonactivate = rabbit_ui#helper#windowstatus(a:context, 'nonactivate') 23 | 24 | let box_left = config['box_left'] 25 | let box_right = config['box_right'] 26 | let box_top = config['box_top'] 27 | let box_bottom = config['box_bottom'] 28 | let box_width = config['box_width'] 29 | let index = config['index'] 30 | let display_offset = config['display_offset'] 31 | let title = config['title'] 32 | let text_items = config['text_items'][(display_offset):(display_offset + config['box_height'])] 33 | 34 | for line_num in range(box_top + 1, box_bottom + 1) 35 | let text = get((nonactivate ? [] : [title]) + text_items, (line_num - (box_top + 1)), repeat(' ', box_width)) 36 | 37 | call rabbit_ui#helper#redraw_line(a:lines, line_num, box_left, text) 38 | 39 | let len = len(substitute(text, ".", "x", "g")) 40 | 41 | if line_num is (box_top + 1) && !nonactivate 42 | if focused 43 | call rabbit_ui#helper#set_highlight('rabbituiTitleLineActive', config, line_num, (box_left + 1), len) 44 | else 45 | call rabbit_ui#helper#set_highlight('rabbituiTitleLineNoActive', config, line_num, (box_left + 1), len) 46 | endif 47 | elseif line_num is (box_top + (nonactivate ? 0 : 1)) + 1 + index - display_offset 48 | call rabbit_ui#helper#set_highlight('rabbituiSelectedItemActive', config, line_num, (box_left + 1), len) 49 | else 50 | if line_num % 2 is 0 51 | call rabbit_ui#helper#set_highlight('rabbituiTextLinesEven', config, line_num, (box_left + 1), len) 52 | else 53 | call rabbit_ui#helper#set_highlight('rabbituiTextLinesOdd', config, line_num, (box_left + 1), len) 54 | endif 55 | endif 56 | endfor 57 | 58 | return index 59 | endfunction 60 | 61 | function! s:keyevent_cursor_to_first_item(...) 62 | let keyevent_arg1 = a:1 63 | let context_list = keyevent_arg1['context_list'] 64 | let active_window_id = keyevent_arg1['active_window_id'] 65 | let config = context_list[active_window_id]['config'] 66 | 67 | let config['index'] = 0 68 | let config['display_offset'] = 0 69 | endfunction 70 | function! s:keyevent_cursor_to_last_item(...) 71 | let keyevent_arg1 = a:1 72 | let context_list = keyevent_arg1['context_list'] 73 | let active_window_id = keyevent_arg1['active_window_id'] 74 | let config = context_list[active_window_id]['config'] 75 | 76 | let box_height = config['box_height'] 77 | let item_size = len(config['text_items']) 78 | let config['index'] = item_size - 1 79 | let config['display_offset'] = ( 80 | \ item_size - 1 < box_height - 1 81 | \ ? 0 82 | \ : item_size - box_height + 1 83 | \ ) 84 | endfunction 85 | function! s:keyevent_cursor_up(...) 86 | let keyevent_arg1 = a:1 87 | let context_list = keyevent_arg1['context_list'] 88 | let active_window_id = keyevent_arg1['active_window_id'] 89 | let config = context_list[active_window_id]['config'] 90 | 91 | if 0 <= config['index'] - 1 92 | let config['index'] -= 1 93 | endif 94 | if config['index'] - config['display_offset'] < config['display_start'] 95 | let config['display_offset'] = config['index'] - config['display_start'] 96 | endif 97 | endfunction 98 | function! s:keyevent_cursor_down(...) 99 | let keyevent_arg1 = a:1 100 | let context_list = keyevent_arg1['context_list'] 101 | let active_window_id = keyevent_arg1['active_window_id'] 102 | let config = context_list[active_window_id]['config'] 103 | 104 | if config['index'] + 1 <= len(config['text_items']) - 1 105 | let config['index'] += 1 106 | endif 107 | if config['display_last'] < config['index'] - config['display_offset'] 108 | let config['display_offset'] = config['index'] - config['display_last'] 109 | endif 110 | endfunction 111 | 112 | function! rabbit_ui#components#choices#get_keymap() 113 | return { 114 | \ 'cursor_to_first_item' : function(s:SID . 'keyevent_cursor_to_first_item'), 115 | \ 'cursor_to_last_item' : function(s:SID . 'keyevent_cursor_to_last_item'), 116 | \ 'cursor_up' : function(s:SID . 'keyevent_cursor_up'), 117 | \ 'cursor_down' : function(s:SID . 'keyevent_cursor_down'), 118 | \ } 119 | endfunction 120 | function! rabbit_ui#components#choices#get_default_keymap() 121 | let keymap = rabbit_ui#keymap#get() 122 | return { 123 | \ char2nr('q') : keymap['common']['quit_window'], 124 | \ char2nr("\") : keymap['common']['enter'], 125 | \ char2nr(' ') : keymap['common']['focus_next_window'], 126 | \ char2nr('j') : keymap['choices']['cursor_down'], 127 | \ char2nr('k') : keymap['choices']['cursor_up'], 128 | \ char2nr('g') : keymap['choices']['cursor_to_first_item'], 129 | \ char2nr('G') : keymap['choices']['cursor_to_last_item'], 130 | \ } 131 | endfunction 132 | 133 | -------------------------------------------------------------------------------- /autoload/rabbit_ui/components/gridview.vim: -------------------------------------------------------------------------------- 1 | 2 | function! s:getSID() 3 | return matchstr(expand(''), '\d\+_\zegetSID$') 4 | endfunction 5 | let s:SID = s:getSID() 6 | 7 | 8 | function! rabbit_ui#components#gridview#init(context) 9 | let context = a:context 10 | call rabbit_ui#helper#set_common_configs(context['config']) 11 | 12 | let context['config']['display_row_size'] = context['config']['box_bottom'] - context['config']['box_top'] + 1 13 | let context['config']['display_col_size'] = get(context['config'], 'display_col_size', 5) 14 | 15 | if context['config']['display_col_size'] < 2 16 | call rabbit_ui#helper#exception('gridview: display_col_size isnot greater than 1') 17 | endif 18 | 19 | if !has_key(context['config'], 'percentage_of_width') 20 | let percentage_of_width = 21 | \ map(repeat([1], context['config']['display_col_size']), "v:val * 100 / context['config']['display_col_size']") 22 | else 23 | let percentage_of_width = context['config']['percentage_of_width'] 24 | endif 25 | 26 | if len(percentage_of_width) isnot context['config']['display_col_size'] 27 | call rabbit_ui#helper#exception('gridview: length of percentage_of_width isnot display_col_size') 28 | endif 29 | 30 | let box_width_sub_border = context['config']['box_width'] - (context['config']['display_col_size'] - 2) 31 | let context['config']['split_widths'] = map(deepcopy(percentage_of_width), 'box_width_sub_border * v:val / 100') 32 | 33 | let total = 0 34 | for width in context['config']['split_widths'] 35 | let total += width 36 | endfor 37 | if total < box_width_sub_border 38 | let context['config']['split_widths'][0] += box_width_sub_border - total 39 | endif 40 | 41 | let context['config']['display_start'] = 0 42 | let context['config']['display_last'] = context['config']['box_bottom'] - context['config']['box_top'] - 1 43 | 44 | let context['config']['selected_col'] = 0 45 | let context['config']['selected_row'] = 0 46 | 47 | let context['config']['display_row_offset'] = 0 48 | let context['config']['display_col_offset'] = 0 49 | 50 | let context['config']['data'] = deepcopy(context['arguments'][0]) 51 | endfunction 52 | function! rabbit_ui#components#gridview#redraw(lines, context) 53 | let config = a:context['config'] 54 | let focused = rabbit_ui#helper#windowstatus(a:context, 'focused') 55 | 56 | let box_left = config['box_left'] 57 | let box_right = config['box_right'] 58 | let box_top = config['box_top'] 59 | let box_bottom = config['box_bottom'] 60 | let box_height = config['box_height'] 61 | 62 | let split_widths = config['split_widths'] 63 | 64 | let selected_row = config['selected_row'] 65 | let selected_col = config['selected_col'] 66 | 67 | let display_row_offset = config['display_row_offset'] 68 | let display_col_offset = config['display_col_offset'] 69 | 70 | let display_col_size = config['display_col_size'] 71 | let display_row_size = config['display_row_size'] 72 | 73 | let fixed_data = deepcopy(config['data']) 74 | 75 | for row_data in fixed_data 76 | for col_index in range(0, len(row_data) - 1) 77 | let row_data[col_index] = rabbit_ui#helper#smart_split( row_data[col_index], 78 | \ get(split_widths, (col_index + 1 - display_col_offset), 0))[0] 79 | endfor 80 | endfor 81 | 82 | let offsets = {} 83 | for col_index in range(0, display_col_size - 1) 84 | for row_index in range(0, display_row_size - 1) 85 | 86 | if !has_key(offsets, row_index) 87 | let offsets[row_index] = 0 88 | endif 89 | 90 | 91 | 92 | if (row_index is 0) || (col_index is 0) 93 | if focused 94 | let gname = 'rabbituiTitleLineActive' 95 | else 96 | let gname = 'rabbituiTitleLineNoActive' 97 | endif 98 | elseif row_index is (selected_row + 1 - display_row_offset) 99 | if col_index is (selected_col + 1 - display_col_offset) 100 | let gname = 'rabbituiSelectedItemNoActive' 101 | else 102 | let gname = 'rabbituiSelectedItemNoActive' 103 | endif 104 | else 105 | if row_index % 2 is 0 106 | let gname = 'rabbituiTextLinesEven' 107 | else 108 | let gname = 'rabbituiTextLinesOdd' 109 | endif 110 | endif 111 | 112 | if 1 < col_index 113 | let text = '|' 114 | let len = len(substitute(text, ".", "x", "g")) 115 | call rabbit_ui#helper#redraw_line(a:lines, row_index + (box_top + 1), box_left + offsets[row_index], text) 116 | 117 | call rabbit_ui#helper#set_highlight(gname, config, row_index + (box_top + 1), 118 | \ box_left + 1 + offsets[row_index], len) 119 | 120 | let offsets[row_index] += len 121 | endif 122 | 123 | 124 | 125 | if (row_index is 0) || (col_index is 0) 126 | if focused 127 | let gname = 'rabbituiTitleLineActive' 128 | else 129 | let gname = 'rabbituiTitleLineNoActive' 130 | endif 131 | elseif row_index is (selected_row + 1 - display_row_offset) 132 | if col_index is (selected_col + 1 - display_col_offset) 133 | let gname = 'rabbituiSelectedItemActive' 134 | else 135 | let gname = 'rabbituiSelectedItemNoActive' 136 | endif 137 | else 138 | if col_index is (selected_col + 1 - display_col_offset) 139 | let gname = 'rabbituiSelectedItemNoActive' 140 | else 141 | if row_index % 2 is 0 142 | let gname = 'rabbituiTextLinesEven' 143 | else 144 | let gname = 'rabbituiTextLinesOdd' 145 | endif 146 | endif 147 | endif 148 | 149 | if col_index is 0 && row_index is 0 150 | let text = repeat(' ', split_widths[col_index]) 151 | elseif col_index is 0 152 | let text = printf('%' . split_widths[col_index] . 'd', row_index + display_row_offset) 153 | elseif row_index is 0 154 | let text = printf('%' . split_widths[col_index] . 's', rabbit_ui#helper#to_alphabet_title(col_index + display_col_offset - 1)) 155 | else 156 | let text = get(get(fixed_data, row_index + display_row_offset - 1, []), 157 | \ col_index + display_col_offset - 1, repeat(' ', split_widths[col_index])) 158 | endif 159 | 160 | let len = len(substitute(text, ".", "x", "g")) 161 | 162 | call rabbit_ui#helper#redraw_line(a:lines, row_index + (box_top + 1), box_left + offsets[row_index], text) 163 | 164 | call rabbit_ui#helper#set_highlight(gname, config, row_index + (box_top + 1), 165 | \ box_left + 1 + offsets[row_index], len) 166 | 167 | let offsets[row_index] += split_widths[col_index] 168 | 169 | endfor 170 | endfor 171 | endfunction 172 | 173 | function! s:keyevent_cursor_up(...) 174 | let keyevent_arg1 = a:1 175 | let context_list = keyevent_arg1['context_list'] 176 | let active_window_id = keyevent_arg1['active_window_id'] 177 | let config = context_list[active_window_id]['config'] 178 | 179 | if config['selected_row'] is 0 180 | " do nothing 181 | elseif (config['selected_row'] + 1 - config['display_row_offset'] - 1) % config['display_row_size'] is 0 182 | let config['selected_row'] -= 1 183 | let config['display_row_offset'] -= 1 184 | elseif 0 < config['selected_row'] 185 | let config['selected_row'] -= 1 186 | endif 187 | endfunction 188 | function! s:keyevent_cursor_down(...) 189 | let keyevent_arg1 = a:1 190 | let context_list = keyevent_arg1['context_list'] 191 | let active_window_id = keyevent_arg1['active_window_id'] 192 | let config = context_list[active_window_id]['config'] 193 | 194 | if (config['selected_row'] - config['display_row_offset'] + 1) % (config['display_row_size'] - 1) is 0 195 | let config['selected_row'] += 1 196 | let config['display_row_offset'] += 1 197 | elseif (config['selected_row'] - config['display_row_offset']) < (config['display_row_size'] - 1) 198 | let config['selected_row'] += 1 199 | endif 200 | endfunction 201 | function! s:keyevent_cursor_left(...) 202 | let keyevent_arg1 = a:1 203 | let context_list = keyevent_arg1['context_list'] 204 | let active_window_id = keyevent_arg1['active_window_id'] 205 | let config = context_list[active_window_id]['config'] 206 | 207 | if config['selected_col'] is 0 208 | " do nothing 209 | elseif (config['selected_col'] + 1 - config['display_col_offset'] - 1) % config['display_col_size'] is 0 210 | let config['selected_col'] -= 1 211 | let config['display_col_offset'] -= 1 212 | elseif 0 < config['selected_col'] 213 | let config['selected_col'] -= 1 214 | endif 215 | endfunction 216 | function! s:keyevent_cursor_right(...) 217 | let keyevent_arg1 = a:1 218 | let context_list = keyevent_arg1['context_list'] 219 | let active_window_id = keyevent_arg1['active_window_id'] 220 | let config = context_list[active_window_id]['config'] 221 | 222 | if (config['selected_col'] - config['display_col_offset'] + 1) % (config['display_col_size'] - 1) is 0 223 | let config['selected_col'] += 1 224 | let config['display_col_offset'] += 1 225 | elseif (config['selected_col'] - config['display_col_offset']) < (config['display_col_size'] - 1) 226 | let config['selected_col'] += 1 227 | endif 228 | endfunction 229 | function! s:keyevent_edit_cell(...) 230 | let keyevent_arg1 = a:1 231 | let context_list = keyevent_arg1['context_list'] 232 | let active_window_id = keyevent_arg1['active_window_id'] 233 | let config = context_list[active_window_id]['config'] 234 | 235 | let selected_col = config['selected_col'] 236 | let selected_row = config['selected_row'] 237 | let text = get(get(config['data'], selected_row, []), selected_col, '') 238 | 239 | while len(config['data']) <= selected_row 240 | let config['data'] += [[]] 241 | endwhile 242 | while len(config['data'][selected_row]) <= selected_col 243 | let config['data'][selected_row] += [''] 244 | endwhile 245 | 246 | redraw! 247 | let config['data'][selected_row][selected_col] = input('>', text) 248 | endfunction 249 | 250 | function! rabbit_ui#components#gridview#get_keymap() 251 | return { 252 | \ 'cursor_up' : function(s:SID . 'keyevent_cursor_up'), 253 | \ 'cursor_down' : function(s:SID . 'keyevent_cursor_down'), 254 | \ 'cursor_left' : function(s:SID . 'keyevent_cursor_left'), 255 | \ 'cursor_right' : function(s:SID . 'keyevent_cursor_right'), 256 | \ 'edit_cell' : function(s:SID . 'keyevent_edit_cell'), 257 | \ } 258 | endfunction 259 | function! rabbit_ui#components#gridview#get_default_keymap() 260 | let keymap = rabbit_ui#keymap#get() 261 | return { 262 | \ char2nr('q') : keymap['common']['quit_window'], 263 | \ char2nr("\") : keymap['common']['enter'], 264 | \ char2nr(' ') : keymap['common']['focus_next_window'], 265 | \ char2nr('k') : keymap['gridview']['cursor_up'], 266 | \ char2nr('j') : keymap['gridview']['cursor_down'], 267 | \ char2nr('h') : keymap['gridview']['cursor_left'], 268 | \ char2nr('l') : keymap['gridview']['cursor_right'], 269 | \ char2nr('e') : keymap['gridview']['edit_cell'], 270 | \ } 271 | endfunction 272 | -------------------------------------------------------------------------------- /autoload/rabbit_ui/components/messagebox.vim: -------------------------------------------------------------------------------- 1 | 2 | function! s:getSID() 3 | return matchstr(expand(''), '\d\+_\zegetSID$') 4 | endfunction 5 | let s:SID = s:getSID() 6 | 7 | 8 | function! rabbit_ui#components#messagebox#init(context) 9 | let context = a:context 10 | call rabbit_ui#helper#set_common_configs(a:context['config']) 11 | let context['config']['title'] = rabbit_ui#helper#smart_split(context['arguments'][0], context['config']['box_width'])[0] 12 | let context['config']['text_lines'] = rabbit_ui#helper#smart_split(context['arguments'][1], context['config']['box_width']) 13 | endfunction 14 | function! rabbit_ui#components#messagebox#redraw(lines, context) 15 | let config = a:context['config'] 16 | let focused = rabbit_ui#helper#windowstatus(a:context, 'focused') 17 | let nonactivate = rabbit_ui#helper#windowstatus(a:context, 'nonactivate') 18 | 19 | let title = config['title'] 20 | let text_lines = config['text_lines'] 21 | let box_left = config['box_left'] 22 | let box_right = config['box_right'] 23 | let box_top = config['box_top'] 24 | let box_bottom = config['box_bottom'] 25 | let box_width = config['box_width'] 26 | 27 | for line_num in range(box_top + 1, box_bottom + 1) 28 | let text = get((nonactivate ? [] : [title]) + text_lines, (line_num - (box_top + 1)), repeat(' ', box_width)) 29 | call rabbit_ui#helper#redraw_line(a:lines, line_num, box_left, text) 30 | let len = len(substitute(text, ".", "x", "g")) 31 | 32 | if line_num is (box_top + 1) && !nonactivate 33 | if focused 34 | call rabbit_ui#helper#set_highlight('rabbituiTitleLineActive', config, line_num, box_left + 1, len) 35 | else 36 | call rabbit_ui#helper#set_highlight('rabbituiTitleLineNoActive', config, line_num, box_left + 1, len) 37 | endif 38 | else 39 | call rabbit_ui#helper#set_highlight('rabbituiTextLinesOdd', config, line_num, box_left + 1, len) 40 | endif 41 | endfor 42 | endfunction 43 | 44 | function! rabbit_ui#components#messagebox#get_keymap() 45 | return {} 46 | endfunction 47 | function! rabbit_ui#components#messagebox#get_default_keymap() 48 | let keymap = rabbit_ui#keymap#get() 49 | return { 50 | \ char2nr('q') : keymap['common']['quit_window'], 51 | \ char2nr("\") : keymap['common']['enter'], 52 | \ char2nr(' ') : keymap['common']['focus_next_window'], 53 | \ } 54 | endfunction 55 | 56 | -------------------------------------------------------------------------------- /autoload/rabbit_ui/components/panel.vim: -------------------------------------------------------------------------------- 1 | 2 | function! s:getSID() 3 | return matchstr(expand(''), '\d\+_\zegetSID$') 4 | endfunction 5 | let s:SID = s:getSID() 6 | 7 | 8 | function! rabbit_ui#components#panel#init(context) 9 | let context = a:context 10 | call rabbit_ui#helper#set_common_configs(context['config']) 11 | 12 | let context['config']['split_size'] = len(context['arguments'][0]) 13 | let context['config']['split_width'] = context['config']['box_width'] / context['config']['split_size'] 14 | 15 | let context['config']['display_start'] = 0 16 | let context['config']['display_last'] = context['config']['box_bottom'] - context['config']['box_top'] - 1 17 | 18 | let context['config']['selected_pane_index'] = 0 19 | 20 | let context['config']['item_index'] = {} 21 | let context['config']['display_offset'] = {} 22 | let context['config']['title'] = {} 23 | let context['config']['text_items'] = {} 24 | for i in range(0, context['config']['split_size'] - 1) 25 | let context['config']['item_index'][i] = 0 26 | let context['config']['display_offset'][i] = 0 27 | let context['config']['title'][i] = rabbit_ui#helper#smart_split(context['arguments'][0][i][0], context['config']['split_width'])[0] 28 | let context['config']['text_items'][i] = context['arguments'][0][i][1] 29 | endfor 30 | endfunction 31 | function! rabbit_ui#components#panel#redraw(lines, context) 32 | let config = a:context['config'] 33 | let focused = rabbit_ui#helper#windowstatus(a:context, 'focused') 34 | let nonactivate = rabbit_ui#helper#windowstatus(a:context, 'nonactivate') 35 | 36 | let box_left = config['box_left'] 37 | let box_right = config['box_right'] 38 | let box_top = config['box_top'] 39 | let box_bottom = config['box_bottom'] 40 | let box_height = config['box_height'] 41 | let split_width = config['split_width'] 42 | let split_size = config['split_size'] 43 | 44 | let offsets = {} 45 | for pane_index in range(0, split_size - 1) 46 | let item_index = config['item_index'][pane_index] 47 | let display_offset = config['display_offset'][pane_index] 48 | let title = config['title'][pane_index] 49 | let text_items = config['text_items'][pane_index][(display_offset):(display_offset + box_height)] 50 | let fixed_text_items = map( text_items, 'rabbit_ui#helper#smart_split(v:val, split_width)[0]') 51 | 52 | for line_num in range(box_top + 1, box_bottom + 1) 53 | 54 | let text = get((nonactivate ? [] : [title]) + fixed_text_items, (line_num - (box_top + 1)), repeat(' ', split_width)) 55 | 56 | let len = len(substitute(text, ".", "x", "g")) 57 | 58 | if !has_key(offsets, line_num) 59 | let offsets[line_num] = 0 60 | else 61 | let offsets[line_num] += split_width 62 | endif 63 | 64 | call rabbit_ui#helper#redraw_line(a:lines, line_num, box_left + offsets[line_num], text) 65 | 66 | if line_num is (box_top + 1) && !nonactivate 67 | if focused 68 | let gname = 'rabbituiTitleLineActive' 69 | else 70 | let gname = 'rabbituiTitleLineNoActive' 71 | endif 72 | elseif line_num is (box_top + 1) + (1 + item_index - display_offset) 73 | if pane_index is config['selected_pane_index'] 74 | let gname = 'rabbituiSelectedItemActive' 75 | else 76 | let gname = 'rabbituiSelectedItemNoActive' 77 | endif 78 | else 79 | if line_num % 2 is 0 80 | let gname = 'rabbituiTextLinesEven' 81 | else 82 | let gname = 'rabbituiTextLinesOdd' 83 | endif 84 | endif 85 | 86 | call rabbit_ui#helper#set_highlight(gname, config, line_num, box_left + 1 + offsets[line_num], len) 87 | endfor 88 | endfor 89 | endfunction 90 | 91 | function! s:keyevent_cursor_to_first_item(...) 92 | let keyevent_arg1 = a:1 93 | let context_list = keyevent_arg1['context_list'] 94 | let active_window_id = keyevent_arg1['active_window_id'] 95 | let config = context_list[active_window_id]['config'] 96 | 97 | let selected_pane_index = config['selected_pane_index'] 98 | let config['item_index'][selected_pane_index] = 0 99 | let config['display_offset'][selected_pane_index] = 0 100 | endfunction 101 | function! s:keyevent_cursor_to_last_item(...) 102 | let keyevent_arg1 = a:1 103 | let context_list = keyevent_arg1['context_list'] 104 | let active_window_id = keyevent_arg1['active_window_id'] 105 | let config = context_list[active_window_id]['config'] 106 | 107 | let selected_pane_index = config['selected_pane_index'] 108 | let box_height = config['box_height'] 109 | let item_size = len(config['text_items'][selected_pane_index]) 110 | let config['item_index'][selected_pane_index] = item_size - 1 111 | let config['display_offset'][selected_pane_index] = ( 112 | \ item_size - 1 < box_height - 1 113 | \ ? 0 114 | \ : item_size - box_height + 1 115 | \ ) 116 | endfunction 117 | function! s:keyevent_cursor_up(...) 118 | let keyevent_arg1 = a:1 119 | let context_list = keyevent_arg1['context_list'] 120 | let active_window_id = keyevent_arg1['active_window_id'] 121 | let config = context_list[active_window_id]['config'] 122 | 123 | let selected_pane_index = config['selected_pane_index'] 124 | if 0 <= config['item_index'][selected_pane_index] - 1 125 | let config['item_index'][selected_pane_index] -= 1 126 | endif 127 | if config['item_index'][selected_pane_index] - config['display_offset'][selected_pane_index] < config['display_start'] 128 | let config['display_offset'][selected_pane_index] = config['item_index'][selected_pane_index] - config['display_start'] 129 | endif 130 | endfunction 131 | function! s:keyevent_cursor_down(...) 132 | let keyevent_arg1 = a:1 133 | let context_list = keyevent_arg1['context_list'] 134 | let active_window_id = keyevent_arg1['active_window_id'] 135 | let config = context_list[active_window_id]['config'] 136 | 137 | let selected_pane_index = config['selected_pane_index'] 138 | if config['item_index'][selected_pane_index] + 1 <= len(config['text_items'][selected_pane_index]) - 1 139 | let config['item_index'][selected_pane_index] += 1 140 | endif 141 | if config['display_last'] < config['item_index'][selected_pane_index] - config['display_offset'][selected_pane_index] 142 | let config['display_offset'][selected_pane_index] = config['item_index'][selected_pane_index] - config['display_last'] 143 | endif 144 | endfunction 145 | function! s:keyevent_cursor_left(...) 146 | let keyevent_arg1 = a:1 147 | let context_list = keyevent_arg1['context_list'] 148 | let active_window_id = keyevent_arg1['active_window_id'] 149 | let config = context_list[active_window_id]['config'] 150 | 151 | let selected_pane_index = config['selected_pane_index'] 152 | if 0 < selected_pane_index 153 | let config['selected_pane_index'] -= 1 154 | endif 155 | endfunction 156 | function! s:keyevent_cursor_right(...) 157 | let keyevent_arg1 = a:1 158 | let context_list = keyevent_arg1['context_list'] 159 | let active_window_id = keyevent_arg1['active_window_id'] 160 | let config = context_list[active_window_id]['config'] 161 | 162 | let selected_pane_index = config['selected_pane_index'] 163 | if selected_pane_index < config['split_size'] - 1 164 | let config['selected_pane_index'] += 1 165 | endif 166 | endfunction 167 | function! s:keyevent_move_item_to_left(...) 168 | let keyevent_arg1 = a:1 169 | let context_list = keyevent_arg1['context_list'] 170 | let active_window_id = keyevent_arg1['active_window_id'] 171 | let config = context_list[active_window_id]['config'] 172 | 173 | let selected_pane_index = config['selected_pane_index'] 174 | let size = len(config['text_items'][selected_pane_index]) 175 | if 0 < size && 0 < selected_pane_index 176 | let from_selected_index = config['item_index'][selected_pane_index] 177 | let to_selected_index = config['item_index'][selected_pane_index - 1] 178 | let item = config['text_items'][selected_pane_index][from_selected_index] 179 | let config['text_items'][selected_pane_index - 1] = 180 | \ insert(config['text_items'][selected_pane_index - 1], item, to_selected_index) 181 | call remove(config['text_items'][selected_pane_index], from_selected_index) 182 | let size = len(config['text_items'][selected_pane_index]) 183 | if 0 < size && size <= from_selected_index 184 | let config['item_index'][selected_pane_index] = size - 1 185 | endif 186 | endif 187 | endfunction 188 | function! s:keyevent_move_item_to_right(...) 189 | let keyevent_arg1 = a:1 190 | let context_list = keyevent_arg1['context_list'] 191 | let active_window_id = keyevent_arg1['active_window_id'] 192 | let config = context_list[active_window_id]['config'] 193 | 194 | let selected_pane_index = config['selected_pane_index'] 195 | let size = len(config['text_items'][selected_pane_index]) 196 | if 0 < size && selected_pane_index < config['split_size'] - 1 197 | let from_selected_index = config['item_index'][selected_pane_index] 198 | let to_selected_index = config['item_index'][selected_pane_index + 1] 199 | let item = config['text_items'][selected_pane_index][from_selected_index] 200 | let config['text_items'][selected_pane_index + 1] = 201 | \ insert(config['text_items'][selected_pane_index + 1], item, to_selected_index) 202 | call remove(config['text_items'][selected_pane_index], from_selected_index) 203 | let size = len(config['text_items'][selected_pane_index]) 204 | if 0 < size && size <= from_selected_index 205 | let config['item_index'][selected_pane_index] = size - 1 206 | endif 207 | endif 208 | endfunction 209 | 210 | function! rabbit_ui#components#panel#get_keymap() 211 | return { 212 | \ 'cursor_to_first_item' : function(s:SID . 'keyevent_cursor_to_first_item'), 213 | \ 'cursor_to_last_item' : function(s:SID . 'keyevent_cursor_to_last_item'), 214 | \ 'cursor_up' : function(s:SID . 'keyevent_cursor_up'), 215 | \ 'cursor_down' : function(s:SID . 'keyevent_cursor_down'), 216 | \ 'cursor_left' : function(s:SID . 'keyevent_cursor_left'), 217 | \ 'cursor_right' : function(s:SID . 'keyevent_cursor_right'), 218 | \ 'move_item_to_right' : function(s:SID . 'keyevent_move_item_to_right'), 219 | \ 'move_item_to_left' : function(s:SID . 'keyevent_move_item_to_left'), 220 | \ } 221 | endfunction 222 | function! rabbit_ui#components#panel#get_default_keymap() 223 | let keymap = rabbit_ui#keymap#get() 224 | return { 225 | \ char2nr('q') : keymap['common']['quit_window'], 226 | \ char2nr("\") : keymap['common']['enter'], 227 | \ char2nr(' ') : keymap['common']['focus_next_window'], 228 | \ char2nr('j') : keymap['panel']['cursor_down'], 229 | \ char2nr('k') : keymap['panel']['cursor_up'], 230 | \ char2nr('h') : keymap['panel']['cursor_left'], 231 | \ char2nr('l') : keymap['panel']['cursor_right'], 232 | \ char2nr('g') : keymap['panel']['cursor_to_first_item'], 233 | \ char2nr('G') : keymap['panel']['cursor_to_last_item'], 234 | \ char2nr('H') : keymap['panel']['move_item_to_left'], 235 | \ char2nr('L') : keymap['panel']['move_item_to_right'], 236 | \ } 237 | endfunction 238 | 239 | -------------------------------------------------------------------------------- /autoload/rabbit_ui/helper.vim: -------------------------------------------------------------------------------- 1 | 2 | function! rabbit_ui#helper#id() 3 | return 'rabbit-ui' 4 | endfunction 5 | function! rabbit_ui#helper#exception(msg) 6 | throw printf('[%s] %s', rabbit_ui#helper#id(), a:msg) 7 | endfunction 8 | function! rabbit_ui#helper#windowstatus(context, name) 9 | if a:name is 'nonactivate' 10 | return get(get(a:context, 'windowstatus', {}), a:name, 0) 11 | elseif a:name is 'focused' 12 | return get(get(a:context, 'windowstatus', {}), a:name, 0) 13 | else 14 | call rabbit_ui#helper#exception('unknown windowstatus:' . a:name) 15 | endif 16 | endfunction 17 | function! rabbit_ui#helper#get_componentname_list() 18 | let r = split(&runtimepath, ',') 19 | call map(r, "globpath(v:val, 'autoload/rabbit_ui/components/*.vim')") 20 | call map(r , 'split(v:val, "\n")') 21 | call filter(r, '!empty(v:val)') 22 | 23 | let xs = [] 24 | for path_list in r 25 | for path in path_list 26 | let xs += [ matchstr(tr(path, '\', '/'), '/\zs\i\+\ze\.vim$') ] 27 | endfor 28 | endfor 29 | return xs 30 | endfunction 31 | function! rabbit_ui#helper#set_common_configs(config) 32 | let config = a:config 33 | 34 | let config['box_top'] = abs(get(config, 'box_top', &lines / 4 * 1)) 35 | let config['box_bottom'] = abs(get(config, 'box_bottom', &lines / 4 * 3)) 36 | if config['box_bottom'] < config['box_top'] 37 | call rabbit_ui#helper#exception('choices: box_top is larger than box_bottom.') 38 | endif 39 | 40 | let config['box_left'] = abs(get(config, 'box_left', &columns / 4 * 1)) 41 | let config['box_right'] = abs(get(config, 'box_right', &columns / 4 * 3)) 42 | if config['box_right'] < config['box_left'] 43 | call rabbit_ui#helper#exception('choices: box_left is larger than box_right.') 44 | endif 45 | 46 | let config['box_width'] = config['box_right'] - config['box_left'] + 1 47 | let config['box_height'] = config['box_bottom'] - config['box_top'] + 1 48 | 49 | call rabbit_ui#helper#init_highlights({}) 50 | 51 | return config 52 | endfunction 53 | " text 54 | function! s:padding_right_space(text, width) 55 | return a:text . repeat(' ', a:width - strdisplaywidth(a:text)) 56 | endfunction 57 | function! rabbit_ui#helper#redraw_line(lines, line_num, box_left, text) 58 | let orgline = a:lines[(a:line_num - 1)] 59 | let line = s:padding_right_space(orgline, &columns) 60 | let str = rabbit_ui#helper#smart_split(line, a:box_left)[0] 61 | let str .= a:text 62 | let str .= line[(strdisplaywidth(str)):] 63 | if orgline isnot str 64 | let a:lines[(a:line_num - 1)] = str 65 | endif 66 | endfunction 67 | function! rabbit_ui#helper#smart_split(str, boxwidth, ...) 68 | let is_wrap = 0 < a:0 ? a:1 : 1 69 | let lines = [] 70 | 71 | let cs = split(a:str, '\zs') 72 | let cs_index = 0 73 | 74 | if a:boxwidth isnot 0 75 | let text = '' 76 | while cs_index < len(cs) 77 | if cs[cs_index] is "\n" 78 | let text = s:padding_right_space(text, a:boxwidth) 79 | let lines += [text] 80 | let text = '' 81 | elseif strdisplaywidth(text . cs[cs_index]) < a:boxwidth 82 | let text .= cs[cs_index] 83 | elseif strdisplaywidth(text . cs[cs_index]) == a:boxwidth 84 | let text .= cs[cs_index] 85 | let lines += [text] 86 | if is_wrap 87 | let text = '' 88 | else 89 | while get(cs, cs_index, "\n") isnot "\n" 90 | let cs_index += 1 91 | endwhile 92 | let text = '' 93 | continue 94 | endif 95 | elseif strdisplaywidth(text . cs[cs_index]) > a:boxwidth 96 | let text .= ' ' 97 | let lines += [text] 98 | if is_wrap 99 | let text = cs[cs_index] 100 | else 101 | while get(cs, cs_index, "\n") isnot "\n" 102 | let cs_index += 1 103 | endwhile 104 | let text = '' 105 | continue 106 | endif 107 | endif 108 | let cs_index += 1 109 | endwhile 110 | let text .= repeat(' ', a:boxwidth - strdisplaywidth(text)) 111 | let lines += [text] 112 | else 113 | let lines += [''] 114 | endif 115 | 116 | return lines 117 | endfunction 118 | " gridview helper 119 | function! rabbit_ui#helper#to_alphabet_title(n) 120 | let n = a:n 121 | let str = '' 122 | while 1 123 | let str = nr2char(0x41 + n % 26) . str 124 | let n = n / 26 - 1 125 | if n < 0 126 | break 127 | endif 128 | endwhile 129 | return str 130 | endfunction 131 | " highlight 132 | function! rabbit_ui#helper#clear_matches(context) 133 | let config = a:context['config'] 134 | for id in get(config, 'matches', []) 135 | call matchdelete(id) 136 | endfor 137 | let config['matches'] = [] 138 | endfunction 139 | function! rabbit_ui#helper#set_highlight(groupname, config, line, col, size) 140 | let config = a:config 141 | execute printf('highlight! default link %s %s', a:groupname, a:groupname) 142 | if !has_key(config, 'matches') 143 | let config['matches'] = [] 144 | endif 145 | let config['matches'] += [ matchadd(a:groupname, printf('\%%%dl\%%%dv.\{%d,%d}', a:line, a:col, a:size, a:size)) ] 146 | endfunction 147 | function! rabbit_ui#helper#init_highlights(highlights) 148 | let highlights = a:highlights 149 | 150 | let default_table = { 151 | \ 'rabbituiTitleLineActive' : { 152 | \ 'guifg' : '#ffffff', 'guibg' : '#aaaaee', 'gui' : 'bold', 153 | \ 'ctermfg' : 'White', 'ctermbg' : 'Blue', 'cterm' : 'bold', 154 | \ }, 155 | \ 'rabbituiTitleLineNoActive' : { 156 | \ 'guifg' : '#ffffff', 'guibg' : '#555555', 'gui' : 'bold', 157 | \ 'ctermfg' : 'White', 'ctermbg' : 'Blue', 'cterm' : 'bold', 158 | \ }, 159 | \ 'rabbituiTextLinesEven' : { 160 | \ 'guifg' : '#000000', 'guibg' : '#ddddff', 'gui' : 'none', 161 | \ 'ctermfg' : 'Black', 'ctermbg' : 'LightRed', 'cterm' : 'bold', 162 | \ }, 163 | \ 'rabbituiTextLinesOdd' : { 164 | \ 'guifg' : '#000000', 'guibg' : '#ffffff', 'gui' : 'none', 165 | \ 'ctermfg' : 'Black', 'ctermbg' : 'White', 'cterm' : 'bold', 166 | \ }, 167 | \ 'rabbituiSelectedItemActive' : { 168 | \ 'guifg' : '#ffff00', 'guibg' : '#888888', 'gui' : 'bold', 169 | \ 'ctermfg' : 'Yellow', 'ctermbg' : 'Gray', 'cterm' : 'bold', 170 | \ }, 171 | \ 'rabbituiSelectedItemNoActive' : { 172 | \ 'guifg' : '#000000', 'guibg' : '#bbbbbb', 'gui' : 'none', 173 | \ 'ctermfg' : 'Black', 'ctermbg' : 'LightGray', 'cterm' : 'bold', 174 | \ }, 175 | \ } 176 | for x in keys(default_table) 177 | execute printf('highlight! %s guifg=%s guibg=%s gui=%s ctermfg=%s ctermbg=%s cterm=%s', 178 | \ x, 179 | \ get(get(highlights, x, {}), 'guifg', default_table[x]['guifg']), 180 | \ get(get(highlights, x, {}), 'guibg', default_table[x]['guibg']), 181 | \ get(get(highlights, x, {}), 'gui', default_table[x]['gui']), 182 | \ get(get(highlights, x, {}), 'ctermfg', default_table[x]['ctermfg']), 183 | \ get(get(highlights, x, {}), 'ctermbg', default_table[x]['ctermbg']), 184 | \ get(get(highlights, x, {}), 'cterm', default_table[x]['cterm']) 185 | \ ) 186 | endfor 187 | endfunction 188 | 189 | 190 | -------------------------------------------------------------------------------- /autoload/rabbit_ui/keymap.vim: -------------------------------------------------------------------------------- 1 | 2 | function! s:getSID() 3 | return matchstr(expand(''), '\d\+_\zegetSID$') 4 | endfunction 5 | let s:SID = s:getSID() 6 | 7 | function! s:keyevent_quit_window(...) 8 | let rabbit_ui = a:1 9 | call rabbit_ui.quit_window() 10 | endfunction 11 | function! s:keyevent_enter(...) 12 | let rabbit_ui = a:1 13 | call rabbit_ui.enter() 14 | endfunction 15 | function! s:keyevent_focus_next_window(...) 16 | let rabbit_ui = a:1 17 | call rabbit_ui.focus_next_window() 18 | endfunction 19 | 20 | function! rabbit_ui#keymap#get() 21 | return { 22 | \ 'common' : { 23 | \ 'quit_window' : function(s:SID . 'keyevent_quit_window'), 24 | \ 'enter' : function(s:SID . 'keyevent_enter'), 25 | \ 'focus_next_window' : function(s:SID . 'keyevent_focus_next_window'), 26 | \ }, 27 | \ 'panel' : rabbit_ui#components#panel#get_keymap(), 28 | \ 'choices' : rabbit_ui#components#choices#get_keymap(), 29 | \ 'messagebox' : rabbit_ui#components#messagebox#get_keymap(), 30 | \ 'gridview' : rabbit_ui#components#gridview#get_keymap(), 31 | \ } 32 | endfunction 33 | -------------------------------------------------------------------------------- /choices.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rbtnn/vim-rabbit-ui/60b314ed922d506126fefd3e87734257ef73bbde/choices.png -------------------------------------------------------------------------------- /doc/rabbit_ui.txt: -------------------------------------------------------------------------------- 1 | *rabbit-ui.txt* Rich UI Vim script Library 2 | 3 | Author : rbtnn 4 | LICENSE: MIT license (see LICENSE.txt) 5 | 6 | ============================================================================== 7 | This is a rich UI Vim script Library. 8 | 9 | rabbit-ui-collection.vim contains commands using rabbit-ui.vim. 10 | > 11 | https://github.com/rbtnn/rabbit-ui-collection.vim 12 | < 13 | ============================================================================== 14 | CONTENTS *rabbit-ui-contents* 15 | 16 | Components |rabbit-ui-components| 17 | MessageBox |rabbit-ui-components-messagebox| 18 | Panel |rabbit-ui-components-panel| 19 | Choices |rabbit-ui-components-choices| 20 | GridView |rabbit-ui-components-gridview| 21 | Config |rabbit-ui-context-config| 22 | KeyMap |rabbit-ui-context-config-keymap| 23 | SampleCodes |rabbit-ui-samplecodes| 24 | 25 | ============================================================================== 26 | Components *rabbit-ui-components* 27 | 28 | ------------------------------------------------------------------------------ 29 | MessageBox *rabbit-ui-components-messagebox* 30 | 31 | * rabbit_ui#messagebox({title}, {text} [, {config}]) 32 | 33 | KeyMap: 34 | 35 | * common.quit_window (default key: `q`) 36 | Returns `{}`. 37 | 38 | * common.enter (default key: ``) 39 | Returns `{ 'value' : [] }`. 40 | 41 | 42 | 43 | * common.focus_next_window (default key: ``) 44 | 45 | ------------------------------------------------------------------------------ 46 | Choices *rabbit-ui-components-choices* 47 | 48 | * rabbit_ui#choices({title}, {items} [, {config}]) 49 | 50 | KeyMap: 51 | 52 | * common.quit_window (default key: `q`) 53 | Returns `{}`. 54 | 55 | * common.enter (default key: ``) 56 | Returns `{ 'value' : (selected index) }`. 57 | 58 | 59 | 60 | * common.focus_next_window (default key: ``) 61 | * choices.cursor_up (default key: `k` ) 62 | * choices.cursor_down (default key: `j` ) 63 | * choices.cursor_to_first_item (default key: `g` ) 64 | * choices.cursor_to_last_item (default key: `G` ) 65 | 66 | ------------------------------------------------------------------------------ 67 | Panel *rabbit-ui-components-panel* 68 | 69 | * rabbit_ui#panel({title_and_items_list} [, {config}]) 70 | 71 | KeyMap: 72 | 73 | * common.quit_window (default key: `q`) 74 | Returns `{}`. 75 | 76 | * common.enter (default key: ``) 77 | Returns `{ 'value' : [[(selected index), [(items)]], ...] }`. 78 | 79 | 80 | 81 | * common.focus_next_window (default key: ``) 82 | * panel.cursor_up (default key: `k` ) 83 | * panel.cursor_down (default key: `j` ) 84 | * panel.cursor_left (default key: `h` ) 85 | * panel.cursor_right (default key: `l` ) 86 | * panel.cursor_to_first_item (default key: `g` ) 87 | * panel.cursor_to_last_item (default key: `G` ) 88 | * panel.move_item_to_left (default key: `H` ) 89 | * panel.move_item_to_right (default key: `L` ) 90 | 91 | ------------------------------------------------------------------------------ 92 | GridView *rabbit-ui-components-gridview* 93 | 94 | * rabbit_ui#gridview({data} [, {config}]) 95 | 96 | KeyMap: 97 | 98 | * common.quit_window (default key: `q` ) 99 | Returns `{}`. 100 | 101 | * common.enter (default key: `` ) 102 | Returns `{ 'value' : [[(edited cell), ...], ...] }`. 103 | 104 | 105 | 106 | * common.focus_next_window (default key: ``) 107 | * gridview.cursor_up (default key: `k` ) 108 | * gridview.cursor_down (default key: `j` ) 109 | * gridview.cursor_left (default key: `h` ) 110 | * gridview.cursor_right (default key: `l` ) 111 | * gridview.edit_cell (default key: `e` ) 112 | 113 | ============================================================================== 114 | Config *rabbit-ui-context-config* 115 | 116 | {config} of Component is a dictionary. It can have following keys. 117 | 118 | 119 | * box_top 120 | Top of a component. See Figure A. 121 | (Default: `&lines / 4 * 1`) 122 | 123 | 124 | * box_bottom 125 | Bottom of a component. See Figure A. 126 | (Default: `&lines / 4 * 3`) 127 | 128 | 129 | * box_left 130 | Left of a component. See Figure A. 131 | (Default: `&columns / 4 * 1`) 132 | 133 | 134 | * box_right 135 | Right of a component. See Figure A. 136 | (Default: `&columns / 4 * 3`) 137 | 138 | 139 | box_left box_right 140 | | | 141 | v v 142 | +-----------------------------------+ 143 | | | 144 | | | 145 | | +-------------+ | 146 | box_top -> | | ___________ | | 147 | | | | | 148 | | | | | 149 | box_bottom -> | | | | 150 | | +-------------+ | 151 | | | 152 | +-----------------------------------+ 153 | Figure A 154 | 155 | 156 | * gridview.display_col_size 157 | This can be had on GridView only. 158 | Columns size displayed. 159 | (Default: `5`) 160 | 161 | 162 | * gridview.percentage_of_width 163 | This can be had on GridView only. 164 | Percentages of component-width splited by display_col_size. 165 | > 166 | component-width = box_right - box_left + 1 167 | < 168 | The sum of elements should be 100. 169 | 170 | 171 | * gridview.display_row_size 172 | This can be had on GridView only. 173 | Row size displayed if the type is Number. 174 | (Default: `5`) 175 | 176 | 177 | ============================================================================== 178 | KeyMap *rabbit-ui-context-config-keymap* 179 | 180 | 181 | 182 | * common.quit_window 183 | Quit a window. 184 | 185 | 186 | * common.enter 187 | Select an item under cursor. 188 | 189 | 190 | * common.focus_next_window 191 | Focus to a next window. 192 | 193 | 194 | * messagebox 195 | No keymap. 196 | 197 | 198 | * choices.cursor_up 199 | Cursor up. 200 | 201 | 202 | * choices.cursor_down 203 | Cursor down. 204 | 205 | 206 | * choices.cursor_to_first_item 207 | Move cursor to first item. 208 | 209 | 210 | * choices.cursor_to_last_item 211 | Move cursor to last item. 212 | 213 | * panel.cursor_up 214 | Cursor up. 215 | 216 | 217 | * panel.cursor_down 218 | Cursor down. 219 | 220 | 221 | * panel.cursor_left 222 | Move cursor to left panel. 223 | 224 | 225 | * panel.cursor_right 226 | Move cursor to right panel. 227 | 228 | 229 | * panel.cursor_to_first_item 230 | Move cursor to first item. 231 | 232 | 233 | * panel.cursor_to_last_item 234 | Move cursor to last item. 235 | 236 | 237 | * panel.move_item_to_left 238 | Move selected item to left panel. 239 | 240 | 241 | * panel.move_item_to_right 242 | Move selected item to right panel. 243 | 244 | 245 | * gridview.cursor_up 246 | Cursor up. 247 | 248 | 249 | * gridview.cursor_down 250 | Cursor down. 251 | 252 | 253 | * gridview.cursor_left 254 | Cursor left. 255 | 256 | 257 | * gridview.cursor_right 258 | Cursor right. 259 | 260 | 261 | * gridview.edit_cell 262 | Edit a cell. 263 | 264 | 265 | ============================================================================== 266 | SampleCodes *rabbit-ui-samplecodes* 267 | 268 | Example of MessageBox: 269 | > 270 | let keymap = rabbit_ui#keymap#get() 271 | call rabbit_ui#messagebox('title','text text ...', { 272 | \ 'box_top' : 0, 273 | \ 'box_bottom' : 20, 274 | \ 'box_right' : 25, 275 | \ 'box_left' : 0, 276 | \ 'keymap' : { 277 | \ char2nr('Q') : keymap.quit_window. 278 | \ char2nr("\") : keymap.focus_next_window. 279 | \ }, 280 | \ }) 281 | < 282 | 283 | Example of Choices: 284 | > 285 | let keymap = rabbit_ui#components#choices#get_default_keymap() 286 | let keymap[char2nr("\")] = keymap[char2nr('q')] 287 | call rabbit_ui#choices('hoge', [1,2,3], { 288 | \ 'box_top' : 0, 289 | \ 'box_bottom' : 20, 290 | \ 'box_right' : 25, 291 | \ 'box_left' : 0, 292 | \ 'keymap' : keymap, 293 | \ }) 294 | < 295 | 296 | Example of Panel: 297 | > 298 | call rabbit_ui#panel( 299 | \ [ 300 | \ ['hoge',["A","B",'C','D']], 301 | \ ['foo',["1","2",'3','4']] 302 | \ ] 303 | \ ) 304 | < 305 | 306 | Example of Panel: 307 | > 308 | call rabbit_ui#gridview([[1,2,3],[4,5,6]]) 309 | < 310 | 311 | Example of User Defined KeyEvent: 312 | > 313 | function! g:CalcCells(...) 314 | let context_list = (a:1)['context_list'] 315 | let active_window_context = context_list['active_window_index'] 316 | if active_window_context['component_name'] is# 'gridview' 317 | let data = active_window_context['config']['data'] 318 | for row in data 319 | if 2 < len(row) 320 | let row[2] = str2nr(row[0]) * str2nr(row[1]) 321 | endif 322 | endfor 323 | endif 324 | endfunction 325 | let s:keymap = rabbit_ui#components#gridview#get_default_keymap() 326 | let s:keymap[char2nr('a')] = function('g:CalcCells') 327 | call rabbit_ui#gridview([[1,2,3],[4,5,6]], { 328 | \ 'keymap' : s:keymap 329 | \ }) 330 | < 331 | 332 | Example of MessageBox And Panel: 333 | > 334 | call rabbit_ui#exec_components([ 335 | \ { 'component_name' : 'messagebox', 336 | \ 'arguments' : ['titleA', 'text'], 337 | \ 'config' : { 338 | \ 'box_top' : 10, 'box_left' : 21, 339 | \ 'box_bottom' : 20, 'box_right' : 48, 340 | \ }, 341 | \ }, 342 | \ { 'component_name' : 'panel', 343 | \ 'arguments' : [ 344 | \ [ 345 | \ ['hoge',["A","B",'C','D']], 346 | \ ['foo',["1","2",'3','4']] 347 | \ ] 348 | \ ], 349 | \ 'config' : { 350 | \ 'box_top' : 10, 'box_left' : 58, 351 | \ 'box_bottom' : 25, 'box_right' : 110, 352 | \ }, 353 | \ }, 354 | \ ]) 355 | < 356 | 357 | Example of GridView And Choices: 358 | > 359 | call rabbit_ui#exec_components([ 360 | \ { 'component_name' : 'gridview', 361 | \ 'arguments' : [[[1,2,3],[4,5,6]]], 362 | \ 'config' : { 363 | \ 'box_top' : 21, 'box_left' : 21, 364 | \ 'box_bottom' : 25, 'box_right' : 52, 365 | \ }, 366 | \ }, 367 | \ { 'component_name' : 'choices', 368 | \ 'arguments' : ['choices',["A","B",'C','D']], 369 | \ 'config' : { 370 | \ 'box_top' : 10, 'box_left' : 10, 371 | \ 'box_bottom' : 25, 'box_right' : 20, 372 | \ }, 373 | \ }, 374 | \ ]) 375 | < 376 | 377 | ============================================================================== 378 | vim:tw=78:ts=8:ft=help:norl:noet:fen:fdl=0: 379 | -------------------------------------------------------------------------------- /gridview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rbtnn/vim-rabbit-ui/60b314ed922d506126fefd3e87734257ef73bbde/gridview.png -------------------------------------------------------------------------------- /messagebox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rbtnn/vim-rabbit-ui/60b314ed922d506126fefd3e87734257ef73bbde/messagebox.png -------------------------------------------------------------------------------- /panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rbtnn/vim-rabbit-ui/60b314ed922d506126fefd3e87734257ef73bbde/panel.png --------------------------------------------------------------------------------