diff --git a/Components/CustomListView.qml b/Components/CustomListView.qml index 51c7110..fa70f6d 100644 --- a/Components/CustomListView.qml +++ b/Components/CustomListView.qml @@ -3,11 +3,29 @@ import QtQuick ListView { id: root + property bool doneFakeFlick + maximumFlickVelocity: 3000 rebound: Transition { + onRunningChanged: { + if (!running && !root.doneFakeFlick) { + root.doneFakeFlick = true; + root.flick(1, 1); + root.flick(-1, -1); + Qt.callLater(() => root.cancelFlick()); + } + } + Anim { properties: "x,y" } } + + Timer { + interval: 10 + running: root.doneFakeFlick + + onTriggered: root.doneFakeFlick = false + } } diff --git a/Components/CustomScrollBar.qml b/Components/CustomScrollBar.qml index 6597f99..e1572fe 100644 --- a/Components/CustomScrollBar.qml +++ b/Components/CustomScrollBar.qml @@ -1,6 +1,6 @@ -import qs.Config import QtQuick import QtQuick.Templates +import qs.Config ScrollBar { id: root @@ -12,7 +12,7 @@ ScrollBar { property real nonAnimPosition property bool shouldBeActive - implicitWidth: 8 + implicitWidth: mouse.containsMouse ? Appearance.padding.extraSmall * 2 : Appearance.padding.extraSmall contentItem: CustomRect { anchors.left: parent.left @@ -33,6 +33,7 @@ ScrollBar { Behavior on opacity { Anim { + type: Anim.DefaultEffects } } @@ -40,9 +41,16 @@ ScrollBar { id: mouse acceptedButtons: Qt.NoButton - anchors.fill: parent + anchors.bottom: parent.bottom + anchors.right: parent.right + anchors.top: parent.top cursorShape: Qt.PointingHandCursor hoverEnabled: true + implicitWidth: Appearance.padding.extraSmall * 2 + } + } + Behavior on implicitWidth { + Anim { } } Behavior on position { @@ -86,23 +94,19 @@ ScrollBar { // Sync nonAnimPosition with flickable when not animating Connections { function onContentYChanged() { - if (!animating && !fullMouse.pressed) { - _updatingFromFlickable = true; - const contentHeight = flickable.contentHeight; - const height = flickable.height; + if (!root.animating && !fullMouse.pressed) { + root._updatingFromFlickable = true; + const contentHeight = root.flickable.contentHeight; + const height = root.flickable.height; if (contentHeight > height) { - nonAnimPosition = Math.max(0, Math.min(1, flickable.contentY / (contentHeight - height))); + root.nonAnimPosition = Math.max(0, Math.min(1, root.flickable.contentY / (contentHeight - height))); } else { - nonAnimPosition = 0; + root.nonAnimPosition = 0; } - _updatingFromFlickable = false; + root._updatingFromFlickable = false; } } - target: flickable - } - - Connections { function onMovingChanged(): void { if (root.flickable.moving) root.shouldBeActive = true; diff --git a/Components/CustomSlider.qml b/Components/CustomSlider.qml index 3041698..442b0d1 100644 --- a/Components/CustomSlider.qml +++ b/Components/CustomSlider.qml @@ -14,7 +14,11 @@ Slider { property color bgColor: enabled ? DynamicColors.palette.m3secondaryContainer : Qt.alpha(DynamicColors.palette.m3onSurface, 0.1) property color fgColor: enabled ? DynamicColors.palette.m3primary : Qt.alpha(DynamicColors.palette.m3onSurface, 0.38) property real filledWidth + property alias inset: inset + property color insetColor: enabled ? (inset.attached ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onPrimary) : DynamicColors.palette.m3onSurface + property string insetIcon: "" property real pos: visualPosition + property int radius: Appearance.rounding.small property int waveDuration: 1000 property real waveFrequency: 6 property bool wavy @@ -38,7 +42,7 @@ Slider { color: root.bgColor implicitHeight: parent.height * (parent.height <= 12 ? opacity : Math.min(opacity * 2, 1)) opacity: Math.min(width, 12) / 12 - radius: Appearance.rounding.small + radius: root.radius topLeftRadius: Appearance.rounding.extraSmall / 2 } @@ -61,9 +65,9 @@ Slider { anchors.verticalCenter: parent.verticalCenter color: root.fgColor implicitHeight: { - const mult = parent.height <= 12 ? 3 : 1.2; - const pressMult = parent.height <= 12 ? 4 : 1.5; - return parent.height * (mouse.pressed ? pressMult : mult); + const t = ZUtils.clamp((parent.height - 12) / 16, 0, 1); + const lerp = (a, b) => a + (b - a) * t; + return parent.height * (mouse.pressed ? lerp(3.5, 1.5) : lerp(3, 1.2)); } implicitWidth: 4 radius: Appearance.rounding.full @@ -84,6 +88,27 @@ Slider { sourceComponent: root.wavy ? waveComp : lineComp } + MaterialIcon { + id: inset + + readonly property bool attached: root.pos < 0.1 + property real dockT: attached ? 1 : 0 + + anchors.bottom: parent.bottom + anchors.top: parent.top + color: root.insetColor + font.pixelSize: Appearance.font.size.smaller * 2 + text: root.insetIcon + verticalAlignment: Text.AlignVCenter + visible: !root.wavy && root.insetIcon !== "" + x: Appearance.spacing.extraSmall + dockT * (handle.x + Appearance.spacing.small) + + Behavior on dockT { + Anim { + } + } + } + Component { id: lineComp @@ -92,7 +117,7 @@ Slider { color: root.fgColor implicitHeight: root.height implicitWidth: root.filledWidth - radius: Appearance.rounding.small + radius: root.radius topRightRadius: Appearance.rounding.extraSmall / 2 } } diff --git a/Modules/Settings/Categories/General.qml b/Modules/Settings/Categories/General.qml index 46efcdc..e62379c 100644 --- a/Modules/Settings/Categories/General.qml +++ b/Modules/Settings/Categories/General.qml @@ -1,6 +1,7 @@ import Quickshell import QtQuick import QtQuick.Layouts +import QtQuick.Controls import qs.Modules.Settings.Controls import qs.Config import qs.Components @@ -238,6 +239,162 @@ SettingsPage { shouldBeActive: Config.general.color.schemeGeneration ? 1 : 0 } + // Item { + // id: timeInput + // + // readonly property bool highlighted: SettingsHighlight.highlightedSetting === name + // property string name + // property var object + // property list settings + // property bool shouldBeActive: true + // + // function commitChoice(choice: int, setting: string): void { + // timeInput.object[setting] = choice; + // Config.save(); + // } + // + // function formattedValue(setting: string): string { + // const value = timeInput.object[setting]; + // + // if (value === null || value === undefined) + // return ""; + // + // return String(value); + // } + // + // function hourToAmPm(hour) { + // var h = Number(hour) % 24; + // var d = new Date(2000, 0, 1, h, 0, 0); + // return Qt.formatTime(d, "h AP"); + // } + // + // anchors.left: parent.left + // anchors.right: parent.right + // implicitHeight: shouldBeActive ? row.implicitHeight + Appearance.padding.smaller * 2 : 0 + // opacity: shouldBeActive ? 1 : 0 + // scale: shouldBeActive ? 1 : 0.8 + // visible: opacity > 0 + // + // Behavior on opacity { + // Anim { + // } + // } + // Behavior on scale { + // Anim { + // } + // } + // Behavior on y { + // Anim { + // } + // } + // + // Rectangle { + // anchors.fill: parent + // anchors.margins: -Appearance.padding.smaller + // color: DynamicColors.palette.m3primaryContainer + // opacity: timeInput.highlighted ? 0.5 : 0 + // radius: Appearance.rounding.small + // + // Behavior on opacity { + // Anim { + // duration: Appearance.anim.durations.normal + // } + // } + // } + // + // RowLayout { + // id: row + // + // anchors.left: parent.left + // anchors.margins: Appearance.padding.small + // anchors.right: parent.right + // anchors.verticalCenter: parent.verticalCenter + // + // ColumnLayout { + // Layout.fillHeight: true + // Layout.fillWidth: true + // + // CustomText { + // id: text + // + // Layout.alignment: Qt.AlignLeft + // Layout.fillWidth: true + // font.pointSize: Appearance.font.size.larger + // text: timeInput.name + // } + // + // CustomText { + // Layout.alignment: Qt.AlignLeft + // color: DynamicColors.palette.m3onSurfaceVariant + // font.pointSize: Appearance.font.size.normal + // text: qsTr("Dark mode will turn on at %1, and turn off at %2.").arg(timeInput.hourToAmPm(timeInput.object[timeInput.settings[0]])).arg(timeInput.hourToAmPm(timeInput.object[timeInput.settings[1]])) + // } + // } + // + // ColumnLayout { + // id: optionLayout + // + // Layout.fillHeight: true + // Layout.preferredWidth: 100 + // + // RowLayout { + // Layout.preferredWidth: optionLayout.width + // + // CustomText { + // Layout.alignment: Qt.AlignLeft | Qt.AlignHCenter + // Layout.fillWidth: true + // text: qsTr("Enabled: ") + // } + // + // CustomSwitch { + // id: enabledSwitch + // + // Layout.alignment: Qt.AlignRight | Qt.AlignHCenter + // checked: timeInput.object[timeInput.settings[2]] + // + // onToggled: { + // timeInput.object[timeInput.settings[2]] = checked; + // Config.save(); + // } + // } + // } + // + // RowLayout { + // Layout.preferredWidth: optionLayout.width + // + // CustomText { + // Layout.alignment: Qt.AlignLeft | Qt.AlignHCenter + // Layout.fillWidth: true + // text: qsTr("Start: ") + // } + // + // CustomRect { + // Layout.preferredHeight: 72 + // Layout.preferredWidth: 96 + // color: startHourField.focus ? DynamicColors.palette.m3primaryContainer : DynamicColors.palette.m3surfaceContainer + // + // CustomTextField { + // id: startHourField + // + // function convertHour(timeValue: int): int { + // return Math.floor(timeValue / 60); + // } + // + // function convertMinute(timeValue: int): int { + // return timeValue % 60; + // } + // + // anchors.fill: parent + // font.family: "Roboto" + // font.pixelSize: 57 + // text: convertHour(Config.general.color.scheduleDarkStart) + // } + // } + // } + // } + // } + // } + Separator { } diff --git a/Modules/SysTray/Popouts/AudioPopup.qml b/Modules/SysTray/Popouts/AudioPopup.qml index e6b8ca8..4dd9fb1 100644 --- a/Modules/SysTray/Popouts/AudioPopup.qml +++ b/Modules/SysTray/Popouts/AudioPopup.qml @@ -19,7 +19,7 @@ Item { required property var wrapper implicitHeight: vol.implicitHeight + Appearance.padding.small * 2 - implicitWidth: 600 + Appearance.padding.small * 2 + implicitWidth: 500 + Appearance.padding.small * 2 CustomRect { anchors.left: parent.left @@ -57,51 +57,11 @@ Item { color: DynamicColors.tPalette.m3surfaceContainer radius: root.rounding - Item { - id: sinkIcon - - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.leftMargin: Appearance.padding.larger - anchors.top: parent.top - implicitWidth: childrenRect.width - - CustomRect { - anchors.centerIn: parent - color: Audio.muted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary - implicitHeight: 54 - implicitWidth: 54 - radius: Appearance.rounding.full - - MaterialIcon { - anchors.alignWhenCentered: false - anchors.centerIn: parent - animate: true - color: Audio.muted ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onPrimary - font.pointSize: Appearance.font.size.extraLarge - text: Audio.muted ? "volume_off" : "volume_up" - } - - StateLayer { - color: Audio.muted ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onPrimary - - onClicked: { - const audio = Audio.sink?.audio; - if (audio) - audio.muted = !audio.muted; - } - } - } - } - ColumnLayout { - anchors.bottom: parent.bottom - anchors.bottomMargin: Appearance.padding.smallest - anchors.left: sinkIcon.right - anchors.leftMargin: Appearance.spacing.larger - anchors.right: parent.right - anchors.rightMargin: Appearance.padding.large - anchors.top: parent.top + anchors.bottomMargin: Appearance.padding.smaller + anchors.fill: parent + anchors.leftMargin: Appearance.padding.larger + anchors.rightMargin: Appearance.padding.larger anchors.topMargin: Appearance.padding.smaller RowLayout { @@ -111,34 +71,53 @@ Item { CustomText { Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft Layout.fillWidth: true - text: "Output Volume" + text: "Output volume" } CustomText { Layout.alignment: Qt.AlignVCenter | Qt.AlignRight + color: Qt.alpha(DynamicColors.palette.m3onSurface, 0.7) font.bold: true text: qsTr("%1").arg(Audio.muted ? qsTr("Muted") : `${Math.round(Audio.volume * 100)}%`) } } - CustomMouseArea { - Layout.bottomMargin: Appearance.padding.normal - Layout.fillWidth: true - Layout.preferredHeight: Appearance.padding.larger * 3 + RowLayout { + spacing: Appearance.spacing.large - CustomSlider { - anchors.left: parent.left - anchors.right: parent.right - fgColor: Audio.muted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary - implicitHeight: parent.height - value: Audio.volume + CustomMouseArea { + Layout.bottomMargin: Appearance.padding.normal + Layout.fillWidth: true + Layout.preferredHeight: Appearance.padding.larger * 3 - Behavior on value { - Anim { + CustomSlider { + anchors.left: parent.left + anchors.right: parent.right + bgColor: Audio.muted ? Qt.alpha(DynamicColors.palette.m3errorContainer, 0.5) : DynamicColors.palette.m3secondaryContainer + fgColor: Audio.muted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary + implicitHeight: parent.height + insetColor: Audio.muted ? (inset.attached ? DynamicColors.palette.m3onErrorContainer : DynamicColors.palette.m3onError) : (inset.attached ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onPrimary) + insetIcon: Audio.muted || Audio.volume < 0.001 ? "volume_off" : "volume_up" + value: Audio.volume + + Behavior on value { + Anim { + } } - } - onInteraction: value => Audio.setVolume(value) + onInteraction: value => Audio.setVolume(value) + } + } + + CustomSwitch { + Layout.bottomMargin: Appearance.padding.normal + checked: !Audio.muted + + onToggled: { + const audio = Audio.sink?.audio; + if (audio) + audio.muted = !audio.muted; + } } } } @@ -173,51 +152,11 @@ Item { } } - Item { - id: sourceIcon - - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.leftMargin: Appearance.padding.larger - anchors.top: parent.top - implicitWidth: childrenRect.width - - CustomRect { - anchors.centerIn: parent - color: Audio.sourceMuted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary - implicitHeight: 54 - implicitWidth: 54 - radius: Appearance.rounding.full - - MaterialIcon { - anchors.alignWhenCentered: false - anchors.centerIn: parent - animate: true - color: Audio.sourceMuted ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onPrimary - font.pointSize: Appearance.font.size.extraLarge - text: Audio.sourceMuted ? "mic_off" : "mic" - } - - StateLayer { - color: Audio.sourceMuted ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onPrimary - - onClicked: { - const audio = Audio.source?.audio; - if (audio) - audio.muted = !audio.muted; - } - } - } - } - ColumnLayout { - anchors.bottom: parent.bottom - anchors.bottomMargin: Appearance.padding.smallest - anchors.left: sourceIcon.right - anchors.leftMargin: Appearance.spacing.larger - anchors.right: parent.right - anchors.rightMargin: Appearance.padding.large - anchors.top: parent.top + anchors.bottomMargin: Appearance.padding.smaller + anchors.fill: parent + anchors.leftMargin: Appearance.padding.larger + anchors.rightMargin: Appearance.padding.larger anchors.topMargin: Appearance.padding.smaller RowLayout { @@ -227,34 +166,53 @@ Item { CustomText { Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft Layout.fillWidth: true - text: "Input Volume" + text: "Input volume" } CustomText { Layout.alignment: Qt.AlignVCenter | Qt.AlignRight + color: Qt.alpha(DynamicColors.palette.m3onSurface, 0.7) font.bold: true text: qsTr("%1").arg(Audio.sourceMuted ? qsTr("Muted") : `${Math.round(Audio.sourceVolume * 100)}%`) } } - CustomMouseArea { - Layout.bottomMargin: Appearance.padding.normal - Layout.fillWidth: true - Layout.preferredHeight: Appearance.padding.larger * 3 + RowLayout { + spacing: Appearance.spacing.large - CustomSlider { - anchors.left: parent.left - anchors.right: parent.right - fgColor: Audio.sourceMuted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary - implicitHeight: parent.height - value: Audio.sourceVolume + CustomMouseArea { + Layout.bottomMargin: Appearance.padding.normal + Layout.fillWidth: true + Layout.preferredHeight: Appearance.padding.larger * 3 - Behavior on value { - Anim { + CustomSlider { + anchors.left: parent.left + anchors.right: parent.right + bgColor: Audio.sourceMuted ? Qt.alpha(DynamicColors.palette.m3errorContainer, 0.5) : DynamicColors.palette.m3secondaryContainer + fgColor: Audio.sourceMuted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary + implicitHeight: parent.height + insetColor: Audio.sourceMuted ? (inset.attached ? DynamicColors.palette.m3onErrorContainer : DynamicColors.palette.m3onError) : (inset.attached ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onPrimary) + insetIcon: Audio.sourceMuted || Audio.sourceVolume < 0.001 ? "mic_off" : "mic" + value: Audio.sourceVolume + + Behavior on value { + Anim { + } } - } - onInteraction: value => Audio.setSourceVolume(value) + onInteraction: value => Audio.setSourceVolume(value) + } + } + + CustomSwitch { + Layout.bottomMargin: Appearance.padding.normal + checked: !Audio.sourceMuted + + onToggled: { + const audio = Audio.source?.audio; + if (audio) + audio.muted = !audio.muted; + } } } } @@ -307,43 +265,6 @@ Item { } } - Item { - id: appBoxIcon - - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.leftMargin: Appearance.padding.larger - anchors.top: parent.top - implicitWidth: childrenRect.width - - CustomRect { - anchors.centerIn: parent - color: appBox.modelData.audio.muted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary - implicitHeight: 54 - implicitWidth: 54 - radius: Appearance.rounding.full - - MaterialIcon { - id: icon - - anchors.centerIn: parent - animate: true - color: appBox.modelData.audio.muted ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onPrimary - font.pointSize: Appearance.font.size.extraLarge - text: appBox.modelData.audio.muted ? "volume_off" : "volume_up" - } - - StateLayer { - color: appBox.modelData.audio.muted ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onPrimary - radius: Appearance.rounding.full - - onClicked: { - appBox.modelData.audio.muted = !appBox.modelData.audio.muted; - } - } - } - } - TextMetrics { id: metrics @@ -353,13 +274,10 @@ Item { } ColumnLayout { - anchors.bottom: parent.bottom - anchors.bottomMargin: Appearance.padding.smallest - anchors.left: appBoxIcon.right - anchors.leftMargin: Appearance.spacing.larger - anchors.right: parent.right - anchors.rightMargin: Appearance.padding.large - anchors.top: parent.top + anchors.bottomMargin: Appearance.padding.smaller + anchors.fill: parent + anchors.leftMargin: Appearance.padding.larger + anchors.rightMargin: Appearance.padding.larger anchors.topMargin: Appearance.padding.smaller RowLayout { @@ -375,25 +293,45 @@ Item { CustomText { Layout.alignment: Qt.AlignVCenter | Qt.AlignRight + color: Qt.alpha(DynamicColors.palette.m3onSurface, 0.7) font.bold: true text: qsTr("%1").arg(appBox.modelData.audio.muted ? qsTr("Muted") : `${Math.round(appBox.modelData.audio.volume * 100)}%`) } } - CustomMouseArea { - Layout.bottomMargin: Appearance.padding.normal - Layout.fillWidth: true - Layout.preferredHeight: Appearance.padding.larger * 3 + RowLayout { + spacing: Appearance.spacing.large - CustomSlider { - anchors.left: parent.left - anchors.right: parent.right - fgColor: appBox.modelData.audio.muted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary - implicitHeight: parent.height - value: appBox.modelData.audio.volume + CustomMouseArea { + Layout.bottomMargin: Appearance.padding.normal + Layout.fillWidth: true + Layout.preferredHeight: Appearance.padding.larger * 3 - onInteraction: value => { - Audio.setStreamVolume(appBox.modelData, value); + CustomSlider { + anchors.left: parent.left + anchors.right: parent.right + bgColor: appBox.modelData.audio.muted ? Qt.alpha(DynamicColors.palette.m3errorContainer, 0.5) : DynamicColors.palette.m3secondaryContainer + fgColor: appBox.modelData.audio.muted ? DynamicColors.palette.m3error : DynamicColors.palette.m3primary + implicitHeight: parent.height + insetColor: appBox.modelData.audio.muted ? (inset.attached ? DynamicColors.palette.m3onErrorContainer : DynamicColors.palette.m3onError) : (inset.attached ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onPrimary) + insetIcon: appBox.modelData.audio.muted || appBox.modelData.audio.volume < 0.001 ? "volume_off" : "volume_up" + value: appBox.modelData.audio.volume + + Behavior on value { + Anim { + } + } + + onInteraction: value => Audio.setStreamVolume(appBox.modelData, value) + } + } + + CustomSwitch { + Layout.bottomMargin: Appearance.padding.normal + checked: !appBox.modelData.audio.muted + + onToggled: { + appBox.modelData.audio.muted = !appBox.modelData.audio.muted; } } }