From 17281a66a5214ae1f91830f5eaeba464fc2f21b8 Mon Sep 17 00:00:00 2001 From: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> Date: Sat, 2 May 2026 18:50:58 +1000 Subject: [PATCH] feat: use button base component --- components/controls/IconButton.qml | 70 ++++--------------- components/controls/IconTextButton.qml | 54 +++----------- components/controls/TextButton.qml | 56 +++------------ .../components/ConnectedButtonGroup.qml | 2 +- modules/dashboard/Media.qml | 2 +- modules/utilities/cards/Record.qml | 2 +- modules/utilities/cards/Toggles.qml | 6 +- 7 files changed, 38 insertions(+), 154 deletions(-) diff --git a/components/controls/IconButton.qml b/components/controls/IconButton.qml index 46d07b9b..b840a58c 100644 --- a/components/controls/IconButton.qml +++ b/components/controls/IconButton.qml @@ -3,75 +3,37 @@ import Caelestia.Config import qs.components import qs.services -StyledRect { +ButtonBase { id: root - enum Type { - Filled, - Tonal, - Text - } - property alias icon: label.text - property bool checked - property bool toggle - property real padding: type === IconButton.Text ? Tokens.padding.extraSmall / 2 : Tokens.padding.small - property alias font: label.fontStyle - property int type: IconButton.Filled - property bool disabled - property bool isRound + readonly property alias label: label - readonly property alias pressed: stateLayer.pressed - readonly property alias hovered: stateLayer.containsMouse + padding: type === IconButton.Text ? Tokens.padding.extraSmall / 2 : Tokens.padding.small - property alias stateLayer: stateLayer - property alias label: label - property alias radiusAnim: radiusAnim - - property bool internalChecked - property color activeColour: type === IconButton.Filled ? Colours.palette.m3primary : Colours.palette.m3secondary - property color inactiveColour: { - if (!toggle && type === IconButton.Filled) + activeColour: type === IconButton.Filled ? Colours.palette.m3primary : Colours.palette.m3secondary + inactiveColour: { + if (!isToggle && type === IconButton.Filled) return Colours.palette.m3primary; return type === IconButton.Filled ? Colours.tPalette.m3surfaceContainer : Colours.palette.m3secondaryContainer; } - property color activeOnColour: type === IconButton.Filled ? Colours.palette.m3onPrimary : type === IconButton.Tonal ? Colours.palette.m3onSecondary : Colours.palette.m3primary - property color inactiveOnColour: { - if (!toggle && type === IconButton.Filled) + activeOnColour: type === IconButton.Filled ? Colours.palette.m3onPrimary : type === IconButton.Tonal ? Colours.palette.m3onSecondary : Colours.palette.m3primary + inactiveOnColour: { + if (!isToggle && type === IconButton.Filled) return Colours.palette.m3onPrimary; return type === IconButton.Tonal ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurfaceVariant; } - property color disabledColour: Qt.alpha(Colours.palette.m3onSurface, 0.1) - property color disabledOnColour: Qt.alpha(Colours.palette.m3onSurface, 0.38) - - signal clicked - - onCheckedChanged: internalChecked = checked - - radius: stateLayer.pressed ? Tokens.rounding.small : internalChecked ? Tokens.rounding.medium : isRound ? implicitHeight / 2 * Math.min(1, Tokens.rounding.scale) : Tokens.rounding.large - color: type === IconButton.Text ? "transparent" : disabled ? disabledColour : internalChecked ? activeColour : inactiveColour implicitWidth: implicitHeight implicitHeight: label.implicitHeight + padding * 2 - StateLayer { - id: stateLayer - - color: root.internalChecked ? root.activeOnColour : root.inactiveOnColour - disabled: root.disabled - onClicked: { - if (root.toggle) - root.internalChecked = !root.internalChecked; - root.clicked(); - } - } - MaterialIcon { id: label anchors.centerIn: parent - color: root.disabled ? root.disabledOnColour : root.internalChecked ? root.activeOnColour : root.inactiveOnColour - fill: !root.toggle || root.internalChecked ? 1 : 0 + color: root.onColour + fontStyle: root.font + fill: !root.isToggle || root.internalChecked ? 1 : 0 Behavior on fill { Anim { @@ -79,12 +41,4 @@ StyledRect { } } } - - Behavior on radius { - Anim { - id: radiusAnim - - type: Anim.DefaultEffects - } - } } diff --git a/components/controls/IconTextButton.qml b/components/controls/IconTextButton.qml index 22dadefb..8d1bf264 100644 --- a/components/controls/IconTextButton.qml +++ b/components/controls/IconTextButton.qml @@ -4,55 +4,23 @@ import Caelestia.Config import qs.components import qs.services -StyledRect { +ButtonBase { id: root - enum Type { - Filled, - Tonal, - Text - } - property alias icon: iconLabel.text property alias text: label.text - property bool checked - property bool toggle - property real horizontalPadding: Tokens.padding.medium - property real verticalPadding: Tokens.padding.small - property alias font: label.font - property int type: IconTextButton.Filled - property alias stateLayer: stateLayer - property alias iconLabel: iconLabel - property alias label: label + readonly property alias iconLabel: iconLabel + readonly property alias label: label - property bool internalChecked - property color activeColour: type === IconTextButton.Filled ? Colours.palette.m3primary : Colours.palette.m3secondary - property color inactiveColour: type === IconTextButton.Filled ? Colours.tPalette.m3surfaceContainer : Colours.palette.m3secondaryContainer - property color activeOnColour: type === IconTextButton.Filled ? Colours.palette.m3onPrimary : Colours.palette.m3onSecondary - property color inactiveOnColour: type === IconTextButton.Filled ? Colours.palette.m3onSurface : Colours.palette.m3onSecondaryContainer - - signal clicked - - onCheckedChanged: internalChecked = checked - - radius: internalChecked ? Tokens.rounding.medium : implicitHeight / 2 * Math.min(1, Tokens.rounding.scale) - color: type === IconTextButton.Text ? "transparent" : internalChecked ? activeColour : inactiveColour + activeColour: type === IconTextButton.Filled ? Colours.palette.m3primary : Colours.palette.m3secondary + inactiveColour: type === IconTextButton.Filled ? Colours.tPalette.m3surfaceContainer : Colours.palette.m3secondaryContainer + activeOnColour: type === IconTextButton.Filled ? Colours.palette.m3onPrimary : Colours.palette.m3onSecondary + inactiveOnColour: type === IconTextButton.Filled ? Colours.palette.m3onSurface : Colours.palette.m3onSecondaryContainer implicitWidth: row.implicitWidth + horizontalPadding * 2 implicitHeight: row.implicitHeight + verticalPadding * 2 - StateLayer { - id: stateLayer - - color: root.internalChecked ? root.activeOnColour : root.inactiveOnColour - onClicked: { - if (root.toggle) - root.internalChecked = !root.internalChecked; - root.clicked(); - } - } - RowLayout { id: row @@ -64,7 +32,7 @@ StyledRect { Layout.alignment: Qt.AlignVCenter Layout.topMargin: Math.round(fontInfo.pointSize * 0.0575) - color: root.internalChecked ? root.activeOnColour : root.inactiveOnColour + color: root.onColour fill: root.internalChecked ? 1 : 0 Behavior on fill { @@ -79,11 +47,7 @@ StyledRect { Layout.alignment: Qt.AlignVCenter Layout.topMargin: -Math.round(iconLabel.fontInfo.pointSize * 0.0575) - color: root.internalChecked ? root.activeOnColour : root.inactiveOnColour + color: root.onColour } } - - Behavior on radius { - Anim {} - } } diff --git a/components/controls/TextButton.qml b/components/controls/TextButton.qml index b37d06d3..5cfff6e8 100644 --- a/components/controls/TextButton.qml +++ b/components/controls/TextButton.qml @@ -3,75 +3,41 @@ import Caelestia.Config import qs.components import qs.services -StyledRect { +ButtonBase { id: root - enum Type { - Filled, - Tonal, - Text - } - property alias text: label.text - property bool checked - property bool toggle - property real horizontalPadding: Tokens.padding.medium - property real verticalPadding: Tokens.padding.small - property alias font: label.font - property int type: TextButton.Filled + readonly property alias label: label - property alias stateLayer: stateLayer - property alias label: label + horizontalPadding: Tokens.padding.medium + verticalPadding: Tokens.padding.small - property bool internalChecked - property color activeColour: type === TextButton.Filled ? Colours.palette.m3primary : Colours.palette.m3secondary - property color inactiveColour: { - if (!toggle && type === TextButton.Filled) + activeColour: type === TextButton.Filled ? Colours.palette.m3primary : Colours.palette.m3secondary + inactiveColour: { + if (!isToggle && type === TextButton.Filled) return Colours.palette.m3primary; return type === TextButton.Filled ? Colours.tPalette.m3surfaceContainer : Colours.palette.m3secondaryContainer; } - property color activeOnColour: { + activeOnColour: { if (type === TextButton.Text) return Colours.palette.m3primary; return type === TextButton.Filled ? Colours.palette.m3onPrimary : Colours.palette.m3onSecondary; } - property color inactiveOnColour: { - if (!toggle && type === TextButton.Filled) + inactiveOnColour: { + if (!isToggle && type === TextButton.Filled) return Colours.palette.m3onPrimary; if (type === TextButton.Text) return Colours.palette.m3primary; return type === TextButton.Filled ? Colours.palette.m3onSurface : Colours.palette.m3onSecondaryContainer; } - signal clicked - - onCheckedChanged: internalChecked = checked - - radius: internalChecked ? Tokens.rounding.medium : implicitHeight / 2 * Math.min(1, Tokens.rounding.scale) - color: type === TextButton.Text ? "transparent" : internalChecked ? activeColour : inactiveColour - implicitWidth: label.implicitWidth + horizontalPadding * 2 implicitHeight: label.implicitHeight + verticalPadding * 2 - StateLayer { - id: stateLayer - - color: root.internalChecked ? root.activeOnColour : root.inactiveOnColour - onClicked: { - if (root.toggle) - root.internalChecked = !root.internalChecked; - root.clicked(); - } - } - StyledText { id: label anchors.centerIn: parent - color: root.internalChecked ? root.activeOnColour : root.inactiveOnColour - } - - Behavior on radius { - Anim {} + color: root.onColour } } diff --git a/modules/controlcenter/components/ConnectedButtonGroup.qml b/modules/controlcenter/components/ConnectedButtonGroup.qml index bd2d1dca..1aedbbd9 100644 --- a/modules/controlcenter/components/ConnectedButtonGroup.qml +++ b/modules/controlcenter/components/ConnectedButtonGroup.qml @@ -63,7 +63,7 @@ StyledRect { Layout.fillWidth: true text: modelData.label checked: _checked - toggle: false + isToggle: false type: TextButton.Tonal // Create binding in Component.onCompleted diff --git a/modules/dashboard/Media.qml b/modules/dashboard/Media.qml index 8ad13e20..697a4b24 100644 --- a/modules/dashboard/Media.qml +++ b/modules/dashboard/Media.qml @@ -266,7 +266,7 @@ Item { PlayerControl { icon: Players.active?.isPlaying ? "pause" : "play_arrow" label.animate: true - toggle: true + isToggle: true padding: Tokens.padding.extraSmall / 2 checked: Players.active?.isPlaying ?? false font: Tokens.font.icon.builders.large.scale(1.5).build() diff --git a/modules/utilities/cards/Record.qml b/modules/utilities/cards/Record.qml index 72f2c2af..98ab4507 100644 --- a/modules/utilities/cards/Record.qml +++ b/modules/utilities/cards/Record.qml @@ -256,7 +256,7 @@ StyledRect { IconButton { label.animate: true icon: Recorder.paused ? "play_arrow" : "pause" - toggle: true + isToggle: true checked: Recorder.paused type: IconButton.Tonal font: Tokens.font.icon.large diff --git a/modules/utilities/cards/Toggles.qml b/modules/utilities/cards/Toggles.qml index a88811c1..2b10a423 100644 --- a/modules/utilities/cards/Toggles.qml +++ b/modules/utilities/cards/Toggles.qml @@ -114,7 +114,7 @@ StyledRect { delegate: Toggle { icon: "settings" inactiveOnColour: Colours.palette.m3onSurfaceVariant - toggle: false + isToggle: false onClicked: { root.visibilities.utilities = false; root.popouts.detach("network"); @@ -143,7 +143,7 @@ StyledRect { icon: "vpn_key" checked: VPN.connected && VPN.status.state !== "needs-auth" && VPN.status.state !== "error" enabled: !VPN.connecting - toggle: VPN.status.state !== "needs-auth" && VPN.status.state !== "error" + isToggle: VPN.status.state !== "needs-auth" && VPN.status.state !== "error" inactiveOnColour: Colours.palette.m3onSurfaceVariant onClicked: VPN.toggle() } @@ -157,7 +157,7 @@ StyledRect { Layout.preferredWidth: implicitWidth + (stateLayer.pressed ? Tokens.padding.large : internalChecked ? Tokens.padding.small : 0) radius: stateLayer.pressed ? Tokens.rounding.medium / 2 : internalChecked ? Tokens.rounding.medium : Tokens.rounding.large inactiveColour: Colours.layer(Colours.palette.m3surfaceContainerHighest, 2) - toggle: true + isToggle: true radiusAnim.duration: Tokens.anim.durations.expressiveFastSpatial radiusAnim.easing: Tokens.anim.expressiveFastSpatial