mirror of
https://github.com/junegunn/fzf.git
synced 2025-11-18 08:13:40 -05:00
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
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user