|
| 1 | +vim9script noclear |
| 2 | + |
| 3 | +# defaults: |
| 4 | +if !exists('g:elevator#timeout_msec') |
| 5 | + g:elevator#timeout_msec = 2000 |
| 6 | +endif |
| 7 | + |
| 8 | +var s_state = { |
| 9 | + scrolloff: -1, |
| 10 | + screenrow: -1, |
| 11 | + topline: -1, |
| 12 | + dragging: false, |
| 13 | + scrolled_winid: -1, |
| 14 | + popup_id: -1, |
| 15 | + timer_id: -1, |
| 16 | +} |
| 17 | + |
| 18 | +def S__clamp(value__a: number, min__a: number, max__a: number): number |
| 19 | + if value__a < min__a |
| 20 | + return min__a |
| 21 | + elseif value__a > max__a |
| 22 | + return max__a |
| 23 | + else |
| 24 | + return value__a |
| 25 | + endif |
| 26 | +enddef |
| 27 | + |
| 28 | +def S__calculate_scale(winid__a: number): float |
| 29 | + var winheight = winheight(winid__a) |
| 30 | + return 1.0 * winheight / (line('$', winid__a) + winheight) |
| 31 | +enddef |
| 32 | + |
| 33 | +export def Show(winid__a: number) |
| 34 | + if s_state.dragging |
| 35 | + return |
| 36 | + endif |
| 37 | + |
| 38 | + S__stop_timer() |
| 39 | + |
| 40 | + if winid__a == 0 # over command line |
| 41 | + var mousepos = getmousepos() |
| 42 | + # find window above mouse cursor: |
| 43 | + for wininfo in getwininfo() |
| 44 | + if ( |
| 45 | + wininfo.winrow + wininfo.height + &cmdheight == &lines && |
| 46 | + wininfo.wincol < mousepos.screencol && wininfo.wincol + wininfo.width > mousepos.screencol |
| 47 | + ) |
| 48 | + s_state.scrolled_winid = wininfo.winid |
| 49 | + break |
| 50 | + endif |
| 51 | + endfor |
| 52 | + else |
| 53 | + s_state.scrolled_winid = winid__a |
| 54 | + endif |
| 55 | + |
| 56 | + if s_state.popup_id == -1 |
| 57 | + s_state.popup_id = popup_create('', { |
| 58 | + pos: 'topleft', |
| 59 | + minwidth: 1, |
| 60 | + dragall: true, |
| 61 | + resize: true, |
| 62 | + zindex: 1, |
| 63 | + }) |
| 64 | + |
| 65 | + endif |
| 66 | + |
| 67 | + S__set_geometry() |
| 68 | + S__restart_timer() |
| 69 | +enddef |
| 70 | + |
| 71 | +def S__stop_timer() |
| 72 | + if s_state.timer_id != -1 |
| 73 | + timer_stop(s_state.timer_id) |
| 74 | + s_state.timer_id = -1 |
| 75 | + endif |
| 76 | +enddef |
| 77 | + |
| 78 | +def S__restart_timer() |
| 79 | + S__stop_timer() |
| 80 | + s_state.timer_id = timer_start(g:elevator#timeout_msec, (_) => S__close()) |
| 81 | +enddef |
| 82 | + |
| 83 | +def S__close() |
| 84 | + popup_close(s_state.popup_id) |
| 85 | + s_state.popup_id = -1 |
| 86 | + s_state.timer_id = -1 |
| 87 | +enddef |
| 88 | + |
| 89 | +export def S__set_geometry() |
| 90 | + var wininfo = getwininfo(s_state.scrolled_winid)[0] |
| 91 | + var scale = S__calculate_scale(s_state.scrolled_winid) |
| 92 | + if scale >= 1.0 |
| 93 | + S__close() |
| 94 | + return |
| 95 | + endif |
| 96 | + |
| 97 | + var popup_height = S__clamp((wininfo.height * scale)->round()->float2nr(), 1, wininfo.height) |
| 98 | + var popup_offset = S__clamp(((wininfo.topline - 1) * scale)->round()->float2nr(), 0, wininfo.height - popup_height) |
| 99 | + |
| 100 | + popup_setoptions(s_state.popup_id, { |
| 101 | + col: wininfo.wincol + wininfo.width - 1, |
| 102 | + line: wininfo.winrow + wininfo.winbar + popup_offset, |
| 103 | + minheight: popup_height, |
| 104 | + }) |
| 105 | +enddef |
| 106 | + |
| 107 | +def S__on_mouse(event__a: string) |
| 108 | + var mousepos = getmousepos() |
| 109 | + if event__a == 'LeftMouse' && mousepos.winid == s_state.popup_id |
| 110 | + s_state.screenrow = mousepos.screenrow |
| 111 | + s_state.topline = line('w0') |
| 112 | + s_state.dragging = true |
| 113 | + s_state.scrolloff = &scrolloff |
| 114 | + &scrolloff = 0 |
| 115 | + S__stop_timer() |
| 116 | + win_gotoid(s_state.scrolled_winid) |
| 117 | + popup_setoptions(s_state.popup_id, {dragall: false}) |
| 118 | + elseif event__a == 'LeftDrag' && s_state.dragging |
| 119 | + var delta = mousepos.screenrow - s_state.screenrow |
| 120 | + var savedview = winsaveview() |
| 121 | + var scale = S__calculate_scale(s_state.scrolled_winid) |
| 122 | + savedview.topline = S__clamp(s_state.topline + (delta / scale)->round()->float2nr(), 1, line('$')) |
| 123 | + savedview.lnum = S__clamp(savedview.lnum, savedview.topline, savedview.topline + line('w$') - line('w0')) |
| 124 | + winrestview(savedview) |
| 125 | + S__set_geometry() |
| 126 | + elseif event__a == 'LeftRelease' && s_state.dragging |
| 127 | + s_state.dragging = false |
| 128 | + s_state.screenrow = -1 |
| 129 | + s_state.topline = -1 |
| 130 | + &scrolloff = s_state.scrolloff |
| 131 | + s_state.scrolloff = -1 |
| 132 | + S__restart_timer() |
| 133 | + popup_setoptions(s_state.popup_id, {dragall: true}) |
| 134 | + endif |
| 135 | +enddef |
| 136 | + |
| 137 | +noremap <LeftDrag> <LeftDrag><ScriptCmd>S__on_mouse('LeftDrag')<CR> |
| 138 | +noremap <LeftMouse> <LeftMouse><ScriptCmd>S__on_mouse('LeftMouse')<CR> |
| 139 | +noremap <LeftRelease> <LeftRelease><ScriptCmd>S__on_mouse('LeftRelease')<CR> |
0 commit comments