diff options
Diffstat (limited to 'components/controls')
| -rw-r--r-- | components/controls/IconTextButton.qml | 86 | ||||
| -rw-r--r-- | components/controls/Menu.qml | 111 | ||||
| -rw-r--r-- | components/controls/MenuItem.qml | 11 | ||||
| -rw-r--r-- | components/controls/SplitButton.qml | 141 | ||||
| -rw-r--r-- | components/controls/TextButton.qml | 12 |
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 |