diff --git a/Components/CollapsibleSection.qml b/Components/CollapsibleSection.qml new file mode 100644 index 0000000..6d6fc3e --- /dev/null +++ b/Components/CollapsibleSection.qml @@ -0,0 +1,135 @@ +import QtQuick +import QtQuick.Layouts +import qs.Config + +ColumnLayout { + id: root + + default property alias content: contentColumn.data + property string description: "" + property bool expanded: false + property bool nested: false + property bool showBackground: false + required property string title + + signal toggleRequested + + Layout.fillWidth: true + spacing: Appearance.spacing.small + + Item { + id: sectionHeaderItem + + Layout.fillWidth: true + Layout.preferredHeight: Math.max(titleRow.implicitHeight + Appearance.padding.normal * 2, 48) + + RowLayout { + id: titleRow + + anchors.left: parent.left + anchors.leftMargin: Appearance.padding.normal + anchors.right: parent.right + anchors.rightMargin: Appearance.padding.normal + anchors.verticalCenter: parent.verticalCenter + spacing: Appearance.spacing.normal + + CustomText { + font.pointSize: Appearance.font.size.larger + font.weight: 500 + text: root.title + } + + Item { + Layout.fillWidth: true + } + + MaterialIcon { + color: DynamicColors.palette.m3onSurfaceVariant + font.pointSize: Appearance.font.size.normal + rotation: root.expanded ? 180 : 0 + text: "expand_more" + + Behavior on rotation { + Anim { + duration: Appearance.anim.durations.small + easing.bezierCurve: Appearance.anim.curves.standard + } + } + } + } + + StateLayer { + function onClicked(): void { + root.toggleRequested(); + root.expanded = !root.expanded; + } + + anchors.fill: parent + color: DynamicColors.palette.m3onSurface + radius: Appearance.rounding.normal + showHoverBackground: false + } + } + + Item { + id: contentWrapper + + Layout.fillWidth: true + Layout.preferredHeight: root.expanded ? (contentColumn.implicitHeight + Appearance.spacing.small * 2) : 0 + clip: true + + Behavior on Layout.preferredHeight { + Anim { + easing.bezierCurve: Appearance.anim.curves.standard + } + } + + CustomRect { + id: backgroundRect + + anchors.fill: parent + color: DynamicColors.transparency.enabled ? DynamicColors.layer(DynamicColors.palette.m3surfaceContainer, root.nested ? 3 : 2) : (root.nested ? DynamicColors.palette.m3surfaceContainerHigh : DynamicColors.palette.m3surfaceContainer) + opacity: root.showBackground && root.expanded ? 1.0 : 0.0 + radius: Appearance.rounding.normal + visible: root.showBackground + + Behavior on opacity { + Anim { + easing.bezierCurve: Appearance.anim.curves.standard + } + } + } + + ColumnLayout { + id: contentColumn + + anchors.bottomMargin: Appearance.spacing.small + anchors.left: parent.left + anchors.leftMargin: Appearance.padding.normal + anchors.right: parent.right + anchors.rightMargin: Appearance.padding.normal + opacity: root.expanded ? 1.0 : 0.0 + spacing: Appearance.spacing.small + y: Appearance.spacing.small + + Behavior on opacity { + Anim { + easing.bezierCurve: Appearance.anim.curves.standard + } + } + + CustomText { + id: descriptionText + + Layout.bottomMargin: root.description !== "" ? Appearance.spacing.small : 0 + Layout.fillWidth: true + Layout.topMargin: root.description !== "" ? Appearance.spacing.smaller : 0 + color: DynamicColors.palette.m3onSurfaceVariant + font.pointSize: Appearance.font.size.small + text: root.description + visible: root.description !== "" + wrapMode: Text.Wrap + } + } + } +} diff --git a/Components/CustomSpinBox.qml b/Components/CustomSpinBox.qml new file mode 100644 index 0000000..fe98e72 --- /dev/null +++ b/Components/CustomSpinBox.qml @@ -0,0 +1,166 @@ +pragma ComponentBehavior: Bound + +import QtQuick +import QtQuick.Layouts +import qs.Config + +RowLayout { + id: root + + property string displayText: root.value.toString() + property bool isEditing: false + property real max: Infinity + property real min: -Infinity + property alias repeatRate: timer.interval + property real step: 1 + property real value + + signal valueModified(value: real) + + spacing: Appearance.spacing.small + + onValueChanged: { + if (!root.isEditing) { + root.displayText = root.value.toString(); + } + } + + CustomTextField { + id: textField + + inputMethodHints: Qt.ImhFormattedNumbersOnly + leftPadding: Appearance.padding.normal + padding: Appearance.padding.small + rightPadding: Appearance.padding.normal + text: root.isEditing ? text : root.displayText + + background: CustomRect { + color: DynamicColors.tPalette.m3surfaceContainerHigh + implicitWidth: 100 + radius: Appearance.rounding.small + } + validator: DoubleValidator { + bottom: root.min + decimals: root.step < 1 ? Math.max(1, Math.ceil(-Math.log10(root.step))) : 0 + top: root.max + } + + onAccepted: { + const numValue = parseFloat(text); + if (!isNaN(numValue)) { + const clampedValue = Math.max(root.min, Math.min(root.max, numValue)); + root.value = clampedValue; + root.displayText = clampedValue.toString(); + root.valueModified(clampedValue); + } else { + text = root.displayText; + } + root.isEditing = false; + } + onActiveFocusChanged: { + if (activeFocus) { + root.isEditing = true; + } else { + root.isEditing = false; + root.displayText = root.value.toString(); + } + } + onEditingFinished: { + if (text !== root.displayText) { + const numValue = parseFloat(text); + if (!isNaN(numValue)) { + const clampedValue = Math.max(root.min, Math.min(root.max, numValue)); + root.value = clampedValue; + root.displayText = clampedValue.toString(); + root.valueModified(clampedValue); + } else { + text = root.displayText; + } + } + root.isEditing = false; + } + } + + CustomRect { + color: DynamicColors.palette.m3primary + implicitHeight: upIcon.implicitHeight + Appearance.padding.small * 2 + implicitWidth: implicitHeight + radius: Appearance.rounding.small + + StateLayer { + id: upState + + function onClicked(): void { + let newValue = Math.min(root.max, root.value + root.step); + // Round to avoid floating point precision errors + const decimals = root.step < 1 ? Math.max(1, Math.ceil(-Math.log10(root.step))) : 0; + newValue = Math.round(newValue * Math.pow(10, decimals)) / Math.pow(10, decimals); + root.value = newValue; + root.displayText = newValue.toString(); + root.valueModified(newValue); + } + + color: DynamicColors.palette.m3onPrimary + + onPressAndHold: timer.start() + onReleased: timer.stop() + } + + MaterialIcon { + id: upIcon + + anchors.centerIn: parent + color: DynamicColors.palette.m3onPrimary + text: "keyboard_arrow_up" + } + } + + CustomRect { + color: DynamicColors.palette.m3primary + implicitHeight: downIcon.implicitHeight + Appearance.padding.small * 2 + implicitWidth: implicitHeight + radius: Appearance.rounding.small + + StateLayer { + id: downState + + function onClicked(): void { + let newValue = Math.max(root.min, root.value - root.step); + // Round to avoid floating point precision errors + const decimals = root.step < 1 ? Math.max(1, Math.ceil(-Math.log10(root.step))) : 0; + newValue = Math.round(newValue * Math.pow(10, decimals)) / Math.pow(10, decimals); + root.value = newValue; + root.displayText = newValue.toString(); + root.valueModified(newValue); + } + + color: DynamicColors.palette.m3onPrimary + + onPressAndHold: timer.start() + onReleased: timer.stop() + } + + MaterialIcon { + id: downIcon + + anchors.centerIn: parent + color: DynamicColors.palette.m3onPrimary + text: "keyboard_arrow_down" + } + } + + Timer { + id: timer + + interval: 100 + repeat: true + triggeredOnStart: true + + onTriggered: { + if (upState.pressed) + upState.onClicked(); + else if (downState.pressed) + downState.onClicked(); + } + } +} diff --git a/Components/MarqueeText.qml b/Components/MarqueeText.qml new file mode 100644 index 0000000..02d123d --- /dev/null +++ b/Components/MarqueeText.qml @@ -0,0 +1,200 @@ +import QtQuick +import QtQuick.Effects + +Item { + id: root + + property color color: DynamicColors.palette.m3onSurface + property int fadeStrengthAnimMs: 180 + property real fadeStrengthIdle: 0.0 + property real fadeStrengthMoving: 1.0 + property alias font: elideText.font + property int gap: 40 + property alias horizontalAlignment: elideText.horizontalAlignment + property bool leftFadeEnabled: false + property real leftFadeStrength: overflowing && leftFadeEnabled ? fadeStrengthMoving : fadeStrengthIdle + property int leftFadeWidth: 28 + property bool marqueeEnabled: true + readonly property bool overflowing: metrics.width > root.width + property int pauseMs: 1200 + property real pixelsPerSecond: 40 + property real rightFadeStrength: overflowing ? fadeStrengthMoving : fadeStrengthIdle + property int rightFadeWidth: 28 + property bool sliding: false + property alias text: elideText.text + + function durationForDistance(px): int { + return Math.max(1, Math.round(Math.abs(px) / root.pixelsPerSecond * 1000)); + } + + clip: true + implicitHeight: elideText.implicitHeight + + Behavior on leftFadeStrength { + Anim { + } + } + Behavior on rightFadeStrength { + Anim { + } + } + + onTextChanged: strip.x = 0 + onVisibleChanged: if (!visible) + strip.x = 0 + onWidthChanged: strip.x = 0 + + TextMetrics { + id: metrics + + font: elideText.font + text: elideText.text + } + + CustomText { + id: elideText + + anchors.verticalCenter: parent.verticalCenter + color: root.color + elide: Text.ElideRight + visible: !root.overflowing + width: root.width + } + + Item { + id: marqueeViewport + + anchors.fill: parent + clip: true + layer.enabled: true + visible: root.overflowing + + layer.effect: OpacityMask { + maskSource: rightFadeMask + } + + Item { + id: strip + + anchors.verticalCenter: parent.verticalCenter + height: t1.implicitHeight + width: t1.width + root.gap + t2.width + x: 0 + + CustomText { + id: t1 + + color: root.color + text: elideText.text + } + + CustomText { + id: t2 + + color: root.color + text: t1.text + x: t1.width + root.gap + } + } + + SequentialAnimation { + id: marqueeAnim + + loops: Animation.Infinite + running: root.marqueeEnabled && root.overflowing && root.visible + + ScriptAction { + script: { + strip.x = 0; + root.sliding = false; + root.leftFadeEnabled = false; + } + } + + PauseAnimation { + duration: root.pauseMs + } + + ScriptAction { + script: { + root.sliding = true; + root.leftFadeEnabled = true; + } + } + + Anim { + duration: root.durationForDistance(t1.width) + easing.bezierCurve: Easing.Linear + easing.type: Easing.Linear + from: 0 + property: "x" + target: strip + to: -t1.width + } + + ScriptAction { + script: { + root.leftFadeEnabled = false; + } + } + + Anim { + duration: root.durationForDistance(root.gap) + easing.bezierCurve: Easing.Linear + easing.type: Easing.Linear + from: -t1.width + property: "x" + target: strip + to: -(t1.width + root.gap) + } + + ScriptAction { + script: { + root.sliding = false; + strip.x = 0; + } + } + } + } + + Rectangle { + id: rightFadeMask + + readonly property real fadeStartPos: { + const w = Math.max(1, width); + return Math.max(0, Math.min(1, (w - root.rightFadeWidth) / w)); + } + readonly property real leftFadeEndPos: { + const w = Math.max(1, width); + return Math.max(0, Math.min(1, root.leftFadeWidth / w)); + } + + anchors.fill: marqueeViewport + layer.enabled: true + visible: false + + gradient: Gradient { + orientation: Gradient.Horizontal + + GradientStop { + color: Qt.rgba(1, 1, 1, 1.0 - root.leftFadeStrength) + position: 0.0 + } + + GradientStop { + color: Qt.rgba(1, 1, 1, 1.0) + position: rightFadeMask.leftFadeEndPos + } + + GradientStop { + color: Qt.rgba(1, 1, 1, 1.0) + position: rightFadeMask.fadeStartPos + } + + GradientStop { + color: Qt.rgba(1, 1, 1, 1.0 - root.rightFadeStrength) + position: 1.0 + } + } + } +} diff --git a/Components/Menu.qml b/Components/Menu.qml new file mode 100644 index 0000000..d4ffb02 --- /dev/null +++ b/Components/Menu.qml @@ -0,0 +1,107 @@ +pragma ComponentBehavior: Bound + +import QtQuick +import QtQuick.Layouts +import qs.Config + +Elevation { + id: root + + property MenuItem active: items[0] ?? null + property bool expanded + property list items + + signal itemSelected(item: MenuItem) + + implicitHeight: root.expanded ? column.implicitHeight : 0 + implicitWidth: Math.max(200, column.implicitWidth) + level: 2 + opacity: root.expanded ? 1 : 0 + radius: Appearance.rounding.small / 2 + + Behavior on implicitHeight { + Anim { + duration: Appearance.anim.durations.expressiveDefaultSpatial + easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial + } + } + Behavior on opacity { + Anim { + duration: Appearance.anim.durations.expressiveDefaultSpatial + } + } + + CustomClippingRect { + anchors.fill: parent + color: DynamicColors.palette.m3surfaceContainer + radius: parent.radius + + ColumnLayout { + id: column + + anchors.left: parent.left + anchors.right: parent.right + spacing: 0 + + Repeater { + model: root.items + + CustomRect { + id: item + + readonly property bool active: modelData === root.active + required property int index + required property MenuItem modelData + + Layout.fillWidth: true + color: Qt.alpha(DynamicColors.palette.m3secondaryContainer, active ? 1 : 0) + implicitHeight: menuOptionRow.implicitHeight + Appearance.padding.normal * 2 + implicitWidth: menuOptionRow.implicitWidth + Appearance.padding.normal * 2 + + StateLayer { + function onClicked(): void { + root.itemSelected(item.modelData); + root.active = item.modelData; + root.expanded = false; + } + + color: item.active ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface + disabled: !root.expanded + } + + RowLayout { + id: menuOptionRow + + anchors.fill: parent + anchors.margins: Appearance.padding.normal + spacing: Appearance.spacing.small + + MaterialIcon { + Layout.alignment: Qt.AlignVCenter + color: item.active ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurfaceVariant + text: item.modelData.icon + } + + CustomText { + Layout.alignment: Qt.AlignVCenter + Layout.fillWidth: true + color: item.active ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface + text: item.modelData.text + } + + Loader { + Layout.alignment: Qt.AlignVCenter + active: item.modelData.trailingIcon.length > 0 + visible: active + + sourceComponent: MaterialIcon { + color: item.active ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface + text: item.modelData.trailingIcon + } + } + } + } + } + } + } +} diff --git a/Components/MenuItem.qml b/Components/MenuItem.qml new file mode 100644 index 0000000..2378dd9 --- /dev/null +++ b/Components/MenuItem.qml @@ -0,0 +1,12 @@ +import QtQuick + +QtObject { + property string activeIcon: icon + property string activeText: text + property string icon + required property string text + property string trailingIcon + property var value + + signal clicked +} diff --git a/Components/SpinBoxRow.qml b/Components/SpinBoxRow.qml new file mode 100644 index 0000000..6686ec7 --- /dev/null +++ b/Components/SpinBoxRow.qml @@ -0,0 +1,50 @@ +import QtQuick +import QtQuick.Layouts +import qs.Config + +CustomRect { + id: root + + required property string label + required property real max + required property real min + property var onValueModified: function (value) {} + property real step: 1 + required property real value + + Layout.fillWidth: true + color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainer, 2) + implicitHeight: row.implicitHeight + Appearance.padding.large * 2 + radius: Appearance.rounding.normal + + Behavior on implicitHeight { + Anim { + } + } + + RowLayout { + id: row + + anchors.left: parent.left + anchors.margins: Appearance.padding.large + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + spacing: Appearance.spacing.normal + + CustomText { + Layout.fillWidth: true + text: root.label + } + + CustomSpinBox { + max: root.max + min: root.min + step: root.step + value: root.value + + onValueModified: value => { + root.onValueModified(value); + } + } + } +} diff --git a/Drawers/Bar.qml b/Drawers/Bar.qml index e8d1728..c5bbdde 100644 --- a/Drawers/Bar.qml +++ b/Drawers/Bar.qml @@ -28,7 +28,7 @@ Variants { property bool trayMenuVisible: false WlrLayershell.exclusionMode: ExclusionMode.Ignore - WlrLayershell.keyboardFocus: visibilities.launcher || visibilities.sidebar || visibilities.dashboard ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None + WlrLayershell.keyboardFocus: visibilities.launcher || visibilities.sidebar || visibilities.dashboard || visibilities.settings ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None WlrLayershell.namespace: "ZShell-Bar" color: "transparent" contentItem.focus: true @@ -37,8 +37,6 @@ Variants { mask: Region { id: region - property list nullRegions: [] - height: bar.screen.height - backgroundRect.implicitHeight intersection: Intersection.Xor regions: popoutRegions.instances @@ -53,6 +51,7 @@ Variants { visibilities.sidebar = false; visibilities.dashboard = false; visibilities.osd = false; + visibilities.settings = false; } PanelWindow { @@ -98,7 +97,7 @@ Variants { HyprlandFocusGrab { id: focusGrab - active: visibilities.launcher || visibilities.sidebar || visibilities.dashboard || (panels.popouts.hasCurrent && panels.popouts.currentName.startsWith("traymenu")) + active: visibilities.launcher || visibilities.sidebar || visibilities.dashboard || visibilities.settings || (panels.popouts.hasCurrent && panels.popouts.currentName.startsWith("traymenu")) windows: [bar] onCleared: { @@ -106,6 +105,7 @@ Variants { visibilities.sidebar = false; visibilities.dashboard = false; visibilities.osd = false; + visibilities.settings = false; panels.popouts.hasCurrent = false; } } diff --git a/Modules/Dashboard/Dash.qml b/Modules/Dashboard/Dash.qml index a9ae042..44851e8 100644 --- a/Modules/Dashboard/Dash.qml +++ b/Modules/Dashboard/Dash.qml @@ -3,7 +3,6 @@ import QtQuick import QtQuick.Layouts import qs.Helpers import qs.Components -import qs.Paths import qs.Modules import qs.Config import qs.Modules.Dashboard.Dash @@ -86,18 +85,6 @@ GridLayout { } } - // Rect { - // Layout.row: 1 - // Layout.preferredWidth: dateTime.implicitWidth - // Layout.fillHeight: true - // - // radius: root.radius - // - // DateTime { - // id: dateTime - // } - // } - Rect { Layout.column: 0 Layout.columnSpan: 3 @@ -128,11 +115,11 @@ GridLayout { } Rect { - Layout.column: 5 - Layout.fillHeight: true - Layout.preferredWidth: media.implicitWidth - Layout.row: 0 - Layout.rowSpan: 2 + Layout.column: 0 + Layout.columnSpan: 5 + Layout.fillWidth: true + Layout.preferredHeight: media.implicitHeight + Layout.row: 2 radius: root.radius Media { diff --git a/Modules/Dashboard/Dash/Media.qml b/Modules/Dashboard/Dash/Media.qml index 35b69d0..456bd3d 100644 --- a/Modules/Dashboard/Dash/Media.qml +++ b/Modules/Dashboard/Dash/Media.qml @@ -1,12 +1,10 @@ -import ZShell.Services import QtQuick +import QtQuick.Layouts import QtQuick.Shapes import qs.Daemons import qs.Components import qs.Config import qs.Helpers -import qs.Modules -import qs.Paths Item { id: root @@ -15,10 +13,11 @@ Item { const active = Players.active; return active?.length ? active.position / active.length : 0; } + property int rowHeight: Appearance.padding.large + Config.dashboard.sizes.mediaProgressThickness + Appearance.spacing.small - anchors.bottom: parent.bottom - anchors.top: parent.top - implicitWidth: Config.dashboard.sizes.mediaWidth + anchors.left: parent.left + anchors.right: parent.right + implicitHeight: cover.height + rowHeight * 2 Behavior on playerProgress { Anim { @@ -35,10 +34,6 @@ Item { onTriggered: Players.active?.positionChanged() } - ServiceRef { - service: Audio.beatTracker - } - Shape { preferredRendererType: Shape.CurveRenderer @@ -85,114 +80,124 @@ Item { } } - CustomClippingRect { - id: cover + RowLayout { + id: layout anchors.left: parent.left - anchors.margins: Appearance.padding.large + Config.dashboard.sizes.mediaProgressThickness + Appearance.spacing.small anchors.right: parent.right - anchors.top: parent.top - color: DynamicColors.tPalette.m3surfaceContainerHigh - implicitHeight: width - radius: Infinity + implicitHeight: root.implicitHeight - MaterialIcon { - anchors.centerIn: parent - color: DynamicColors.palette.m3onSurfaceVariant - font.pointSize: (parent.width * 0.4) || 1 - grade: 200 - text: "art_track" - } + CustomClippingRect { + id: cover - Image { - id: image + Layout.alignment: Qt.AlignLeft + Layout.bottomMargin: Appearance.padding.large + Config.dashboard.sizes.mediaProgressThickness + Appearance.spacing.small + Layout.leftMargin: Appearance.padding.large + Config.dashboard.sizes.mediaProgressThickness + Appearance.spacing.small + Layout.preferredHeight: Config.dashboard.sizes.mediaCoverArtSize + Layout.preferredWidth: Config.dashboard.sizes.mediaCoverArtSize + Layout.topMargin: Appearance.padding.large + Config.dashboard.sizes.mediaProgressThickness + Appearance.spacing.small + color: DynamicColors.tPalette.m3surfaceContainerHigh + radius: Infinity - anchors.fill: parent - asynchronous: true - fillMode: Image.PreserveAspectCrop - source: Players.active?.trackArtUrl ?? "" - sourceSize.height: height - sourceSize.width: width - } - } - - CustomText { - id: title - - anchors.horizontalCenter: parent.horizontalCenter - anchors.top: cover.bottom - anchors.topMargin: Appearance.spacing.normal - animate: true - color: DynamicColors.palette.m3primary - elide: Text.ElideRight - font.pointSize: Appearance.font.size.normal - horizontalAlignment: Text.AlignHCenter - text: (Players.active?.trackTitle ?? qsTr("No media")) || qsTr("Unknown title") - width: parent.implicitWidth - Appearance.padding.large * 2 - } - - CustomText { - id: album - - anchors.horizontalCenter: parent.horizontalCenter - anchors.top: title.bottom - anchors.topMargin: Appearance.spacing.small - animate: true - color: DynamicColors.palette.m3outline - elide: Text.ElideRight - font.pointSize: Appearance.font.size.small - horizontalAlignment: Text.AlignHCenter - text: (Players.active?.trackAlbum ?? qsTr("No media")) || qsTr("Unknown album") - width: parent.implicitWidth - Appearance.padding.large * 2 - } - - CustomText { - id: artist - - anchors.horizontalCenter: parent.horizontalCenter - anchors.top: album.bottom - anchors.topMargin: Appearance.spacing.small - animate: true - color: DynamicColors.palette.m3secondary - elide: Text.ElideRight - horizontalAlignment: Text.AlignHCenter - text: (Players.active?.trackArtist ?? qsTr("No media")) || qsTr("Unknown artist") - width: parent.implicitWidth - Appearance.padding.large * 2 - } - - Row { - id: controls - - anchors.horizontalCenter: parent.horizontalCenter - anchors.top: artist.bottom - anchors.topMargin: Appearance.spacing.smaller - spacing: Appearance.spacing.small - - Control { - function onClicked(): void { - Players.active?.previous(); + MaterialIcon { + anchors.centerIn: parent + color: DynamicColors.palette.m3onSurfaceVariant + font.pointSize: (parent.width * 0.4) || 1 + grade: 200 + text: "art_track" } - canUse: Players.active?.canGoPrevious ?? false - icon: "skip_previous" + Image { + id: image + + anchors.fill: parent + asynchronous: true + fillMode: Image.PreserveAspectCrop + source: Players.active?.trackArtUrl ?? "" + sourceSize.height: Math.floor(height) + sourceSize.width: Math.floor(width) + } } - Control { - function onClicked(): void { - Players.active?.togglePlaying(); + CustomRect { + Layout.fillWidth: true + Layout.preferredHeight: childrenRect.height + + MarqueeText { + id: title + + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + color: DynamicColors.palette.m3primary + font.pointSize: Appearance.font.size.normal + horizontalAlignment: Text.AlignHCenter + pauseMs: 4000 + text: (Players.active?.trackTitle ?? qsTr("No media")) || qsTr("Unknown title") + width: parent.width - Appearance.padding.large * 4 } - canUse: Players.active?.canTogglePlaying ?? false - icon: Players.active?.isPlaying ? "pause" : "play_arrow" - } + CustomText { + id: album - Control { - function onClicked(): void { - Players.active?.next(); + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: title.bottom + anchors.topMargin: Appearance.spacing.small + animate: true + color: DynamicColors.palette.m3outline + elide: Text.ElideRight + font.pointSize: Appearance.font.size.small + horizontalAlignment: Text.AlignHCenter + text: (Players.active?.trackAlbum ?? qsTr("No media")) || qsTr("Unknown album") } - canUse: Players.active?.canGoNext ?? false - icon: "skip_next" + CustomText { + id: artist + + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: album.bottom + anchors.topMargin: Appearance.spacing.small + animate: true + color: DynamicColors.palette.m3secondary + elide: Text.ElideRight + horizontalAlignment: Text.AlignHCenter + text: (Players.active?.trackArtist ?? qsTr("No media")) || qsTr("Unknown artist") + } + + Row { + id: controls + + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: artist.bottom + anchors.topMargin: Appearance.spacing.smaller + spacing: Appearance.spacing.small + + Control { + function onClicked(): void { + Players.active?.previous(); + } + + canUse: Players.active?.canGoPrevious ?? false + icon: "skip_previous" + } + + Control { + function onClicked(): void { + Players.active?.togglePlaying(); + } + + canUse: Players.active?.canTogglePlaying ?? false + icon: Players.active?.isPlaying ? "pause" : "play_arrow" + } + + Control { + function onClicked(): void { + Players.active?.next(); + } + + canUse: Players.active?.canGoNext ?? false + icon: "skip_next" + } + } } } diff --git a/Modules/Settings/Background.qml b/Modules/Settings/Background.qml deleted file mode 100644 index cc242f7..0000000 --- a/Modules/Settings/Background.qml +++ /dev/null @@ -1,66 +0,0 @@ -import QtQuick -import QtQuick.Shapes -import qs.Components -import qs.Config - -ShapePath { - id: root - - readonly property bool flatten: wrapper.height < rounding * 2 - readonly property real rounding: 8 - readonly property real roundingY: flatten ? wrapper.height / 2 : rounding - required property Wrapper wrapper - - fillColor: DynamicColors.palette.m3surface - strokeWidth: -1 - - Behavior on fillColor { - CAnim { - } - } - - PathArc { - radiusX: root.rounding - radiusY: Math.min(root.roundingY, root.wrapper.height) - relativeX: root.rounding - relativeY: root.roundingY - } - - PathLine { - relativeX: 0 - relativeY: root.wrapper.height - root.roundingY * 2 - } - - PathArc { - direction: PathArc.Counterclockwise - radiusX: root.rounding - radiusY: Math.min(root.rounding, root.wrapper.height) - relativeX: root.rounding - relativeY: root.roundingY - } - - PathLine { - relativeX: root.wrapper.width - root.rounding * 2 - relativeY: 0 - } - - PathArc { - direction: PathArc.Counterclockwise - radiusX: root.rounding - radiusY: Math.min(root.rounding, root.wrapper.height) - relativeX: root.rounding - relativeY: -root.roundingY - } - - PathLine { - relativeX: 0 - relativeY: -(root.wrapper.height - root.roundingY * 2) - } - - PathArc { - radiusX: root.rounding - radiusY: Math.min(root.rounding, root.wrapper.height) - relativeX: root.rounding - relativeY: -root.roundingY - } -} diff --git a/Modules/Settings/Categories.qml b/Modules/Settings/Categories.qml deleted file mode 100644 index 08bcacc..0000000 --- a/Modules/Settings/Categories.qml +++ /dev/null @@ -1,178 +0,0 @@ -pragma ComponentBehavior: Bound - -import Quickshell -import Quickshell.Widgets -import QtQuick -import QtQuick.Layouts -import qs.Components -import qs.Modules as Modules -import qs.Config -import qs.Helpers - -Item { - id: root - - required property Item content - - implicitHeight: clayout.contentHeight + Appearance.padding.smaller * 2 - implicitWidth: clayout.contentWidth + Appearance.padding.smaller * 2 - - ListModel { - id: listModel - - ListElement { - icon: "settings" - name: "General" - } - - ListElement { - icon: "wallpaper" - name: "Wallpaper" - } - - ListElement { - icon: "settop_component" - name: "Bar" - } - - ListElement { - icon: "lock" - name: "Lockscreen" - } - - ListElement { - icon: "build_circle" - name: "Services" - } - - ListElement { - icon: "notifications" - name: "Notifications" - } - - ListElement { - icon: "view_sidebar" - name: "Sidebar" - } - - ListElement { - icon: "handyman" - name: "Utilities" - } - - ListElement { - icon: "dashboard" - name: "Dashboard" - } - - ListElement { - icon: "colors" - name: "Appearance" - } - - ListElement { - icon: "display_settings" - name: "On screen display" - } - - ListElement { - icon: "rocket_launch" - name: "Launcher" - } - - ListElement { - icon: "colors" - name: "Colors" - } - } - - CustomRect { - anchors.fill: parent - color: DynamicColors.tPalette.m3surfaceContainer - radius: 4 - - CustomListView { - id: clayout - - anchors.centerIn: parent - contentHeight: contentItem.childrenRect.height - contentWidth: contentItem.childrenRect.width - highlightFollowsCurrentItem: false - implicitHeight: contentItem.childrenRect.height - implicitWidth: contentItem.childrenRect.width - model: listModel - spacing: 5 - - delegate: Category { - } - highlight: CustomRect { - color: DynamicColors.palette.m3primary - implicitHeight: clayout.currentItem?.implicitHeight ?? 0 - implicitWidth: clayout.width - radius: 4 - y: clayout.currentItem?.y ?? 0 - - Behavior on y { - Anim { - duration: Appearance.anim.durations.small - easing.bezierCurve: Appearance.anim.curves.expressiveEffects - } - } - } - } - } - - component Category: CustomRect { - id: categoryItem - - required property string icon - required property int index - required property string name - - implicitHeight: 42 - implicitWidth: 200 - radius: 4 - - RowLayout { - id: layout - - anchors.left: parent.left - anchors.margins: Appearance.padding.smaller - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - - MaterialIcon { - id: icon - - Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - Layout.fillHeight: true - Layout.preferredWidth: icon.contentWidth - color: categoryItem.index === clayout.currentIndex ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface - font.pointSize: 22 - text: categoryItem.icon - verticalAlignment: Text.AlignVCenter - } - - CustomText { - id: text - - Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - Layout.fillHeight: true - Layout.fillWidth: true - Layout.leftMargin: Appearance.spacing.normal - color: categoryItem.index === clayout.currentIndex ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface - text: categoryItem.name - verticalAlignment: Text.AlignVCenter - } - } - - StateLayer { - id: layer - - onClicked: { - root.content.currentCategory = categoryItem.name.toLowerCase(); - clayout.currentIndex = categoryItem.index; - } - } - } -} diff --git a/Modules/Settings/Categories/Appearance.qml b/Modules/Settings/Categories/Appearance.qml deleted file mode 100644 index a75b264..0000000 --- a/Modules/Settings/Categories/Appearance.qml +++ /dev/null @@ -1,66 +0,0 @@ -import Quickshell -import Quickshell.Widgets -import QtQuick -import QtQuick.Layouts -import qs.Components -import qs.Modules as Modules -import qs.Modules.Settings.Controls -import qs.Config -import qs.Helpers - -CustomRect { - id: root - - ColumnLayout { - id: clayout - - anchors.left: parent.left - anchors.right: parent.right - - CustomRect { - Layout.fillWidth: true - Layout.preferredHeight: colorLayout.implicitHeight - color: DynamicColors.tPalette.m3surfaceContainer - - ColumnLayout { - id: colorLayout - - anchors.left: parent.left - anchors.margins: Appearance.padding.large - anchors.right: parent.right - - Settings { - name: "smth" - } - - SettingSwitch { - name: "wallust" - object: Config.general.color - setting: "wallust" - } - } - } - } - - component Settings: CustomRect { - id: settingsItem - - required property string name - - Layout.preferredHeight: 42 - Layout.preferredWidth: 200 - radius: 4 - - CustomText { - id: text - - anchors.left: parent.left - anchors.margins: Appearance.padding.smaller - anchors.right: parent.right - font.bold: true - font.pointSize: 32 - text: settingsItem.name - verticalAlignment: Text.AlignVCenter - } - } -} diff --git a/Modules/Settings/Categories/Background.qml b/Modules/Settings/Categories/Background.qml deleted file mode 100644 index 2d24d68..0000000 --- a/Modules/Settings/Categories/Background.qml +++ /dev/null @@ -1,13 +0,0 @@ -import Quickshell -import Quickshell.Widgets -import QtQuick -import QtQuick.Layouts -import qs.Components -import qs.Modules as Modules -import qs.Config -import qs.Helpers - -CustomRect { - id: root - -} diff --git a/Modules/Settings/Categories/General.qml b/Modules/Settings/Categories/General.qml deleted file mode 100644 index 3e8335c..0000000 --- a/Modules/Settings/Categories/General.qml +++ /dev/null @@ -1,55 +0,0 @@ -import Quickshell -import Quickshell.Widgets -import QtQuick -import QtQuick.Layouts -import qs.Components -import qs.Modules as Modules -import qs.Config -import qs.Helpers - -CustomRect { - id: root - - ColumnLayout { - id: clayout - - anchors.fill: parent - - Settings { - name: "apps" - } - - Item { - } - } - - component Settings: CustomRect { - id: settingsItem - - required property string name - - implicitHeight: 42 - implicitWidth: 200 - radius: 4 - - RowLayout { - id: layout - - anchors.left: parent.left - anchors.margins: Appearance.padding.smaller - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - - CustomText { - id: text - - Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - Layout.fillHeight: true - Layout.fillWidth: true - Layout.leftMargin: Appearance.spacing.normal - text: settingsItem.name - verticalAlignment: Text.AlignVCenter - } - } - } -} diff --git a/Modules/Settings/Content.qml b/Modules/Settings/Content.qml deleted file mode 100644 index 1037a21..0000000 --- a/Modules/Settings/Content.qml +++ /dev/null @@ -1,102 +0,0 @@ -import Quickshell -import Quickshell.Widgets -import QtQuick -import QtQuick.Controls -import qs.Components -import qs.Modules as Modules -import qs.Modules.Settings.Categories as Cat -import qs.Config -import qs.Helpers - -Item { - id: root - - property string currentCategory: "general" - readonly property real nonAnimHeight: view.implicitHeight + viewWrapper.anchors.margins * 2 - readonly property real nonAnimWidth: view.implicitWidth + 500 + viewWrapper.anchors.margins * 2 - required property PersistentProperties visibilities - - implicitHeight: nonAnimHeight - implicitWidth: nonAnimWidth - - Connections { - function onCurrentCategoryChanged() { - stack.pop(); - if (currentCategory === "general") { - stack.push(general); - } else if (currentCategory === "wallpaper") { - stack.push(background); - } else if (currentCategory === "appearance") { - stack.push(appearance); - } - } - - target: root - } - - ClippingRectangle { - id: viewWrapper - - anchors.fill: parent - anchors.margins: Appearance.padding.smaller - color: "transparent" - - Item { - id: view - - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.top: parent.top - implicitHeight: layout.implicitHeight - implicitWidth: layout.implicitWidth - - Categories { - id: layout - - anchors.fill: parent - content: root - } - } - - CustomClippingRect { - id: categoryContent - - anchors.bottom: parent.bottom - anchors.left: view.right - anchors.leftMargin: Appearance.spacing.smaller - anchors.right: parent.right - anchors.top: parent.top - color: DynamicColors.tPalette.m3surfaceContainer - radius: 4 - - StackView { - id: stack - - anchors.fill: parent - anchors.margins: Appearance.padding.smaller - initialItem: general - } - } - } - - Component { - id: general - - Cat.General { - } - } - - Component { - id: background - - Cat.Background { - } - } - - Component { - id: appearance - - Cat.Appearance { - } - } -} diff --git a/Modules/Settings/Controls/SettingSwitch.qml b/Modules/Settings/Controls/SettingSwitch.qml deleted file mode 100644 index 524e740..0000000 --- a/Modules/Settings/Controls/SettingSwitch.qml +++ /dev/null @@ -1,36 +0,0 @@ -import QtQuick -import QtQuick.Layouts -import qs.Components -import qs.Config - -RowLayout { - id: root - - required property string name - required property var object - required property string setting - - Layout.fillWidth: true - Layout.preferredHeight: 42 - - CustomText { - id: text - - Layout.alignment: Qt.AlignLeft - Layout.fillWidth: true - font.pointSize: 16 - text: root.name - } - - CustomSwitch { - id: cswitch - - Layout.alignment: Qt.AlignRight - checked: root.object[root.setting] - - onToggled: { - root.object[root.setting] = checked; - Config.save(); - } - } -} diff --git a/Modules/Settings/Settings.qml b/Modules/Settings/Settings.qml deleted file mode 100644 index dd3bfc3..0000000 --- a/Modules/Settings/Settings.qml +++ /dev/null @@ -1,11 +0,0 @@ -import Quickshell -import Quickshell.Widgets -import QtQuick -import QtQuick.Layouts -import qs.Components -import qs.Modules as Modules -import qs.Config -import qs.Helpers - -Item { -} diff --git a/Modules/Settings/Wrapper.qml b/Modules/Settings/Wrapper.qml deleted file mode 100644 index dec6264..0000000 --- a/Modules/Settings/Wrapper.qml +++ /dev/null @@ -1,61 +0,0 @@ -import Quickshell -import QtQuick -import qs.Components -import qs.Config -import qs.Helpers - -Item { - id: root - - required property var panels - required property PersistentProperties visibilities - - implicitHeight: 0 - implicitWidth: content.implicitWidth - visible: height > 0 - - states: State { - name: "visible" - when: root.visibilities.settings - - PropertyChanges { - root.implicitHeight: content.implicitHeight - } - } - transitions: [ - Transition { - from: "" - to: "visible" - - Anim { - duration: MaterialEasing.expressiveEffectsTime - easing.bezierCurve: MaterialEasing.expressiveEffects - property: "implicitHeight" - target: root - } - }, - Transition { - from: "visible" - to: "" - - Anim { - easing.bezierCurve: MaterialEasing.expressiveEffects - property: "implicitHeight" - target: root - } - } - ] - - Loader { - id: content - - active: true - anchors.bottom: parent.bottom - anchors.horizontalCenter: parent.horizontalCenter - visible: true - - sourceComponent: Content { - visibilities: root.visibilities - } - } -} diff --git a/assets/shaders/opacitymask.frag.qsb b/assets/shaders/opacitymask.frag.qsb index 4aedc35..7bf97c2 100644 Binary files a/assets/shaders/opacitymask.frag.qsb and b/assets/shaders/opacitymask.frag.qsb differ