Use hand pointer when hovering over buttons in ask kitten

This commit is contained in:
Kovid Goyal
2023-10-15 21:35:51 +05:30
parent d66074f19f
commit 17ce474b79
4 changed files with 81 additions and 10 deletions

View File

@@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"io" "io"
"kitty/tools/cli/markup" "kitty/tools/cli/markup"
"kitty/tools/tty"
"kitty/tools/tui/loop" "kitty/tools/tui/loop"
"kitty/tools/utils" "kitty/tools/utils"
"kitty/tools/utils/style" "kitty/tools/utils/style"
@@ -60,13 +61,16 @@ func extra_for(width, screen_width int) int {
return max(0, screen_width-width)/2 + 1 return max(0, screen_width-width)/2 + 1
} }
var debugprintln = tty.DebugPrintln
var _ = debugprintln
func GetChoices(o *Options) (response string, err error) { func GetChoices(o *Options) (response string, err error) {
response = "" response = ""
lp, err := loop.New() lp, err := loop.New()
if err != nil { if err != nil {
return "", err return "", err
} }
lp.MouseTrackingMode(loop.BUTTONS_ONLY_MOUSE_TRACKING) lp.MouseTrackingMode(loop.FULL_MOUSE_TRACKING)
prefix_style_pat := regexp.MustCompile("^(?:\x1b\\[[^m]*?m)+") prefix_style_pat := regexp.MustCompile("^(?:\x1b\\[[^m]*?m)+")
choice_order := make([]Choice, 0, len(o.Choices)) choice_order := make([]Choice, 0, len(o.Choices))
@@ -398,16 +402,31 @@ func GetChoices(o *Options) (response string, err error) {
} }
lp.OnMouseEvent = func(ev *loop.MouseEvent) error { lp.OnMouseEvent = func(ev *loop.MouseEvent) error {
if ev.Event_type == loop.MOUSE_CLICK { on_letter := ""
for letter, ranges := range clickable_ranges { for letter, ranges := range clickable_ranges {
for _, r := range ranges { for _, r := range ranges {
if r.has_point(ev.Cell.X, ev.Cell.Y) { if r.has_point(ev.Cell.X, ev.Cell.Y) {
response = letter on_letter = letter
lp.Quit(0) break
return nil
}
} }
} }
}
if on_letter != "" {
if s, has_shape := lp.CurrentPointerShape(); !has_shape && s != loop.POINTER_POINTER {
lp.PushPointerShape(loop.POINTER_POINTER)
}
} else {
if _, has_shape := lp.CurrentPointerShape(); has_shape {
lp.PopPointerShape()
}
}
if ev.Event_type == loop.MOUSE_CLICK {
if on_letter != "" {
response = on_letter
lp.Quit(0)
return nil
}
if hidden_text != "" && replacement_range.has_point(ev.Cell.X, ev.Cell.Y) { if hidden_text != "" && replacement_range.has_point(ev.Cell.X, ev.Cell.Y) {
unhide() unhide()
} }

View File

@@ -1741,7 +1741,8 @@ def set_pointer_shape(screen: Screen, value: str, os_window_id: int = 0) -> str:
value = value[1:] value = value[1:]
if op in '=>': if op in '=>':
for v in value.split(','): for v in value.split(','):
screen.change_pointer_shape(op, v) if v:
screen.change_pointer_shape(op, v)
if os_window_id and current_focused_os_window_id() == os_window_id: if os_window_id and current_focused_os_window_id() == os_window_id:
update_pointer_shape(os_window_id) update_pointer_shape(os_window_id)
elif op == '<': elif op == '<':

View File

@@ -54,6 +54,7 @@ type Loop struct {
style_cache map[string]func(...any) string style_cache map[string]func(...any) string
style_ctx style.Context style_ctx style.Context
atomic_update_active bool atomic_update_active bool
pointer_shapes []PointerShape
// Suspend the loop restoring terminal state, and run the provided function. When it returns terminal state is // Suspend the loop restoring terminal state, and run the provided function. When it returns terminal state is
// put back to what it was before suspending unless the function returns an error or an error occurs saving/restoring state. // put back to what it was before suspending unless the function returns an error or an error occurs saving/restoring state.
@@ -449,3 +450,48 @@ func (self *Loop) CopyTextToPrimarySelection(text string) {
func (self *Loop) CopyTextToClipboard(text string) { func (self *Loop) CopyTextToClipboard(text string) {
self.copy_text_to(text, "c") self.copy_text_to(text, "c")
} }
func (self *Loop) PushPointerShape(s PointerShape) {
self.pointer_shapes = append(self.pointer_shapes, s)
self.QueueWriteString("\x1b]22;" + s.String() + "\x1b\\")
}
func (self *Loop) PopPointerShape() {
if len(self.pointer_shapes) > 0 {
self.pointer_shapes = self.pointer_shapes[:len(self.pointer_shapes)-1]
self.QueueWriteString("\x1b]22;<\x1b\\")
}
}
func (self *Loop) ClearPointerShapes() (ans []PointerShape) {
ans = self.pointer_shapes
for i := len(self.pointer_shapes) - 1; i >= 0; i-- {
self.QueueWriteString("\x1b]22;<\x1b\\")
}
self.pointer_shapes = nil
return ans
}
func (self *Loop) SetPointerShapes(ps []PointerShape) {
self.pointer_shapes = ps
if len(ps) > 0 {
s := strings.Builder{}
s.WriteString("\x1b]22;>")
for i, x := range ps {
s.WriteString(x.String())
if i+1 < len(ps) {
s.WriteByte(',')
}
}
s.WriteString("\x1b\\")
self.QueueWriteString(s.String())
}
}
func (self *Loop) CurrentPointerShape() (ans PointerShape, has_shape bool) {
if len(self.pointer_shapes) > 0 {
has_shape = true
ans = self.pointer_shapes[len(self.pointer_shapes)-1]
}
return
}

View File

@@ -346,6 +346,7 @@ func (self *Loop) run() (err error) {
self.QueueWriteString(finalizer) self.QueueWriteString(finalizer)
} }
if needs_reset_escape_codes { if needs_reset_escape_codes {
self.ClearPointerShapes()
self.QueueWriteString(self.terminal_options.ResetStateEscapeCodes()) self.QueueWriteString(self.terminal_options.ResetStateEscapeCodes())
} }
// flush queued data and wait for it to be written for a timeout, then wait for writer to shutdown // flush queued data and wait for it to be written for a timeout, then wait for writer to shutdown
@@ -365,6 +366,7 @@ func (self *Loop) run() (err error) {
} }
self.SuspendAndRun = func(run func() error) (err error) { self.SuspendAndRun = func(run func() error) (err error) {
ps := self.ClearPointerShapes()
write_id := self.QueueWriteString(self.terminal_options.ResetStateEscapeCodes()) write_id := self.QueueWriteString(self.terminal_options.ResetStateEscapeCodes())
needs_reset_escape_codes = false needs_reset_escape_codes = false
if err = self.wait_for_write_to_complete(write_id, self.tty_write_channel, write_done_channel, 2*time.Second); err != nil { if err = self.wait_for_write_to_complete(write_id, self.tty_write_channel, write_done_channel, 2*time.Second); err != nil {
@@ -386,11 +388,13 @@ func (self *Loop) run() (err error) {
return err return err
} }
write_id = self.QueueWriteString(self.terminal_options.SetStateEscapeCodes()) write_id = self.QueueWriteString(self.terminal_options.SetStateEscapeCodes())
self.SetPointerShapes(ps)
needs_reset_escape_codes = true needs_reset_escape_codes = true
return self.wait_for_write_to_complete(write_id, self.tty_write_channel, write_done_channel, 2*time.Second) return self.wait_for_write_to_complete(write_id, self.tty_write_channel, write_done_channel, 2*time.Second)
} }
self.on_SIGTSTP = func() error { self.on_SIGTSTP = func() error {
ps := self.ClearPointerShapes()
write_id := self.QueueWriteString(self.terminal_options.ResetStateEscapeCodes()) write_id := self.QueueWriteString(self.terminal_options.ResetStateEscapeCodes())
needs_reset_escape_codes = false needs_reset_escape_codes = false
err := self.wait_for_write_to_complete(write_id, self.tty_write_channel, write_done_channel, 2*time.Second) err := self.wait_for_write_to_complete(write_id, self.tty_write_channel, write_done_channel, 2*time.Second)
@@ -406,6 +410,7 @@ func (self *Loop) run() (err error) {
return err return err
} }
write_id = self.QueueWriteString(self.terminal_options.SetStateEscapeCodes()) write_id = self.QueueWriteString(self.terminal_options.SetStateEscapeCodes())
self.SetPointerShapes(ps)
needs_reset_escape_codes = true needs_reset_escape_codes = true
err = self.wait_for_write_to_complete(write_id, self.tty_write_channel, write_done_channel, 2*time.Second) err = self.wait_for_write_to_complete(write_id, self.tty_write_channel, write_done_channel, 2*time.Second)
if err != nil { if err != nil {