diff options
| author | AlexanderCurl <alexc@alexc.hu> | 2026-04-18 17:46:06 +0100 |
|---|---|---|
| committer | AlexanderCurl <alexc@alexc.hu> | 2026-04-18 17:46:06 +0100 |
| commit | 70ca3fef77ee8bdc6e3ac28589a6fa08c024cc69 (patch) | |
| tree | ab1123d4067c1b086dd6faa7ee4ea643236b565a /raveos-hyprland-theme/theme-data/DankMaterialShell/quickshell/Modules/Dock/DockAppButton.qml | |
| parent | 5d94c0a7d44a2255b81815a52a7056a94a39842d (diff) | |
| download | RaveOS-PKGBUILD-70ca3fef77ee8bdc6e3ac28589a6fa08c024cc69.tar.gz RaveOS-PKGBUILD-70ca3fef77ee8bdc6e3ac28589a6fa08c024cc69.zip | |
Replaced file structures for themes in the correct format
Diffstat (limited to 'raveos-hyprland-theme/theme-data/DankMaterialShell/quickshell/Modules/Dock/DockAppButton.qml')
| -rw-r--r-- | raveos-hyprland-theme/theme-data/DankMaterialShell/quickshell/Modules/Dock/DockAppButton.qml | 670 |
1 files changed, 670 insertions, 0 deletions
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/quickshell/Modules/Dock/DockAppButton.qml b/raveos-hyprland-theme/theme-data/DankMaterialShell/quickshell/Modules/Dock/DockAppButton.qml new file mode 100644 index 0000000..1d9fb75 --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/quickshell/Modules/Dock/DockAppButton.qml @@ -0,0 +1,670 @@ +import QtQuick +import QtQuick.Effects +import Quickshell +import Quickshell.Hyprland +import Quickshell.Wayland +import Quickshell.Widgets +import qs.Common +import qs.Services +import qs.Widgets + +Item { + id: root + + clip: false + property var appData + property var contextMenu: null + property var dockApps: null + property int index: -1 + property var parentDockScreen: null + property bool longPressing: false + property bool dragging: false + property point dragStartPos: Qt.point(0, 0) + property real dragAxisOffset: 0 + property int targetIndex: -1 + property int originalIndex: -1 + property bool isVertical: SettingsData.dockPosition === SettingsData.Position.Left || SettingsData.dockPosition === SettingsData.Position.Right + property bool showWindowTitle: false + property string windowTitle: "" + property bool isHovered: mouseArea.containsMouse && !dragging + property bool showTooltip: mouseArea.containsMouse && !dragging + property var cachedDesktopEntry: null + property real actualIconSize: 40 + property bool shouldShowIndicator: { + if (!appData) + return false; + if (appData.type === "window") + return true; + if (appData.type === "grouped") + return appData.windowCount > 0; + return appData.isRunning; + } + readonly property string coreIconColorOverride: SettingsData.dockLauncherLogoColorOverride + readonly property bool coreIconHasCustomColor: coreIconColorOverride !== "" && coreIconColorOverride !== "primary" && coreIconColorOverride !== "surface" + readonly property color effectiveCoreIconColor: { + if (coreIconColorOverride === "primary") + return Theme.primary; + if (coreIconColorOverride === "surface") + return Theme.surfaceText; + if (coreIconColorOverride !== "") + return coreIconColorOverride; + return Theme.surfaceText; + } + readonly property real effectiveCoreIconBrightness: coreIconHasCustomColor ? SettingsData.dockLauncherLogoBrightness : 0.0 + readonly property real effectiveCoreIconContrast: coreIconHasCustomColor ? SettingsData.dockLauncherLogoContrast : 0.0 + + function updateDesktopEntry() { + if (!appData || appData.appId === "__SEPARATOR__") { + cachedDesktopEntry = null; + return; + } + if (appData.isCoreApp) { + cachedDesktopEntry = null; + return; + } + const moddedId = Paths.moddedAppId(appData.appId); + cachedDesktopEntry = DesktopEntries.heuristicLookup(moddedId); + } + + Component.onCompleted: updateDesktopEntry() + + onAppDataChanged: updateDesktopEntry() + + Connections { + target: DesktopEntries + function onApplicationsChanged() { + updateDesktopEntry(); + } + } + + Connections { + target: SettingsData + function onAppIdSubstitutionsChanged() { + updateDesktopEntry(); + } + } + property bool isWindowFocused: { + if (!appData) { + return false; + } + + if (appData.type === "window") { + const toplevel = getToplevelObject(); + if (!toplevel) { + return false; + } + return toplevel.activated; + } else if (appData.type === "grouped") { + // For grouped apps, check if any window is focused + const allToplevels = ToplevelManager.toplevels.values; + for (let i = 0; i < allToplevels.length; i++) { + const toplevel = allToplevels[i]; + if (toplevel.appId === appData.appId && toplevel.activated) { + return true; + } + } + } + + return false; + } + property string tooltipText: { + if (!appData || !appData.appId) { + return ""; + } + + let appName; + if (appData.isCoreApp && appData.coreAppData) { + appName = appData.coreAppData.name || appData.appId; + } else { + appName = Paths.getAppName(appData.appId, cachedDesktopEntry); + } + + if ((appData.type === "window" && showWindowTitle) || (appData.type === "grouped" && appData.windowTitle)) { + const title = appData.type === "window" ? windowTitle : appData.windowTitle; + return appName + (title ? " • " + title : ""); + } + + return appName; + } + + function getToplevelObject() { + return appData?.toplevel || null; + } + + function getGroupedToplevels() { + return appData?.allWindows?.map(w => w.toplevel).filter(t => t !== null) || []; + } + + function getHyprToplevelForWayland(waylandToplevel) { + if (!waylandToplevel || !CompositorService.isHyprland || !Hyprland.toplevels) + return null; + const hyprToplevels = Array.from(Hyprland.toplevels.values); + for (let i = 0; i < hyprToplevels.length; i++) { + if (hyprToplevels[i].wayland === waylandToplevel) + return hyprToplevels[i]; + } + return null; + } + + function getSpecialWorkspaceName(waylandToplevel) { + const hyprToplevel = getHyprToplevelForWayland(waylandToplevel); + if (!hyprToplevel) + return ""; + const wsName = String(hyprToplevel.lastIpcObject?.workspace?.name || hyprToplevel.workspace?.name || ""); + if (!wsName.startsWith("special:")) + return ""; + return wsName.slice("special:".length); + } + + function restoreSpecialWorkspaceWindow(waylandToplevel) { + if (!SettingsData.dockRestoreSpecialWorkspaceOnClick || !CompositorService.isHyprland || !waylandToplevel) + return false; + + const specialName = getSpecialWorkspaceName(waylandToplevel); + if (!specialName) + return false; + + Hyprland.dispatch("togglespecialworkspace " + specialName); + Qt.callLater(() => waylandToplevel.activate()); + return true; + } + onIsHoveredChanged: { + if (mouseArea.pressed || dragging) + return; + if (isHovered) { + exitAnimation.stop(); + if (!bounceAnimation.running) { + bounceAnimation.restart(); + } + } else { + bounceAnimation.stop(); + exitAnimation.restart(); + } + } + + readonly property bool animateX: SettingsData.dockPosition === SettingsData.Position.Left || SettingsData.dockPosition === SettingsData.Position.Right + readonly property real animationDistance: actualIconSize + readonly property real animationDirection: { + if (SettingsData.dockPosition === SettingsData.Position.Bottom) + return -1; + if (SettingsData.dockPosition === SettingsData.Position.Top) + return 1; + if (SettingsData.dockPosition === SettingsData.Position.Right) + return -1; + if (SettingsData.dockPosition === SettingsData.Position.Left) + return 1; + return -1; + } + + SequentialAnimation { + id: bounceAnimation + + running: false + + NumberAnimation { + target: root + property: "hoverAnimOffset" + to: animationDirection * animationDistance * 0.25 + duration: Anims.durShort + easing.type: Easing.BezierSpline + easing.bezierCurve: Anims.emphasizedAccel + } + + NumberAnimation { + target: root + property: "hoverAnimOffset" + to: animationDirection * animationDistance * 0.2 + duration: Anims.durShort + easing.type: Easing.BezierSpline + easing.bezierCurve: Anims.emphasizedDecel + } + } + + NumberAnimation { + id: exitAnimation + + running: false + target: root + property: "hoverAnimOffset" + to: 0 + duration: Anims.durShort + easing.type: Easing.BezierSpline + easing.bezierCurve: Anims.emphasizedDecel + } + + Timer { + id: longPressTimer + + interval: 500 + repeat: false + onTriggered: { + if (appData && appData.isPinned) { + longPressing = true; + } + } + } + + MouseArea { + id: mouseArea + + anchors.fill: parent + hoverEnabled: true + enabled: true + preventStealing: dragging || longPressing + cursorShape: longPressing ? Qt.DragMoveCursor : Qt.PointingHandCursor + acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton + onPressed: mouse => { + if (mouse.button === Qt.LeftButton && appData && appData.isPinned) { + dragStartPos = Qt.point(mouse.x, mouse.y); + longPressTimer.start(); + } + } + onReleased: mouse => { + longPressTimer.stop(); + + const wasDragging = dragging; + const didReorder = wasDragging && targetIndex >= 0 && targetIndex !== originalIndex && dockApps; + + if (didReorder) + dockApps.movePinnedApp(originalIndex, targetIndex); + + longPressing = false; + dragging = false; + dragAxisOffset = 0; + targetIndex = -1; + originalIndex = -1; + + if (dockApps) { + dockApps.draggedIndex = -1; + dockApps.dropTargetIndex = -1; + } + + if (wasDragging || mouse.button !== Qt.LeftButton) + return; + + handleLeftClick(); + } + + function handleLeftClick() { + if (!appData) + return; + + switch (appData.type) { + case "pinned": + if (!appData.appId) + return; + if (appData.isCoreApp && appData.coreAppData) { + AppSearchService.executeCoreApp(appData.coreAppData); + return; + } + const pinnedEntry = cachedDesktopEntry; + if (pinnedEntry) { + AppUsageHistoryData.addAppUsage({ + "id": appData.appId, + "name": pinnedEntry.name || appData.appId, + "icon": pinnedEntry.icon ? String(pinnedEntry.icon) : "", + "exec": pinnedEntry.exec || "", + "comment": pinnedEntry.comment || "" + }); + } + SessionService.launchDesktopEntry(pinnedEntry); + break; + case "window": + const windowToplevel = getToplevelObject(); + if (windowToplevel) { + if (restoreSpecialWorkspaceWindow(windowToplevel)) + return; + windowToplevel.activate(); + } + break; + case "grouped": + if (appData.windowCount === 0) { + if (!appData.appId) + return; + if (appData.isCoreApp && appData.coreAppData) { + AppSearchService.executeCoreApp(appData.coreAppData); + return; + } + const groupedEntry = cachedDesktopEntry; + if (groupedEntry) { + AppUsageHistoryData.addAppUsage({ + "id": appData.appId, + "name": groupedEntry.name || appData.appId, + "icon": groupedEntry.icon ? String(groupedEntry.icon) : "", + "exec": groupedEntry.exec || "", + "comment": groupedEntry.comment || "" + }); + } + SessionService.launchDesktopEntry(groupedEntry); + } else if (appData.windowCount === 1) { + const groupedToplevel = getToplevelObject(); + if (groupedToplevel) { + if (restoreSpecialWorkspaceWindow(groupedToplevel)) + return; + groupedToplevel.activate(); + } + } else if (contextMenu) { + const shouldHidePin = appData.appId === "org.quickshell" || appData.appId === "com.danklinux.dms"; + contextMenu.showForButton(root, appData, root.height + 25, shouldHidePin, cachedDesktopEntry, parentDockScreen, dockApps); + } + break; + } + } + onPositionChanged: mouse => { + if (longPressing && !dragging) { + const distance = Math.sqrt(Math.pow(mouse.x - dragStartPos.x, 2) + Math.pow(mouse.y - dragStartPos.y, 2)); + if (distance > 5) { + dragging = true; + targetIndex = index; + originalIndex = index; + if (dockApps) { + dockApps.draggedIndex = index; + dockApps.dropTargetIndex = index; + } + } + } + + if (!dragging || !dockApps) + return; + + const axisOffset = isVertical ? (mouse.y - dragStartPos.y) : (mouse.x - dragStartPos.x); + dragAxisOffset = axisOffset; + + const spacing = Math.min(8, Math.max(4, actualIconSize * 0.08)); + const itemSize = actualIconSize * 1.2 + spacing; + const slotOffset = Math.round(axisOffset / itemSize); + const newTargetIndex = Math.max(0, Math.min(dockApps.pinnedAppCount - 1, originalIndex + slotOffset)); + + if (newTargetIndex !== targetIndex) { + targetIndex = newTargetIndex; + dockApps.dropTargetIndex = newTargetIndex; + } + } + onClicked: mouse => { + if (!appData) + return; + + if (mouse.button === Qt.MiddleButton) { + switch (appData.type) { + case "window": + appData.toplevel?.close(); + break; + case "grouped": + if (contextMenu) { + const shouldHidePin = appData.appId === "org.quickshell" || appData.appId === "com.danklinux.dms"; + contextMenu.showForButton(root, appData, root.height, shouldHidePin, cachedDesktopEntry, parentDockScreen, dockApps); + } + break; + default: + if (!appData.appId) + return; + const desktopEntry = cachedDesktopEntry; + if (desktopEntry) { + AppUsageHistoryData.addAppUsage({ + "id": appData.appId, + "name": desktopEntry.name || appData.appId, + "icon": desktopEntry.icon ? String(desktopEntry.icon) : "", + "exec": desktopEntry.exec || "", + "comment": desktopEntry.comment || "" + }); + } + SessionService.launchDesktopEntry(desktopEntry); + break; + } + } else if (mouse.button === Qt.RightButton) { + if (!contextMenu) + return; + const shouldHidePin = appData.appId === "org.quickshell" || appData.appId === "com.danklinux.dms"; + contextMenu.showForButton(root, appData, root.height, shouldHidePin, cachedDesktopEntry, parentDockScreen, dockApps); + } + } + } + + property real hoverAnimOffset: 0 + + Item { + id: visualContent + anchors.fill: parent + + transform: Translate { + id: iconTransform + x: { + if (dragging && !isVertical) + return dragAxisOffset; + if (!dragging && isVertical) + return hoverAnimOffset; + return 0; + } + y: { + if (dragging && isVertical) + return dragAxisOffset; + if (!dragging && !isVertical) + return hoverAnimOffset; + return 0; + } + } + + Rectangle { + anchors.fill: parent + radius: Theme.cornerRadius + color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.3) + border.width: 2 + border.color: Theme.primary + visible: dragging + z: -1 + } + + AppIconRenderer { + id: coreIcon + + anchors.centerIn: parent + iconSize: actualIconSize + iconValue: appData && appData.isCoreApp && appData.coreAppData ? (appData.coreAppData.icon || "") : "" + colorOverride: effectiveCoreIconColor + brightnessOverride: effectiveCoreIconBrightness + contrastOverride: effectiveCoreIconContrast + fallbackText: "?" + visible: iconValue !== "" + } + + IconImage { + id: iconImg + + anchors.centerIn: parent + implicitSize: appData && (appData.appId === "org.quickshell" || appData.appId === "com.danklinux.dms") ? actualIconSize * 0.85 : actualIconSize + source: { + if (!appData || appData.appId === "__SEPARATOR__") { + return ""; + } + if (appData.isCoreApp && appData.coreAppData) { + return ""; + } + return Paths.getAppIcon(appData.appId, cachedDesktopEntry); + } + mipmap: true + smooth: true + asynchronous: true + visible: status === Image.Ready && !coreIcon.visible + layer.enabled: appData && (appData.appId === "org.quickshell" || appData.appId === "com.danklinux.dms") + layer.smooth: true + layer.mipmap: true + layer.effect: MultiEffect { + saturation: 0 + colorization: 1 + colorizationColor: Theme.primary + } + } + + Rectangle { + width: actualIconSize + height: actualIconSize + anchors.centerIn: parent + visible: !coreIcon.visible && iconImg.status !== Image.Ready && appData && appData.appId && !Paths.isSteamApp(appData.appId) + color: Theme.surfaceLight + radius: Theme.cornerRadius + border.width: 1 + border.color: Theme.primarySelected + + Text { + anchors.centerIn: parent + text: { + if (!appData || !appData.appId) { + return "?"; + } + + let appName; + if (appData.isCoreApp && appData.coreAppData) { + appName = appData.coreAppData.name || appData.appId; + } else { + appName = Paths.getAppName(appData.appId, cachedDesktopEntry); + } + return appName.charAt(0).toUpperCase(); + } + font.pixelSize: Math.max(8, parent.width * 0.35) + color: Theme.primary + font.weight: Font.Bold + } + } + + DankIcon { + anchors.centerIn: parent + size: actualIconSize + name: "sports_esports" + color: Theme.surfaceText + visible: !coreIcon.visible && iconImg.status !== Image.Ready && appData && appData.appId && Paths.isSteamApp(appData.appId) + } + + Loader { + anchors.horizontalCenter: SettingsData.dockPosition === SettingsData.Position.Left || SettingsData.dockPosition === SettingsData.Position.Right ? undefined : parent.horizontalCenter + anchors.verticalCenter: SettingsData.dockPosition === SettingsData.Position.Left || SettingsData.dockPosition === SettingsData.Position.Right ? parent.verticalCenter : undefined + anchors.bottom: SettingsData.dockPosition === SettingsData.Position.Bottom ? parent.bottom : undefined + anchors.top: SettingsData.dockPosition === SettingsData.Position.Top ? parent.top : undefined + anchors.left: SettingsData.dockPosition === SettingsData.Position.Left ? parent.left : undefined + anchors.right: SettingsData.dockPosition === SettingsData.Position.Right ? parent.right : undefined + anchors.bottomMargin: SettingsData.dockPosition === SettingsData.Position.Bottom ? -(SettingsData.dockSpacing / 2 + 1.4) : 0 + anchors.topMargin: SettingsData.dockPosition === SettingsData.Position.Top ? -(SettingsData.dockSpacing / 2 + 1.4) : 0 + anchors.leftMargin: SettingsData.dockPosition === SettingsData.Position.Left ? -(SettingsData.dockSpacing / 2 + 1.4) : 0 + anchors.rightMargin: SettingsData.dockPosition === SettingsData.Position.Right ? -(SettingsData.dockSpacing / 2 + 1.4) : 0 + + sourceComponent: SettingsData.dockPosition === SettingsData.Position.Left || SettingsData.dockPosition === SettingsData.Position.Right ? columnIndicator : rowIndicator + + visible: root.shouldShowIndicator + } + } + + Component { + id: rowIndicator + + Row { + spacing: 2 + + Repeater { + model: { + if (!appData) + return 0; + if (appData.type === "grouped") { + return Math.min(appData.windowCount, 4); + } else if (appData.type === "window" || appData.isRunning) { + return 1; + } + return 0; + } + + Rectangle { + width: { + if (SettingsData.dockIndicatorStyle === "circle") { + return Math.max(4, actualIconSize * 0.1); + } + return appData && appData.type === "grouped" && appData.windowCount > 1 ? Math.max(3, actualIconSize * 0.1) : Math.max(6, actualIconSize * 0.2); + } + height: { + if (SettingsData.dockIndicatorStyle === "circle") { + return Math.max(4, actualIconSize * 0.1); + } + return Math.max(2, actualIconSize * 0.05); + } + radius: SettingsData.dockIndicatorStyle === "circle" ? width / 2 : Theme.cornerRadius + color: { + if (!appData) { + return "transparent"; + } + + if (appData.type !== "grouped" || appData.windowCount === 1) { + if (isWindowFocused) { + return Theme.primary; + } + return Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6); + } + + if (appData.type === "grouped" && appData.windowCount > 1) { + const groupToplevels = getGroupedToplevels(); + if (index < groupToplevels.length && groupToplevels[index].activated) { + return Theme.primary; + } + } + + return Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6); + } + } + } + } + } + + Component { + id: columnIndicator + + Column { + spacing: 2 + + Repeater { + model: { + if (!appData) + return 0; + if (appData.type === "grouped") { + return Math.min(appData.windowCount, 4); + } else if (appData.type === "window" || appData.isRunning) { + return 1; + } + return 0; + } + + Rectangle { + width: { + if (SettingsData.dockIndicatorStyle === "circle") { + return Math.max(4, actualIconSize * 0.1); + } + return Math.max(2, actualIconSize * 0.05); + } + height: { + if (SettingsData.dockIndicatorStyle === "circle") { + return Math.max(4, actualIconSize * 0.1); + } + return appData && appData.type === "grouped" && appData.windowCount > 1 ? Math.max(3, actualIconSize * 0.1) : Math.max(6, actualIconSize * 0.2); + } + radius: SettingsData.dockIndicatorStyle === "circle" ? width / 2 : Theme.cornerRadius + color: { + if (!appData) { + return "transparent"; + } + + if (appData.type !== "grouped" || appData.windowCount === 1) { + if (isWindowFocused) { + return Theme.primary; + } + return Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6); + } + + if (appData.type === "grouped" && appData.windowCount > 1) { + const groupToplevels = getGroupedToplevels(); + if (index < groupToplevels.length && groupToplevels[index].activated) { + return Theme.primary; + } + } + + return Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6); + } + } + } + } + } +} |