diff --git a/quickshell/Modals/Clipboard/ClipboardEntry.qml b/quickshell/Modals/Clipboard/ClipboardEntry.qml index 8889b3c7..9522242e 100644 --- a/quickshell/Modals/Clipboard/ClipboardEntry.qml +++ b/quickshell/Modals/Clipboard/ClipboardEntry.qml @@ -30,6 +30,12 @@ Rectangle { return mouseArea.containsMouse ? Theme.primaryHoverLight : Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency); } + DankRipple { + id: rippleLayer + rippleColor: Theme.surfaceText + cornerRadius: root.radius + } + Rectangle { id: indexBadge anchors.left: parent.left @@ -138,6 +144,10 @@ Rectangle { anchors.rightMargin: 80 hoverEnabled: true cursorShape: Qt.PointingHandCursor + onPressed: mouse => { + const pos = mouseArea.mapToItem(root, mouse.x, mouse.y); + rippleLayer.trigger(pos.x, pos.y); + } onClicked: copyRequested() } } diff --git a/quickshell/Modals/DankLauncherV2/GridItem.qml b/quickshell/Modals/DankLauncherV2/GridItem.qml index 63bb645b..d6d2fdc0 100644 --- a/quickshell/Modals/DankLauncherV2/GridItem.qml +++ b/quickshell/Modals/DankLauncherV2/GridItem.qml @@ -53,6 +53,12 @@ Rectangle { radius: Theme.cornerRadius color: isSelected ? Theme.primaryPressed : isHovered ? Theme.primaryHoverLight : "transparent" + DankRipple { + id: rippleLayer + rippleColor: Theme.surfaceText + cornerRadius: root.radius + } + Column { anchors.centerIn: parent anchors.margins: Theme.spacingS @@ -99,6 +105,10 @@ Rectangle { cursorShape: Qt.PointingHandCursor acceptedButtons: Qt.LeftButton | Qt.RightButton + onPressed: mouse => { + if (mouse.button === Qt.LeftButton) + rippleLayer.trigger(mouse.x, mouse.y); + } onClicked: mouse => { if (mouse.button === Qt.RightButton) { var scenePos = mapToItem(null, mouse.x, mouse.y); diff --git a/quickshell/Modals/DankLauncherV2/LauncherContextMenu.qml b/quickshell/Modals/DankLauncherV2/LauncherContextMenu.qml index 3fbcc346..e9502850 100644 --- a/quickshell/Modals/DankLauncherV2/LauncherContextMenu.qml +++ b/quickshell/Modals/DankLauncherV2/LauncherContextMenu.qml @@ -475,6 +475,12 @@ Popup { } } + DankRipple { + id: menuItemRipple + rippleColor: Theme.surfaceText + cornerRadius: Theme.cornerRadius + } + MouseArea { id: itemMouseArea anchors.fill: parent @@ -484,6 +490,7 @@ Popup { root.keyboardNavigation = false; root.selectedMenuIndex = menuItemDelegate.itemIndex; } + onPressed: mouse => menuItemRipple.trigger(mouse.x, mouse.y) onClicked: { var menuItem = menuItemDelegate.modelData; if (menuItem.action) diff --git a/quickshell/Modals/DankLauncherV2/ResultItem.qml b/quickshell/Modals/DankLauncherV2/ResultItem.qml index 85bcb677..4ada194a 100644 --- a/quickshell/Modals/DankLauncherV2/ResultItem.qml +++ b/quickshell/Modals/DankLauncherV2/ResultItem.qml @@ -53,6 +53,12 @@ Rectangle { color: isSelected ? Theme.primaryPressed : isHovered ? Theme.primaryHoverLight : "transparent" radius: Theme.cornerRadius + DankRipple { + id: rippleLayer + rippleColor: Theme.surfaceText + cornerRadius: root.radius + } + MouseArea { id: itemArea z: 1 @@ -62,6 +68,10 @@ Rectangle { cursorShape: Qt.PointingHandCursor acceptedButtons: Qt.LeftButton | Qt.RightButton + onPressed: mouse => { + if (mouse.button === Qt.LeftButton) + rippleLayer.trigger(mouse.x, mouse.y); + } onClicked: mouse => { if (mouse.button === Qt.RightButton) { var scenePos = mapToItem(null, mouse.x, mouse.y); diff --git a/quickshell/Modals/DankLauncherV2/TileItem.qml b/quickshell/Modals/DankLauncherV2/TileItem.qml index e6e6af19..1869fe62 100644 --- a/quickshell/Modals/DankLauncherV2/TileItem.qml +++ b/quickshell/Modals/DankLauncherV2/TileItem.qml @@ -82,6 +82,12 @@ Rectangle { return ["jpg", "jpeg", "png", "gif", "webp", "svg", "bmp"].indexOf(ext) >= 0; } + DankRipple { + id: rippleLayer + rippleColor: Theme.surfaceText + cornerRadius: root.radius + } + Item { anchors.fill: parent anchors.margins: 4 @@ -193,6 +199,10 @@ Rectangle { cursorShape: Qt.PointingHandCursor acceptedButtons: Qt.LeftButton | Qt.RightButton + onPressed: mouse => { + if (mouse.button === Qt.LeftButton) + rippleLayer.trigger(mouse.x, mouse.y); + } onClicked: mouse => { if (mouse.button === Qt.RightButton) { var scenePos = mapToItem(null, mouse.x, mouse.y); diff --git a/quickshell/Modals/Settings/SettingsSidebar.qml b/quickshell/Modals/Settings/SettingsSidebar.qml index 865d3d00..08fdd6ea 100644 --- a/quickshell/Modals/Settings/SettingsSidebar.qml +++ b/quickshell/Modals/Settings/SettingsSidebar.qml @@ -239,7 +239,6 @@ Rectangle { "icon": "computer", "collapsedByDefault": true, "children": [ - { "id": "audio", "text": I18n.tr("Audio"), @@ -702,6 +701,12 @@ Rectangle { return "transparent"; } + DankRipple { + id: resultRipple + rippleColor: root.searchSelectedIndex === resultDelegate.index ? Theme.buttonText : Theme.surfaceText + cornerRadius: resultDelegate.radius + } + Row { id: resultContent anchors.left: parent.left @@ -749,6 +754,7 @@ Rectangle { anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor + onPressed: mouse => resultRipple.trigger(mouse.x, mouse.y) onClicked: root.selectSearchResult(resultDelegate.modelData) } @@ -825,6 +831,12 @@ Rectangle { return "transparent"; } + DankRipple { + id: categoryRipple + rippleColor: categoryRow.isActive ? Theme.buttonText : Theme.surfaceText + cornerRadius: categoryRow.radius + } + Row { id: categoryRowContent anchors.left: parent.left @@ -864,6 +876,7 @@ Rectangle { anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor + onPressed: mouse => categoryRipple.trigger(mouse.x, mouse.y) onClicked: { root.keyboardHighlightIndex = -1; if (categoryDelegate.modelData.children) { @@ -915,6 +928,12 @@ Rectangle { return "transparent"; } + DankRipple { + id: childRipple + rippleColor: childDelegate.isActive ? Theme.buttonText : Theme.surfaceText + cornerRadius: childDelegate.radius + } + Row { id: childRowContent anchors.left: parent.left @@ -943,6 +962,7 @@ Rectangle { anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor + onPressed: mouse => childRipple.trigger(mouse.x, mouse.y) onClicked: { root.keyboardHighlightIndex = -1; root.tabChangeRequested(childDelegate.modelData.tabIndex); diff --git a/quickshell/Modules/ControlCenter/Components/ActionTile.qml b/quickshell/Modules/ControlCenter/Components/ActionTile.qml index 7385064a..d85c779b 100644 --- a/quickshell/Modules/ControlCenter/Components/ActionTile.qml +++ b/quickshell/Modules/ControlCenter/Components/ActionTile.qml @@ -101,12 +101,18 @@ Rectangle { } } + DankRipple { + id: ripple + cornerRadius: root.radius + } + MouseArea { id: mouseArea anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor enabled: root.enabled + onPressed: mouse => ripple.trigger(mouse.x, mouse.y) onClicked: root.clicked() } diff --git a/quickshell/Modules/ControlCenter/Components/PowerButton.qml b/quickshell/Modules/ControlCenter/Components/PowerButton.qml index 68d6c4d3..8ed74445 100644 --- a/quickshell/Modules/ControlCenter/Components/PowerButton.qml +++ b/quickshell/Modules/ControlCenter/Components/PowerButton.qml @@ -11,19 +11,11 @@ Rectangle { property string iconName: "" property string text: "" - signal pressed() + signal pressed height: 34 radius: Theme.cornerRadius - color: mouseArea.containsMouse ? Qt.rgba( - Theme.primary.r, - Theme.primary.g, - Theme.primary.b, - 0.12) : Qt.rgba( - Theme.surfaceVariant.r, - Theme.surfaceVariant.g, - Theme.surfaceVariant.b, - 0.5) + color: mouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5) Row { anchors.centerIn: parent @@ -44,12 +36,20 @@ Rectangle { } } + DankRipple { + id: ripple + cornerRadius: root.radius + } + MouseArea { id: mouseArea anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor - onPressed: root.pressed() + onPressed: mouse => { + ripple.trigger(mouse.x, mouse.y); + root.pressed(); + } } } diff --git a/quickshell/Modules/ControlCenter/Details/AudioInputDetail.qml b/quickshell/Modules/ControlCenter/Details/AudioInputDetail.qml index 12536527..26e650c6 100644 --- a/quickshell/Modules/ControlCenter/Details/AudioInputDetail.qml +++ b/quickshell/Modules/ControlCenter/Details/AudioInputDetail.qml @@ -61,11 +61,17 @@ Rectangle { radius: (Theme.iconSize + Theme.spacingS * 2) / 2 color: iconArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent" + DankRipple { + id: iconRipple + cornerRadius: parent.radius + } + MouseArea { id: iconArea anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor + onPressed: mouse => iconRipple.trigger(mouse.x, mouse.y) onClicked: { if (AudioService.source && AudioService.source.audio) { AudioService.source.audio.muted = !AudioService.source.audio.muted; @@ -126,15 +132,15 @@ Rectangle { function normalizePinList(value) { if (Array.isArray(value)) - return value.filter(v => v) + return value.filter(v => v); if (typeof value === "string" && value.length > 0) - return [value] - return [] + return [value]; + return []; } function getPinnedInputs() { - const pins = SettingsData.audioInputDevicePins || {} - return normalizePinList(pins["preferredInput"]) + const pins = SettingsData.audioInputDevicePins || {}; + return normalizePinList(pins["preferredInput"]); } Column { @@ -153,14 +159,14 @@ Rectangle { let sorted = [...nodes]; sorted.sort((a, b) => { // Pinned device first - const aPinnedIndex = pinnedList.indexOf(a.name) - const bPinnedIndex = pinnedList.indexOf(b.name) + const aPinnedIndex = pinnedList.indexOf(a.name); + const bPinnedIndex = pinnedList.indexOf(b.name); if (aPinnedIndex !== -1 || bPinnedIndex !== -1) { if (aPinnedIndex === -1) - return 1 + return 1; if (bPinnedIndex === -1) - return -1 - return aPinnedIndex - bPinnedIndex + return -1; + return aPinnedIndex - bPinnedIndex; } // Then active device if (a === AudioService.source && b !== AudioService.source) @@ -276,38 +282,53 @@ Rectangle { } } + DankRipple { + id: pinRipple + cornerRadius: parent.radius + } + MouseArea { anchors.fill: parent cursorShape: Qt.PointingHandCursor + onPressed: mouse => pinRipple.trigger(mouse.x, mouse.y) onClicked: { - const pins = JSON.parse(JSON.stringify(SettingsData.audioInputDevicePins || {})) - let pinnedList = audioContent.normalizePinList(pins["preferredInput"]) - const pinIndex = pinnedList.indexOf(modelData.name) + const pins = JSON.parse(JSON.stringify(SettingsData.audioInputDevicePins || {})); + let pinnedList = audioContent.normalizePinList(pins["preferredInput"]); + const pinIndex = pinnedList.indexOf(modelData.name); if (pinIndex !== -1) { - pinnedList.splice(pinIndex, 1) + pinnedList.splice(pinIndex, 1); } else { - pinnedList.unshift(modelData.name) + pinnedList.unshift(modelData.name); if (pinnedList.length > audioContent.maxPinnedInputs) - pinnedList = pinnedList.slice(0, audioContent.maxPinnedInputs) + pinnedList = pinnedList.slice(0, audioContent.maxPinnedInputs); } if (pinnedList.length > 0) - pins["preferredInput"] = pinnedList + pins["preferredInput"] = pinnedList; else - delete pins["preferredInput"] + delete pins["preferredInput"]; - SettingsData.set("audioInputDevicePins", pins) + SettingsData.set("audioInputDevicePins", pins); } } } + DankRipple { + id: deviceRipple + cornerRadius: parent.radius + } + MouseArea { id: deviceMouseArea anchors.fill: parent anchors.rightMargin: pinInputRow.width + Theme.spacingS * 4 hoverEnabled: true cursorShape: Qt.PointingHandCursor + onPressed: mouse => { + let mapped = mapToItem(parent, mouse.x, mouse.y); + deviceRipple.trigger(mapped.x, mapped.y); + } onClicked: { if (modelData) { Pipewire.preferredDefaultAudioSource = modelData; diff --git a/quickshell/Modules/ControlCenter/Details/AudioOutputDetail.qml b/quickshell/Modules/ControlCenter/Details/AudioOutputDetail.qml index 03c6a148..96499df7 100644 --- a/quickshell/Modules/ControlCenter/Details/AudioOutputDetail.qml +++ b/quickshell/Modules/ControlCenter/Details/AudioOutputDetail.qml @@ -61,11 +61,17 @@ Rectangle { radius: (Theme.iconSize + Theme.spacingS * 2) / 2 color: iconArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent" + DankRipple { + id: muteRipple + cornerRadius: parent.radius + } + MouseArea { id: iconArea anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor + onPressed: mouse => muteRipple.trigger(mouse.x, mouse.y) onClicked: { if (AudioService.sink && AudioService.sink.audio) { AudioService.sink.audio.muted = !AudioService.sink.audio.muted; @@ -184,6 +190,7 @@ Rectangle { } delegate: Rectangle { + id: outputDelegate required property var modelData required property int index @@ -194,6 +201,11 @@ Rectangle { border.color: modelData === AudioService.sink ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12) border.width: 0 + DankRipple { + id: deviceRipple + cornerRadius: outputDelegate.radius + } + Row { anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter @@ -288,9 +300,15 @@ Rectangle { } } + DankRipple { + id: pinRipple + cornerRadius: parent.radius + } + MouseArea { anchors.fill: parent cursorShape: Qt.PointingHandCursor + onPressed: mouse => pinRipple.trigger(mouse.x, mouse.y) onClicked: { const pins = JSON.parse(JSON.stringify(SettingsData.audioOutputDevicePins || {})); let pinnedList = audioContent.normalizePinList(pins["preferredOutput"]); @@ -320,6 +338,10 @@ Rectangle { anchors.rightMargin: pinOutputRow.width + Theme.spacingS * 4 hoverEnabled: true cursorShape: Qt.PointingHandCursor + onPressed: mouse => { + let mapped = deviceMouseArea.mapToItem(outputDelegate, mouse.x, mouse.y); + deviceRipple.trigger(mapped.x, mapped.y); + } onClicked: { if (modelData) { Pipewire.preferredDefaultAudioSink = modelData; @@ -428,12 +450,18 @@ Rectangle { radius: Theme.cornerRadius color: appIconArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Theme.withAlpha(Theme.primary, 0) + DankRipple { + id: appMuteRipple + cornerRadius: parent.radius + } + MouseArea { id: appIconArea anchors.fill: parent visible: modelData !== null hoverEnabled: true cursorShape: Qt.PointingHandCursor + onPressed: mouse => appMuteRipple.trigger(mouse.x, mouse.y) onClicked: { if (modelData) { SessionData.suppressOSDTemporarily(); diff --git a/quickshell/Modules/ControlCenter/Details/BluetoothCodecSelector.qml b/quickshell/Modules/ControlCenter/Details/BluetoothCodecSelector.qml index 4c74584f..976a1277 100644 --- a/quickshell/Modules/ControlCenter/Details/BluetoothCodecSelector.qml +++ b/quickshell/Modules/ControlCenter/Details/BluetoothCodecSelector.qml @@ -293,6 +293,11 @@ Item { visible: modelData.name === currentCodec } + DankRipple { + id: codecRipple + cornerRadius: parent.radius + } + MouseArea { id: codecMouseArea @@ -300,6 +305,7 @@ Item { hoverEnabled: true cursorShape: Qt.PointingHandCursor enabled: modelData.name !== currentCodec && !isLoading + onPressed: mouse => codecRipple.trigger(mouse.x, mouse.y) onClicked: { selectCodec(modelData.profile); } diff --git a/quickshell/Modules/ControlCenter/Details/BluetoothDetail.qml b/quickshell/Modules/ControlCenter/Details/BluetoothDetail.qml index cd3be830..d0ea91a6 100644 --- a/quickshell/Modules/ControlCenter/Details/BluetoothDetail.qml +++ b/quickshell/Modules/ControlCenter/Details/BluetoothDetail.qml @@ -139,12 +139,18 @@ Rectangle { } } + DankRipple { + id: scanRipple + cornerRadius: scanButton.radius + } + MouseArea { id: scanMouseArea anchors.fill: parent hoverEnabled: true enabled: scanButton.adapterEnabled cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor + onPressed: mouse => scanRipple.trigger(mouse.x, mouse.y) onClicked: { if (!BluetoothService.adapter) return; @@ -399,12 +405,21 @@ Rectangle { } } + DankRipple { + id: deviceRipple + cornerRadius: pairedDelegate.radius + } + MouseArea { id: deviceMouseArea anchors.fill: parent anchors.rightMargin: pairedOptionsButton.width + Theme.spacingM + pinBluetoothRow.width + Theme.spacingS * 4 hoverEnabled: true cursorShape: Qt.PointingHandCursor + onPressed: mouse => { + const pos = mapToItem(pairedDelegate, mouse.x, mouse.y); + deviceRipple.trigger(pos.x, pos.y); + } onClicked: { if (pairedDelegate.isConnected) { pairedDelegate.modelData.disconnect(); @@ -547,12 +562,18 @@ Rectangle { font.weight: Font.Medium } + DankRipple { + id: availableRipple + cornerRadius: availableDelegate.radius + } + MouseArea { id: availableMouseArea anchors.fill: parent hoverEnabled: true cursorShape: availableDelegate.isInteractive ? Qt.PointingHandCursor : Qt.ArrowCursor enabled: availableDelegate.isInteractive + onPressed: mouse => availableRipple.trigger(mouse.x, mouse.y) onClicked: root.handlePairDevice(availableDelegate.modelData) } } diff --git a/quickshell/Modules/ControlCenter/Details/BrightnessDetail.qml b/quickshell/Modules/ControlCenter/Details/BrightnessDetail.qml index 937a6d4a..0d42e627 100644 --- a/quickshell/Modules/ControlCenter/Details/BrightnessDetail.qml +++ b/quickshell/Modules/ControlCenter/Details/BrightnessDetail.qml @@ -211,9 +211,15 @@ Rectangle { } } + DankRipple { + id: pinRipple + cornerRadius: parent.radius + } + MouseArea { anchors.fill: parent cursorShape: Qt.PointingHandCursor + onPressed: mouse => pinRipple.trigger(mouse.x, mouse.y) onClicked: root.togglePinToScreen() } } @@ -443,9 +449,15 @@ Rectangle { } } + DankRipple { + id: expToggleRipple + cornerRadius: parent.radius + } + MouseArea { anchors.fill: parent cursorShape: Qt.PointingHandCursor + onPressed: mouse => expToggleRipple.trigger(mouse.x, mouse.y) onClicked: { const currentState = SessionData.getBrightnessExponential(modelData.name); SessionData.setBrightnessExponential(modelData.name, !currentState); @@ -454,12 +466,18 @@ Rectangle { } } + DankRipple { + id: deviceRipple + cornerRadius: parent.radius + } + MouseArea { anchors.fill: parent anchors.bottomMargin: 28 anchors.rightMargin: SessionData.getBrightnessExponential(modelData.name) ? 145 : 0 hoverEnabled: true cursorShape: Qt.PointingHandCursor + onPressed: mouse => deviceRipple.trigger(mouse.x, mouse.y) onClicked: { const pinKey = root.getScreenPinKey(); if (pinKey.length > 0 && modelData.name !== currentDeviceName) { diff --git a/quickshell/Modules/ControlCenter/Details/DiskUsageDetail.qml b/quickshell/Modules/ControlCenter/Details/DiskUsageDetail.qml index faea1154..6099ec12 100644 --- a/quickshell/Modules/ControlCenter/Details/DiskUsageDetail.qml +++ b/quickshell/Modules/ControlCenter/Details/DiskUsageDetail.qml @@ -155,10 +155,16 @@ Rectangle { } } + DankRipple { + id: mountRipple + cornerRadius: parent.radius + } + MouseArea { anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor + onPressed: mouse => mountRipple.trigger(mouse.x, mouse.y) onClicked: { currentMountPath = modelData.mount; mountPathChanged(modelData.mount); diff --git a/quickshell/Modules/ControlCenter/Details/NetworkDetail.qml b/quickshell/Modules/ControlCenter/Details/NetworkDetail.qml index f85c190b..2310dd4a 100644 --- a/quickshell/Modules/ControlCenter/Details/NetworkDetail.qml +++ b/quickshell/Modules/ControlCenter/Details/NetworkDetail.qml @@ -344,12 +344,18 @@ Rectangle { } } + DankRipple { + id: wiredRipple + cornerRadius: parent.radius + } + MouseArea { id: wiredNetworkMouseArea anchors.fill: parent anchors.rightMargin: wiredOptionsButton.width + Theme.spacingS hoverEnabled: true cursorShape: Qt.PointingHandCursor + onPressed: mouse => wiredRipple.trigger(mouse.x, mouse.y) onClicked: function (event) { if (modelData.uuid !== NetworkService.ethernetConnectionUuid) { NetworkService.connectToSpecificWiredConfig(modelData.uuid); @@ -467,15 +473,15 @@ Rectangle { function normalizePinList(value) { if (Array.isArray(value)) - return value.filter(v => v) + return value.filter(v => v); if (typeof value === "string" && value.length > 0) - return [value] - return [] + return [value]; + return []; } function getPinnedNetworks() { - const pins = SettingsData.wifiNetworkPins || {} - return normalizePinList(pins["preferredWifi"]) + const pins = SettingsData.wifiNetworkPins || {}; + return normalizePinList(pins["preferredWifi"]); } property var frozenNetworks: [] @@ -483,18 +489,18 @@ Rectangle { property var sortedNetworks: { const ssid = NetworkService.currentWifiSSID; const networks = NetworkService.wifiNetworks; - const pinnedList = getPinnedNetworks() + const pinnedList = getPinnedNetworks(); let sorted = [...networks]; sorted.sort((a, b) => { - const aPinnedIndex = pinnedList.indexOf(a.ssid) - const bPinnedIndex = pinnedList.indexOf(b.ssid) + const aPinnedIndex = pinnedList.indexOf(a.ssid); + const bPinnedIndex = pinnedList.indexOf(b.ssid); if (aPinnedIndex !== -1 || bPinnedIndex !== -1) { if (aPinnedIndex === -1) - return 1 + return 1; if (bPinnedIndex === -1) - return -1 - return aPinnedIndex - bPinnedIndex + return -1; + return aPinnedIndex - bPinnedIndex; } if (a.ssid === ssid) return -1; @@ -677,38 +683,50 @@ Rectangle { } } + DankRipple { + id: pinRipple + cornerRadius: parent.radius + } + MouseArea { anchors.fill: parent cursorShape: Qt.PointingHandCursor + onPressed: mouse => pinRipple.trigger(mouse.x, mouse.y) onClicked: { - const pins = JSON.parse(JSON.stringify(SettingsData.wifiNetworkPins || {})) - let pinnedList = wifiContent.normalizePinList(pins["preferredWifi"]) - const pinIndex = pinnedList.indexOf(modelData.ssid) + const pins = JSON.parse(JSON.stringify(SettingsData.wifiNetworkPins || {})); + let pinnedList = wifiContent.normalizePinList(pins["preferredWifi"]); + const pinIndex = pinnedList.indexOf(modelData.ssid); if (pinIndex !== -1) { - pinnedList.splice(pinIndex, 1) + pinnedList.splice(pinIndex, 1); } else { - pinnedList.unshift(modelData.ssid) + pinnedList.unshift(modelData.ssid); if (pinnedList.length > wifiContent.maxPinnedNetworks) - pinnedList = pinnedList.slice(0, wifiContent.maxPinnedNetworks) + pinnedList = pinnedList.slice(0, wifiContent.maxPinnedNetworks); } if (pinnedList.length > 0) - pins["preferredWifi"] = pinnedList + pins["preferredWifi"] = pinnedList; else - delete pins["preferredWifi"] + delete pins["preferredWifi"]; - SettingsData.set("wifiNetworkPins", pins) + SettingsData.set("wifiNetworkPins", pins); } } } + DankRipple { + id: wifiRipple + cornerRadius: parent.radius + } + MouseArea { id: networkMouseArea anchors.fill: parent anchors.rightMargin: optionsButton.width + Theme.spacingM + Theme.spacingS + pinWifiRow.width + Theme.spacingS * 4 hoverEnabled: true cursorShape: Qt.PointingHandCursor + onPressed: mouse => wifiRipple.trigger(mouse.x, mouse.y) onClicked: function (event) { if (modelData.ssid !== NetworkService.currentWifiSSID) { if (modelData.secured && !modelData.saved) { diff --git a/quickshell/Modules/ControlCenter/Widgets/AudioSliderRow.qml b/quickshell/Modules/ControlCenter/Widgets/AudioSliderRow.qml index 07e3adc4..81d346d6 100644 --- a/quickshell/Modules/ControlCenter/Widgets/AudioSliderRow.qml +++ b/quickshell/Modules/ControlCenter/Widgets/AudioSliderRow.qml @@ -22,12 +22,18 @@ Row { radius: (Theme.iconSize + Theme.spacingS * 2) / 2 color: iconArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Theme.withAlpha(Theme.primary, 0) + DankRipple { + id: iconRipple + cornerRadius: parent.radius + } + MouseArea { id: iconArea anchors.fill: parent visible: defaultSink !== null hoverEnabled: true cursorShape: Qt.PointingHandCursor + onPressed: mouse => iconRipple.trigger(mouse.x, mouse.y) onClicked: { if (defaultSink) { SessionData.suppressOSDTemporarily(); diff --git a/quickshell/Modules/ControlCenter/Widgets/BrightnessSliderRow.qml b/quickshell/Modules/ControlCenter/Widgets/BrightnessSliderRow.qml index 2265f7cd..037c08f9 100644 --- a/quickshell/Modules/ControlCenter/Widgets/BrightnessSliderRow.qml +++ b/quickshell/Modules/ControlCenter/Widgets/BrightnessSliderRow.qml @@ -91,12 +91,18 @@ Row { radius: (Theme.iconSize + Theme.spacingS * 2) / 2 color: iconArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Theme.withAlpha(Theme.primary, 0) + DankRipple { + id: iconRipple + cornerRadius: parent.radius + } + MouseArea { id: iconArea anchors.fill: parent hoverEnabled: true cursorShape: DisplayService.devices && DisplayService.devices.length > 1 ? Qt.PointingHandCursor : Qt.ArrowCursor + onPressed: mouse => iconRipple.trigger(mouse.x, mouse.y) onClicked: { if (DisplayService.devices && DisplayService.devices.length > 1) { root.iconClicked(); diff --git a/quickshell/Modules/ControlCenter/Widgets/CompoundPill.qml b/quickshell/Modules/ControlCenter/Widgets/CompoundPill.qml index 440795ba..9165ec42 100644 --- a/quickshell/Modules/ControlCenter/Widgets/CompoundPill.qml +++ b/quickshell/Modules/ControlCenter/Widgets/CompoundPill.qml @@ -72,6 +72,11 @@ Rectangle { } } + DankRipple { + id: bodyRipple + cornerRadius: root.radius + } + Row { id: row anchors.fill: parent @@ -112,11 +117,17 @@ Rectangle { color: isActive ? _tileIconActive : _tileIconInactive } + DankRipple { + id: tileRipple + cornerRadius: _tileRadius + } + MouseArea { id: tileMouse anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor + onPressed: mouse => tileRipple.trigger(mouse.x, mouse.y) onClicked: root.toggled() } } @@ -167,7 +178,11 @@ Rectangle { rightHoverOverlay.opacity = 0.0; rightHoverOverlay.visible = false; } - onPressed: rightHoverOverlay.opacity = 0.16 + onPressed: mouse => { + const pos = mapToItem(root, mouse.x, mouse.y); + bodyRipple.trigger(pos.x, pos.y); + rightHoverOverlay.opacity = 0.16; + } onReleased: rightHoverOverlay.opacity = containsMouse ? 0.08 : 0.0 onClicked: root.expandClicked() onWheel: function (ev) { diff --git a/quickshell/Modules/ControlCenter/Widgets/InputAudioSliderRow.qml b/quickshell/Modules/ControlCenter/Widgets/InputAudioSliderRow.qml index 73c018f6..49ea30e7 100644 --- a/quickshell/Modules/ControlCenter/Widgets/InputAudioSliderRow.qml +++ b/quickshell/Modules/ControlCenter/Widgets/InputAudioSliderRow.qml @@ -22,12 +22,18 @@ Row { radius: (Theme.iconSize + Theme.spacingS * 2) / 2 color: iconArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Theme.withAlpha(Theme.primary, 0) + DankRipple { + id: iconRipple + cornerRadius: parent.radius + } + MouseArea { id: iconArea anchors.fill: parent visible: defaultSource !== null hoverEnabled: true cursorShape: Qt.PointingHandCursor + onPressed: mouse => iconRipple.trigger(mouse.x, mouse.y) onClicked: { if (defaultSource) { SessionData.suppressOSDTemporarily(); diff --git a/quickshell/Modules/ControlCenter/Widgets/SmallBatteryButton.qml b/quickshell/Modules/ControlCenter/Widgets/SmallBatteryButton.qml index ab7bea7c..02da286a 100644 --- a/quickshell/Modules/ControlCenter/Widgets/SmallBatteryButton.qml +++ b/quickshell/Modules/ControlCenter/Widgets/SmallBatteryButton.qml @@ -89,12 +89,18 @@ Rectangle { } } + DankRipple { + id: ripple + cornerRadius: root.radius + } + MouseArea { id: mouseArea anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor enabled: root.enabled + onPressed: mouse => ripple.trigger(mouse.x, mouse.y) onClicked: root.clicked() } diff --git a/quickshell/Modules/ControlCenter/Widgets/SmallDiskUsageButton.qml b/quickshell/Modules/ControlCenter/Widgets/SmallDiskUsageButton.qml index 81847836..30c4e367 100644 --- a/quickshell/Modules/ControlCenter/Widgets/SmallDiskUsageButton.qml +++ b/quickshell/Modules/ControlCenter/Widgets/SmallDiskUsageButton.qml @@ -105,12 +105,18 @@ Rectangle { } } + DankRipple { + id: ripple + cornerRadius: root.radius + } + MouseArea { id: mouseArea anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor enabled: root.enabled + onPressed: mouse => ripple.trigger(mouse.x, mouse.y) onClicked: root.clicked() } diff --git a/quickshell/Modules/ControlCenter/Widgets/SmallToggleButton.qml b/quickshell/Modules/ControlCenter/Widgets/SmallToggleButton.qml index d203e86c..7d86947f 100644 --- a/quickshell/Modules/ControlCenter/Widgets/SmallToggleButton.qml +++ b/quickshell/Modules/ControlCenter/Widgets/SmallToggleButton.qml @@ -66,12 +66,18 @@ Rectangle { onRotationCompleted: root.iconRotationCompleted() } + DankRipple { + id: ripple + cornerRadius: root.radius + } + MouseArea { id: mouseArea anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor enabled: root.enabled + onPressed: mouse => ripple.trigger(mouse.x, mouse.y) onClicked: root.clicked() } diff --git a/quickshell/Modules/ControlCenter/Widgets/ToggleButton.qml b/quickshell/Modules/ControlCenter/Widgets/ToggleButton.qml index 318290ad..b8adda84 100644 --- a/quickshell/Modules/ControlCenter/Widgets/ToggleButton.qml +++ b/quickshell/Modules/ControlCenter/Widgets/ToggleButton.qml @@ -110,12 +110,18 @@ Rectangle { } } + DankRipple { + id: ripple + cornerRadius: root.radius + } + MouseArea { id: mouseArea anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor enabled: root.enabled + onPressed: mouse => ripple.trigger(mouse.x, mouse.y) onClicked: root.clicked() } diff --git a/quickshell/Modules/DankBar/Widgets/AppsDock.qml b/quickshell/Modules/DankBar/Widgets/AppsDock.qml index 1589527d..7cbad4cd 100644 --- a/quickshell/Modules/DankBar/Widgets/AppsDock.qml +++ b/quickshell/Modules/DankBar/Widgets/AppsDock.qml @@ -1,29 +1,23 @@ import QtQuick -import QtQuick.Controls import QtQuick.Effects import Quickshell -import Quickshell.Wayland import Quickshell.Widgets import qs.Common +import qs.Modules.Plugins import qs.Services import qs.Widgets -Item { +BasePill { id: root + enableBackgroundHover: false + enableCursor: false + section: "left" + property var widgetData: null - property var barConfig: null - property bool isVertical: axis?.isVertical ?? false - property var axis: null - property string section: "left" - property var parentScreen property var hoveredItem: null property var topBar: null - property real widgetThickness: 30 - property real barThickness: 48 - property real barSpacing: 4 property bool isAutoHideBar: false - readonly property real horizontalPadding: (barConfig?.noBackground ?? false) ? 2 : Theme.spacingS property Item windowRoot: (Window.window ? Window.window.contentItem : null) property int draggedIndex: -1 @@ -66,7 +60,7 @@ Item { readonly property real barY: barBounds.y readonly property real minTooltipY: { - if (!parentScreen || !isVertical) { + if (!parentScreen || !isVerticalOrientation) { return 0; } @@ -322,7 +316,9 @@ Item { } function applyOverflow(baseResult) { - const { items } = baseResult; + const { + items + } = baseResult; const maxPinned = root.maxVisibleApps; const maxRunning = root.maxVisibleRunningApps; @@ -403,110 +399,19 @@ Item { Component.onCompleted: updateModel() - readonly property int calculatedSize: { - const count = dockItems.length; - if (count === 0) - return 0; - - if (widgetData?.runningAppsCompactMode !== undefined ? widgetData.runningAppsCompactMode : SettingsData.runningAppsCompactMode) { - return count * 24 + (count - 1) * Theme.spacingXS + horizontalPadding * 2; - } else { - return count * (24 + Theme.spacingXS + 120) + (count - 1) * Theme.spacingXS + horizontalPadding * 2; - } - } - - readonly property real realCalculatedSize: { - let total = horizontalPadding * 2; - const compact = (widgetData?.runningAppsCompactMode !== undefined ? widgetData.runningAppsCompactMode : SettingsData.runningAppsCompactMode); - - for (let i = 0; i < dockItems.length; i++) { - const item = dockItems[i]; - const isInOverflow = item.isInOverflow === true; - if (isInOverflow && !root.overflowExpanded) - continue; - - let itemSize = 0; - if (item.type === "separator") { - itemSize = 8; - } else { - itemSize = compact ? 24 : (24 + Theme.spacingXS + 120); - } - - total += itemSize; - if (i < dockItems.length - 1) - total += Theme.spacingXS; - } - return total; - } - - width: dockItems.length > 0 ? (isVertical ? barThickness : realCalculatedSize) : 0 - height: dockItems.length > 0 ? (isVertical ? realCalculatedSize : barThickness) : 0 visible: dockItems.length > 0 - Item { - id: visualBackground - width: root.isVertical ? root.widgetThickness : root.realCalculatedSize - height: root.isVertical ? root.realCalculatedSize : root.widgetThickness - anchors.centerIn: parent - clip: false + content: Component { + Item { + implicitWidth: layoutLoader.item ? layoutLoader.item.implicitWidth : 0 + implicitHeight: layoutLoader.item ? layoutLoader.item.implicitHeight : 0 - Rectangle { - id: outline - anchors.centerIn: parent - width: { - const borderWidth = (barConfig?.widgetOutlineEnabled ?? false) ? (barConfig?.widgetOutlineThickness ?? 1) : 0; - return parent.width + borderWidth * 2; - } - height: { - const borderWidth = (barConfig?.widgetOutlineEnabled ?? false) ? (barConfig?.widgetOutlineThickness ?? 1) : 0; - return parent.height + borderWidth * 2; - } - radius: (barConfig?.noBackground ?? false) ? 0 : Theme.cornerRadius - color: "transparent" - border.width: (barConfig?.widgetOutlineEnabled ?? false) ? (barConfig?.widgetOutlineThickness ?? 1) : 0 - border.color: { - if (!(barConfig?.widgetOutlineEnabled ?? false)) { - return "transparent"; - } - const colorOption = barConfig?.widgetOutlineColor || "primary"; - const opacity = barConfig?.widgetOutlineOpacity ?? 1.0; - switch (colorOption) { - case "surfaceText": - return Theme.withAlpha(Theme.surfaceText, opacity); - case "secondary": - return Theme.withAlpha(Theme.secondary, opacity); - case "primary": - return Theme.withAlpha(Theme.primary, opacity); - default: - return Theme.withAlpha(Theme.primary, opacity); - } + Loader { + id: layoutLoader + anchors.centerIn: parent + sourceComponent: root.isVerticalOrientation ? columnLayout : rowLayout } } - - Rectangle { - id: background - anchors.fill: parent - radius: (barConfig?.noBackground ?? false) ? 0 : Theme.cornerRadius - color: { - if (dockItems.length === 0) - return "transparent"; - if ((barConfig?.noBackground ?? false)) - return "transparent"; - - const baseColor = Theme.widgetBaseBackgroundColor; - const transparency = (root.barConfig && root.barConfig.widgetTransparency !== undefined) ? root.barConfig.widgetTransparency : 1.0; - if (Theme.widgetBackgroundHasAlpha) { - return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * transparency); - } - return Theme.withAlpha(baseColor, transparency); - } - } - } - - Loader { - id: layoutLoader - anchors.centerIn: parent - sourceComponent: root.isVertical ? columnLayout : rowLayout } Component { @@ -556,8 +461,8 @@ Item { readonly property bool isInOverflow: modelData.isInOverflow === true readonly property real visualSize: isSeparator ? 8 : ((widgetData?.runningAppsCompactMode !== undefined ? widgetData.runningAppsCompactMode : SettingsData.runningAppsCompactMode) ? 24 : (24 + Theme.spacingXS + 120)) - readonly property real visualWidth: root.isVertical ? root.barThickness : visualSize - readonly property real visualHeight: root.isVertical ? visualSize : root.barThickness + readonly property real visualWidth: root.isVerticalOrientation ? root.barThickness : visualSize + readonly property real visualHeight: root.isVerticalOrientation ? visualSize : root.barThickness visible: !isInOverflow || root.overflowExpanded opacity: (isInOverflow && !root.overflowExpanded) ? 0 : 1 @@ -604,8 +509,8 @@ Item { } transform: Translate { - x: root.isVertical ? 0 : delegateItem.shiftOffset - y: root.isVertical ? delegateItem.shiftOffset : 0 + x: root.isVerticalOrientation ? 0 : delegateItem.shiftOffset + y: root.isVerticalOrientation ? delegateItem.shiftOffset : 0 Behavior on x { enabled: !root.suppressShiftAnimation @@ -625,8 +530,8 @@ Item { Rectangle { visible: isSeparator - width: root.isVertical ? root.barThickness * 0.6 : 2 - height: root.isVertical ? 2 : root.barThickness * 0.6 + width: root.isVerticalOrientation ? root.barThickness * 0.6 : 2 + height: root.isVerticalOrientation ? 2 : root.barThickness * 0.6 color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.3) radius: 1 anchors.centerIn: parent @@ -640,7 +545,7 @@ Item { iconSize: 24 overflowCount: modelData.overflowCount || 0 overflowExpanded: root.overflowExpanded - isVertical: root.isVertical + isVertical: root.isVerticalOrientation showBadge: root.showOverflowBadge z: 10 onClicked: { @@ -709,14 +614,14 @@ Item { } transform: Translate { - x: (dragHandler.dragging && !root.isVertical) ? dragHandler.dragAxisOffset : 0 - y: (dragHandler.dragging && root.isVertical) ? dragHandler.dragAxisOffset : 0 + x: (dragHandler.dragging && !root.isVerticalOrientation) ? dragHandler.dragAxisOffset : 0 + y: (dragHandler.dragging && root.isVerticalOrientation) ? dragHandler.dragAxisOffset : 0 } Rectangle { id: visualContent - width: root.isVertical ? 24 : delegateItem.visualSize - height: root.isVertical ? delegateItem.visualSize : 24 + width: root.isVerticalOrientation ? 24 : delegateItem.visualSize + height: root.isVerticalOrientation ? delegateItem.visualSize : 24 anchors.centerIn: parent radius: Theme.cornerRadius color: { @@ -735,11 +640,11 @@ Item { AppIconRenderer { id: coreIcon readonly property bool isCompact: (widgetData?.runningAppsCompactMode !== undefined ? widgetData.runningAppsCompactMode : SettingsData.runningAppsCompactMode) - anchors.left: (root.isVertical || isCompact) ? undefined : parent.left - anchors.leftMargin: (root.isVertical || isCompact) ? 0 : Theme.spacingXS - anchors.top: (root.isVertical && !isCompact) ? parent.top : undefined - anchors.topMargin: (root.isVertical && !isCompact) ? Theme.spacingXS : 0 - anchors.centerIn: (root.isVertical || isCompact) ? parent : undefined + anchors.left: (root.isVerticalOrientation || isCompact) ? undefined : parent.left + anchors.leftMargin: (root.isVerticalOrientation || isCompact) ? 0 : Theme.spacingXS + anchors.top: (root.isVerticalOrientation && !isCompact) ? parent.top : undefined + anchors.topMargin: (root.isVerticalOrientation && !isCompact) ? Theme.spacingXS : 0 + anchors.centerIn: (root.isVerticalOrientation || isCompact) ? parent : undefined iconSize: appItem.effectiveIconSize materialIconSizeAdjustment: 0 @@ -770,11 +675,11 @@ Item { IconImage { id: iconImg readonly property bool isCompact: (widgetData?.runningAppsCompactMode !== undefined ? widgetData.runningAppsCompactMode : SettingsData.runningAppsCompactMode) - anchors.left: (root.isVertical || isCompact) ? undefined : parent.left - anchors.leftMargin: (root.isVertical || isCompact) ? 0 : Theme.spacingXS - anchors.top: (root.isVertical && !isCompact) ? parent.top : undefined - anchors.topMargin: (root.isVertical && !isCompact) ? Theme.spacingXS : 0 - anchors.centerIn: (root.isVertical || isCompact) ? parent : undefined + anchors.left: (root.isVerticalOrientation || isCompact) ? undefined : parent.left + anchors.leftMargin: (root.isVerticalOrientation || isCompact) ? 0 : Theme.spacingXS + anchors.top: (root.isVerticalOrientation && !isCompact) ? parent.top : undefined + anchors.topMargin: (root.isVerticalOrientation && !isCompact) ? Theme.spacingXS : 0 + anchors.centerIn: (root.isVerticalOrientation || isCompact) ? parent : undefined width: appItem.effectiveIconSize height: appItem.effectiveIconSize @@ -815,11 +720,11 @@ Item { DankIcon { readonly property bool isCompact: (widgetData?.runningAppsCompactMode !== undefined ? widgetData.runningAppsCompactMode : SettingsData.runningAppsCompactMode) - anchors.left: (root.isVertical || isCompact) ? undefined : parent.left - anchors.leftMargin: (root.isVertical || isCompact) ? 0 : Theme.spacingXS - anchors.top: (root.isVertical && !isCompact) ? parent.top : undefined - anchors.topMargin: (root.isVertical && !isCompact) ? Theme.spacingXS : 0 - anchors.centerIn: (root.isVertical || isCompact) ? parent : undefined + anchors.left: (root.isVerticalOrientation || isCompact) ? undefined : parent.left + anchors.leftMargin: (root.isVerticalOrientation || isCompact) ? 0 : Theme.spacingXS + anchors.top: (root.isVerticalOrientation && !isCompact) ? parent.top : undefined + anchors.topMargin: (root.isVerticalOrientation && !isCompact) ? Theme.spacingXS : 0 + anchors.centerIn: (root.isVerticalOrientation || isCompact) ? parent : undefined size: appItem.effectiveIconSize name: "sports_esports" @@ -882,7 +787,7 @@ Item { } StyledText { - visible: !root.isVertical && !(widgetData?.runningAppsCompactMode !== undefined ? widgetData.runningAppsCompactMode : SettingsData.runningAppsCompactMode) + visible: !root.isVerticalOrientation && !(widgetData?.runningAppsCompactMode !== undefined ? widgetData.runningAppsCompactMode : SettingsData.runningAppsCompactMode) anchors.left: iconImg.right anchors.leftMargin: Theme.spacingXS anchors.right: parent.right @@ -897,16 +802,16 @@ Item { Rectangle { visible: modelData.isRunning && !(widgetData?.appsDockHideIndicators !== undefined ? widgetData.appsDockHideIndicators : SettingsData.appsDockHideIndicators) - width: root.isVertical ? 2 : 20 - height: root.isVertical ? 20 : 2 + width: root.isVerticalOrientation ? 2 : 20 + height: root.isVerticalOrientation ? 20 : 2 radius: 1 color: appItem.isFocused ? Theme.primary : Theme.surfaceText opacity: appItem.isFocused ? 1 : 0.5 - anchors.bottom: root.isVertical ? undefined : parent.bottom - anchors.right: root.isVertical ? parent.right : undefined - anchors.horizontalCenter: root.isVertical ? undefined : parent.horizontalCenter - anchors.verticalCenter: root.isVertical ? parent.verticalCenter : undefined + anchors.bottom: root.isVerticalOrientation ? undefined : parent.bottom + anchors.right: root.isVerticalOrientation ? parent.right : undefined + anchors.horizontalCenter: root.isVerticalOrientation ? undefined : parent.horizontalCenter + anchors.verticalCenter: root.isVerticalOrientation ? parent.verticalCenter : undefined anchors.margins: 0 z: 5 @@ -1009,10 +914,10 @@ Item { if (!dragHandler.dragging) return; - const axisOffset = root.isVertical ? (mouse.y - dragHandler.dragStartPos.y) : (mouse.x - dragHandler.dragStartPos.x); + const axisOffset = root.isVerticalOrientation ? (mouse.y - dragHandler.dragStartPos.y) : (mouse.x - dragHandler.dragStartPos.x); dragHandler.dragAxisOffset = axisOffset; - const itemSize = (root.isVertical ? delegateItem.height : delegateItem.width) + Theme.spacingXS; + const itemSize = (root.isVerticalOrientation ? delegateItem.height : delegateItem.width) + Theme.spacingXS; const slotOffset = Math.round(axisOffset / itemSize); const newTargetIndex = Math.max(0, Math.min(root.pinnedAppCount - 1, index + slotOffset)); @@ -1028,7 +933,7 @@ Item { tooltipLoader.active = true; if (tooltipLoader.item) { - if (root.isVertical) { + if (root.isVerticalOrientation) { const globalPos = delegateItem.mapToGlobal(0, delegateItem.height / 2); const screenX = root.parentScreen ? root.parentScreen.x : 0; const screenY = root.parentScreen ? root.parentScreen.y : 0; diff --git a/quickshell/Modules/DankBar/Widgets/AppsDockContextMenu.qml b/quickshell/Modules/DankBar/Widgets/AppsDockContextMenu.qml index e9013ada..166befce 100644 --- a/quickshell/Modules/DankBar/Widgets/AppsDockContextMenu.qml +++ b/quickshell/Modules/DankBar/Widgets/AppsDockContextMenu.qml @@ -1,7 +1,6 @@ import QtQuick import Quickshell import Quickshell.Wayland -import Quickshell.Hyprland import Quickshell.Widgets import qs.Common import qs.Services @@ -213,12 +212,19 @@ PanelWindow { } } + DankRipple { + id: windowRipple + rippleColor: Theme.surfaceText + cornerRadius: Theme.cornerRadius + } + MouseArea { id: windowArea anchors.fill: parent anchors.rightMargin: 24 hoverEnabled: true cursorShape: Qt.PointingHandCursor + onPressed: mouse => windowRipple.trigger(mouse.x, mouse.y) onClicked: { if (modelData && modelData.activate) { modelData.activate(); @@ -285,11 +291,18 @@ PanelWindow { } } + DankRipple { + id: actionRipple + rippleColor: Theme.surfaceText + cornerRadius: Theme.cornerRadius + } + MouseArea { id: actionArea anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor + onPressed: mouse => actionRipple.trigger(mouse.x, mouse.y) onClicked: { if (modelData) { SessionService.launchDesktopAction(root.desktopEntry, modelData); @@ -333,11 +346,18 @@ PanelWindow { wrapMode: Text.NoWrap } + DankRipple { + id: pinRipple + rippleColor: Theme.surfaceText + cornerRadius: Theme.cornerRadius + } + MouseArea { id: pinArea anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor + onPressed: mouse => pinRipple.trigger(mouse.x, mouse.y) onClicked: { if (!root.appData) { return; @@ -386,11 +406,18 @@ PanelWindow { wrapMode: Text.NoWrap } + DankRipple { + id: nvidiaRipple + rippleColor: Theme.surfaceText + cornerRadius: Theme.cornerRadius + } + MouseArea { id: nvidiaArea anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor + onPressed: mouse => nvidiaRipple.trigger(mouse.x, mouse.y) onClicked: { if (root.desktopEntry) { SessionService.launchDesktopEntry(root.desktopEntry, true); @@ -426,11 +453,18 @@ PanelWindow { wrapMode: Text.NoWrap } + DankRipple { + id: closeRipple + rippleColor: Theme.error + cornerRadius: Theme.cornerRadius + } + MouseArea { id: closeArea anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor + onPressed: mouse => closeRipple.trigger(mouse.x, mouse.y) onClicked: { if (root.appData?.type === "window") { root.appData?.toplevel?.close(); diff --git a/quickshell/Modules/DankBar/Widgets/Battery.qml b/quickshell/Modules/DankBar/Widgets/Battery.qml index e08a6d27..b7851c8d 100644 --- a/quickshell/Modules/DankBar/Widgets/Battery.qml +++ b/quickshell/Modules/DankBar/Widgets/Battery.qml @@ -115,7 +115,8 @@ BasePill { height: battery.height + battery.topMargin + battery.bottomMargin cursorShape: Qt.PointingHandCursor acceptedButtons: Qt.LeftButton - onPressed: { + onPressed: mouse => { + battery.triggerRipple(this, mouse.x, mouse.y); toggleBatteryPopup(); } } diff --git a/quickshell/Modules/DankBar/Widgets/ClipboardButton.qml b/quickshell/Modules/DankBar/Widgets/ClipboardButton.qml index 4a3e96b0..9db066f7 100644 --- a/quickshell/Modules/DankBar/Widgets/ClipboardButton.qml +++ b/quickshell/Modules/DankBar/Widgets/ClipboardButton.qml @@ -65,6 +65,7 @@ BasePill { anchors.fill: parent acceptedButtons: Qt.LeftButton | Qt.RightButton cursorShape: Qt.PointingHandCursor + onPressed: mouse => root.triggerRipple(this, mouse.x, mouse.y) onClicked: function (mouse) { switch (mouse.button) { case Qt.RightButton: diff --git a/quickshell/Modules/DankBar/Widgets/Clock.qml b/quickshell/Modules/DankBar/Widgets/Clock.qml index 2c567ab2..17ac40f9 100644 --- a/quickshell/Modules/DankBar/Widgets/Clock.qml +++ b/quickshell/Modules/DankBar/Widgets/Clock.qml @@ -11,6 +11,8 @@ BasePill { property bool compactMode: false signal clockClicked + onClicked: clockClicked() + content: Component { Item { implicitWidth: root.isVerticalOrientation ? (root.widgetThickness - root.horizontalPadding * 2) : clockRow.implicitWidth @@ -326,15 +328,4 @@ BasePill { } } } - - MouseArea { - x: -root.leftMargin - y: -root.topMargin - width: root.width + root.leftMargin + root.rightMargin - height: root.height + root.topMargin + root.bottomMargin - cursorShape: Qt.PointingHandCursor - onPressed: { - root.clockClicked(); - } - } } diff --git a/quickshell/Modules/DankBar/Widgets/ColorPicker.qml b/quickshell/Modules/DankBar/Widgets/ColorPicker.qml index b3639d31..9d7492a5 100644 --- a/quickshell/Modules/DankBar/Widgets/ColorPicker.qml +++ b/quickshell/Modules/DankBar/Widgets/ColorPicker.qml @@ -29,7 +29,8 @@ BasePill { z: 1 anchors.fill: parent cursorShape: Qt.PointingHandCursor - onPressed: { + onPressed: mouse => { + root.triggerRipple(this, mouse.x, mouse.y); root.colorPickerRequested(); } } diff --git a/quickshell/Modules/DankBar/Widgets/CpuMonitor.qml b/quickshell/Modules/DankBar/Widgets/CpuMonitor.qml index c0919239..3ad30530 100644 --- a/quickshell/Modules/DankBar/Widgets/CpuMonitor.qml +++ b/quickshell/Modules/DankBar/Widgets/CpuMonitor.qml @@ -138,7 +138,8 @@ BasePill { anchors.fill: parent cursorShape: Qt.PointingHandCursor acceptedButtons: Qt.LeftButton - onPressed: { + onPressed: mouse => { + root.triggerRipple(this, mouse.x, mouse.y); DgopService.setSortBy("cpu"); cpuClicked(); } diff --git a/quickshell/Modules/DankBar/Widgets/CpuTemperature.qml b/quickshell/Modules/DankBar/Widgets/CpuTemperature.qml index 1791fa59..96242a9e 100644 --- a/quickshell/Modules/DankBar/Widgets/CpuTemperature.qml +++ b/quickshell/Modules/DankBar/Widgets/CpuTemperature.qml @@ -138,7 +138,8 @@ BasePill { anchors.fill: parent cursorShape: Qt.PointingHandCursor acceptedButtons: Qt.LeftButton - onPressed: { + onPressed: mouse => { + root.triggerRipple(this, mouse.x, mouse.y); DgopService.setSortBy("cpu"); cpuTempClicked(); } diff --git a/quickshell/Modules/DankBar/Widgets/GpuTemperature.qml b/quickshell/Modules/DankBar/Widgets/GpuTemperature.qml index a51da700..826c3581 100644 --- a/quickshell/Modules/DankBar/Widgets/GpuTemperature.qml +++ b/quickshell/Modules/DankBar/Widgets/GpuTemperature.qml @@ -206,7 +206,8 @@ BasePill { anchors.fill: parent cursorShape: Qt.PointingHandCursor acceptedButtons: Qt.LeftButton - onPressed: { + onPressed: mouse => { + root.triggerRipple(this, mouse.x, mouse.y); DgopService.setSortBy("cpu"); gpuTempClicked(); } diff --git a/quickshell/Modules/DankBar/Widgets/IdleInhibitor.qml b/quickshell/Modules/DankBar/Widgets/IdleInhibitor.qml index b7efe3f3..ca53b190 100644 --- a/quickshell/Modules/DankBar/Widgets/IdleInhibitor.qml +++ b/quickshell/Modules/DankBar/Widgets/IdleInhibitor.qml @@ -26,6 +26,9 @@ BasePill { z: 1 anchors.fill: parent cursorShape: Qt.PointingHandCursor + onPressed: mouse => { + root.triggerRipple(this, mouse.x, mouse.y); + } onClicked: { SessionService.toggleIdleInhibit(); } diff --git a/quickshell/Modules/DankBar/Widgets/KeyboardLayoutName.qml b/quickshell/Modules/DankBar/Widgets/KeyboardLayoutName.qml index 568b27fe..06dedbb8 100644 --- a/quickshell/Modules/DankBar/Widgets/KeyboardLayoutName.qml +++ b/quickshell/Modules/DankBar/Widgets/KeyboardLayoutName.qml @@ -12,31 +12,101 @@ BasePill { property var widgetData: null property bool compactMode: widgetData?.keyboardLayoutNameCompactMode !== undefined ? widgetData.keyboardLayoutNameCompactMode : SettingsData.keyboardLayoutNameCompactMode readonly property var langCodes: ({ - "afrikaans": "af", "albanian": "sq", "amharic": "am", "arabic": "ar", - "armenian": "hy", "azerbaijani": "az", "basque": "eu", "belarusian": "be", - "bengali": "bn", "bosnian": "bs", "bulgarian": "bg", "burmese": "my", - "catalan": "ca", "chinese": "zh", "croatian": "hr", "czech": "cs", - "danish": "da", "dutch": "nl", "english": "en", "esperanto": "eo", - "estonian": "et", "filipino": "fil", "finnish": "fi", "french": "fr", - "galician": "gl", "georgian": "ka", "german": "de", "greek": "el", - "gujarati": "gu", "hausa": "ha", "hebrew": "he", "hindi": "hi", - "hungarian": "hu", "icelandic": "is", "igbo": "ig", "indonesian": "id", - "irish": "ga", "italian": "it", "japanese": "ja", "javanese": "jv", - "kannada": "kn", "kazakh": "kk", "khmer": "km", "korean": "ko", - "kurdish": "ku", "kyrgyz": "ky", "lao": "lo", "latvian": "lv", - "lithuanian": "lt", "luxembourgish": "lb", "macedonian": "mk", "malay": "ms", - "malayalam": "ml", "maltese": "mt", "maori": "mi", "marathi": "mr", - "mongolian": "mn", "nepali": "ne", "norwegian": "no", "pashto": "ps", - "persian": "fa", "iranian": "fa", "farsi": "fa", "polish": "pl", - "portuguese": "pt", "punjabi": "pa", "romanian": "ro", "russian": "ru", - "serbian": "sr", "sindhi": "sd", "sinhala": "si", "slovak": "sk", - "slovenian": "sl", "somali": "so", "spanish": "es", "swahili": "sw", - "swedish": "sv", "tajik": "tg", "tamil": "ta", "tatar": "tt", - "telugu": "te", "thai": "th", "tibetan": "bo", "turkish": "tr", - "turkmen": "tk", "ukrainian": "uk", "urdu": "ur", "uyghur": "ug", - "uzbek": "uz", "vietnamese": "vi", "welsh": "cy", "yiddish": "yi", - "yoruba": "yo", "zulu": "zu" - }) + "afrikaans": "af", + "albanian": "sq", + "amharic": "am", + "arabic": "ar", + "armenian": "hy", + "azerbaijani": "az", + "basque": "eu", + "belarusian": "be", + "bengali": "bn", + "bosnian": "bs", + "bulgarian": "bg", + "burmese": "my", + "catalan": "ca", + "chinese": "zh", + "croatian": "hr", + "czech": "cs", + "danish": "da", + "dutch": "nl", + "english": "en", + "esperanto": "eo", + "estonian": "et", + "filipino": "fil", + "finnish": "fi", + "french": "fr", + "galician": "gl", + "georgian": "ka", + "german": "de", + "greek": "el", + "gujarati": "gu", + "hausa": "ha", + "hebrew": "he", + "hindi": "hi", + "hungarian": "hu", + "icelandic": "is", + "igbo": "ig", + "indonesian": "id", + "irish": "ga", + "italian": "it", + "japanese": "ja", + "javanese": "jv", + "kannada": "kn", + "kazakh": "kk", + "khmer": "km", + "korean": "ko", + "kurdish": "ku", + "kyrgyz": "ky", + "lao": "lo", + "latvian": "lv", + "lithuanian": "lt", + "luxembourgish": "lb", + "macedonian": "mk", + "malay": "ms", + "malayalam": "ml", + "maltese": "mt", + "maori": "mi", + "marathi": "mr", + "mongolian": "mn", + "nepali": "ne", + "norwegian": "no", + "pashto": "ps", + "persian": "fa", + "iranian": "fa", + "farsi": "fa", + "polish": "pl", + "portuguese": "pt", + "punjabi": "pa", + "romanian": "ro", + "russian": "ru", + "serbian": "sr", + "sindhi": "sd", + "sinhala": "si", + "slovak": "sk", + "slovenian": "sl", + "somali": "so", + "spanish": "es", + "swahili": "sw", + "swedish": "sv", + "tajik": "tg", + "tamil": "ta", + "tatar": "tt", + "telugu": "te", + "thai": "th", + "tibetan": "bo", + "turkish": "tr", + "turkmen": "tk", + "ukrainian": "uk", + "urdu": "ur", + "uyghur": "ug", + "uzbek": "uz", + "vietnamese": "vi", + "welsh": "cy", + "yiddish": "yi", + "yoruba": "yo", + "zulu": "zu" + }) readonly property var validVariants: ["US", "UK", "GB", "AZERTY", "QWERTY", "Dvorak", "Colemak", "Mac", "Intl", "International"] property string currentLayout: { if (CompositorService.isNiri) { @@ -119,6 +189,9 @@ BasePill { z: 1 anchors.fill: parent cursorShape: Qt.PointingHandCursor + onPressed: mouse => { + root.triggerRipple(this, mouse.x, mouse.y); + } onClicked: { if (CompositorService.isNiri) { NiriService.cycleKeyboardLayout(); diff --git a/quickshell/Modules/DankBar/Widgets/Media.qml b/quickshell/Modules/DankBar/Widgets/Media.qml index ec936f6b..fc1634a0 100644 --- a/quickshell/Modules/DankBar/Widgets/Media.qml +++ b/quickshell/Modules/DankBar/Widgets/Media.qml @@ -171,6 +171,9 @@ BasePill { MouseArea { anchors.fill: parent cursorShape: Qt.PointingHandCursor + onPressed: mouse => { + root.triggerRipple(this, mouse.x, mouse.y); + } onClicked: { if (root.popoutTarget && root.popoutTarget.setTriggerPosition) { const globalPos = parent.mapToItem(null, 0, 0); @@ -326,7 +329,8 @@ BasePill { anchors.fill: parent enabled: root.playerAvailable cursorShape: Qt.PointingHandCursor - onPressed: { + onPressed: mouse => { + root.triggerRipple(this, mouse.x, mouse.y); if (root.popoutTarget && root.popoutTarget.setTriggerPosition) { const globalPos = mapToItem(null, 0, 0); const currentScreen = root.parentScreen || Screen; diff --git a/quickshell/Modules/DankBar/Widgets/NotepadButton.qml b/quickshell/Modules/DankBar/Widgets/NotepadButton.qml index 5dd68194..661bcca9 100644 --- a/quickshell/Modules/DankBar/Widgets/NotepadButton.qml +++ b/quickshell/Modules/DankBar/Widgets/NotepadButton.qml @@ -130,6 +130,9 @@ BasePill { MouseArea { anchors.fill: parent acceptedButtons: Qt.LeftButton | Qt.RightButton + onPressed: mouse => { + root.triggerRipple(this, mouse.x, mouse.y); + } onClicked: function (mouse) { if (mouse.button === Qt.RightButton) { openContextMenu(); diff --git a/quickshell/Modules/DankBar/Widgets/PrivacyIndicator.qml b/quickshell/Modules/DankBar/Widgets/PrivacyIndicator.qml index a76f14b0..a42f54ff 100644 --- a/quickshell/Modules/DankBar/Widgets/PrivacyIndicator.qml +++ b/quickshell/Modules/DankBar/Widgets/PrivacyIndicator.qml @@ -1,247 +1,171 @@ import QtQuick import qs.Common +import qs.Modules.Plugins import qs.Services import qs.Widgets -Item { +BasePill { id: root - property bool isVertical: axis?.isVertical ?? false - property var axis: null - property string section: "right" - property var popupTarget: null - property var parentScreen: null - property real widgetThickness: 30 - property real barThickness: 48 - property var barConfig: null + section: "right" property bool showMicIcon: SettingsData.privacyShowMicIcon property bool showCameraIcon: SettingsData.privacyShowCameraIcon property bool showScreenSharingIcon: SettingsData.privacyShowScreenShareIcon - readonly property real horizontalPadding: (barConfig?.noBackground ?? false) ? 2 : Theme.spacingS readonly property bool hasActivePrivacy: showMicIcon || showCameraIcon || showScreenSharingIcon || PrivacyService.anyPrivacyActive readonly property int activeCount: (showMicIcon ? 1 : PrivacyService.microphoneActive) + (showCameraIcon ? 1 : PrivacyService.cameraActive) + (showScreenSharingIcon ? 1 : PrivacyService.screensharingActive) readonly property real contentWidth: hasActivePrivacy ? (activeCount * 18 + (activeCount - 1) * Theme.spacingXS) : 0 readonly property real contentHeight: hasActivePrivacy ? (activeCount * 18 + (activeCount - 1) * Theme.spacingXS) : 0 - readonly property real visualWidth: isVertical ? widgetThickness : (hasActivePrivacy ? (contentWidth + horizontalPadding * 2) : 0) - readonly property real visualHeight: isVertical ? (hasActivePrivacy ? (contentHeight + horizontalPadding * 2) : 0) : widgetThickness - width: isVertical ? barThickness : visualWidth - height: isVertical ? visualHeight : barThickness visible: hasActivePrivacy opacity: hasActivePrivacy ? 1 : 0 enabled: hasActivePrivacy - Item { - id: visualContent - width: root.visualWidth - height: root.visualHeight - anchors.centerIn: parent + content: Component { + Item { + implicitWidth: root.hasActivePrivacy ? root.contentWidth : 0 + implicitHeight: root.hasActivePrivacy ? root.contentHeight : 0 - Rectangle { - id: outline - anchors.centerIn: parent - width: { - const borderWidth = (barConfig?.widgetOutlineEnabled ?? false) ? (barConfig?.widgetOutlineThickness ?? 1) : 0; - return parent.width + borderWidth * 2; - } - height: { - const borderWidth = (barConfig?.widgetOutlineEnabled ?? false) ? (barConfig?.widgetOutlineThickness ?? 1) : 0; - return parent.height + borderWidth * 2; - } - radius: (barConfig?.noBackground ?? false) ? 0 : Theme.cornerRadius - color: "transparent" - border.width: { - if (barConfig?.widgetOutlineEnabled ?? false) { - return barConfig?.widgetOutlineThickness ?? 1; - } - return 0; - } - border.color: { - if (!(barConfig?.widgetOutlineEnabled ?? false)) { - return "transparent"; - } - const colorOption = barConfig?.widgetOutlineColor || "primary"; - const opacity = barConfig?.widgetOutlineOpacity ?? 1.0; - switch (colorOption) { - case "surfaceText": - return Theme.withAlpha(Theme.surfaceText, opacity); - case "secondary": - return Theme.withAlpha(Theme.secondary, opacity); - case "primary": - return Theme.withAlpha(Theme.primary, opacity); - default: - return Theme.withAlpha(Theme.primary, opacity); - } - } - } + Column { + anchors.centerIn: parent + spacing: Theme.spacingXS + visible: root.isVerticalOrientation && root.hasActivePrivacy - Rectangle { - id: background - anchors.fill: parent - radius: (barConfig?.noBackground ?? false) ? 0 : Theme.cornerRadius - color: { - if (barConfig?.noBackground ?? false) { - return "transparent"; - } + Item { + width: 18 + height: 18 + visible: PrivacyService.microphoneActive + anchors.horizontalCenter: parent.horizontalCenter - const rawTransparency = (root.barConfig && root.barConfig.widgetTransparency !== undefined) ? root.barConfig.widgetTransparency : 1.0; - const isHovered = privacyArea.containsMouse; - const transparency = isHovered ? Math.max(0.3, rawTransparency) : rawTransparency; - const baseColor = isHovered ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor; - return Theme.withAlpha(baseColor, transparency); - } - } - - Column { - anchors.centerIn: parent - spacing: Theme.spacingXS - visible: root.isVertical && root.hasActivePrivacy - - Item { - width: 18 - height: 18 - visible: PrivacyService.microphoneActive - anchors.horizontalCenter: parent.horizontalCenter - - DankIcon { - name: { - const sourceAudio = AudioService.source?.audio; - const muted = !sourceAudio || sourceAudio.muted || sourceAudio.volume === 0.0; - if (muted) - return "mic_off"; - return "mic"; + DankIcon { + name: { + const sourceAudio = AudioService.source?.audio; + const muted = !sourceAudio || sourceAudio.muted || sourceAudio.volume === 0.0; + if (muted) + return "mic_off"; + return "mic"; + } + size: Theme.iconSizeSmall + color: Theme.error + filled: true + anchors.centerIn: parent } - size: Theme.iconSizeSmall - color: Theme.error - filled: true - anchors.centerIn: parent - } - } - - Item { - width: 18 - height: 18 - visible: PrivacyService.cameraActive - anchors.horizontalCenter: parent.horizontalCenter - - DankIcon { - name: "camera_video" - size: Theme.iconSizeSmall - color: Theme.widgetTextColor - filled: true - anchors.centerIn: parent } - Rectangle { - width: 6 - height: 6 - radius: 3 - color: Theme.error - anchors.right: parent.right - anchors.top: parent.top - anchors.rightMargin: -2 - anchors.topMargin: -1 - } - } - - Item { - width: 18 - height: 18 - visible: PrivacyService.screensharingActive - anchors.horizontalCenter: parent.horizontalCenter - - DankIcon { - name: "screen_share" - size: Theme.iconSizeSmall - color: Theme.warning - filled: true - anchors.centerIn: parent - } - } - } - - Row { - anchors.centerIn: parent - spacing: Theme.spacingXS - visible: !root.isVertical && root.hasActivePrivacy - - Item { - width: 18 - height: 18 - visible: showMicIcon || PrivacyService.microphoneActive - anchors.verticalCenter: parent.verticalCenter - - DankIcon { - name: { - const sourceAudio = AudioService.source?.audio; - const muted = !sourceAudio || sourceAudio.muted || sourceAudio.volume === 0.0; - if (muted) - return "mic_off"; - return "mic"; - } - size: Theme.iconSizeSmall - color: PrivacyService.microphoneActive ? Theme.error : Theme.surfaceText - filled: true - anchors.centerIn: parent - } - } - - Item { - width: 18 - height: 18 - visible: showCameraIcon || PrivacyService.cameraActive - anchors.verticalCenter: parent.verticalCenter - - DankIcon { - name: "camera_video" - size: Theme.iconSizeSmall - color: PrivacyService.cameraActive ? Theme.error : Theme.surfaceText - filled: true - anchors.centerIn: parent - } - - Rectangle { - width: 6 - height: 6 - radius: 3 - color: Theme.error - anchors.right: parent.right - anchors.top: parent.top - anchors.rightMargin: -2 - anchors.topMargin: -1 + Item { + width: 18 + height: 18 visible: PrivacyService.cameraActive + anchors.horizontalCenter: parent.horizontalCenter + + DankIcon { + name: "camera_video" + size: Theme.iconSizeSmall + color: Theme.widgetTextColor + filled: true + anchors.centerIn: parent + } + + Rectangle { + width: 6 + height: 6 + radius: 3 + color: Theme.error + anchors.right: parent.right + anchors.top: parent.top + anchors.rightMargin: -2 + anchors.topMargin: -1 + } + } + + Item { + width: 18 + height: 18 + visible: PrivacyService.screensharingActive + anchors.horizontalCenter: parent.horizontalCenter + + DankIcon { + name: "screen_share" + size: Theme.iconSizeSmall + color: Theme.warning + filled: true + anchors.centerIn: parent + } } } - Item { - width: 18 - height: 18 - visible: showScreenSharingIcon || PrivacyService.screensharingActive - anchors.verticalCenter: parent.verticalCenter + Row { + anchors.centerIn: parent + spacing: Theme.spacingXS + visible: !root.isVerticalOrientation && root.hasActivePrivacy - DankIcon { - name: "screen_share" - size: Theme.iconSizeSmall - color: PrivacyService.screensharingActive ? Theme.warning : Theme.surfaceText - filled: true - anchors.centerIn: parent + Item { + width: 18 + height: 18 + visible: root.showMicIcon || PrivacyService.microphoneActive + anchors.verticalCenter: parent.verticalCenter + + DankIcon { + name: { + const sourceAudio = AudioService.source?.audio; + const muted = !sourceAudio || sourceAudio.muted || sourceAudio.volume === 0.0; + if (muted) + return "mic_off"; + return "mic"; + } + size: Theme.iconSizeSmall + color: PrivacyService.microphoneActive ? Theme.error : Theme.surfaceText + filled: true + anchors.centerIn: parent + } + } + + Item { + width: 18 + height: 18 + visible: root.showCameraIcon || PrivacyService.cameraActive + anchors.verticalCenter: parent.verticalCenter + + DankIcon { + name: "camera_video" + size: Theme.iconSizeSmall + color: PrivacyService.cameraActive ? Theme.error : Theme.surfaceText + filled: true + anchors.centerIn: parent + } + + Rectangle { + width: 6 + height: 6 + radius: 3 + color: Theme.error + anchors.right: parent.right + anchors.top: parent.top + anchors.rightMargin: -2 + anchors.topMargin: -1 + visible: PrivacyService.cameraActive + } + } + + Item { + width: 18 + height: 18 + visible: root.showScreenSharingIcon || PrivacyService.screensharingActive + anchors.verticalCenter: parent.verticalCenter + + DankIcon { + name: "screen_share" + size: Theme.iconSizeSmall + color: PrivacyService.screensharingActive ? Theme.warning : Theme.surfaceText + filled: true + anchors.centerIn: parent + } } } } } - MouseArea { - id: privacyArea - z: -1 - anchors.fill: parent - hoverEnabled: hasActivePrivacy - enabled: hasActivePrivacy - cursorShape: Qt.PointingHandCursor - onClicked: {} - } - Rectangle { id: tooltip width: tooltipText.contentWidth + Theme.spacingM * 2 @@ -251,7 +175,7 @@ Item { border.color: Theme.outlineMedium border.width: 1 visible: false - opacity: privacyArea.containsMouse && hasActivePrivacy ? 1 : 0 + opacity: root.isMouseHovered && hasActivePrivacy ? 1 : 0 z: 100 x: (parent.width - width) / 2 y: -height - Theme.spacingXS @@ -287,7 +211,7 @@ Item { } Behavior on width { - enabled: hasActivePrivacy && visible && !isVertical + enabled: hasActivePrivacy && visible && !isVerticalOrientation NumberAnimation { duration: Theme.mediumDuration @@ -296,7 +220,7 @@ Item { } Behavior on height { - enabled: hasActivePrivacy && visible && isVertical + enabled: hasActivePrivacy && visible && isVerticalOrientation NumberAnimation { duration: Theme.mediumDuration diff --git a/quickshell/Modules/DankBar/Widgets/RamMonitor.qml b/quickshell/Modules/DankBar/Widgets/RamMonitor.qml index bee3af5e..2d9b224c 100644 --- a/quickshell/Modules/DankBar/Widgets/RamMonitor.qml +++ b/quickshell/Modules/DankBar/Widgets/RamMonitor.qml @@ -161,7 +161,8 @@ BasePill { anchors.fill: parent cursorShape: Qt.PointingHandCursor acceptedButtons: Qt.LeftButton - onPressed: { + onPressed: mouse => { + root.triggerRipple(this, mouse.x, mouse.y); DgopService.setSortBy("memory"); ramClicked(); } diff --git a/quickshell/Modules/DankBar/Widgets/RunningApps.qml b/quickshell/Modules/DankBar/Widgets/RunningApps.qml index c3297f4b..b95171f8 100644 --- a/quickshell/Modules/DankBar/Widgets/RunningApps.qml +++ b/quickshell/Modules/DankBar/Widgets/RunningApps.qml @@ -5,25 +5,21 @@ import Quickshell import Quickshell.Wayland import Quickshell.Widgets import qs.Common +import qs.Modules.Plugins import qs.Services import qs.Widgets -Item { +BasePill { id: root + enableBackgroundHover: false + enableCursor: false + section: "left" + property var widgetData: null - property var barConfig: null - property bool isVertical: axis?.isVertical ?? false - property var axis: null - property string section: "left" - property var parentScreen property var hoveredItem: null property var topBar: null - property real widgetThickness: 30 - property real barThickness: 48 - property real barSpacing: 4 property bool isAutoHideBar: false - readonly property real horizontalPadding: (barConfig?.noBackground ?? false) ? 2 : Theme.spacingS property Item windowRoot: (Window.window ? Window.window.contentItem : null) readonly property real effectiveBarThickness: { @@ -52,7 +48,7 @@ Item { readonly property real barY: barBounds.y readonly property real minTooltipY: { - if (!parentScreen || !isVertical) { + if (!parentScreen || !isVerticalOrientation) { return 0; } @@ -139,108 +135,42 @@ Item { } } readonly property int windowCount: _groupByApp ? (groupedWindows?.length || 0) : (sortedToplevels?.length || 0) - readonly property int calculatedSize: { - if (windowCount === 0) { - return 0; - } - if (widgetData?.runningAppsCompactMode !== undefined ? widgetData.runningAppsCompactMode : SettingsData.runningAppsCompactMode) { - return windowCount * 24 + (windowCount - 1) * Theme.spacingXS + horizontalPadding * 2; - } else { - return windowCount * (24 + Theme.spacingXS + 120) + (windowCount - 1) * Theme.spacingXS + horizontalPadding * 2; - } - } - - width: windowCount > 0 ? (isVertical ? barThickness : calculatedSize) : 0 - height: windowCount > 0 ? (isVertical ? calculatedSize : barThickness) : 0 visible: windowCount > 0 - Item { - id: visualBackground - width: root.isVertical ? root.widgetThickness : root.calculatedSize - height: root.isVertical ? root.calculatedSize : root.widgetThickness - anchors.centerIn: parent - clip: false + property real scrollAccumulator: 0 + property real touchpadThreshold: 500 - Rectangle { - id: outline - anchors.centerIn: parent - width: { - const borderWidth = (barConfig?.widgetOutlineEnabled ?? false) ? (barConfig?.widgetOutlineThickness ?? 1) : 0; - return parent.width + borderWidth * 2; - } - height: { - const borderWidth = (barConfig?.widgetOutlineEnabled ?? false) ? (barConfig?.widgetOutlineThickness ?? 1) : 0; - return parent.height + borderWidth * 2; - } - radius: (barConfig?.noBackground ?? false) ? 0 : Theme.cornerRadius - color: "transparent" - border.width: { - if (barConfig?.widgetOutlineEnabled ?? false) { - return barConfig?.widgetOutlineThickness ?? 1; - } - return 0; - } - border.color: { - if (!(barConfig?.widgetOutlineEnabled ?? false)) { - return "transparent"; - } - const colorOption = barConfig?.widgetOutlineColor || "primary"; - const opacity = barConfig?.widgetOutlineOpacity ?? 1.0; - switch (colorOption) { - case "surfaceText": - return Theme.withAlpha(Theme.surfaceText, opacity); - case "secondary": - return Theme.withAlpha(Theme.secondary, opacity); - case "primary": - return Theme.withAlpha(Theme.primary, opacity); - default: - return Theme.withAlpha(Theme.primary, opacity); + onWheel: function (wheelEvent) { + const deltaY = wheelEvent.angleDelta.y; + const isMouseWheel = Math.abs(deltaY) >= 120 && (Math.abs(deltaY) % 120) === 0; + + const windows = root.sortedToplevels; + if (windows.length < 2) + return; + + if (isMouseWheel) { + let currentIndex = -1; + for (var i = 0; i < windows.length; i++) { + if (windows[i].activated) { + currentIndex = i; + break; } } - } - Rectangle { - id: background - anchors.fill: parent - radius: (barConfig?.noBackground ?? false) ? 0 : Theme.cornerRadius - color: { - if (windowCount === 0) { - return "transparent"; - } - - if ((barConfig?.noBackground ?? false)) { - return "transparent"; - } - - const baseColor = Theme.widgetBaseBackgroundColor; - const transparency = (root.barConfig && root.barConfig.widgetTransparency !== undefined) ? root.barConfig.widgetTransparency : 1.0; - if (Theme.widgetBackgroundHasAlpha) { - return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * transparency); - } - return Theme.withAlpha(baseColor, transparency); - } - } - } - - MouseArea { - anchors.fill: parent - hoverEnabled: true - acceptedButtons: Qt.NoButton - - property real scrollAccumulator: 0 - property real touchpadThreshold: 500 - - onWheel: wheel => { - const deltaY = wheel.angleDelta.y; - const isMouseWheel = Math.abs(deltaY) >= 120 && (Math.abs(deltaY) % 120) === 0; - - const windows = root.sortedToplevels; - if (windows.length < 2) { - return; + let nextIndex; + if (deltaY < 0) { + nextIndex = currentIndex === -1 ? 0 : Math.min(currentIndex + 1, windows.length - 1); + } else { + nextIndex = currentIndex === -1 ? windows.length - 1 : Math.max(currentIndex - 1, 0); } - if (isMouseWheel) { - // Direct mouse wheel action + const nextWindow = windows[nextIndex]; + if (nextWindow) + nextWindow.activate(); + } else { + scrollAccumulator += deltaY; + + if (Math.abs(scrollAccumulator) >= touchpadThreshold) { let currentIndex = -1; for (var i = 0; i < windows.length; i++) { if (windows[i].activated) { @@ -250,69 +180,32 @@ Item { } let nextIndex; - if (deltaY < 0) { - if (currentIndex === -1) { - nextIndex = 0; - } else { - nextIndex = Math.min(currentIndex + 1, windows.length - 1); - } + if (scrollAccumulator < 0) { + nextIndex = currentIndex === -1 ? 0 : Math.min(currentIndex + 1, windows.length - 1); } else { - if (currentIndex === -1) { - nextIndex = windows.length - 1; - } else { - nextIndex = Math.max(currentIndex - 1, 0); - } + nextIndex = currentIndex === -1 ? windows.length - 1 : Math.max(currentIndex - 1, 0); } const nextWindow = windows[nextIndex]; - if (nextWindow) { + if (nextWindow) nextWindow.activate(); - } - } else { - // Touchpad - accumulate small deltas - scrollAccumulator += deltaY; - if (Math.abs(scrollAccumulator) >= touchpadThreshold) { - let currentIndex = -1; - for (var i = 0; i < windows.length; i++) { - if (windows[i].activated) { - currentIndex = i; - break; - } - } - - let nextIndex; - if (scrollAccumulator < 0) { - if (currentIndex === -1) { - nextIndex = 0; - } else { - nextIndex = Math.min(currentIndex + 1, windows.length - 1); - } - } else { - if (currentIndex === -1) { - nextIndex = windows.length - 1; - } else { - nextIndex = Math.max(currentIndex - 1, 0); - } - } - - const nextWindow = windows[nextIndex]; - if (nextWindow) { - nextWindow.activate(); - } - - scrollAccumulator = 0; - } + scrollAccumulator = 0; } - - wheel.accepted = true; } } - Loader { - id: layoutLoader - anchors.centerIn: parent - sourceComponent: root.isVertical ? columnLayout : rowLayout + content: Component { + Item { + implicitWidth: layoutLoader.item ? layoutLoader.item.implicitWidth : 0 + implicitHeight: layoutLoader.item ? layoutLoader.item.implicitHeight : 0 + + Loader { + id: layoutLoader + anchors.centerIn: parent + sourceComponent: root.isVerticalOrientation ? columnLayout : rowLayout + } + } } Component { @@ -466,6 +359,7 @@ Item { hoverEnabled: true cursorShape: Qt.PointingHandCursor acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton + onPressed: mouse => root.triggerRipple(this, mouse.x, mouse.y) onClicked: mouse => { if (mouse.button === Qt.LeftButton) { if (isGrouped && windowCount > 1) { @@ -495,7 +389,7 @@ Item { windowContextMenuLoader.item.triggerBarPosition = root.axis.edge === "left" ? 2 : (root.axis.edge === "right" ? 3 : (root.axis.edge === "top" ? 0 : 1)); windowContextMenuLoader.item.triggerBarThickness = root.barThickness; windowContextMenuLoader.item.triggerBarSpacing = root.barSpacing; - if (root.isVertical) { + if (root.isVerticalOrientation) { const globalPos = delegateItem.mapToGlobal(delegateItem.width / 2, delegateItem.height / 2); const screenX = root.parentScreen ? root.parentScreen.x : 0; const screenY = root.parentScreen ? root.parentScreen.y : 0; @@ -526,7 +420,7 @@ Item { root.hoveredItem = delegateItem; tooltipLoader.active = true; if (tooltipLoader.item) { - if (root.isVertical) { + if (root.isVerticalOrientation) { const globalPos = delegateItem.mapToGlobal(delegateItem.width / 2, delegateItem.height / 2); const screenX = root.parentScreen ? root.parentScreen.x : 0; const screenY = root.parentScreen ? root.parentScreen.y : 0; @@ -711,6 +605,7 @@ Item { hoverEnabled: true cursorShape: Qt.PointingHandCursor acceptedButtons: Qt.LeftButton | Qt.RightButton + onPressed: mouse => root.triggerRipple(this, mouse.x, mouse.y) onClicked: mouse => { if (mouse.button === Qt.LeftButton) { if (isGrouped && windowCount > 1) { @@ -740,7 +635,7 @@ Item { windowContextMenuLoader.item.triggerBarPosition = root.axis.edge === "left" ? 2 : (root.axis.edge === "right" ? 3 : (root.axis.edge === "top" ? 0 : 1)); windowContextMenuLoader.item.triggerBarThickness = root.barThickness; windowContextMenuLoader.item.triggerBarSpacing = root.barSpacing; - if (root.isVertical) { + if (root.isVerticalOrientation) { const globalPos = delegateItem.mapToGlobal(delegateItem.width / 2, delegateItem.height / 2); const screenX = root.parentScreen ? root.parentScreen.x : 0; const screenY = root.parentScreen ? root.parentScreen.y : 0; @@ -765,7 +660,7 @@ Item { root.hoveredItem = delegateItem; tooltipLoader.active = true; if (tooltipLoader.item) { - if (root.isVertical) { + if (root.isVerticalOrientation) { const globalPos = delegateItem.mapToGlobal(delegateItem.width / 2, delegateItem.height / 2); const screenX = root.parentScreen ? root.parentScreen.x : 0; const screenY = root.parentScreen ? root.parentScreen.y : 0; diff --git a/quickshell/Modules/DankBar/Widgets/SystemTrayBar.qml b/quickshell/Modules/DankBar/Widgets/SystemTrayBar.qml index 69147b25..ea5254db 100644 --- a/quickshell/Modules/DankBar/Widgets/SystemTrayBar.qml +++ b/quickshell/Modules/DankBar/Widgets/SystemTrayBar.qml @@ -6,23 +6,19 @@ import Quickshell.Services.SystemTray import Quickshell.Wayland import Quickshell.Widgets import qs.Common +import qs.Modules.Plugins import qs.Services import qs.Widgets -Item { +BasePill { id: root - property bool isVertical: axis?.isVertical ?? false - property var axis: null + enableBackgroundHover: false + enableCursor: false + property var parentWindow: null - property var parentScreen: null - property real widgetThickness: 30 - property real barThickness: 48 - property real barSpacing: 4 property bool isAtBottom: false - property var barConfig: null property bool isAutoHideBar: false - readonly property real horizontalPadding: (barConfig?.noBackground ?? false) ? 2 : Theme.spacingS readonly property var hiddenTrayIds: { const envValue = Quickshell.env("DMS_HIDE_TRAYIDS") || ""; return envValue ? envValue.split(",").map(id => id.trim().toLowerCase()) : []; @@ -102,21 +98,10 @@ Item { property int dropTargetIndex: -1 property bool suppressShiftAnimation: false readonly property bool hasHiddenItems: allTrayItems.length > mainBarItems.length - readonly property int calculatedSize: { - if (allTrayItems.length === 0) - return 0; - const itemCount = mainBarItems.length + (hasHiddenItems ? 1 : 0); - return itemCount * 24 + horizontalPadding * 2; - } - readonly property real visualWidth: isVertical ? widgetThickness : calculatedSize - readonly property real visualHeight: isVertical ? calculatedSize : widgetThickness - - width: isVertical ? barThickness : visualWidth - height: isVertical ? visualHeight : barThickness visible: allTrayItems.length > 0 readonly property real minTooltipY: { - if (!parentScreen || !isVertical) { + if (!parentScreen || !isVerticalOrientation) { return 0; } @@ -135,77 +120,17 @@ Item { property bool menuOpen: false property var currentTrayMenu: null - Item { - id: visualBackground - width: root.visualWidth - height: root.visualHeight - anchors.centerIn: parent + content: Component { + Item { + implicitWidth: layoutLoader.item ? layoutLoader.item.implicitWidth : 0 + implicitHeight: layoutLoader.item ? layoutLoader.item.implicitHeight : 0 - Rectangle { - id: outline - anchors.centerIn: parent - width: { - const borderWidth = (barConfig?.widgetOutlineEnabled ?? false) ? (barConfig?.widgetOutlineThickness ?? 1) : 0; - return parent.width + borderWidth * 2; - } - height: { - const borderWidth = (barConfig?.widgetOutlineEnabled ?? false) ? (barConfig?.widgetOutlineThickness ?? 1) : 0; - return parent.height + borderWidth * 2; - } - radius: (barConfig?.noBackground ?? false) ? 0 : Theme.cornerRadius - color: "transparent" - border.width: { - if (barConfig?.widgetOutlineEnabled ?? false) { - return barConfig?.widgetOutlineThickness ?? 1; - } - return 0; - } - border.color: { - if (!(barConfig?.widgetOutlineEnabled ?? false)) { - return "transparent"; - } - const colorOption = barConfig?.widgetOutlineColor || "primary"; - const opacity = barConfig?.widgetOutlineOpacity ?? 1.0; - switch (colorOption) { - case "surfaceText": - return Theme.withAlpha(Theme.surfaceText, opacity); - case "secondary": - return Theme.withAlpha(Theme.secondary, opacity); - case "primary": - return Theme.withAlpha(Theme.primary, opacity); - default: - return Theme.withAlpha(Theme.primary, opacity); - } + Loader { + id: layoutLoader + anchors.centerIn: parent + sourceComponent: root.isVerticalOrientation ? columnComp : rowComp } } - - Rectangle { - id: background - anchors.fill: parent - radius: (barConfig?.noBackground ?? false) ? 0 : Theme.cornerRadius - color: { - if (allTrayItems.length === 0) { - return "transparent"; - } - - if ((barConfig?.noBackground ?? false)) { - return "transparent"; - } - - const baseColor = Theme.widgetBaseBackgroundColor; - const transparency = (root.barConfig && root.barConfig.widgetTransparency !== undefined) ? root.barConfig.widgetTransparency : 1.0; - if (Theme.widgetBackgroundHasAlpha) { - return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * transparency); - } - return Theme.withAlpha(baseColor, transparency); - } - } - } - - Loader { - id: layoutLoader - anchors.centerIn: parent - sourceComponent: root.isVertical ? columnComp : rowComp } Component { @@ -334,6 +259,11 @@ Item { font.pixelSize: 10 color: Theme.widgetTextColor } + + DankRipple { + id: itemRipple + cornerRadius: Theme.cornerRadius + } } MouseArea { @@ -344,6 +274,8 @@ Item { cursorShape: dragHandler.longPressing ? Qt.DragMoveCursor : Qt.PointingHandCursor onPressed: mouse => { + const pos = mapToItem(visualContent, mouse.x, mouse.y); + itemRipple.trigger(pos.x, pos.y); if (mouse.button === Qt.LeftButton) { dragHandler.dragStartPos = Qt.point(mouse.x, mouse.y); longPressTimer.start(); @@ -379,7 +311,7 @@ Item { if (!delegateRoot.trayItem.hasMenu) return; root.menuOpen = false; - root.showForTrayItem(delegateRoot.trayItem, visualContent, parentScreen, root.isAtBottom, root.isVertical, root.axis); + root.showForTrayItem(delegateRoot.trayItem, visualContent, parentScreen, root.isAtBottom, root.isVerticalOrientation, root.axis); } onPositionChanged: mouse => { @@ -412,7 +344,7 @@ Item { if (!delegateRoot.trayItem?.hasMenu) return; root.menuOpen = false; - root.showForTrayItem(delegateRoot.trayItem, visualContent, parentScreen, root.isAtBottom, root.isVertical, root.axis); + root.showForTrayItem(delegateRoot.trayItem, visualContent, parentScreen, root.isAtBottom, root.isVerticalOrientation, root.axis); } } } @@ -438,11 +370,19 @@ Item { color: Theme.widgetTextColor } + DankRipple { + id: caretRipple + cornerRadius: Theme.cornerRadius + } + MouseArea { id: caretArea anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor + onPressed: mouse => { + caretRipple.trigger(mouse.x, mouse.y); + } onClicked: root.menuOpen = !root.menuOpen } } @@ -576,6 +516,11 @@ Item { font.pixelSize: 10 color: Theme.widgetTextColor } + + DankRipple { + id: itemRipple + cornerRadius: Theme.cornerRadius + } } MouseArea { @@ -586,6 +531,8 @@ Item { cursorShape: dragHandler.longPressing ? Qt.DragMoveCursor : Qt.PointingHandCursor onPressed: mouse => { + const pos = mapToItem(visualContent, mouse.x, mouse.y); + itemRipple.trigger(pos.x, pos.y); if (mouse.button === Qt.LeftButton) { dragHandler.dragStartPos = Qt.point(mouse.x, mouse.y); longPressTimer.start(); @@ -621,7 +568,7 @@ Item { if (!delegateRoot.trayItem.hasMenu) return; root.menuOpen = false; - root.showForTrayItem(delegateRoot.trayItem, visualContent, parentScreen, root.isAtBottom, root.isVertical, root.axis); + root.showForTrayItem(delegateRoot.trayItem, visualContent, parentScreen, root.isAtBottom, root.isVerticalOrientation, root.axis); } onPositionChanged: mouse => { @@ -654,7 +601,7 @@ Item { if (!delegateRoot.trayItem?.hasMenu) return; root.menuOpen = false; - root.showForTrayItem(delegateRoot.trayItem, visualContent, parentScreen, root.isAtBottom, root.isVertical, root.axis); + root.showForTrayItem(delegateRoot.trayItem, visualContent, parentScreen, root.isAtBottom, root.isVerticalOrientation, root.axis); } } } @@ -687,11 +634,19 @@ Item { color: Theme.widgetTextColor } + DankRipple { + id: caretRippleVert + cornerRadius: Theme.cornerRadius + } + MouseArea { id: caretAreaVert anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor + onPressed: mouse => { + caretRippleVert.trigger(mouse.x, mouse.y); + } onClicked: root.menuOpen = !root.menuOpen } } @@ -862,7 +817,7 @@ Item { const relativeX = globalPos.x - screenX; const relativeY = globalPos.y - screenY; - if (root.isVertical) { + if (root.isVerticalOrientation) { const edge = root.axis?.edge; let targetX = edge === "left" ? root.barThickness + root.barSpacing + Theme.popupDistance : screen.width - (root.barThickness + root.barSpacing + Theme.popupDistance); const adjustedY = relativeY + root.height / 2 + root.minTooltipY; @@ -900,7 +855,7 @@ Item { height: alignedHeight x: Theme.snap((() => { - if (root.isVertical) { + if (root.isVerticalOrientation) { const edge = root.axis?.edge; if (edge === "left") { const targetX = overflowMenu.anchorPos.x; @@ -918,7 +873,7 @@ Item { })(), overflowMenu.dpr) y: Theme.snap((() => { - if (root.isVertical) { + if (root.isVerticalOrientation) { const top = Math.max(overflowMenu.barY, 10); const bottom = overflowMenu.height - alignedHeight - 10; const want = overflowMenu.anchorPos.y - alignedHeight / 2; @@ -1074,7 +1029,7 @@ Item { if (!trayItem.hasMenu) return; - root.showForTrayItem(trayItem, menuContainer, parentScreen, root.isAtBottom, root.isVertical, root.axis); + root.showForTrayItem(trayItem, menuContainer, parentScreen, root.isAtBottom, root.isVerticalOrientation, root.axis); } } } diff --git a/quickshell/Modules/DankBar/Widgets/SystemUpdate.qml b/quickshell/Modules/DankBar/Widgets/SystemUpdate.qml index 196fb090..99fc0f84 100644 --- a/quickshell/Modules/DankBar/Widgets/SystemUpdate.qml +++ b/quickshell/Modules/DankBar/Widgets/SystemUpdate.qml @@ -133,7 +133,8 @@ BasePill { z: 1 anchors.fill: parent cursorShape: Qt.PointingHandCursor - onPressed: { + onPressed: mouse => { + root.triggerRipple(this, mouse.x, mouse.y); if (popoutTarget && popoutTarget.setTriggerPosition) { const globalPos = root.visualContent.mapToItem(null, 0, 0); const currentScreen = parentScreen || Screen; diff --git a/quickshell/Modules/DankBar/Widgets/Vpn.qml b/quickshell/Modules/DankBar/Widgets/Vpn.qml index b70caff4..758d28a1 100644 --- a/quickshell/Modules/DankBar/Widgets/Vpn.qml +++ b/quickshell/Modules/DankBar/Widgets/Vpn.qml @@ -71,6 +71,7 @@ BasePill { acceptedButtons: Qt.LeftButton | Qt.RightButton enabled: !DMSNetworkService.isBusy onPressed: event => { + root.triggerRipple(this, event.x, event.y); switch (event.button) { case Qt.RightButton: DMSNetworkService.toggleVpn(); diff --git a/quickshell/Modules/Dock/DockContextMenu.qml b/quickshell/Modules/Dock/DockContextMenu.qml index 7bb53bcb..663c4f3d 100644 --- a/quickshell/Modules/Dock/DockContextMenu.qml +++ b/quickshell/Modules/Dock/DockContextMenu.qml @@ -268,12 +268,19 @@ PanelWindow { } } + DankRipple { + id: windowRipple + rippleColor: Theme.surfaceText + cornerRadius: Theme.cornerRadius + } + MouseArea { id: windowArea anchors.fill: parent anchors.rightMargin: 24 hoverEnabled: true cursorShape: Qt.PointingHandCursor + onPressed: mouse => windowRipple.trigger(mouse.x, mouse.y) onClicked: { if (modelData && modelData.activate) { modelData.activate(); @@ -340,11 +347,18 @@ PanelWindow { } } + DankRipple { + id: actionRipple + rippleColor: Theme.surfaceText + cornerRadius: Theme.cornerRadius + } + MouseArea { id: actionArea anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor + onPressed: mouse => actionRipple.trigger(mouse.x, mouse.y) onClicked: { if (modelData) { SessionService.launchDesktopAction(root.desktopEntry, modelData); @@ -388,11 +402,18 @@ PanelWindow { wrapMode: Text.NoWrap } + DankRipple { + id: pinRipple + rippleColor: Theme.surfaceText + cornerRadius: Theme.cornerRadius + } + MouseArea { id: pinArea anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor + onPressed: mouse => pinRipple.trigger(mouse.x, mouse.y) onClicked: { if (!root.appData) return; @@ -441,11 +462,18 @@ PanelWindow { wrapMode: Text.NoWrap } + DankRipple { + id: nvidiaRipple + rippleColor: Theme.surfaceText + cornerRadius: Theme.cornerRadius + } + MouseArea { id: nvidiaArea anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor + onPressed: mouse => nvidiaRipple.trigger(mouse.x, mouse.y) onClicked: { if (root.desktopEntry) { SessionService.launchDesktopEntry(root.desktopEntry, true); @@ -481,11 +509,18 @@ PanelWindow { wrapMode: Text.NoWrap } + DankRipple { + id: closeRipple + rippleColor: Theme.error + cornerRadius: Theme.cornerRadius + } + MouseArea { id: closeArea anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor + onPressed: mouse => closeRipple.trigger(mouse.x, mouse.y) onClicked: { if (root.appData?.type === "window") { root.appData?.toplevel?.close(); diff --git a/quickshell/Modules/Plugins/BasePill.qml b/quickshell/Modules/Plugins/BasePill.qml index 20a48fa3..e6f2ad11 100644 --- a/quickshell/Modules/Plugins/BasePill.qml +++ b/quickshell/Modules/Plugins/BasePill.qml @@ -1,6 +1,7 @@ import QtQuick import qs.Common import qs.Services +import qs.Widgets Item { id: root @@ -18,6 +19,9 @@ Item { property bool isFirst: false property bool isLast: false property real sectionSpacing: 0 + property bool enableBackgroundHover: true + property bool enableCursor: true + readonly property bool isMouseHovered: mouseArea.containsMouse property bool isLeftBarEdge: false property bool isRightBarEdge: false property bool isTopBarEdge: false @@ -38,6 +42,11 @@ Item { signal rightClicked(real rootX, real rootY) signal wheel(var wheelEvent) + function triggerRipple(sourceItem, mouseX, mouseY) { + const pos = sourceItem.mapToItem(visualContent, mouseX, mouseY); + rippleLayer.trigger(pos.x, pos.y); + } + width: isVerticalOrientation ? barThickness : visualWidth height: isVerticalOrientation ? visualHeight : barThickness @@ -95,7 +104,7 @@ Item { } const rawTransparency = (root.barConfig && root.barConfig.widgetTransparency !== undefined) ? root.barConfig.widgetTransparency : 1.0; - const isHovered = mouseArea.containsMouse || (root.isHovered || false); + const isHovered = root.enableBackgroundHover && (mouseArea.containsMouse || (root.isHovered || false)); const transparency = isHovered ? Math.max(0.3, rawTransparency) : rawTransparency; const baseColor = isHovered ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor; @@ -106,6 +115,12 @@ Item { } } + DankRipple { + id: rippleLayer + rippleColor: Theme.surfaceText + cornerRadius: background.radius + } + Loader { id: contentLoader anchors.verticalCenter: parent.verticalCenter @@ -121,7 +136,7 @@ Item { width: root.width + root.leftMargin + root.rightMargin height: root.height + root.topMargin + root.bottomMargin hoverEnabled: true - cursorShape: Qt.PointingHandCursor + cursorShape: root.enableCursor ? Qt.PointingHandCursor : Qt.ArrowCursor acceptedButtons: Qt.LeftButton | Qt.RightButton onPressed: function (mouse) { if (mouse.button === Qt.RightButton) { @@ -129,6 +144,8 @@ Item { root.rightClicked(rPos.x, rPos.y); return; } + const ripplePos = mouseArea.mapToItem(visualContent, mouse.x, mouse.y); + rippleLayer.trigger(ripplePos.x, ripplePos.y); if (popoutTarget) { // Ensure bar context is set first if supported if (popoutTarget.setBarContext) { diff --git a/quickshell/Modules/ProcessList/ProcessContextMenu.qml b/quickshell/Modules/ProcessList/ProcessContextMenu.qml index 4ed00b0d..8dfba1ed 100644 --- a/quickshell/Modules/ProcessList/ProcessContextMenu.qml +++ b/quickshell/Modules/ProcessList/ProcessContextMenu.qml @@ -313,6 +313,12 @@ Popup { } } + DankRipple { + id: menuItemRipple + rippleColor: modelData.dangerous ? Theme.error : Theme.surfaceText + cornerRadius: menuItem.radius + } + MouseArea { id: menuItemArea anchors.fill: parent @@ -323,6 +329,7 @@ Popup { keyboardNavigation = false; selectedIndex = index; } + onPressed: mouse => menuItemRipple.trigger(mouse.x, mouse.y) onClicked: modelData.action() } } diff --git a/quickshell/Widgets/DankButton.qml b/quickshell/Widgets/DankButton.qml index ecedf83a..ea8a202b 100644 --- a/quickshell/Widgets/DankButton.qml +++ b/quickshell/Widgets/DankButton.qml @@ -16,7 +16,7 @@ Rectangle { property int buttonHeight: 40 property int horizontalPadding: Theme.spacingL property bool enableScaleAnimation: false - property bool enableRipple: false + property bool enableRipple: typeof SettingsData !== "undefined" ? (SettingsData.enableRippleEffects ?? true) : true signal clicked @@ -55,6 +55,13 @@ Rectangle { } } + DankRipple { + id: rippleLayer + rippleColor: root.textColor + cornerRadius: root.radius + enableRipple: root.enableRipple + } + Row { id: contentRow anchors.centerIn: parent @@ -83,6 +90,10 @@ Rectangle { hoverEnabled: true cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor enabled: root.enabled + onPressed: mouse => { + if (root.enableRipple) + rippleLayer.trigger(mouse.x, mouse.y); + } onClicked: root.clicked() } } diff --git a/quickshell/Widgets/DankButtonGroup.qml b/quickshell/Widgets/DankButtonGroup.qml index 5c558fc2..8d0cb6db 100644 --- a/quickshell/Widgets/DankButtonGroup.qml +++ b/quickshell/Widgets/DankButtonGroup.qml @@ -169,6 +169,12 @@ Flow { } } + DankRipple { + id: segmentRipple + cornerRadius: Theme.cornerRadius + rippleColor: segment.selected ? Theme.buttonText : Theme.surfaceVariantText + } + Item { id: contentItem anchors.centerIn: parent @@ -222,6 +228,7 @@ Flow { anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor + onPressed: mouse => segmentRipple.trigger(mouse.x, mouse.y) onClicked: root.selectItem(index) } } diff --git a/quickshell/Widgets/DankRipple.qml b/quickshell/Widgets/DankRipple.qml index 1cacbeaa..2c536506 100644 --- a/quickshell/Widgets/DankRipple.qml +++ b/quickshell/Widgets/DankRipple.qml @@ -1,8 +1,8 @@ import QtQuick +import QtQuick.Effects import qs.Common -// Material Design 3 ripple effect component -MouseArea { +Item { id: root property color rippleColor: Theme.primary @@ -11,10 +11,10 @@ MouseArea { property real _rippleX: 0 property real _rippleY: 0 - property real _rippleRadius: 0 + property real _rippleSize: 0 + readonly property alias animating: rippleAnim.running - enabled: false - hoverEnabled: false + anchors.fill: parent function trigger(x, y) { if (!enableRipple || Theme.currentAnimationSpeed === SettingsData.AnimationSpeed.None) @@ -24,7 +24,7 @@ MouseArea { _rippleY = y; const dist = (ox, oy) => ox * ox + oy * oy; - _rippleRadius = Math.sqrt(Math.max(dist(x, y), dist(x, height - y), dist(width - x, y), dist(width - x, height - y))); + _rippleSize = Math.sqrt(Math.max(dist(x, y), dist(x, height - y), dist(width - x, y), dist(width - x, height - y))) * 2; rippleAnim.restart(); } @@ -42,10 +42,20 @@ MouseArea { property: "y" value: root._rippleY } + PropertyAction { + target: ripple + property: "implicitWidth" + value: 0 + } + PropertyAction { + target: ripple + property: "implicitHeight" + value: 0 + } PropertyAction { target: ripple property: "opacity" - value: 0.08 + value: 0.10 } ParallelAnimation { @@ -53,39 +63,74 @@ MouseArea { target: ripple property: "implicitWidth" from: 0 - to: root._rippleRadius * 2 - duration: Theme.expressiveDurations.expressiveEffects + to: root._rippleSize + duration: Theme.expressiveDurations.expressiveDefaultSpatial easing.bezierCurve: Theme.expressiveCurves.standardDecel } DankAnim { target: ripple property: "implicitHeight" from: 0 - to: root._rippleRadius * 2 - duration: Theme.expressiveDurations.expressiveEffects + to: root._rippleSize + duration: Theme.expressiveDurations.expressiveDefaultSpatial easing.bezierCurve: Theme.expressiveCurves.standardDecel } - } - - DankAnim { - target: ripple - property: "opacity" - to: 0 - duration: Theme.expressiveDurations.expressiveEffects - easing.bezierCurve: Theme.expressiveCurves.standard + SequentialAnimation { + PauseAnimation { + duration: Math.round(Theme.expressiveDurations.expressiveDefaultSpatial * 0.6) + } + DankAnim { + target: ripple + property: "opacity" + to: 0 + duration: Theme.expressiveDurations.expressiveDefaultSpatial + easing.bezierCurve: Theme.expressiveCurves.standard + } + } } } - Rectangle { - id: ripple + Item { + id: rippleContainer + anchors.fill: parent + visible: root.cornerRadius <= 0 - radius: Math.min(width, height) / 2 - color: root.rippleColor - opacity: 0 + Rectangle { + id: ripple - transform: Translate { - x: -ripple.width / 2 - y: -ripple.height / 2 + radius: Math.min(width, height) / 2 + color: root.rippleColor + opacity: 0 + + transform: Translate { + x: -ripple.width / 2 + y: -ripple.height / 2 + } } } + + Item { + id: rippleMask + anchors.fill: parent + layer.enabled: root.cornerRadius > 0 + layer.smooth: true + visible: false + + Rectangle { + anchors.fill: parent + radius: root.cornerRadius + color: "black" + antialiasing: true + } + } + + MultiEffect { + anchors.fill: parent + source: rippleContainer + maskEnabled: true + maskSource: rippleMask + maskThresholdMin: 0.5 + maskSpreadAtMin: 1.0 + visible: root.cornerRadius > 0 && rippleAnim.running + } } diff --git a/quickshell/Widgets/DankTabBar.qml b/quickshell/Widgets/DankTabBar.qml index f29a62f2..d3b72f12 100644 --- a/quickshell/Widgets/DankTabBar.qml +++ b/quickshell/Widgets/DankTabBar.qml @@ -27,81 +27,80 @@ FocusScope { KeyNavigation.backtab: previousFocusTarget KeyNavigation.up: previousFocusTarget - Keys.onPressed: (event) => { + Keys.onPressed: event => { if (!tabBar.activeFocus || tabRepeater.count === 0) - return - + return; function findSelectableIndex(startIndex, step) { - let idx = startIndex + let idx = startIndex; for (let i = 0; i < tabRepeater.count; i++) { - idx = (idx + step + tabRepeater.count) % tabRepeater.count - const item = tabRepeater.itemAt(idx) + idx = (idx + step + tabRepeater.count) % tabRepeater.count; + const item = tabRepeater.itemAt(idx); if (item && !item.isAction) - return idx + return idx; } - return -1 + return -1; } - const goToIndex = (nextIndex) => { + const goToIndex = nextIndex => { if (nextIndex >= 0 && nextIndex !== tabBar.currentIndex) { - tabBar.currentIndex = nextIndex - tabBar.tabClicked(nextIndex) + tabBar.currentIndex = nextIndex; + tabBar.tabClicked(nextIndex); } - } + }; - const resolveTarget = (item) => { + const resolveTarget = item => { if (!item) - return null + return null; if (item.focusTarget) - return resolveTarget(item.focusTarget) + return resolveTarget(item.focusTarget); - return item - } + return item; + }; - const focusItem = (item) => { - const target = resolveTarget(item) + const focusItem = item => { + const target = resolveTarget(item); if (!target) - return false + return false; if (target.requestFocus) { - Qt.callLater(() => target.requestFocus()) - return true + Qt.callLater(() => target.requestFocus()); + return true; } if (target.forceActiveFocus) { - Qt.callLater(() => target.forceActiveFocus()) - return true + Qt.callLater(() => target.forceActiveFocus()); + return true; } - return false - } + return false; + }; if (event.key === Qt.Key_Right && tabBar.enableArrowNavigation) { - const baseIndex = (tabBar.currentIndex >= 0 && tabBar.currentIndex < tabRepeater.count) ? tabBar.currentIndex : -1 - const nextIndex = findSelectableIndex(baseIndex, 1) + const baseIndex = (tabBar.currentIndex >= 0 && tabBar.currentIndex < tabRepeater.count) ? tabBar.currentIndex : -1; + const nextIndex = findSelectableIndex(baseIndex, 1); if (nextIndex >= 0) { - goToIndex(nextIndex) - event.accepted = true + goToIndex(nextIndex); + event.accepted = true; } } else if (event.key === Qt.Key_Left && tabBar.enableArrowNavigation) { - const baseIndex = (tabBar.currentIndex >= 0 && tabBar.currentIndex < tabRepeater.count) ? tabBar.currentIndex : 0 - const nextIndex = findSelectableIndex(baseIndex, -1) + const baseIndex = (tabBar.currentIndex >= 0 && tabBar.currentIndex < tabRepeater.count) ? tabBar.currentIndex : 0; + const nextIndex = findSelectableIndex(baseIndex, -1); if (nextIndex >= 0) { - goToIndex(nextIndex) - event.accepted = true + goToIndex(nextIndex); + event.accepted = true; } } else if (event.key === Qt.Key_Tab && (event.modifiers & Qt.ShiftModifier)) { if (focusItem(tabBar.previousFocusTarget)) { - event.accepted = true + event.accepted = true; } } else if (event.key === Qt.Key_Tab || event.key === Qt.Key_Down) { if (focusItem(tabBar.nextFocusTarget)) { - event.accepted = true + event.accepted = true; } } else if (event.key === Qt.Key_Up) { if (focusItem(tabBar.previousFocusTarget)) { - event.accepted = true + event.accepted = true; } } } @@ -142,7 +141,7 @@ FocusScope { anchors.horizontalCenter: parent.horizontalCenter font.pixelSize: Theme.fontSizeMedium color: tabItem.isActive ? Theme.primary : Theme.surfaceText - font.weight: tabItem.isActive ? Font.Medium : Font.Normal + font.weight: Font.Medium visible: hasText } } @@ -154,7 +153,17 @@ FocusScope { opacity: tabArea.pressed ? 0.12 : (tabArea.containsMouse ? 0.08 : 0) visible: opacity > 0 radius: Theme.cornerRadius - Behavior on opacity { NumberAnimation { duration: Theme.shortDuration; easing.type: Theme.standardEasing } } + Behavior on opacity { + NumberAnimation { + duration: Theme.shortDuration + easing.type: Theme.standardEasing + } + } + } + + DankRipple { + id: tabRipple + cornerRadius: Theme.cornerRadius } MouseArea { @@ -162,15 +171,15 @@ FocusScope { anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor + onPressed: mouse => tabRipple.trigger(mouse.x, mouse.y) onClicked: { if (tabItem.isAction) { - tabBar.actionTriggered(index) + tabBar.actionTriggered(index); } else { - tabBar.tabClicked(index) + tabBar.tabClicked(index); } } } - } } } @@ -216,39 +225,39 @@ FocusScope { function updateIndicator() { if (tabRepeater.count === 0 || currentIndex < 0 || currentIndex >= tabRepeater.count) { - return + return; } - const item = tabRepeater.itemAt(currentIndex) + const item = tabRepeater.itemAt(currentIndex); if (!item || item.isAction) { - return + return; } - const tabPos = item.mapToItem(tabBar, 0, 0) - const tabCenterX = tabPos.x + item.width / 2 - const indicatorWidth = 60 + const tabPos = item.mapToItem(tabBar, 0, 0); + const tabCenterX = tabPos.x + item.width / 2; + const indicatorWidth = 60; if (tabPos.x < 10 && currentIndex > 0) { - Qt.callLater(updateIndicator) - return + Qt.callLater(updateIndicator); + return; } if (!indicator.initialSetupComplete) { - indicator.animationEnabled = false - indicator.width = indicatorWidth - indicator.x = tabCenterX - indicatorWidth / 2 - indicator.visible = true - indicator.initialSetupComplete = true - indicator.animationEnabled = true + indicator.animationEnabled = false; + indicator.width = indicatorWidth; + indicator.x = tabCenterX - indicatorWidth / 2; + indicator.visible = true; + indicator.initialSetupComplete = true; + indicator.animationEnabled = true; } else { - indicator.width = indicatorWidth - indicator.x = tabCenterX - indicatorWidth / 2 - indicator.visible = true + indicator.width = indicatorWidth; + indicator.x = tabCenterX - indicatorWidth / 2; + indicator.visible = true; } } onCurrentIndexChanged: { - Qt.callLater(updateIndicator) + Qt.callLater(updateIndicator); } onWidthChanged: Qt.callLater(updateIndicator) }