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
main
Daniel Barlow 2023-01-17 21:43:53 +00:00
parent d76769752b
commit a5a65006ca
3 changed files with 34 additions and 15 deletions

View File

@ -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

View File

@ -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,

View File

@ -35,7 +35,7 @@
(completion {
:text keyword
:value keyword
:widget (Gtk.Button { :label name })
:widget (Gtk.Label { :label name })
}))))