import { UiRadius } from "ui_radius.slint"; import { CompactButton, FontAwesomeIcons } from "common.slint"; export struct GalleryMenuItem { label: string, enabled: bool } export component GalleryMenuButton inherits Rectangle { in property text; in property primary: false; in property enabled: true; in property opens-up: false; in property menu-width: 200px; in property horizontal-padding: 12px; out property preferred-button-width: ceil(button-label.preferred-width / 1px) * 1px + root.horizontal-padding * 2 + 18px; in property <[GalleryMenuItem]> items; private property current-index: 0; callback opened; callback selected(int); height: 24px; forward-focus: trigger-focus; accessible-role: button; accessible-label: root.text; accessible-enabled: root.enabled; accessible-expandable: true; accessible-expanded: popup.is-open; accessible-action-default => { root.open-menu(); } changed enabled => { if !root.enabled { popup.close(); } } changed visible => { if !root.visible { popup.close(); } } public function close-menu() { popup.close(); } function open-menu() { if !root.enabled || root.items.length == 0 { return; } if popup.is-open { popup.close(); return; } root.opened(); root.current-index = 0; popup.show(); } function choose(index: int) { if !root.enabled || index < 0 || index >= root.items.length || !root.items[index].enabled { return; } popup.close(); trigger-focus.focus(); root.selected(index); } function handle-key(event: KeyEvent) -> EventResult { if !root.enabled { return reject; } if event.text == Key.Escape && popup.is-open { popup.close(); trigger-focus.focus(); return accept; } if event.text == Key.Return || event.text == " " { if popup.is-open { root.choose(root.current-index); } else { root.open-menu(); } return accept; } if event.text == Key.UpArrow || event.text == Key.DownArrow { if !popup.is-open { root.open-menu(); } else { root.current-index = clamp(root.current-index + (event.text == Key.UpArrow ? -1 : 1), 0, root.items.length - 1); } return accept; } return reject; } trigger-focus := FocusScope { enabled: root.enabled; key-pressed(event) => { return root.handle-key(event); } CompactButton { width: parent.width; height: parent.height; border-radius: UiRadius.control; enabled: root.enabled; primary: root.primary; clicked => { trigger-focus.focus(); root.open-menu(); } } button-label := Text { x: root.horizontal-padding; width: parent.width - root.horizontal-padding * 2 - 18px; height: parent.height; text: root.text; color: root.enabled ? #24272d : #8e9298; font-size: 11px; overflow: elide; horizontal-alignment: center; vertical-alignment: center; } Text { x: parent.width - root.horizontal-padding - 12px; width: 12px; height: parent.height; text: root.opens-up ? FontAwesomeIcons.angle-up : FontAwesomeIcons.angle-down; font-family: "Font Awesome 6 Free"; font-weight: 900; font-size: 9px; color: root.enabled ? #62666d : #8e9298; horizontal-alignment: center; vertical-alignment: center; } } popup := PopupWindow { x: root.width - self.width; y: root.opens-up ? -self.height - 4px : root.height + 4px; width: root.menu-width; height: root.items.length * 30px + 8px; close-policy: close-on-click-outside; forward-focus: popup-focus; popup-focus := FocusScope { key-pressed(event) => { return root.handle-key(event); } Rectangle { background: white; border-width: 1px; border-color: #c9cdd2; border-radius: UiRadius.menu; for item[index] in root.items: Rectangle { x: 4px; y: 4px + index * 30px; width: parent.width - 8px; height: 30px; border-radius: UiRadius.control; background: item.enabled && index == root.current-index ? #edf4fc : transparent; accessible-role: button; accessible-label: item.label; accessible-enabled: item.enabled; accessible-action-default => { root.choose(index); } Text { x: 10px; width: parent.width - 20px; height: parent.height; text: item.label; color: item.enabled ? #30343b : #92979f; font-size: 12px; vertical-alignment: center; } TouchArea { enabled: item.enabled; changed has-hover => { if self.has-hover { root.current-index = index; } } clicked => { root.choose(index); } } } } } } }