import { FlatSurface, UiSurface } from "ui_surface.slint"; import { UiRadius } from "ui_radius.slint"; export global FloatingBarStyle { out property corner-radius: UiRadius.menu; } // Fields and selection controls share a muted fill. export global ControlStyle { out property surface: #dce6f3; out property hover: #cedef1; out property pressed: #bfd2ec; out property selected: #bcd4f7; out property selected-hover: #adcaf1; out property selected-pressed: #99bce8; out property selected-text: #1d4e9e; out property disabled: #f0f2f5; out property icon: #52657f; out property input-surface: #dee7f2; out property input-focused: #e5ecf5; out property input-placeholder: #566a84; // These tints over white match the field fills for standard LineEdit. out property input-tint: #235eaa26; out property input-focus-tint: #235eaa1e; } // Action buttons use a light surface and a crisp edge, distinct from fields. export global ButtonStyle { out property surface: #ffffff; out property hover: #f0f5ff; out property pressed: #dfe9fa; out property border: #b8c7db; out property hover-border: #7e9fd1; out property pressed-border: #6488bf; out property disabled-border: #dce2eb; out property selected: #3564e3; out property selected-hover: #4574f0; out property selected-pressed: #254cc0; } // ImGui loads a 32px font atlas and applies per-window font scales. These // logical sizes reproduce those visual tiers on Slint's HiDPI renderer. export global ImGuiFontStyle { out property base: 12px; out property section-title: 18px; out property panel-label: 17px; out property field-value: 22px; out property prominent: 16px; out property body: 11px; out property small: 10px; } // Font Awesome 6 Free Solid glyphs used by the legacy ImGui client. The font // data is registered for every Slint window by GuiApplication before show(). export global FontAwesomeIcons { out property angle-down: "\u{f107}"; out property angle-left: "\u{f104}"; out property angle-right: "\u{f105}"; out property angle-up: "\u{f106}"; out property arrow-right-long: "\u{f178}"; out property arrow-up-right-from-square: "\u{f35d}"; out property bars: "\u{f0c9}"; out property check: "\u{f00c}"; out property circle-arrow-up: "\u{f0aa}"; out property code: "\u{f121}"; out property compress: "\u{f066}"; out property computer-mouse: "\u{f8cc}"; out property copy: "\u{f0c5}"; out property display: "\u{e163}"; out property expand: "\u{f065}"; out property eye: "\u{f06e}"; out property eye-slash: "\u{f070}"; out property file-lines: "\u{f15c}"; out property folder-open: "\u{f07c}"; out property keyboard: "\u{f11c}"; out property layer-group: "\u{f5fd}"; out property minus: "\u{f068}"; out property pen: "\u{f304}"; out property rotate: "\u{f2f1}"; out property signal: "\u{f012}"; out property shield-halved: "\u{f3ed}"; out property square-full: "\u{f45c}"; out property tags: "\u{f02c}"; out property trash-can: "\u{f2ed}"; out property user-secret: "\u{f21b}"; out property volume-high: "\u{f028}"; out property volume-xmark: "\u{f6a9}"; out property xmark: "\u{f00d}"; } export component IconButton inherits Rectangle { in property icon; in property tooltip; in property tooltip-above: false; in property enabled: true; in property danger: false; in property icon-size: 16px; in property icon-color: ControlStyle.icon; in property normal-background: transparent; in property hover-background: ControlStyle.hover; in property pressed-background: ControlStyle.pressed; in property slashed: false; callback clicked; width: 30px; height: 30px; background: touch.pressed ? (danger ? #d92d20 : root.pressed-background) : touch.has-hover ? (danger ? #f04438 : root.hover-background) : root.normal-background; border-radius: UiRadius.control; opacity: root.enabled ? 1 : 0.4; Text { text: root.icon; color: touch.has-hover && root.danger ? white : root.icon-color; font-family: "Font Awesome 6 Free"; font-weight: 900; font-size: root.icon-size; horizontal-alignment: center; vertical-alignment: center; } if root.slashed: Text { text: "/"; color: touch.has-hover && root.danger ? white : root.icon-color; font-size: root.icon-size + 7px; font-weight: 700; horizontal-alignment: center; vertical-alignment: center; } touch := TouchArea { enabled: root.enabled; clicked => { root.clicked(); } } if touch.has-hover && root.tooltip != "": Rectangle { x: min(root.width - self.width, 0px); y: root.tooltip-above ? -self.height - 3px : root.height + 3px; width: tip.preferred-width + 12px; height: 24px; background: #22252be8; border-radius: UiRadius.control; z: 20; tip := Text { text: root.tooltip; color: white; font-size: ImGuiFontStyle.small; horizontal-alignment: center; vertical-alignment: center; } } } // Retained for non-font artwork. UI action icons use IconButton so the Slint // and ImGui clients share Font Awesome's glyph geometry. export component ImageButton inherits Rectangle { in property icon; in property tooltip; in property danger: false; in property icon-width: 16px; in property icon-height: 16px; callback clicked; width: 30px; height: 30px; background: touch.pressed ? (danger ? #d92d20 : ControlStyle.pressed) : touch.has-hover ? (danger ? #f04438 : ControlStyle.hover) : transparent; border-radius: UiRadius.control; Image { x: (parent.width - self.width) / 2; y: (parent.height - self.height) / 2; width: root.icon-width; height: root.icon-height; source: root.icon; image-fit: contain; } touch := TouchArea { clicked => { root.clicked(); } } if touch.has-hover && root.tooltip != "": Rectangle { x: min(root.width - self.width, 0px); y: root.height + 3px; width: tip.preferred-width + 12px; height: 24px; background: #22252be8; border-radius: UiRadius.control; z: 20; tip := Text { text: root.tooltip; color: white; font-size: ImGuiFontStyle.small; horizontal-alignment: center; vertical-alignment: center; } } } export component CompactComboBox inherits FlatSurface { in property <[string]> model; in-out property current-index: 0; in property enabled: true; in property subtle: false; in property show-arrow: true; in property normal-outline-color: ButtonStyle.border; in property hover-outline-color: ButtonStyle.hover-border; property popup-animation-enabled: false; property popup-progress: 0.0; callback selected(int); // Session state can disable a setting while its popup is already open. // Remove the stale options and stop a pending reveal on every backend. changed enabled => { if !root.enabled { popup-open-timer.running = false; root.popup-animation-enabled = false; root.popup-progress = 0.0; combo-popup.close(); } } animate popup-progress { duration: 160ms; easing: ease-out; enabled: root.popup-animation-enabled; } width: 73px; height: 24px; background: !root.enabled ? ControlStyle.disabled : combo-touch.pressed ? ButtonStyle.pressed : combo-popup.is-open || combo-touch.has-hover ? ButtonStyle.hover : ButtonStyle.surface; outline-focused: root.enabled && combo-popup.is-open; outline-color: !root.enabled ? ButtonStyle.disabled-border : combo-popup.is-open ? UiSurface.focus : combo-touch.pressed ? ButtonStyle.pressed-border : combo-touch.has-hover ? root.hover-outline-color : root.normal-outline-color; border-radius: UiRadius.control; animate background, outline-color { duration: 120ms; } accessible-role: combobox; accessible-enabled: root.enabled; accessible-expandable: true; accessible-expanded: combo-popup.is-open; accessible-value: root.model[root.current-index]; Text { x: root.subtle ? 10px : 8px; width: parent.width - self.x - (root.show-arrow ? 28px : self.x); height: parent.height; text: root.model[root.current-index]; color: root.enabled ? #202124 : #8b8f95; font-size: root.subtle ? 12px : ImGuiFontStyle.body; overflow: elide; vertical-alignment: center; accessible-role: none; } if root.show-arrow: Text { x: parent.width - 26px; width: 18px; height: parent.height; // Switch glyphs so the software renderer also shows the open state. text: combo-popup.is-open ? FontAwesomeIcons.angle-up : FontAwesomeIcons.angle-down; color: !root.enabled ? #98a2b3 : combo-popup.is-open ? UiSurface.focus : ControlStyle.icon; font-family: "Font Awesome 6 Free"; font-weight: 900; font-size: 10px; horizontal-alignment: center; vertical-alignment: center; accessible-role: none; } combo-touch := TouchArea { enabled: root.enabled; mouse-cursor: pointer; clicked => { root.popup-animation-enabled = false; root.popup-progress = 0.0; combo-popup.show(); popup-open-timer.running = true; } } // Start on the next frame so the native popup first renders at zero // height, making the downward expansion visible on every backend. popup-open-timer := Timer { interval: 16ms; running: false; triggered => { self.running = false; if combo-popup.is-open { root.popup-animation-enabled = true; root.popup-progress = 1.0; } } } combo-popup := PopupWindow { x: 0px; y: root.height + 4px; width: root.width; height: root.model.length * 23px + 8px; close-policy: close-on-click-outside; FlatSurface { // Keep the reveal anchored to the popup's top edge while the // content settles downward into its final position. y: (1.0 - root.popup-progress) * -8px; width: parent.width; height: parent.height * root.popup-progress; opacity: root.popup-progress; background: white; outline-color: root.normal-outline-color; border-radius: UiRadius.menu; clip: true; for choice[index] in root.model: Rectangle { x: 4px; y: 4px + index * 23px; width: parent.width - 8px; height: 23px; border-radius: UiRadius.control; background: index == root.current-index ? (option-touch.pressed ? ButtonStyle.pressed : ButtonStyle.hover) : option-touch.pressed ? ButtonStyle.pressed : option-touch.has-hover ? ButtonStyle.hover : white; Text { x: 6px; width: parent.width - 28px; height: parent.height; text: choice; color: index == root.current-index ? ControlStyle.selected-text : #202124; font-weight: index == root.current-index ? 600 : 400; font-size: root.subtle ? 12px : ImGuiFontStyle.body; overflow: elide; vertical-alignment: center; } if index == root.current-index: Text { x: parent.width - 20px; width: 14px; height: parent.height; text: FontAwesomeIcons.check; color: UiSurface.focus; font-family: "Font Awesome 6 Free"; font-weight: 900; font-size: 10px; horizontal-alignment: center; vertical-alignment: center; accessible-role: none; } option-touch := TouchArea { enabled: root.enabled; mouse-cursor: pointer; clicked => { popup-open-timer.running = false; root.popup-animation-enabled = false; root.popup-progress = 0.0; if root.enabled && index >= 0 && index < root.model.length { root.current-index = index; root.selected(index); } combo-popup.close(); } } } FlatSurface { width: parent.width; height: parent.height; background: transparent; clip: true; outline-color: root.normal-outline-color; border-radius: UiRadius.menu; z: 2; } } } } export component ImGuiCheckBox inherits FlatSurface { in-out property checked; in property enabled: true; width: 21px; height: 21px; background: !root.enabled ? ControlStyle.disabled : root.checked ? (check-touch.pressed ? #254cc0 : check-touch.has-hover ? #4574f0 : #3564e3) : check-touch.pressed ? ControlStyle.pressed : check-touch.has-hover ? ControlStyle.hover : ControlStyle.surface; outline-color: check-touch.has-hover && root.enabled ? UiSurface.focus : UiSurface.edge; border-radius: 4px; if root.checked: Text { text: FontAwesomeIcons.check; color: root.enabled ? white : #8b8f95; font-family: "Font Awesome 6 Free"; font-weight: 900; font-size: 16px; horizontal-alignment: center; vertical-alignment: center; } check-touch := TouchArea { enabled: root.enabled; clicked => { root.checked = !root.checked; } } } export component MiniToggleSwitch inherits Rectangle { in-out property checked; in property enabled: true; callback toggled(bool); width: 29px; height: 16px; background: !root.enabled ? (root.checked ? #6e9ddacc : #9a9a9acc) : root.checked ? #2463c7 : #9a9a9a; border-radius: 8px; Rectangle { x: root.checked ? parent.width - self.width - 2px : 2px; y: 2px; width: 12px; height: 12px; border-radius: 6px; background: white; } TouchArea { enabled: root.enabled; clicked => { root.checked = !root.checked; root.toggled(root.checked); } } } export component CompactButton inherits FlatSurface { in property text; in property text-size: ImGuiFontStyle.body; in property primary: false; in property quiet: false; in property enabled: true; callback clicked; width: 34px; height: 24px; background: !root.enabled ? (root.quiet ? transparent : ControlStyle.disabled) : button-touch.pressed ? (root.primary ? #82b8f2 : ButtonStyle.pressed) : button-touch.has-hover ? (root.primary ? #b8d9fb : ButtonStyle.hover) : root.primary ? #a8d0fa : root.quiet ? transparent : ButtonStyle.surface; outline-enabled: !root.quiet; outline-color: !root.enabled ? ButtonStyle.disabled-border : root.primary ? UiSurface.focus : button-touch.pressed ? ButtonStyle.pressed-border : button-touch.has-hover ? ButtonStyle.hover-border : ButtonStyle.border; border-radius: UiRadius.control; animate background, outline-color { duration: 100ms; } Text { text: root.text; color: root.enabled ? #24272d : #8e9298; font-size: root.text-size; overflow: elide; horizontal-alignment: center; vertical-alignment: center; } button-touch := TouchArea { enabled: root.enabled; mouse-cursor: pointer; clicked => { root.clicked(); } } } export component PrimaryButton inherits FlatSurface { in property text; in property text-size: 12px; in property enabled: true; callback clicked; min-width: 72px; height: 32px; background: !root.enabled ? #b8c1d1 : touch.pressed ? #254cc0 : touch.has-hover ? #4574f0 : #3564e3; outline-enabled: false; border-radius: UiRadius.control; animate background { duration: 100ms; } Text { text: root.text; color: white; font-size: root.text-size; font-weight: 600; horizontal-alignment: center; vertical-alignment: center; } touch := TouchArea { enabled: root.enabled; clicked => { root.clicked(); } } } export component SecondaryButton inherits FlatSurface { in property text; in property text-size: 12px; in property enabled: true; callback clicked; min-width: 72px; height: 32px; background: !root.enabled ? ControlStyle.disabled : touch.pressed ? ButtonStyle.pressed : touch.has-hover ? ButtonStyle.hover : ButtonStyle.surface; outline-color: !root.enabled ? ButtonStyle.disabled-border : touch.pressed ? ButtonStyle.pressed-border : touch.has-hover ? ButtonStyle.hover-border : ButtonStyle.border; border-radius: UiRadius.control; animate background, outline-color { duration: 100ms; } Text { text: root.text; color: root.enabled ? #303f55 : #949aa5; font-size: root.text-size; font-weight: 500; horizontal-alignment: center; vertical-alignment: center; } touch := TouchArea { enabled: root.enabled; mouse-cursor: pointer; clicked => { root.clicked(); } } } export component ToggleSwitch inherits Rectangle { in-out property checked; in property enabled: true; in property toggle-on-click: true; callback clicked(); callback toggled(bool); width: 36px; height: 20px; background: !root.enabled ? #d0d5dd : root.checked ? #2463c7 : #98a2b3; border-radius: self.height / 2; Rectangle { x: root.checked ? parent.width - self.width - 3px : 3px; y: 3px; width: 14px; height: 14px; border-radius: 7px; background: white; animate x { duration: 100ms; easing: ease-in-out; } } TouchArea { enabled: root.enabled; clicked => { if root.toggle-on-click { root.checked = !root.checked; root.toggled(root.checked); } root.clicked(); } } } export component SectionTitle inherits Text { color: #00000080; font-size: ImGuiFontStyle.section-title; font-weight: 500; vertical-alignment: center; } export component DialogSurface inherits FlatSurface { background: white; outline-color: UiSurface.edge; border-radius: UiRadius.surface; }