mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-23 08:47:47 +02:00
Make the filter labels clickable
This commit is contained in:
@@ -11,6 +11,12 @@ import (
|
|||||||
|
|
||||||
var _ = fmt.Print
|
var _ = fmt.Print
|
||||||
|
|
||||||
|
type single_line_region struct {
|
||||||
|
x, width, y int
|
||||||
|
id string
|
||||||
|
callback func(string) error
|
||||||
|
}
|
||||||
|
|
||||||
func (h *Handler) draw_footer() (num_lines int, err error) {
|
func (h *Handler) draw_footer() (num_lines int, err error) {
|
||||||
lines := []string{}
|
lines := []string{}
|
||||||
screen_width := h.screen_size.width
|
screen_width := h.screen_size.width
|
||||||
@@ -20,26 +26,41 @@ func (h *Handler) draw_footer() (num_lines int, err error) {
|
|||||||
pos := 0
|
pos := 0
|
||||||
current_style := sctx.SprintFunc("italic fg=green intense")
|
current_style := sctx.SprintFunc("italic fg=green intense")
|
||||||
non_current_style := sctx.SprintFunc("dim")
|
non_current_style := sctx.SprintFunc("dim")
|
||||||
w := func(text string, sfunc func(...any) string) {
|
var crs []single_line_region
|
||||||
sz := wcswidth.Stringwidth(text)
|
w := func(presep, text string, sfunc func(...any) string, click_name string) {
|
||||||
|
sz := len(presep)
|
||||||
if sz+pos >= screen_width {
|
if sz+pos >= screen_width {
|
||||||
lines = append(lines, buf.String())
|
lines = append(lines, buf.String())
|
||||||
pos = 0
|
pos = 0
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
}
|
}
|
||||||
|
buf.WriteString(presep)
|
||||||
|
pos += sz
|
||||||
|
sz = wcswidth.Stringwidth(text)
|
||||||
if sfunc != nil {
|
if sfunc != nil {
|
||||||
text = sfunc(text)
|
text = sfunc(text)
|
||||||
}
|
}
|
||||||
buf.WriteString(text)
|
buf.WriteString(text)
|
||||||
|
if click_name != "" {
|
||||||
|
crs = append(crs, single_line_region{x: pos, width: sz, y: len(lines), id: click_name, callback: func(filter string) error {
|
||||||
|
h.set_filter(filter)
|
||||||
|
h.state.redraw_needed = true
|
||||||
|
return nil
|
||||||
|
}})
|
||||||
|
}
|
||||||
pos += sz
|
pos += sz
|
||||||
}
|
}
|
||||||
w(" Filter:", nil)
|
w("", " Filter:", nil, "")
|
||||||
for _, name := range h.state.filter_names {
|
for _, name := range h.state.filter_names {
|
||||||
w(" "+name, utils.IfElse(name == h.state.current_filter, current_style, non_current_style))
|
w(" ", name, utils.IfElse(name == h.state.current_filter, current_style, non_current_style), name)
|
||||||
}
|
}
|
||||||
if s := buf.String(); s != "" {
|
if s := buf.String(); s != "" {
|
||||||
lines = append(lines, s)
|
lines = append(lines, s)
|
||||||
}
|
}
|
||||||
|
offset := h.screen_size.height - len(lines)
|
||||||
|
for _, cr := range crs {
|
||||||
|
h.state.mouse_state.AddCellRegion(cr.id, cr.x, cr.y+offset, cr.x+cr.width, cr.y+offset, cr.callback)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if len(lines) > 0 {
|
if len(lines) > 0 {
|
||||||
h.lp.MoveCursorTo(1, h.screen_size.height-len(lines)+1)
|
h.lp.MoveCursorTo(1, h.screen_size.height-len(lines)+1)
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/kovidgoyal/kitty/tools/cli"
|
"github.com/kovidgoyal/kitty/tools/cli"
|
||||||
"github.com/kovidgoyal/kitty/tools/config"
|
"github.com/kovidgoyal/kitty/tools/config"
|
||||||
"github.com/kovidgoyal/kitty/tools/tty"
|
"github.com/kovidgoyal/kitty/tools/tty"
|
||||||
|
"github.com/kovidgoyal/kitty/tools/tui"
|
||||||
"github.com/kovidgoyal/kitty/tools/tui/loop"
|
"github.com/kovidgoyal/kitty/tools/tui/loop"
|
||||||
"github.com/kovidgoyal/kitty/tools/tui/readline"
|
"github.com/kovidgoyal/kitty/tools/tui/readline"
|
||||||
"github.com/kovidgoyal/kitty/tools/utils"
|
"github.com/kovidgoyal/kitty/tools/utils"
|
||||||
@@ -116,6 +117,8 @@ type State struct {
|
|||||||
selections []string
|
selections []string
|
||||||
current_idx CollectionIndex
|
current_idx CollectionIndex
|
||||||
last_render render_state
|
last_render render_state
|
||||||
|
mouse_state tui.MouseState
|
||||||
|
redraw_needed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s State) BaseDir() string { return utils.IfElse(s.base_dir == "", default_cwd, s.base_dir) }
|
func (s State) BaseDir() string { return utils.IfElse(s.base_dir == "", default_cwd, s.base_dir) }
|
||||||
@@ -182,9 +185,11 @@ type Handler struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) draw_screen() (err error) {
|
func (h *Handler) draw_screen() (err error) {
|
||||||
|
h.state.redraw_needed = false
|
||||||
h.lp.StartAtomicUpdate()
|
h.lp.StartAtomicUpdate()
|
||||||
defer h.lp.EndAtomicUpdate()
|
defer h.lp.EndAtomicUpdate()
|
||||||
h.lp.ClearScreen()
|
h.lp.ClearScreen()
|
||||||
|
h.state.mouse_state.ClearCellRegions()
|
||||||
switch h.state.screen {
|
switch h.state.screen {
|
||||||
case NORMAL:
|
case NORMAL:
|
||||||
matches, is_complete := h.get_results()
|
matches, is_complete := h.get_results()
|
||||||
@@ -202,6 +207,8 @@ func (h *Handler) draw_screen() (err error) {
|
|||||||
case SAVE_FILE:
|
case SAVE_FILE:
|
||||||
err = h.draw_save_file_name_screen()
|
err = h.draw_save_file_name_screen()
|
||||||
}
|
}
|
||||||
|
h.state.mouse_state.UpdateHoveredIds()
|
||||||
|
h.state.mouse_state.ApplyHoverStyles(h.lp)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -402,6 +409,19 @@ func (h *Handler) OnKeyEvent(ev *loop.KeyEvent) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Handler) OnMouseEvent(event *loop.MouseEvent) (err error) {
|
||||||
|
h.state.redraw_needed = h.state.mouse_state.UpdateState(event)
|
||||||
|
if event.Event_type == loop.MOUSE_CLICK && event.Buttons&loop.LEFT_MOUSE_BUTTON != 0 {
|
||||||
|
if err = h.state.mouse_state.ClickHoveredRegions(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if h.state.redraw_needed {
|
||||||
|
err = h.draw_screen()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (h *Handler) OnText(text string, from_key_event, in_bracketed_paste bool) (err error) {
|
func (h *Handler) OnText(text string, from_key_event, in_bracketed_paste bool) (err error) {
|
||||||
switch h.state.screen {
|
switch h.state.screen {
|
||||||
case NORMAL:
|
case NORMAL:
|
||||||
@@ -533,6 +553,7 @@ func main(_ *cli.Command, opts *Options, args []string) (rc int, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return 1, err
|
return 1, err
|
||||||
}
|
}
|
||||||
|
lp.MouseTrackingMode(loop.FULL_MOUSE_TRACKING)
|
||||||
handler := Handler{lp: lp, err_chan: make(chan error, 8), rl: readline.New(lp, readline.RlInit{
|
handler := Handler{lp: lp, err_chan: make(chan error, 8), rl: readline.New(lp, readline.RlInit{
|
||||||
Prompt: "> ", ContinuationPrompt: ". ",
|
Prompt: "> ", ContinuationPrompt: ". ",
|
||||||
})}
|
})}
|
||||||
@@ -571,6 +592,7 @@ func main(_ *cli.Command, opts *Options, args []string) (rc int, err error) {
|
|||||||
}
|
}
|
||||||
lp.OnKeyEvent = handler.OnKeyEvent
|
lp.OnKeyEvent = handler.OnKeyEvent
|
||||||
lp.OnText = handler.OnText
|
lp.OnText = handler.OnText
|
||||||
|
lp.OnMouseEvent = handler.OnMouseEvent
|
||||||
lp.OnWakeup = func() (err error) {
|
lp.OnWakeup = func() (err error) {
|
||||||
select {
|
select {
|
||||||
case err = <-handler.err_chan:
|
case err = <-handler.err_chan:
|
||||||
|
|||||||
@@ -205,10 +205,15 @@ func (ms *MouseSelection) DragScroll(ev *loop.MouseEvent, lp *loop.Loop, callbac
|
|||||||
ms.drag_scroll.mouse_event = *ev
|
ms.drag_scroll.mouse_event = *ev
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Point struct {
|
||||||
|
X, Y int
|
||||||
|
}
|
||||||
|
|
||||||
type CellRegion struct {
|
type CellRegion struct {
|
||||||
TopLeft, BottomRight struct{ X, Y int }
|
TopLeft, BottomRight Point
|
||||||
Id string
|
Id string
|
||||||
OnClick []func(id string) error
|
OnClick []func(id string) error
|
||||||
|
PointerShape loop.PointerShape
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c CellRegion) Contains(x, y int) bool { // 0-based
|
func (c CellRegion) Contains(x, y int) bool { // 0-based
|
||||||
@@ -224,6 +229,7 @@ type MouseState struct {
|
|||||||
|
|
||||||
regions []*CellRegion
|
regions []*CellRegion
|
||||||
region_id_map map[string][]*CellRegion
|
region_id_map map[string][]*CellRegion
|
||||||
|
region_line_map map[int][]*CellRegion
|
||||||
hovered_ids *utils.Set[string]
|
hovered_ids *utils.Set[string]
|
||||||
default_url_style struct {
|
default_url_style struct {
|
||||||
value string
|
value string
|
||||||
@@ -231,25 +237,35 @@ type MouseState struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MouseState) AddCellRegion(id string, start_x, start_y, end_x, end_y int, on_click ...func(id string) error) *CellRegion {
|
func (m *MouseState) AddRegion(cr CellRegion) *CellRegion {
|
||||||
cr := CellRegion{TopLeft: struct{ X, Y int }{start_x, start_y}, BottomRight: struct{ X, Y int }{end_x, end_y}, Id: id, OnClick: on_click}
|
|
||||||
m.regions = append(m.regions, &cr)
|
m.regions = append(m.regions, &cr)
|
||||||
if m.region_id_map == nil {
|
if m.region_id_map == nil {
|
||||||
m.region_id_map = make(map[string][]*CellRegion)
|
m.region_id_map = make(map[string][]*CellRegion)
|
||||||
|
m.region_line_map = make(map[int][]*CellRegion)
|
||||||
|
}
|
||||||
|
m.region_id_map[cr.Id] = append(m.region_id_map[cr.Id], &cr)
|
||||||
|
for y := cr.TopLeft.Y; y <= cr.BottomRight.Y; y++ {
|
||||||
|
m.region_line_map[y] = append(m.region_line_map[y], &cr)
|
||||||
}
|
}
|
||||||
m.region_id_map[id] = append(m.region_id_map[id], &cr)
|
|
||||||
return &cr
|
return &cr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *MouseState) AddCellRegion(id string, start_x, start_y, end_x, end_y int, on_click ...func(id string) error) *CellRegion {
|
||||||
|
cr := CellRegion{
|
||||||
|
TopLeft: Point{start_x, start_y}, BottomRight: Point{end_x, end_y}, Id: id, OnClick: on_click, PointerShape: loop.POINTER_POINTER}
|
||||||
|
return m.AddRegion(cr)
|
||||||
|
}
|
||||||
|
|
||||||
func (m *MouseState) ClearCellRegions() {
|
func (m *MouseState) ClearCellRegions() {
|
||||||
m.regions = nil
|
m.regions = nil
|
||||||
m.region_id_map = nil
|
m.region_id_map = nil
|
||||||
m.hovered_ids = nil
|
m.hovered_ids = nil
|
||||||
|
m.region_line_map = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MouseState) UpdateHoveredIds() (changed bool) {
|
func (m *MouseState) UpdateHoveredIds() (changed bool) {
|
||||||
h := utils.NewSet[string]()
|
h := utils.NewSet[string]()
|
||||||
for _, r := range m.regions {
|
for _, r := range m.region_line_map[m.Cell.Y] {
|
||||||
if r.Contains(m.Cell.X, m.Cell.Y) {
|
if r.Contains(m.Cell.X, m.Cell.Y) {
|
||||||
h.Add(r.Id)
|
h.Add(r.Id)
|
||||||
}
|
}
|
||||||
@@ -260,7 +276,8 @@ func (m *MouseState) UpdateHoveredIds() (changed bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *MouseState) ApplyHoverStyles(lp *loop.Loop, style ...string) {
|
func (m *MouseState) ApplyHoverStyles(lp *loop.Loop, style ...string) {
|
||||||
if m.hovered_ids == nil {
|
if m.hovered_ids == nil || m.hovered_ids.Len() == 0 {
|
||||||
|
lp.ClearPointerShapes()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
hs := ""
|
hs := ""
|
||||||
@@ -287,15 +304,17 @@ func (m *MouseState) ApplyHoverStyles(lp *loop.Loop, style ...string) {
|
|||||||
hs = style[0]
|
hs = style[0]
|
||||||
}
|
}
|
||||||
is_hovered := false
|
is_hovered := false
|
||||||
|
ps := loop.DEFAULT_POINTER
|
||||||
for id := range m.hovered_ids.Iterable() {
|
for id := range m.hovered_ids.Iterable() {
|
||||||
for _, r := range m.region_id_map[id] {
|
for _, r := range m.region_id_map[id] {
|
||||||
lp.StyleRegion(hs, r.TopLeft.X, r.TopLeft.Y, r.BottomRight.X, r.BottomRight.Y)
|
lp.StyleRegion(hs, r.TopLeft.X, r.TopLeft.Y, r.BottomRight.X, r.BottomRight.Y)
|
||||||
is_hovered = true
|
is_hovered = true
|
||||||
|
ps = r.PointerShape
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if is_hovered {
|
if is_hovered {
|
||||||
if s, has := lp.CurrentPointerShape(); !has || s != loop.POINTER_POINTER {
|
if s, has := lp.CurrentPointerShape(); !has || s != ps {
|
||||||
lp.PushPointerShape(loop.POINTER_POINTER)
|
lp.PushPointerShape(ps)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
lp.ClearPointerShapes()
|
lp.ClearPointerShapes()
|
||||||
|
|||||||
Reference in New Issue
Block a user