summaryrefslogtreecommitdiff
path: root/components/controls
diff options
context:
space:
mode:
author2 * r + 2 * t <61896496+soramanew@users.noreply.github.com>2025-09-15 15:59:24 +1000
committer2 * r + 2 * t <61896496+soramanew@users.noreply.github.com>2025-09-15 15:59:24 +1000
commit4f54763e02dab4e33764cdd57c204368da57dd1c (patch)
tree3f0626f2ba67b1c95b352061ab090f441c7cb694 /components/controls
parentutilities/record: app2unit (diff)
downloadcaelestia-shell-4f54763e02dab4e33764cdd57c204368da57dd1c.tar.gz
caelestia-shell-4f54763e02dab4e33764cdd57c204368da57dd1c.tar.bz2
caelestia-shell-4f54763e02dab4e33764cdd57c204368da57dd1c.zip
utilities/record: select mode
Diffstat (limited to 'components/controls')
-rw-r--r--components/controls/IconTextButton.qml86
-rw-r--r--components/controls/Menu.qml111
-rw-r--r--components/controls/MenuItem.qml11
-rw-r--r--components/controls/SplitButton.qml141
-rw-r--r--components/controls/TextButton.qml12
5 files changed, 355 insertions, 6 deletions
diff --git a/components/controls/IconTextButton.qml b/components/controls/IconTextButton.qml
new file mode 100644
index 0000000..ba60f3b
--- /dev/null
+++ b/components/controls/IconTextButton.qml
@@ -0,0 +1,86 @@
+import ".."
+import qs.services
+import qs.config
+import QtQuick
+
+StyledRect {
+ 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: Appearance.padding.normal
+ property real verticalPadding: Appearance.padding.smaller
+ property alias font: label.font
+ property int type: IconTextButton.Filled
+
+ property alias stateLayer: stateLayer
+ property alias iconLabel: iconLabel
+ 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.palette.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
+
+ function onClicked(): void {
+ }
+
+ onCheckedChanged: internalChecked = checked
+
+ radius: internalChecked ? Appearance.rounding.small : implicitHeight / 2
+ color: type == IconTextButton.Text ? "transparent" : internalChecked ? activeColour : inactiveColour
+
+ implicitWidth: row.implicitWidth + horizontalPadding * 2
+ implicitHeight: row.implicitHeight + verticalPadding * 2
+
+ StateLayer {
+ id: stateLayer
+
+ color: root.internalChecked ? root.activeOnColour : root.inactiveOnColour
+
+ function onClicked(): void {
+ if (root.toggle)
+ root.internalChecked = !root.internalChecked;
+ root.onClicked();
+ }
+ }
+
+ Row {
+ id: row
+
+ anchors.centerIn: parent
+ spacing: Appearance.spacing.small
+
+ MaterialIcon {
+ id: iconLabel
+
+ anchors.verticalCenter: parent.verticalCenter
+ color: root.internalChecked ? root.activeOnColour : root.inactiveOnColour
+ fill: root.internalChecked ? 1 : 0
+
+ Behavior on fill {
+ Anim {}
+ }
+ }
+
+ StyledText {
+ id: label
+
+ anchors.verticalCenter: parent.verticalCenter
+ color: root.internalChecked ? root.activeOnColour : root.inactiveOnColour
+ }
+ }
+
+ Behavior on radius {
+ Anim {}
+ }
+}
diff --git a/components/controls/Menu.qml b/components/controls/Menu.qml
new file mode 100644
index 0000000..7121536
--- /dev/null
+++ b/components/controls/Menu.qml
@@ -0,0 +1,111 @@
+pragma ComponentBehavior: Bound
+
+import ".."
+import "../effects"
+import qs.services
+import qs.config
+import QtQuick
+import QtQuick.Layouts
+
+Elevation {
+ id: root
+
+ property list<MenuItem> items
+ property MenuItem active: items[0] ?? null
+ property bool expanded
+
+ signal itemSelected(item: MenuItem)
+
+ radius: Appearance.rounding.small / 2
+ level: 2
+
+ implicitWidth: column.implicitWidth
+ implicitHeight: root.expanded ? column.implicitHeight : 0
+ opacity: root.expanded ? 1 : 0
+
+ StyledClippingRect {
+ anchors.fill: parent
+ radius: parent.radius
+ color: Colours.palette.m3surfaceContainer
+
+ ColumnLayout {
+ id: column
+
+ spacing: 0
+
+ Repeater {
+ model: root.items
+
+ StyledRect {
+ id: item
+
+ required property int index
+ required property MenuItem modelData
+ readonly property bool active: modelData === root.active
+
+ Layout.fillWidth: true
+ implicitWidth: menuOptionRow.implicitWidth + Appearance.padding.normal * 2
+ implicitHeight: menuOptionRow.implicitHeight + Appearance.padding.normal * 2
+
+ color: Qt.alpha(Colours.palette.m3secondaryContainer, active ? 1 : 0)
+
+ StateLayer {
+ color: item.active ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurface
+ disabled: !root.expanded
+
+ function onClicked(): void {
+ root.itemSelected(item.modelData);
+ root.active = item.modelData;
+ root.expanded = false;
+ }
+ }
+
+ RowLayout {
+ id: menuOptionRow
+
+ anchors.fill: parent
+ anchors.margins: Appearance.padding.normal
+ spacing: Appearance.spacing.small
+
+ MaterialIcon {
+ Layout.alignment: Qt.AlignVCenter
+ text: item.modelData.icon
+ color: item.active ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurfaceVariant
+ }
+
+ StyledText {
+ Layout.alignment: Qt.AlignVCenter
+ Layout.fillWidth: true
+ text: item.modelData.text
+ color: item.active ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurface
+ }
+
+ Loader {
+ Layout.alignment: Qt.AlignVCenter
+ active: item.modelData.trailingIcon.length > 0
+ visible: active
+
+ sourceComponent: MaterialIcon {
+ text: item.modelData.trailingIcon
+ color: item.active ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurface
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Behavior on opacity {
+ Anim {
+ duration: Appearance.anim.durations.expressiveDefaultSpatial
+ }
+ }
+
+ Behavior on implicitHeight {
+ Anim {
+ duration: Appearance.anim.durations.expressiveDefaultSpatial
+ easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
+ }
+ }
+}
diff --git a/components/controls/MenuItem.qml b/components/controls/MenuItem.qml
new file mode 100644
index 0000000..5348bbe
--- /dev/null
+++ b/components/controls/MenuItem.qml
@@ -0,0 +1,11 @@
+import QtQuick
+
+QtObject {
+ required property string text
+ property string icon
+ property string trailingIcon
+ property string activeIcon: icon
+ property string activeText: text
+
+ signal clicked
+}
diff --git a/components/controls/SplitButton.qml b/components/controls/SplitButton.qml
new file mode 100644
index 0000000..6c92285
--- /dev/null
+++ b/components/controls/SplitButton.qml
@@ -0,0 +1,141 @@
+import ".."
+import qs.services
+import qs.config
+import QtQuick
+import QtQuick.Layouts
+
+Row {
+ id: root
+
+ enum Type {
+ Filled,
+ Tonal
+ }
+
+ property real horizontalPadding: Appearance.padding.normal
+ property real verticalPadding: Appearance.padding.smaller
+ property int type: SplitButton.Filled
+ property alias menuItems: menu.items
+ property alias active: menu.active
+ property alias expanded: menu.expanded
+ property alias menu: menu
+
+ property color colour: type == SplitButton.Filled ? Colours.palette.m3primary : Colours.palette.m3secondaryContainer
+ property color textColour: type == SplitButton.Filled ? Colours.palette.m3onPrimary : Colours.palette.m3onSecondaryContainer
+
+ spacing: Math.floor(Appearance.spacing.small / 2)
+
+ StyledRect {
+ radius: implicitHeight / 2
+ topRightRadius: Appearance.rounding.small / 2
+ bottomRightRadius: Appearance.rounding.small / 2
+ color: root.colour
+
+ implicitWidth: textRow.implicitWidth + root.horizontalPadding * 2
+ implicitHeight: expandBtn.implicitHeight
+
+ StateLayer {
+ id: stateLayer
+
+ rect.topRightRadius: parent.topRightRadius
+ rect.bottomRightRadius: parent.bottomRightRadius
+ color: root.textColour
+
+ function onClicked(): void {
+ root.active?.clicked();
+ }
+ }
+
+ RowLayout {
+ id: textRow
+
+ anchors.centerIn: parent
+ anchors.horizontalCenterOffset: Math.floor(root.verticalPadding / 4)
+ spacing: Appearance.spacing.small
+
+ MaterialIcon {
+ id: iconLabel
+
+ Layout.alignment: Qt.AlignVCenter
+ animate: true
+ text: root.active?.activeIcon ?? ""
+ color: root.textColour
+ fill: 1
+ }
+
+ StyledText {
+ id: label
+
+ Layout.alignment: Qt.AlignVCenter
+ Layout.preferredWidth: implicitWidth
+ animate: true
+ text: root.active?.activeText ?? ""
+ color: root.textColour
+ clip: true
+
+ Behavior on Layout.preferredWidth {
+ Anim {
+ easing.bezierCurve: Appearance.anim.curves.emphasized
+ }
+ }
+ }
+ }
+ }
+
+ StyledRect {
+ id: expandBtn
+
+ property real rad: root.expanded ? implicitHeight / 2 : Appearance.rounding.small / 2
+
+ radius: implicitHeight / 2
+ topLeftRadius: rad
+ bottomLeftRadius: rad
+ color: root.colour
+
+ implicitWidth: implicitHeight
+ implicitHeight: expandIcon.implicitHeight + root.verticalPadding * 2
+
+ StateLayer {
+ id: expandStateLayer
+
+ rect.topRightRadius: parent.topRightRadius
+ rect.bottomRightRadius: parent.bottomRightRadius
+ color: root.textColour
+
+ function onClicked(): void {
+ root.expanded = !root.expanded;
+ }
+ }
+
+ MaterialIcon {
+ id: expandIcon
+
+ anchors.centerIn: parent
+ anchors.horizontalCenterOffset: root.expanded ? 0 : -Math.floor(root.verticalPadding / 4)
+
+ text: "expand_more"
+ color: root.textColour
+ rotation: root.expanded ? 180 : 0
+
+ Behavior on anchors.horizontalCenterOffset {
+ Anim {}
+ }
+
+ Behavior on rotation {
+ Anim {}
+ }
+ }
+
+ Behavior on rad {
+ Anim {}
+ }
+
+ Menu {
+ id: menu
+
+ anchors.top: parent.bottom
+ anchors.right: parent.right
+ anchors.topMargin: Appearance.spacing.small
+ }
+ }
+}
diff --git a/components/controls/TextButton.qml b/components/controls/TextButton.qml
index 55749cb..a4c8666 100644
--- a/components/controls/TextButton.qml
+++ b/components/controls/TextButton.qml
@@ -18,16 +18,16 @@ StyledRect {
property real horizontalPadding: Appearance.padding.normal
property real verticalPadding: Appearance.padding.smaller
property alias font: label.font
- property int type: IconButton.Filled
+ property int type: TextButton.Filled
property alias stateLayer: stateLayer
property alias label: label
property bool internalChecked
- property color activeColour: type == IconButton.Filled ? Colours.palette.m3primary : Colours.palette.m3secondary
- property color inactiveColour: type == IconButton.Filled ? Colours.palette.m3surfaceContainer : Colours.palette.m3secondaryContainer
- property color activeOnColour: type == IconButton.Filled ? Colours.palette.m3onPrimary : Colours.palette.m3onSecondary
- property color inactiveOnColour: type == IconButton.Filled ? Colours.palette.m3onSurface : Colours.palette.m3onSecondaryContainer
+ property color activeColour: type == TextButton.Filled ? Colours.palette.m3primary : Colours.palette.m3secondary
+ property color inactiveColour: type == TextButton.Filled ? Colours.palette.m3surfaceContainer : Colours.palette.m3secondaryContainer
+ property color activeOnColour: type == TextButton.Filled ? Colours.palette.m3onPrimary : Colours.palette.m3onSecondary
+ property color inactiveOnColour: type == TextButton.Filled ? Colours.palette.m3onSurface : Colours.palette.m3onSecondaryContainer
function onClicked(): void {
}
@@ -35,7 +35,7 @@ StyledRect {
onCheckedChanged: internalChecked = checked
radius: internalChecked ? Appearance.rounding.small : implicitHeight / 2
- color: type == IconButton.Text ? "transparent" : internalChecked ? activeColour : inactiveColour
+ color: type == TextButton.Text ? "transparent" : internalChecked ? activeColour : inactiveColour
implicitWidth: label.implicitWidth + horizontalPadding * 2
implicitHeight: label.implicitHeight + verticalPadding * 2