diff options
| author | Junegunn Choi <junegunn.c@gmail.com> | 2025-01-27 01:10:08 +0900 |
|---|---|---|
| committer | Junegunn Choi <junegunn.c@gmail.com> | 2025-01-27 01:10:08 +0900 |
| commit | e91f10ab167f10328816fab65fc38370059986f3 (patch) | |
| tree | f8e7d059843f3a7a81835a888fe29d7588ee4038 /src | |
| parent | 2c15cd792306119ead573c473c982b6996e11952 (diff) | |
| download | fzf-e91f10ab167f10328816fab65fc38370059986f3.tar.gz | |
Enhance click-header event
* Expose the name of the mouse action as $FZF_KEY
* Trigger click-header on mouse up
* Enhanced clickable header for `kill` completion
Diffstat (limited to 'src')
| -rw-r--r-- | src/terminal.go | 75 | ||||
| -rw-r--r-- | src/tui/light.go | 14 | ||||
| -rw-r--r-- | src/tui/tcell.go | 20 | ||||
| -rw-r--r-- | src/tui/tui.go | 36 |
4 files changed, 94 insertions, 51 deletions
diff --git a/src/terminal.go b/src/terminal.go index 56b70e9b..5ff9f8de 100644 --- a/src/terminal.go +++ b/src/terminal.go @@ -4620,6 +4620,7 @@ func (t *Terminal) Loop() error { pbarDragging := false pborderDragging := -1 wasDown := false + pmx, pmy := -1, -1 needBarrier := true // If an action is bound to 'start', we're going to process it before reading @@ -5422,25 +5423,30 @@ func (t *Terminal) Loop() error { case actMouse: me := event.MouseEvent mx, my := me.X, me.Y - clicked := !wasDown && me.Down + click := !wasDown && me.Down + clicked := wasDown && !me.Down && (mx == pmx && my == pmy) wasDown = me.Down + if click { + pmx, pmy = mx, my + } if !me.Down { barDragging = false pbarDragging = false pborderDragging = -1 previewDraggingPos = -1 + pmx, pmy = -1, -1 } // Scrolling if me.S != 0 { if t.window.Enclose(my, mx) && t.merger.Length() > 0 { evt := tui.ScrollUp - if me.Mod { + if me.Mod() { evt = tui.SScrollUp } if me.S < 0 { evt = tui.ScrollDown - if me.Mod { + if me.Mod() { evt = tui.SScrollDown } } @@ -5456,7 +5462,7 @@ func (t *Terminal) Loop() error { } // Preview dragging - if me.Down && (previewDraggingPos >= 0 || clicked && t.hasPreviewWindow() && t.pwindow.Enclose(my, mx)) { + if me.Down && (previewDraggingPos >= 0 || click && t.hasPreviewWindow() && t.pwindow.Enclose(my, mx)) { if previewDraggingPos > 0 { scrollPreviewBy(previewDraggingPos - my) } @@ -5466,7 +5472,7 @@ func (t *Terminal) Loop() error { // Preview scrollbar dragging headerLines := t.activePreviewOpts.headerLines - pbarDragging = me.Down && (pbarDragging || clicked && t.hasPreviewWindow() && my >= t.pwindow.Top()+headerLines && my < t.pwindow.Top()+t.pwindow.Height() && mx == t.pwindow.Left()+t.pwindow.Width()) + pbarDragging = me.Down && (pbarDragging || click && t.hasPreviewWindow() && my >= t.pwindow.Top()+headerLines && my < t.pwindow.Top()+t.pwindow.Height() && mx == t.pwindow.Left()+t.pwindow.Width()) if pbarDragging { effectiveHeight := t.pwindow.Height() - headerLines numLines := len(t.previewer.lines) - headerLines @@ -5483,7 +5489,7 @@ func (t *Terminal) Loop() error { } // Preview border dragging (resizing) - if pborderDragging < 0 && clicked && t.hasPreviewWindow() { + if pborderDragging < 0 && click && t.hasPreviewWindow() { switch t.activePreviewOpts.position { case posUp: if t.pborder.Enclose(my, mx) && my == t.pborder.Top()+t.pborder.Height()-1 { @@ -5564,9 +5570,7 @@ func (t *Terminal) Loop() error { } // Inside the header window - // TODO: Should we trigger this on mouse up instead? - // Should we still trigger it when the position has changed from the down event? - if t.headerVisible && t.headerWindow != nil && t.headerWindow.Enclose(my, mx) { + if clicked && t.headerVisible && t.headerWindow != nil && t.headerWindow.Enclose(my, mx) { mx -= t.headerWindow.Left() + t.headerIndent(t.headerBorderShape) my -= t.headerWindow.Top() if mx < 0 { @@ -5580,7 +5584,7 @@ func (t *Terminal) Loop() error { return doActions(actionsFor(tui.ClickHeader)) } - if t.headerVisible && t.headerLinesWindow != nil && t.headerLinesWindow.Enclose(my, mx) { + if clicked && t.headerVisible && t.headerLinesWindow != nil && t.headerLinesWindow.Enclose(my, mx) { mx -= t.headerLinesWindow.Left() + t.headerIndent(t.headerLinesShape) my -= t.headerLinesWindow.Top() if mx < 0 { @@ -5616,7 +5620,7 @@ func (t *Terminal) Loop() error { } // Scrollbar dragging - barDragging = me.Down && (barDragging || clicked && my >= min && mx == t.window.Width()-1) + barDragging = me.Down && (barDragging || click && my >= min && mx == t.window.Width()-1) if barDragging { barLength, barStart := t.getScrollbar() if barLength > 0 { @@ -5649,7 +5653,6 @@ func (t *Terminal) Loop() error { return doActions(actionsFor(tui.DoubleClick)) } } - break } if me.Down { @@ -5661,40 +5664,40 @@ func (t *Terminal) Loop() error { t.vset(cy) req(reqList) evt := tui.RightClick - if me.Mod { + if me.Mod() { evt = tui.SRightClick } if me.Left { evt = tui.LeftClick - if me.Mod { + if me.Mod() { evt = tui.SLeftClick } } return doActions(actionsFor(evt)) - } else if t.headerVisible && t.headerWindow == nil { - // Header - // TODO: Should we trigger this on mouse up instead? - numLines := t.visibleHeaderLinesInList() - lineOffset := 0 - if t.inputWindow == nil && !t.headerFirst { - // offset for info line - if t.noSeparatorLine() { - lineOffset = 1 - } else { - lineOffset = 2 - } + } + } + if clicked && t.headerVisible && t.headerWindow == nil { + // Header + numLines := t.visibleHeaderLinesInList() + lineOffset := 0 + if t.inputWindow == nil && !t.headerFirst { + // offset for info line + if t.noSeparatorLine() { + lineOffset = 1 + } else { + lineOffset = 2 } - my -= lineOffset - mx -= t.pointerLen + t.markerLen - if my >= 0 && my < numLines && mx >= 0 { - if t.layout == layoutReverse { - t.clickHeaderLine = my + 1 - } else { - t.clickHeaderLine = numLines - my - } - t.clickHeaderColumn = mx + 1 - return doActions(actionsFor(tui.ClickHeader)) + } + my -= lineOffset + mx -= t.pointerLen + t.markerLen + if my >= 0 && my < numLines && mx >= 0 { + if t.layout == layoutReverse { + t.clickHeaderLine = my + 1 + } else { + t.clickHeaderLine = numLines - my } + t.clickHeaderColumn = mx + 1 + return doActions(actionsFor(tui.ClickHeader)) } } case actReload, actReloadSync: diff --git a/src/tui/light.go b/src/tui/light.go index 56e9ae0b..54c38c18 100644 --- a/src/tui/light.go +++ b/src/tui/light.go @@ -626,15 +626,13 @@ func (r *LightRenderer) mouseSequence(sz *int) Event { // middle := t & 0b1 left := t&0b11 == 0 - - // shift := t & 0b100 - // ctrl := t & 0b1000 - mod := t&0b1100 > 0 - - drag := t&0b100000 > 0 + ctrl := t&0b10000 > 0 + alt := t&0b01000 > 0 + shift := t&0b00100 > 0 + drag := t&0b100000 > 0 // 32 if scroll != 0 { - return Event{Mouse, 0, &MouseEvent{y, x, scroll, false, false, false, mod}} + return Event{Mouse, 0, &MouseEvent{y, x, scroll, false, false, false, ctrl, alt, shift}} } double := false @@ -658,7 +656,7 @@ func (r *LightRenderer) mouseSequence(sz *int) Event { } } } - return Event{Mouse, 0, &MouseEvent{y, x, 0, left, down, double, mod}} + return Event{Mouse, 0, &MouseEvent{y, x, 0, left, down, double, ctrl, alt, shift}} } func (r *LightRenderer) smcup() { diff --git a/src/tui/tcell.go b/src/tui/tcell.go index 3c3e13fc..3738214a 100644 --- a/src/tui/tcell.go +++ b/src/tui/tcell.go @@ -266,7 +266,11 @@ func (r *FullscreenRenderer) GetChar() Event { // so mouse click is three consecutive events, but the first and last are indistinguishable from movement events (with released buttons) // dragging has same structure, it only repeats the middle (main) event appropriately x, y := ev.Position() - mod := ev.Modifiers() != 0 + + mod := ev.Modifiers() + ctrl := (mod & tcell.ModCtrl) > 0 + alt := (mod & tcell.ModAlt) > 0 + shift := (mod & tcell.ModShift) > 0 // since we dont have mouse down events (unlike LightRenderer), we need to track state in prevButton prevButton, button := _prevMouseButton, ev.Buttons() @@ -275,9 +279,9 @@ func (r *FullscreenRenderer) GetChar() Event { switch { case button&tcell.WheelDown != 0: - return Event{Mouse, 0, &MouseEvent{y, x, -1, false, false, false, mod}} + return Event{Mouse, 0, &MouseEvent{y, x, -1, false, false, false, ctrl, alt, shift}} case button&tcell.WheelUp != 0: - return Event{Mouse, 0, &MouseEvent{y, x, +1, false, false, false, mod}} + return Event{Mouse, 0, &MouseEvent{y, x, +1, false, false, false, ctrl, alt, shift}} case button&tcell.Button1 != 0: double := false if !drag { @@ -300,9 +304,9 @@ func (r *FullscreenRenderer) GetChar() Event { } } // fire single or double click event - return Event{Mouse, 0, &MouseEvent{y, x, 0, true, !double, double, mod}} + return Event{Mouse, 0, &MouseEvent{y, x, 0, true, !double, double, ctrl, alt, shift}} case button&tcell.Button2 != 0: - return Event{Mouse, 0, &MouseEvent{y, x, 0, false, true, false, mod}} + return Event{Mouse, 0, &MouseEvent{y, x, 0, false, true, false, ctrl, alt, shift}} default: // double and single taps on Windows don't quite work due to // the console acting on the events and not allowing us @@ -311,7 +315,11 @@ func (r *FullscreenRenderer) GetChar() Event { down := left || button&tcell.Button3 != 0 double := false - return Event{Mouse, 0, &MouseEvent{y, x, 0, left, down, double, mod}} + // No need to report mouse movement events when no button is pressed + if drag { + return Event{Invalid, 0, nil} + } + return Event{Mouse, 0, &MouseEvent{y, x, 0, left, down, double, ctrl, alt, shift}} } // process keyboard: diff --git a/src/tui/tui.go b/src/tui/tui.go index eab5ad7e..fe8fc243 100644 --- a/src/tui/tui.go +++ b/src/tui/tui.go @@ -150,6 +150,10 @@ func (e Event) Comparable() Event { } func (e Event) KeyName() string { + if me := e.MouseEvent; me != nil { + return me.Name() + } + if e.Type >= Invalid { return "" } @@ -367,7 +371,37 @@ type MouseEvent struct { Left bool Down bool Double bool - Mod bool + Ctrl bool + Alt bool + Shift bool +} + +func (e MouseEvent) Mod() bool { + return e.Ctrl || e.Alt || e.Shift +} + +func (e MouseEvent) Name() string { + name := "" + if e.Down { + return name + } + + if e.Ctrl { + name += "ctrl-" + } + if e.Alt { + name += "alt-" + } + if e.Shift { + name += "shift-" + } + if e.Double { + name += "double-" + } + if !e.Left { + name += "right-" + } + return name + "click" } type BorderShape int |
