1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-24 13:32:50 -05:00
Files
DankMaterialShell/quickshell/Modules/Dock/DockLauncherButton.qml
2026-01-22 18:15:00 -05:00

295 lines
11 KiB
QML

import QtQuick
import QtQuick.Effects
import Quickshell.Widgets
import qs.Common
import qs.Services
import qs.Widgets
Item {
id: root
clip: false
property var dockApps: null
property int index: -1
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 isHovered: mouseArea.containsMouse && !dragging
property bool showTooltip: mouseArea.containsMouse && !dragging
property real actualIconSize: 40
readonly property string tooltipText: I18n.tr("Applications")
readonly property color effectiveLogoColor: {
const override = SettingsData.dockLauncherLogoColorOverride;
if (override === "primary")
return Theme.primary;
if (override === "surface")
return Theme.surfaceText;
if (override !== "")
return override;
return Theme.surfaceText;
}
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: {
longPressing = true;
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
enabled: true
preventStealing: true
cursorShape: longPressing ? Qt.DragMoveCursor : Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton
onPressed: mouse => {
if (mouse.button === Qt.LeftButton) {
dragStartPos = Qt.point(mouse.x, mouse.y);
longPressTimer.start();
}
}
onReleased: mouse => {
longPressTimer.stop();
const wasDragging = dragging;
const didReorder = wasDragging && targetIndex >= 0 && dockApps;
if (didReorder) {
SessionData.setDockLauncherPosition(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;
PopoutService.toggleDankLauncherV2();
}
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, originalIndex + slotOffset));
if (newTargetIndex !== targetIndex) {
targetIndex = newTargetIndex;
dockApps.dropTargetIndex = newTargetIndex;
}
}
}
property real hoverAnimOffset: 0
Item {
id: visualContent
anchors.fill: parent
transform: Translate {
x: dragging && !isVertical ? dragAxisOffset : (!dragging && isVertical ? hoverAnimOffset : 0)
y: dragging && isVertical ? dragAxisOffset : (!dragging && !isVertical ? hoverAnimOffset : 0)
}
Item {
anchors.centerIn: parent
width: actualIconSize
height: actualIconSize
DankIcon {
visible: SettingsData.dockLauncherLogoMode === "apps"
anchors.centerIn: parent
name: "apps"
size: actualIconSize - 4
color: Theme.widgetIconColor
}
SystemLogo {
visible: SettingsData.dockLauncherLogoMode === "os"
anchors.centerIn: parent
width: actualIconSize + SettingsData.dockLauncherLogoSizeOffset
height: actualIconSize + SettingsData.dockLauncherLogoSizeOffset
colorOverride: effectiveLogoColor
brightnessOverride: SettingsData.dockLauncherLogoBrightness
contrastOverride: SettingsData.dockLauncherLogoContrast
}
IconImage {
visible: SettingsData.dockLauncherLogoMode === "dank"
anchors.centerIn: parent
width: actualIconSize + SettingsData.dockLauncherLogoSizeOffset
height: actualIconSize + SettingsData.dockLauncherLogoSizeOffset
smooth: true
mipmap: true
asynchronous: true
source: "file://" + Theme.shellDir + "/assets/danklogo.svg"
layer.enabled: effectiveLogoColor !== ""
layer.smooth: true
layer.mipmap: true
layer.effect: MultiEffect {
saturation: 0
colorization: 1
colorizationColor: effectiveLogoColor
}
}
IconImage {
visible: SettingsData.dockLauncherLogoMode === "compositor" && (CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isDwl || CompositorService.isSway || CompositorService.isScroll || CompositorService.isLabwc)
anchors.centerIn: parent
width: actualIconSize + SettingsData.dockLauncherLogoSizeOffset
height: actualIconSize + SettingsData.dockLauncherLogoSizeOffset
smooth: true
asynchronous: true
source: {
if (CompositorService.isNiri) {
return "file://" + Theme.shellDir + "/assets/niri.svg";
} else if (CompositorService.isHyprland) {
return "file://" + Theme.shellDir + "/assets/hyprland.svg";
} else if (CompositorService.isDwl) {
return "file://" + Theme.shellDir + "/assets/mango.png";
} else if (CompositorService.isSway) {
return "file://" + Theme.shellDir + "/assets/sway.svg";
} else if (CompositorService.isScroll) {
return "file://" + Theme.shellDir + "/assets/sway.svg";
} else if (CompositorService.isLabwc) {
return "file://" + Theme.shellDir + "/assets/labwc.png";
}
return "";
}
layer.enabled: effectiveLogoColor !== ""
layer.effect: MultiEffect {
saturation: 0
colorization: 1
colorizationColor: effectiveLogoColor
brightness: {
SettingsData.dockLauncherLogoBrightness;
}
contrast: {
SettingsData.dockLauncherLogoContrast;
}
}
}
IconImage {
visible: SettingsData.dockLauncherLogoMode === "custom" && SettingsData.dockLauncherLogoCustomPath !== ""
anchors.centerIn: parent
width: actualIconSize + SettingsData.dockLauncherLogoSizeOffset
height: actualIconSize + SettingsData.dockLauncherLogoSizeOffset
smooth: true
asynchronous: true
source: SettingsData.dockLauncherLogoCustomPath ? "file://" + SettingsData.dockLauncherLogoCustomPath.replace("file://", "") : ""
layer.enabled: effectiveLogoColor !== ""
layer.effect: MultiEffect {
saturation: 0
colorization: 1
colorizationColor: effectiveLogoColor
brightness: SettingsData.dockLauncherLogoBrightness
contrast: SettingsData.dockLauncherLogoContrast
}
}
}
}
}