commit a5a65006ca58cc2edd9a14056938d8a31c50641d
parent d76769752bd83ebee8c12ffbfd5a5add99a057eb
Author: Daniel Barlow <dan@telent.net>
Date: Tue, 17 Jan 2023 21:43:53 +0000
make keyboard completions work
1. When adding a button to flowbox, the flowbox widget interposes a
flowboxchild between the two, which can receive events and so becomes
part of the tab order. this is why our buttons weren't getting
activated - they weren't focused even though they looked focused. So,
use labels instead of buttons for completions
2. For some reason I don't understand, flowboxchild widgets receive
:activate *only* on keyboard activation. So instead of using it,
we connect to :child-activated on the flowbox.
3. Setting widget:on_foo *adds* a handler to the widget instead of
replacing what was previously there. There is no nice way to remove
handlers from a widget either. Because we need a new
on_child_activated handler every time the completions change,
the only way I can see to achieve this is to create the flowbox
afresh on each keystroke instead of creating it once when the
frame is created
Diffstat:
3 files changed, 34 insertions(+), 15 deletions(-)
diff --git a/command.fnl b/command.fnl
@@ -20,7 +20,7 @@
(fn completion [{: widget : text : value }]
(let [value (or value (assert text "must have text"))]
{
- :widget (or widget (Gtk.Button { :label text }))
+ :widget (or widget (Gtk.Label { :label text }))
: text
: value
}))
@@ -52,7 +52,7 @@
(icollect [v (_G.history:find-distinct term)]
(let [label (.. v.url " " (or v.title ""))]
(completion { :text v.url
- :widget (Gtk.Button { : label })
+ :widget (Gtk.Label { : label })
:value v.url
})))
[])]
@@ -147,6 +147,8 @@
(if (not result.active)
(completions-widget:hide))
(set entry.text (or result.default result.error ""))
+ (if result.active (entry:grab_focus))
+
(when widget.parent
(widget.parent:set_visible_child_name
(if result.active "commander" "echo-area"))))
@@ -155,14 +157,29 @@
(let [parent self.completions-widget
set-completions
(fn [completions]
- (parent:foreach #(parent:remove $1))
- (each [_ c (pairs completions)]
- (parent:add
- (doto c.widget
- (tset :on_clicked
- #(update-widget-state
- self
- (self:on-input-finished c.text))))))
+ (let [flowbox (Gtk.FlowBox {
+ :activate_on_single_click true
+ :selection_mode Gtk.SelectionMode.SINGLE
+ })
+ ;; I don't know why, but the flowboxchild activate signal
+ ;; is working only for keyboard activation not for
+ ;; clicking. So instead of using it we connect to
+ ;; child_activated, and use kids-map to find out which
+ ;; child it was
+ kids-map {}]
+ (parent:foreach #(parent:remove $1)) ; expect only 1 direct child
+ (each [_ c (pairs completions)]
+ (let [fbc (Gtk.FlowBoxChild)]
+ (tset kids-map fbc c.text)
+ (fbc:add c.widget)
+ (flowbox:add fbc)))
+ (tset flowbox :on_child_activated
+ (fn [_self child]
+ (match (. kids-map child)
+ text (update-widget-state
+ self
+ (self:on-input-finished text)))))
+ (parent:add flowbox))
(parent:show_all))]
(match self.state
{:command c :this-param param-name}
@@ -210,7 +227,7 @@
prompt (Gtk.Label { :label ""})
box (Gtk.Box { :orientation Gtk.Orientation.VERTICAL })
hbox (Gtk.Box { :orientation Gtk.Orientation.HORIZONTAL })
- completions (Gtk.FlowBox)
+ completions (Gtk.Box { :orientation Gtk.Orientation.VERTICAL })
self {
:state default-state
: activate
diff --git a/musing.md b/musing.md
@@ -66,18 +66,20 @@ focus from entry to step through the completions then RET activates
* [done] back binding
* [done] save url history, use it in completions
* [done] autocomplete command name
+* [done] keyboard navigation of completions
* custom rendering for completions (e.g. buffer thumbnails)
* less ugly default completions rendering
* buffer name is often going to be useless. find buffers by url/title
still need some 1:1 mapping between the buffer object and
a text-representable form of same
-* bind event to echo-area click, ideally dependent on what's being shown
- in there
+* bind event to echo-area click, ideally dependent on what's being shown in there
* in general, can we bind commands to widget events?
* command to create new buffer
-* keyboard navigation of completions
* suppress "Return is undefined" message after a command executes
----
+
+can we increase the testability? e.g. for command processing,
+define a command, feed in some keystrokes,
diff --git a/search.fnl b/search.fnl
@@ -35,7 +35,7 @@
(completion {
:text keyword
:value keyword
- :widget (Gtk.Button { :label name })
+ :widget (Gtk.Label { :label name })
}))))