Menus, context menus and keyboard shortcuts
A menu bar, submenus and menu items are views. A menu item's items are an ItemsControl's, so they are patched as a list's. A context menu wraps a view: the view and its menu are two places of the wrapper. Keys are matched in the window's KeyDown, after the focused control had its chance: an enabled menu item in the view whose shortcut it is, or else the app's #:keys. Avalonia's own key bindings take an ICommand, which a Bjolang program cannot make. app-press! presses keys for a test. 🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8) Co-Authored-By: eca-agent <git@eca.dev>
This commit is contained in:
parent
1e46202eef
commit
be288ac03e
3 changed files with 300 additions and 28 deletions
61
src/view.bjo
61
src/view.bjo
|
|
@ -20,9 +20,13 @@
|
|||
;; Constructors.
|
||||
text button text-input text-area checkbox slider progress-bar dropdown image
|
||||
list-box tabs tab
|
||||
menu-bar submenu menu-item menu-separator
|
||||
column row grid scroll border
|
||||
;; Wrappers.
|
||||
keyed at paint
|
||||
keyed at paint with-context-menu
|
||||
|
||||
;; Keyboard shortcuts.
|
||||
Key key key-gesture key-message
|
||||
|
||||
;; Reading one.
|
||||
view-kind view-props view-events view-children view-key)
|
||||
|
|
@ -37,6 +41,7 @@
|
|||
(type (: Kind (Union TextKind ButtonKind TextInputKind TextAreaKind CheckBoxKind
|
||||
SliderKind ProgressKind DropdownKind ImageKind
|
||||
ListBoxKind TabsKind TabKind
|
||||
MenuBarKind MenuItemKind SeparatorKind ContextKind ContextMenuKind
|
||||
ColumnKind RowKind GridKind ScrollKind BorderKind)))
|
||||
|
||||
;; A property of a control. A kind takes only some of them; the app ignores
|
||||
|
|
@ -70,7 +75,8 @@
|
|||
(: Foreground string) ; the colour of the text
|
||||
(: BorderColour string)
|
||||
(: BorderWidth double)
|
||||
(: Radius double)))) ; how round a border's corners are
|
||||
(: Radius double) ; how round a border's corners are
|
||||
(: Shortcut string)))) ; a menu item's keys, as "Ctrl+S"
|
||||
|
||||
;; An event, and the message it sends.
|
||||
(type (: (On %m) (Union (: Click %m)
|
||||
|
|
@ -124,6 +130,12 @@
|
|||
(defun (with props make c) (if (string-empty? c) props (vec-add props (make c))))
|
||||
(record-set v (props (with (with (.props v) Foreground colour) Background background))))
|
||||
|
||||
;; The view, with a menu that opens when it is clicked with the right button.
|
||||
;; The items are made by `menu-item`, `submenu` and `menu-separator`.
|
||||
(: with-context-menu (-> (View %m) #:rest (View %m) (View %m)))
|
||||
(defun (with-context-menu v #:rest items)
|
||||
(container ContextKind [] [v (container ContextMenuKind [] (array->vec items))]))
|
||||
|
||||
;; The view, in a cell of its grid. Rows and columns count from 0.
|
||||
(: at (-> int int (View %m) (#:row-span int) (#:column-span int) (View %m)))
|
||||
(defun (at row column v #:row-span 1 #:column-span 1)
|
||||
|
|
@ -261,6 +273,33 @@
|
|||
(defun (tab header content)
|
||||
(View (kind TabKind) (props [(Label header)]) (events []) (children [content]) (key None)))
|
||||
|
||||
;; A bar of menus, made by `submenu`, for the top of the window.
|
||||
(: (-> #:rest (View %m) (View %m)))
|
||||
(defun (menu-bar #:rest menus)
|
||||
(container MenuBarKind [] (array->vec menus)))
|
||||
|
||||
;; A menu of items, in a menu bar or in another menu.
|
||||
(: (-> string #:rest (View %m) (View %m)))
|
||||
(defun (submenu label #:rest items)
|
||||
(View (kind MenuItemKind) (props [(Label label)]) (events []) (children (array->vec items))
|
||||
(key None)))
|
||||
|
||||
;; An item that sends `msg` when it is chosen. `#:shortcut`, such as
|
||||
;; "Ctrl+S", is shown beside it, and the keys send `msg` too while the item
|
||||
;; is in the view and enabled.
|
||||
(: (-> string %m (#:shortcut string) (#:enabled bool) (View %m)))
|
||||
(defun (menu-item label msg #:shortcut "" #:enabled #t)
|
||||
(leaf MenuItemKind
|
||||
(if (string-empty? shortcut)
|
||||
[(Label label) (Enabled enabled)]
|
||||
[(Label label) (Enabled enabled) (Shortcut shortcut)])
|
||||
[(Click msg)]))
|
||||
|
||||
;; A line between items of a menu.
|
||||
(: (-> (View %m)))
|
||||
(defun (menu-separator)
|
||||
(leaf SeparatorKind [] []))
|
||||
|
||||
;; A checkbox that is `checked?`. A click sends `(on-toggle checked)`, with
|
||||
;; the new state.
|
||||
(: (-> string bool (-> bool %m) (#:enabled bool) (#:margin double) (View %m)))
|
||||
|
|
@ -321,3 +360,21 @@
|
|||
(: (-> (View %m) (#:width double) (#:height double) (#:margin double) (View %m)))
|
||||
(defun (scroll child #:width 0.0 #:height 0.0 #:margin 0.0)
|
||||
(container ScrollKind (with-layout [] width height margin 0.0) [child]))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Keyboard shortcuts
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
;; Keys, such as "Ctrl+N" or "F5", and the message they send.
|
||||
(type (: (Key %m) (Record (: gesture string) (: msg %m))))
|
||||
|
||||
;; `msg` is sent when `gesture` is pressed in the window, and no control that
|
||||
;; has the keyboard takes the keys itself.
|
||||
(: key (-> string %m (Key %m)))
|
||||
(defun (key gesture msg) (Key (gesture gesture) (msg msg)))
|
||||
|
||||
(: key-gesture (-> (Key %m) string))
|
||||
(defun (key-gesture k) (.gesture k))
|
||||
|
||||
(: key-message (-> (Key %m) %m))
|
||||
(defun (key-message k) (.msg k))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue