mirror of
https://github.com/arthur-pbty/arthur-os.git
synced 2026-08-07 12:40:55 +02:00
Three small fixes around the new Button + ButtonGroup so the cursor is
always visible and the section reads as a single Button showing flag
combinations.
ButtonGroup now exposes cursorIndex + hovered(index, isHovered) so
panels can drive a single Button's hasCursor without reaching past
ButtonGroup's API. Every chip gets bordered: true so the row reads as
a real picker — distinct outlines, not just a tinted fill that vanishes
into the background.
Button's border treatment grows two paths so the cursor is always
visible regardless of other state: bordered + hot recolors the border
to the accent and thickens to the focus-ring width, and selected + hot
thickens the existing accent border to the focus-ring width (so the
cursor landing on the chosen option still reads as cursor presence,
instead of disappearing into the selected fill).
Gallery's Button section pairs each demo button with a small caption
('idle', 'active', 'icon only', 'icon + active', 'bordered +
focusable') so the row reads as one Button with its state flags side
by side rather than five unrelated buttons. The ButtonGroup section
gets the cursor wiring (cursorIndex + onHovered) plus an
onFocusedChanged auto-scroll so the cursor visual is reachable via
j/k h/l from the panel cursor model.
75 lines
2.5 KiB
QML
75 lines
2.5 KiB
QML
import QtQuick
|
|
import qs.Commons
|
|
|
|
// Mutually-exclusive row of Buttons — the form-style "pick one of N"
|
|
// pattern (bar position top/right/bottom/left, theme preset chips, etc.).
|
|
// Emits `changed(value)` when the user activates a different option.
|
|
//
|
|
// `options` is either a plain string[] (label == value) or an array of
|
|
// { value, label, icon?, tooltip? } objects. Mixing is fine.
|
|
//
|
|
// Panels with their own keyboard cursor model bind `cursorIndex` to the
|
|
// currently-focused option (-1 = no cursor) and listen on `hovered` to
|
|
// keep that state synced with the mouse. Forms that don't care about
|
|
// the panel cursor model can leave both alone.
|
|
Row {
|
|
id: root
|
|
|
|
property var options: []
|
|
property string value: ""
|
|
property color foreground: Color.foreground
|
|
property color background: Color.background
|
|
property color accent: Color.accent
|
|
property string fontFamily: Style.font.family
|
|
property real fontSize: Style.font.body
|
|
property bool focusable: false
|
|
|
|
// -1 disables the cursor highlight (the form case). Set from a panel
|
|
// to drive Button.hasCursor on the matching index.
|
|
property int cursorIndex: -1
|
|
|
|
signal changed(string value)
|
|
signal hovered(int index, bool isHovered)
|
|
|
|
spacing: 6
|
|
|
|
function optionValue(o) {
|
|
return (o && typeof o === "object") ? String(o.value) : String(o)
|
|
}
|
|
function optionLabel(o) {
|
|
return (o && typeof o === "object" && o.label !== undefined) ? String(o.label) : String(o)
|
|
}
|
|
function optionIcon(o) {
|
|
return (o && typeof o === "object" && o.icon) ? String(o.icon) : ""
|
|
}
|
|
function optionTooltip(o) {
|
|
return (o && typeof o === "object" && o.tooltip) ? String(o.tooltip) : ""
|
|
}
|
|
|
|
Repeater {
|
|
model: root.options
|
|
|
|
delegate: Button {
|
|
required property var modelData
|
|
required property int index
|
|
text: root.optionLabel(modelData)
|
|
iconText: root.optionIcon(modelData)
|
|
tooltipText: root.optionTooltip(modelData)
|
|
selected: root.optionValue(modelData) === root.value
|
|
hasCursor: root.cursorIndex === index
|
|
// Every chip carries an idle border so the group reads as a row of
|
|
// distinct options. selected paints accent; the cursor recolors the
|
|
// chip's border to accent via Button's bordered+hot path.
|
|
bordered: true
|
|
foreground: root.foreground
|
|
background: root.background
|
|
accent: root.accent
|
|
fontFamily: root.fontFamily
|
|
fontSize: root.fontSize
|
|
focusable: root.focusable
|
|
onClicked: root.changed(root.optionValue(modelData))
|
|
onHovered: function(h) { root.hovered(index, h) }
|
|
}
|
|
}
|
|
}
|