Fold settings ActionPill + IconButton into qs.Ui kit
This commit is contained in:
@@ -12,6 +12,12 @@ import qs.Commons
|
||||
// hover state visuals; mouse hover does NOT update any panel cursor state
|
||||
// here because action buttons are not cursor targets — the row they live
|
||||
// in is.
|
||||
//
|
||||
// Set `focusable: true` to make the button keyboard-tabbable with an
|
||||
// accent focus ring (Style.focusBorderColor / FillColor / Width). Use this
|
||||
// in form contexts (the bar settings widget cards) where Tab walks a list
|
||||
// of controls; leave it false for the right-edge actions on panel rows
|
||||
// where the row's CursorSurface owns the keyboard cursor.
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
@@ -24,15 +30,28 @@ Rectangle {
|
||||
property real fontSize: 14
|
||||
property real size: 22
|
||||
|
||||
property bool focusable: false
|
||||
|
||||
signal clicked()
|
||||
|
||||
activeFocusOnTab: focusable
|
||||
Keys.onReturnPressed: if (focusable) root.clicked()
|
||||
Keys.onEnterPressed: if (focusable) root.clicked()
|
||||
Keys.onSpacePressed: if (focusable) root.clicked()
|
||||
|
||||
implicitWidth: size
|
||||
implicitHeight: size
|
||||
radius: Style.cornerRadius
|
||||
|
||||
color: mouse.containsMouse && root.enabled
|
||||
? Qt.rgba(hoverColor.r, hoverColor.g, hoverColor.b, 0.20)
|
||||
: "transparent"
|
||||
readonly property bool _showFocusRing: focusable && activeFocus
|
||||
|
||||
color: _showFocusRing
|
||||
? Style.focusFillColor
|
||||
: (mouse.containsMouse && root.enabled
|
||||
? Qt.rgba(hoverColor.r, hoverColor.g, hoverColor.b, 0.20)
|
||||
: "transparent")
|
||||
border.width: _showFocusRing ? Style.focusBorderWidth : 0
|
||||
border.color: _showFocusRing ? Style.focusBorderColor : "transparent"
|
||||
|
||||
Behavior on color { ColorAnimation { duration: 60 } }
|
||||
|
||||
@@ -52,7 +71,10 @@ Rectangle {
|
||||
hoverEnabled: true
|
||||
cursorShape: root.enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
enabled: root.enabled
|
||||
onClicked: root.clicked()
|
||||
onClicked: {
|
||||
if (root.focusable) root.forceActiveFocus()
|
||||
root.clicked()
|
||||
}
|
||||
}
|
||||
|
||||
PanelToolTip {
|
||||
|
||||
@@ -28,6 +28,24 @@ Rectangle {
|
||||
// indistinguishable. Default false; bind from a panel's cursor state.
|
||||
property bool hasCursor: false
|
||||
|
||||
// Tab-focusable form-button mode. Enables activeFocusOnTab and
|
||||
// Enter/Return/Space activation, and uses Style.focusBorderColor / FillColor
|
||||
// for the focus ring (distinct from the panel-cursor `hot` state). Set
|
||||
// true for settings buttons (Save, Cancel, Reset); leave false for the
|
||||
// panel-cursor pills (DNS picker, header actions).
|
||||
property bool focusable: false
|
||||
|
||||
// Persistent 1px foreground border at idle. Use for "primary" form buttons
|
||||
// (Save, Apply, + Add widget) so they read as buttons before the cursor
|
||||
// hits them. The hot/cursor state paints its own 1px border, so changing
|
||||
// this doesn't affect panel-pill visuals.
|
||||
property bool bordered: false
|
||||
|
||||
activeFocusOnTab: focusable
|
||||
Keys.onReturnPressed: if (focusable) root.clicked()
|
||||
Keys.onEnterPressed: if (focusable) root.clicked()
|
||||
Keys.onSpacePressed: if (focusable) root.clicked()
|
||||
|
||||
ToolTip {
|
||||
visible: root.tooltipText !== "" && mouseArea.containsMouse
|
||||
text: root.tooltipText
|
||||
@@ -66,11 +84,18 @@ Rectangle {
|
||||
// as hovered, matching CursorSurface's behaviour for navigable rows).
|
||||
readonly property bool hot: mouseArea.containsMouse || hasCursor
|
||||
|
||||
// Tab-focus styling wins over hot — the accent ring is the strongest
|
||||
// signal and shouldn't be masked by a hover landing on the focused item.
|
||||
readonly property bool _showFocusRing: focusable && activeFocus
|
||||
|
||||
color: mouseArea.pressed ? pressedBackground
|
||||
: _showFocusRing ? Style.focusFillColor
|
||||
: hot ? hoverBackground
|
||||
: (active ? activeBackground : background)
|
||||
border.width: hot ? 1 : 0
|
||||
border.color: foreground
|
||||
border.width: _showFocusRing ? Style.focusBorderWidth
|
||||
: hot ? 1
|
||||
: (bordered ? 1 : 0)
|
||||
border.color: _showFocusRing ? Style.focusBorderColor : foreground
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: 120 }
|
||||
@@ -110,6 +135,7 @@ Rectangle {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
onClicked: function(mouse) {
|
||||
if (root.focusable) root.forceActiveFocus()
|
||||
if (mouse.button === Qt.RightButton) root.rightClicked()
|
||||
else root.clicked()
|
||||
}
|
||||
|
||||
@@ -53,12 +53,12 @@ Grouped by what they're for, not alphabetically.
|
||||
|
||||
| Type | Purpose |
|
||||
|---|---|
|
||||
| `PillButton` | Rounded button with optional icon + label + tooltip. Has `active`, `hasCursor` (keyboard cursor), and `enabled`. Hover and keyboard cursor render identically (fill + border) via the shared `hot` state. |
|
||||
| `PillButton` | Rounded button with optional icon + label + tooltip. Has `active`, `hasCursor` (keyboard cursor), `focusable` (Tab-focus with accent ring), `bordered` (persistent 1px idle border for primary form buttons), and `enabled`. Hover and keyboard cursor render identically (fill + border) via the shared `hot` state; Tab-focus uses an accent ring that wins over both. |
|
||||
| `CursorPill` | `PillButton` that participates in a panel's single-cursor model. Adds a `hovered(bool)` signal so the panel can update its cursor state on mouse enter/leave. Use for DNS-pill / header-pill / segmented-choice patterns. |
|
||||
| `ChoiceButton` | A single button in a mutually-exclusive choice group (segmented control). `selected` uses accent fill+border; focus uses `Style.focusBorderColor` so keyboard nav reads differently from selection. |
|
||||
| `Toggle` | Title + description + switch. Click anywhere on the row to flip; caller updates `checked` in response. |
|
||||
| `TextField` | Single-line input. Inherits from Qt Quick Controls `TextField` so all of its base API (text, placeholderText, accepted, validator, ...) is available. Adds `password: bool`, `foreground` / `accent` / `selectionTint` color overrides, and `horizontalPadding` / `verticalPadding` size knobs. Focus styling uses `Style.focusBorderColor` to match `Toggle` and `ChoiceButton`. |
|
||||
| `PanelActionButton` | 22×22 right-edge action button (confirm, forget, unpair). `hoverColor` swaps between default foreground tint and urgent (red) tint. |
|
||||
| `PanelActionButton` | 22×22 right-edge action button (confirm, forget, unpair). `hoverColor` swaps between default foreground tint and urgent (red) tint. `focusable: true` enables Tab-focus with an accent ring — used for the bar settings widget-card row controls. |
|
||||
| `PanelSlider` | Volume/progress slider. Drag, click track, or wheel. `moved(value)` fires per change, `released(value)` once at end. (Named to avoid colliding with `QtQuick.Controls.Slider`.) |
|
||||
| `WidgetButton` | Bar widget chrome — for the strip itself, not for inside panels. |
|
||||
|
||||
|
||||
@@ -394,6 +394,14 @@ Item {
|
||||
fontFamily: root.fontFamily
|
||||
active: true
|
||||
}
|
||||
|
||||
PillButton {
|
||||
text: "Apply"
|
||||
foreground: root.foreground
|
||||
fontFamily: root.fontFamily
|
||||
focusable: true
|
||||
bordered: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -516,6 +524,17 @@ Item {
|
||||
fontFamily: root.fontFamily
|
||||
enabled: false
|
||||
}
|
||||
|
||||
PanelActionButton {
|
||||
iconText: "⚙"
|
||||
tooltipText: "Focusable (settings form button)"
|
||||
foreground: root.foreground
|
||||
panelBackground: root.background
|
||||
fontFamily: root.fontFamily
|
||||
fontSize: 13
|
||||
size: 26
|
||||
focusable: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -671,9 +671,12 @@ Item {
|
||||
|
||||
Row {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
ActionPill {
|
||||
PillButton {
|
||||
text: "Reset bar to defaults"
|
||||
foreground: root.urgent
|
||||
fontFamily: root.fontFamily
|
||||
focusable: true
|
||||
bordered: true
|
||||
onClicked: root.resetBarToDefaults()
|
||||
}
|
||||
}
|
||||
@@ -725,87 +728,6 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
component ActionPill: Rectangle {
|
||||
id: pill
|
||||
property string text: ""
|
||||
property color foreground: root.foreground
|
||||
property bool bordered: true
|
||||
signal clicked()
|
||||
|
||||
activeFocusOnTab: true
|
||||
Keys.onReturnPressed: pill.clicked()
|
||||
Keys.onEnterPressed: pill.clicked()
|
||||
Keys.onSpacePressed: pill.clicked()
|
||||
|
||||
implicitWidth: pillLabel.implicitWidth + 22
|
||||
implicitHeight: 26
|
||||
radius: root.cornerRadius
|
||||
color: pill.activeFocus
|
||||
? root.focusFillColor
|
||||
: (pillArea.containsMouse ? Qt.rgba(pill.foreground.r, pill.foreground.g, pill.foreground.b, 0.15) : "transparent")
|
||||
border.color: pill.activeFocus ? root.focusBorderColor : (pill.bordered ? pill.foreground : "transparent")
|
||||
border.width: pill.activeFocus ? root.focusBorderWidth : 1
|
||||
|
||||
Behavior on color { ColorAnimation { duration: 100 } }
|
||||
|
||||
Text {
|
||||
id: pillLabel
|
||||
anchors.centerIn: parent
|
||||
text: pill.text
|
||||
color: pill.foreground
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: 11
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: pillArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: pill.clicked()
|
||||
}
|
||||
}
|
||||
|
||||
component IconButton: Rectangle {
|
||||
id: iconButton
|
||||
property string glyph: ""
|
||||
property string tooltip: ""
|
||||
property color foreground: root.foreground
|
||||
signal clicked()
|
||||
|
||||
activeFocusOnTab: true
|
||||
Keys.onReturnPressed: iconButton.clicked()
|
||||
Keys.onEnterPressed: iconButton.clicked()
|
||||
Keys.onSpacePressed: iconButton.clicked()
|
||||
|
||||
implicitWidth: 26
|
||||
implicitHeight: 26
|
||||
radius: root.cornerRadius
|
||||
color: iconButton.activeFocus
|
||||
? root.focusFillColor
|
||||
: (iconArea.containsMouse ? Qt.rgba(iconButton.foreground.r, iconButton.foreground.g, iconButton.foreground.b, 0.18) : "transparent")
|
||||
border.color: iconButton.activeFocus ? root.focusBorderColor : "transparent"
|
||||
border.width: iconButton.activeFocus ? root.focusBorderWidth : 0
|
||||
|
||||
Behavior on color { ColorAnimation { duration: 100 } }
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: iconButton.glyph
|
||||
color: iconButton.foreground
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: 13
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: iconArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: iconButton.clicked()
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== bar layout pieces =================================
|
||||
component SectionEditor: Column {
|
||||
id: section
|
||||
@@ -848,9 +770,13 @@ Item {
|
||||
height: 1
|
||||
}
|
||||
|
||||
ActionPill {
|
||||
PillButton {
|
||||
id: addPill
|
||||
text: "+ Add widget"
|
||||
foreground: root.foreground
|
||||
fontFamily: root.fontFamily
|
||||
focusable: true
|
||||
bordered: true
|
||||
onClicked: addPopup.open()
|
||||
}
|
||||
}
|
||||
@@ -993,26 +919,50 @@ Item {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 4
|
||||
|
||||
IconButton {
|
||||
glyph: "↑"
|
||||
tooltip: "Move up"
|
||||
PanelActionButton {
|
||||
iconText: "↑"
|
||||
tooltipText: "Move up"
|
||||
foreground: root.foreground
|
||||
panelBackground: root.background
|
||||
fontFamily: root.fontFamily
|
||||
fontSize: 13
|
||||
size: 26
|
||||
focusable: true
|
||||
onClicked: root.moveEntry(card.sectionKey, card.entryIndex, card.entryIndex - 1)
|
||||
}
|
||||
IconButton {
|
||||
glyph: "↓"
|
||||
tooltip: "Move down"
|
||||
PanelActionButton {
|
||||
iconText: "↓"
|
||||
tooltipText: "Move down"
|
||||
foreground: root.foreground
|
||||
panelBackground: root.background
|
||||
fontFamily: root.fontFamily
|
||||
fontSize: 13
|
||||
size: 26
|
||||
focusable: true
|
||||
onClicked: root.moveEntry(card.sectionKey, card.entryIndex, card.entryIndex + 1)
|
||||
}
|
||||
IconButton {
|
||||
glyph: "⚙"
|
||||
tooltip: "Settings"
|
||||
PanelActionButton {
|
||||
iconText: "⚙"
|
||||
tooltipText: "Settings"
|
||||
foreground: root.foreground
|
||||
panelBackground: root.background
|
||||
fontFamily: root.fontFamily
|
||||
fontSize: 13
|
||||
size: 26
|
||||
focusable: true
|
||||
visible: card.hasSettings
|
||||
onClicked: settingsLoader.open(card.entry)
|
||||
}
|
||||
IconButton {
|
||||
glyph: "✕"
|
||||
tooltip: "Remove"
|
||||
PanelActionButton {
|
||||
iconText: "✕"
|
||||
tooltipText: "Remove"
|
||||
foreground: root.urgent
|
||||
hoverColor: root.urgent
|
||||
panelBackground: root.background
|
||||
fontFamily: root.fontFamily
|
||||
fontSize: 13
|
||||
size: 26
|
||||
focusable: true
|
||||
onClicked: root.removeEntry(card.sectionKey, card.entryIndex)
|
||||
}
|
||||
}
|
||||
@@ -1140,8 +1090,21 @@ Item {
|
||||
Row {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
spacing: 8
|
||||
ActionPill { text: "Cancel"; bordered: false; onClicked: dialog.discard() }
|
||||
ActionPill { text: "Apply"; onClicked: dialog.commit() }
|
||||
PillButton {
|
||||
text: "Cancel"
|
||||
foreground: root.foreground
|
||||
fontFamily: root.fontFamily
|
||||
focusable: true
|
||||
onClicked: dialog.discard()
|
||||
}
|
||||
PillButton {
|
||||
text: "Apply"
|
||||
foreground: root.foreground
|
||||
fontFamily: root.fontFamily
|
||||
focusable: true
|
||||
bordered: true
|
||||
onClicked: dialog.commit()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user