mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-24 13:32:50 -05:00
feat: Implement Dank Launcher button on the Dock
- Configurable with custom icons/logos - Respects light/dark theme - Drag & Drop in place
This commit is contained in:
@@ -83,6 +83,7 @@ Singleton {
|
|||||||
property string nightModeLocationProvider: ""
|
property string nightModeLocationProvider: ""
|
||||||
|
|
||||||
property var pinnedApps: []
|
property var pinnedApps: []
|
||||||
|
property int dockLauncherPosition: 0
|
||||||
property var hiddenTrayIds: []
|
property var hiddenTrayIds: []
|
||||||
property var recentColors: []
|
property var recentColors: []
|
||||||
property bool showThirdPartyPlugins: false
|
property bool showThirdPartyPlugins: false
|
||||||
|
|||||||
@@ -431,6 +431,13 @@ Singleton {
|
|||||||
property real dockBorderOpacity: 1.0
|
property real dockBorderOpacity: 1.0
|
||||||
property int dockBorderThickness: 1
|
property int dockBorderThickness: 1
|
||||||
property bool dockIsolateDisplays: false
|
property bool dockIsolateDisplays: false
|
||||||
|
property bool dockLauncherEnabled: false
|
||||||
|
property string dockLauncherLogoMode: "apps"
|
||||||
|
property string dockLauncherLogoCustomPath: ""
|
||||||
|
property string dockLauncherLogoColorOverride: ""
|
||||||
|
property int dockLauncherLogoSizeOffset: 0
|
||||||
|
property real dockLauncherLogoBrightness: 0.5
|
||||||
|
property real dockLauncherLogoContrast: 1
|
||||||
|
|
||||||
property bool notificationOverlayEnabled: false
|
property bool notificationOverlayEnabled: false
|
||||||
property int overviewRows: 2
|
property int overviewRows: 2
|
||||||
|
|||||||
@@ -255,6 +255,13 @@ var SPEC = {
|
|||||||
dockBorderOpacity: { def: 1.0, coerce: percentToUnit },
|
dockBorderOpacity: { def: 1.0, coerce: percentToUnit },
|
||||||
dockBorderThickness: { def: 1 },
|
dockBorderThickness: { def: 1 },
|
||||||
dockIsolateDisplays: { def: false },
|
dockIsolateDisplays: { def: false },
|
||||||
|
dockLauncherEnabled: { def: false },
|
||||||
|
dockLauncherLogoMode: { def: "apps" },
|
||||||
|
dockLauncherLogoCustomPath: { def: "" },
|
||||||
|
dockLauncherLogoColorOverride: { def: "" },
|
||||||
|
dockLauncherLogoSizeOffset: { def: 0 },
|
||||||
|
dockLauncherLogoBrightness: { def: 0.5, coerce: percentToUnit },
|
||||||
|
dockLauncherLogoContrast: { def: 1, coerce: percentToUnit },
|
||||||
|
|
||||||
notificationOverlayEnabled: { def: false },
|
notificationOverlayEnabled: { def: false },
|
||||||
overviewRows: { def: 2, persist: false },
|
overviewRows: { def: 2, persist: false },
|
||||||
|
|||||||
@@ -208,7 +208,7 @@ Item {
|
|||||||
targetIndex = -1;
|
targetIndex = -1;
|
||||||
originalIndex = -1;
|
originalIndex = -1;
|
||||||
|
|
||||||
if (dockApps && !didReorder) {
|
if (dockApps) {
|
||||||
dockApps.draggedIndex = -1;
|
dockApps.draggedIndex = -1;
|
||||||
dockApps.dropTargetIndex = -1;
|
dockApps.dropTargetIndex = -1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,18 +22,34 @@ Item {
|
|||||||
implicitWidth: isVertical ? appLayout.height : appLayout.width
|
implicitWidth: isVertical ? appLayout.height : appLayout.width
|
||||||
implicitHeight: isVertical ? appLayout.width : appLayout.height
|
implicitHeight: isVertical ? appLayout.width : appLayout.height
|
||||||
|
|
||||||
function movePinnedApp(fromIndex, toIndex) {
|
function dockIndexToPinnedIndex(dockIndex) {
|
||||||
if (fromIndex === toIndex) {
|
if (!SettingsData.dockLauncherEnabled) {
|
||||||
|
return dockIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
const launcherPos = SessionData.dockLauncherPosition;
|
||||||
|
if (dockIndex < launcherPos) {
|
||||||
|
return dockIndex;
|
||||||
|
} else {
|
||||||
|
return dockIndex - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function movePinnedApp(fromDockIndex, toDockIndex) {
|
||||||
|
const fromPinnedIndex = dockIndexToPinnedIndex(fromDockIndex);
|
||||||
|
const toPinnedIndex = dockIndexToPinnedIndex(toDockIndex);
|
||||||
|
|
||||||
|
if (fromPinnedIndex === toPinnedIndex) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentPinned = [...(SessionData.pinnedApps || [])];
|
const currentPinned = [...(SessionData.pinnedApps || [])];
|
||||||
if (fromIndex < 0 || fromIndex >= currentPinned.length || toIndex < 0 || toIndex >= currentPinned.length) {
|
if (fromPinnedIndex < 0 || fromPinnedIndex >= currentPinned.length || toPinnedIndex < 0 || toPinnedIndex >= currentPinned.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const movedApp = currentPinned.splice(fromIndex, 1)[0];
|
const movedApp = currentPinned.splice(fromPinnedIndex, 1)[0];
|
||||||
currentPinned.splice(toIndex, 0, movedApp);
|
currentPinned.splice(toPinnedIndex, 0, movedApp);
|
||||||
|
|
||||||
SessionData.setPinnedApps(currentPinned);
|
SessionData.setPinnedApps(currentPinned);
|
||||||
}
|
}
|
||||||
@@ -75,6 +91,23 @@ Item {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function insertLauncher(targetArray) {
|
||||||
|
if (!SettingsData.dockLauncherEnabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const launcherItem = {
|
||||||
|
uniqueKey: "launcher_button",
|
||||||
|
type: "launcher",
|
||||||
|
appId: "__LAUNCHER__",
|
||||||
|
toplevel: null,
|
||||||
|
isPinned: true,
|
||||||
|
isRunning: false
|
||||||
|
};
|
||||||
|
|
||||||
|
const pos = Math.max(0, Math.min(SessionData.dockLauncherPosition, targetArray.length));
|
||||||
|
targetArray.splice(pos, 0, launcherItem);
|
||||||
|
}
|
||||||
|
|
||||||
function updateModel() {
|
function updateModel() {
|
||||||
const items = [];
|
const items = [];
|
||||||
const pinnedApps = [...(SessionData.pinnedApps || [])];
|
const pinnedApps = [...(SessionData.pinnedApps || [])];
|
||||||
@@ -136,6 +169,8 @@ Item {
|
|||||||
|
|
||||||
pinnedGroups.forEach(item => items.push(item));
|
pinnedGroups.forEach(item => items.push(item));
|
||||||
|
|
||||||
|
insertLauncher(items);
|
||||||
|
|
||||||
if (pinnedGroups.length > 0 && unpinnedGroups.length > 0) {
|
if (pinnedGroups.length > 0 && unpinnedGroups.length > 0) {
|
||||||
items.push({
|
items.push({
|
||||||
uniqueKey: "separator_grouped",
|
uniqueKey: "separator_grouped",
|
||||||
@@ -148,7 +183,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
unpinnedGroups.forEach(item => items.push(item));
|
unpinnedGroups.forEach(item => items.push(item));
|
||||||
root.pinnedAppCount = pinnedGroups.length;
|
root.pinnedAppCount = pinnedGroups.length + (SettingsData.dockLauncherEnabled ? 1 : 0);
|
||||||
} else {
|
} else {
|
||||||
pinnedApps.forEach(rawAppId => {
|
pinnedApps.forEach(rawAppId => {
|
||||||
const appId = Paths.moddedAppId(rawAppId);
|
const appId = Paths.moddedAppId(rawAppId);
|
||||||
@@ -162,7 +197,9 @@ Item {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
root.pinnedAppCount = pinnedApps.length;
|
root.pinnedAppCount = pinnedApps.length + (SettingsData.dockLauncherEnabled ? 1 : 0);
|
||||||
|
|
||||||
|
insertLauncher(items);
|
||||||
|
|
||||||
if (pinnedApps.length > 0 && sortedToplevels.length > 0) {
|
if (pinnedApps.length > 0 && sortedToplevels.length > 0) {
|
||||||
items.push({
|
items.push({
|
||||||
@@ -203,10 +240,10 @@ Item {
|
|||||||
|
|
||||||
delegate: Item {
|
delegate: Item {
|
||||||
id: delegateItem
|
id: delegateItem
|
||||||
property alias dockButton: button
|
property var dockButton: itemData.type === "launcher" ? launcherButton : button
|
||||||
property var itemData: modelData
|
property var itemData: modelData
|
||||||
clip: false
|
clip: false
|
||||||
z: button.dragging ? 100 : 0
|
z: (itemData.type === "launcher" ? launcherButton.dragging : button.dragging) ? 100 : 0
|
||||||
|
|
||||||
width: itemData.type === "separator" ? (root.isVertical ? root.iconSize : 8) : (root.isVertical ? root.iconSize : root.iconSize * 1.2)
|
width: itemData.type === "separator" ? (root.isVertical ? root.iconSize : 8) : (root.isVertical ? root.iconSize : root.iconSize * 1.2)
|
||||||
height: itemData.type === "separator" ? (root.isVertical ? 8 : root.iconSize) : (root.isVertical ? root.iconSize * 1.2 : root.iconSize)
|
height: itemData.type === "separator" ? (root.isVertical ? 8 : root.iconSize) : (root.isVertical ? root.iconSize * 1.2 : root.iconSize)
|
||||||
@@ -261,9 +298,22 @@ Item {
|
|||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DockLauncherButton {
|
||||||
|
id: launcherButton
|
||||||
|
visible: itemData.type === "launcher"
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
width: delegateItem.width
|
||||||
|
height: delegateItem.height
|
||||||
|
actualIconSize: root.iconSize
|
||||||
|
|
||||||
|
dockApps: root
|
||||||
|
index: model.index
|
||||||
|
}
|
||||||
|
|
||||||
DockAppButton {
|
DockAppButton {
|
||||||
id: button
|
id: button
|
||||||
visible: itemData.type !== "separator"
|
visible: itemData.type !== "separator" && itemData.type !== "launcher"
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
|
||||||
width: delegateItem.width
|
width: delegateItem.width
|
||||||
@@ -314,5 +364,27 @@ Item {
|
|||||||
function onDockIsolateDisplaysChanged() {
|
function onDockIsolateDisplaysChanged() {
|
||||||
repeater.updateModel();
|
repeater.updateModel();
|
||||||
}
|
}
|
||||||
|
function onDockLauncherEnabledChanged() {
|
||||||
|
root.suppressShiftAnimation = true;
|
||||||
|
root.draggedIndex = -1;
|
||||||
|
root.dropTargetIndex = -1;
|
||||||
|
repeater.updateModel();
|
||||||
|
Qt.callLater(() => {
|
||||||
|
root.suppressShiftAnimation = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: SessionData
|
||||||
|
function onDockLauncherPositionChanged() {
|
||||||
|
root.suppressShiftAnimation = true;
|
||||||
|
root.draggedIndex = -1;
|
||||||
|
root.dropTargetIndex = -1;
|
||||||
|
repeater.updateModel();
|
||||||
|
Qt.callLater(() => {
|
||||||
|
root.suppressShiftAnimation = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
294
quickshell/Modules/Dock/DockLauncherButton.qml
Normal file
294
quickshell/Modules/Dock/DockLauncherButton.qml
Normal file
@@ -0,0 +1,294 @@
|
|||||||
|
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.dockLauncherPosition = 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -164,6 +164,265 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SettingsCard {
|
||||||
|
width: parent.width
|
||||||
|
iconName: "apps"
|
||||||
|
title: I18n.tr("Launcher Button")
|
||||||
|
settingKey: "dockLauncher"
|
||||||
|
|
||||||
|
SettingsToggleRow {
|
||||||
|
settingKey: "dockLauncherEnabled"
|
||||||
|
tags: ["dock", "launcher", "button", "apps"]
|
||||||
|
text: I18n.tr("Show Launcher Button")
|
||||||
|
description: I18n.tr("Add a draggable launcher button to the dock")
|
||||||
|
checked: SettingsData.dockLauncherEnabled
|
||||||
|
onToggled: checked => SettingsData.set("dockLauncherEnabled", checked)
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingL
|
||||||
|
visible: SettingsData.dockLauncherEnabled
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
width: parent.width
|
||||||
|
text: I18n.tr("Long press and drag the launcher button to reposition it in the dock")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Launcher Icon")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: logoModeGroup.implicitHeight
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
DankButtonGroup {
|
||||||
|
id: logoModeGroup
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
buttonPadding: parent.width < 480 ? Theme.spacingS : Theme.spacingL
|
||||||
|
minButtonWidth: parent.width < 480 ? 44 : 64
|
||||||
|
textSize: parent.width < 480 ? Theme.fontSizeSmall : Theme.fontSizeMedium
|
||||||
|
model: {
|
||||||
|
const modes = [I18n.tr("Apps Icon"), I18n.tr("OS Logo"), I18n.tr("Dank")];
|
||||||
|
if (CompositorService.isNiri) {
|
||||||
|
modes.push("niri");
|
||||||
|
} else if (CompositorService.isHyprland) {
|
||||||
|
modes.push("Hyprland");
|
||||||
|
} else if (CompositorService.isDwl) {
|
||||||
|
modes.push("mango");
|
||||||
|
} else if (CompositorService.isSway) {
|
||||||
|
modes.push("Sway");
|
||||||
|
} else if (CompositorService.isScroll) {
|
||||||
|
modes.push("Scroll");
|
||||||
|
} else {
|
||||||
|
modes.push(I18n.tr("Compositor"));
|
||||||
|
}
|
||||||
|
modes.push(I18n.tr("Custom"));
|
||||||
|
return modes;
|
||||||
|
}
|
||||||
|
currentIndex: {
|
||||||
|
if (SettingsData.dockLauncherLogoMode === "apps")
|
||||||
|
return 0;
|
||||||
|
if (SettingsData.dockLauncherLogoMode === "os")
|
||||||
|
return 1;
|
||||||
|
if (SettingsData.dockLauncherLogoMode === "dank")
|
||||||
|
return 2;
|
||||||
|
if (SettingsData.dockLauncherLogoMode === "compositor")
|
||||||
|
return 3;
|
||||||
|
if (SettingsData.dockLauncherLogoMode === "custom")
|
||||||
|
return 4;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
onSelectionChanged: (index, selected) => {
|
||||||
|
if (!selected)
|
||||||
|
return;
|
||||||
|
switch (index) {
|
||||||
|
case 0:
|
||||||
|
SettingsData.set("dockLauncherLogoMode", "apps");
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
SettingsData.set("dockLauncherLogoMode", "os");
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
SettingsData.set("dockLauncherLogoMode", "dank");
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
SettingsData.set("dockLauncherLogoMode", "compositor");
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
SettingsData.set("dockLauncherLogoMode", "custom");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingL
|
||||||
|
visible: SettingsData.dockLauncherLogoMode !== "apps"
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Color Override")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: colorOverrideRow.implicitHeight
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: colorOverrideRow
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
DankButtonGroup {
|
||||||
|
id: colorModeGroup
|
||||||
|
buttonPadding: parent.parent.width < 480 ? Theme.spacingS : Theme.spacingL
|
||||||
|
minButtonWidth: parent.parent.width < 480 ? 44 : 64
|
||||||
|
textSize: parent.parent.width < 480 ? Theme.fontSizeSmall : Theme.fontSizeMedium
|
||||||
|
model: [I18n.tr("Default"), I18n.tr("Primary"), I18n.tr("Surface"), I18n.tr("Custom")]
|
||||||
|
currentIndex: {
|
||||||
|
const override = SettingsData.dockLauncherLogoColorOverride;
|
||||||
|
if (override === "")
|
||||||
|
return 0;
|
||||||
|
if (override === "primary")
|
||||||
|
return 1;
|
||||||
|
if (override === "surface")
|
||||||
|
return 2;
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
onSelectionChanged: (index, selected) => {
|
||||||
|
if (!selected)
|
||||||
|
return;
|
||||||
|
switch (index) {
|
||||||
|
case 0:
|
||||||
|
SettingsData.set("dockLauncherLogoColorOverride", "");
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
SettingsData.set("dockLauncherLogoColorOverride", "primary");
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
SettingsData.set("dockLauncherLogoColorOverride", "surface");
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
const currentOverride = SettingsData.dockLauncherLogoColorOverride;
|
||||||
|
const isPreset = currentOverride === "" || currentOverride === "primary" || currentOverride === "surface";
|
||||||
|
if (isPreset) {
|
||||||
|
SettingsData.set("dockLauncherLogoColorOverride", "#ffffff");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: colorPickerCircle
|
||||||
|
visible: {
|
||||||
|
const override = SettingsData.dockLauncherLogoColorOverride;
|
||||||
|
return override !== "" && override !== "primary" && override !== "surface";
|
||||||
|
}
|
||||||
|
width: 36
|
||||||
|
height: 36
|
||||||
|
radius: 18
|
||||||
|
color: {
|
||||||
|
const override = SettingsData.dockLauncherLogoColorOverride;
|
||||||
|
if (override !== "" && override !== "primary" && override !== "surface")
|
||||||
|
return override;
|
||||||
|
return "#ffffff";
|
||||||
|
}
|
||||||
|
border.color: Theme.outline
|
||||||
|
border.width: 1
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
if (!PopoutService.colorPickerModal)
|
||||||
|
return;
|
||||||
|
PopoutService.colorPickerModal.selectedColor = SettingsData.dockLauncherLogoColorOverride;
|
||||||
|
PopoutService.colorPickerModal.pickerTitle = I18n.tr("Choose Dock Launcher Logo Color");
|
||||||
|
PopoutService.colorPickerModal.onColorSelectedCallback = function (selectedColor) {
|
||||||
|
SettingsData.set("dockLauncherLogoColorOverride", selectedColor);
|
||||||
|
};
|
||||||
|
PopoutService.colorPickerModal.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsSliderRow {
|
||||||
|
settingKey: "dockLauncherLogoSizeOffset"
|
||||||
|
tags: ["dock", "launcher", "logo", "size", "offset", "scale"]
|
||||||
|
text: I18n.tr("Size Offset")
|
||||||
|
minimum: -12
|
||||||
|
maximum: 12
|
||||||
|
value: SettingsData.dockLauncherLogoSizeOffset
|
||||||
|
defaultValue: 0
|
||||||
|
onSliderValueChanged: newValue => SettingsData.set("dockLauncherLogoSizeOffset", newValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
visible: {
|
||||||
|
const override = SettingsData.dockLauncherLogoColorOverride;
|
||||||
|
return override !== "" && override !== "primary" && override !== "surface";
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsSliderRow {
|
||||||
|
settingKey: "dockLauncherLogoBrightness"
|
||||||
|
tags: ["dock", "launcher", "logo", "brightness", "color"]
|
||||||
|
text: I18n.tr("Brightness")
|
||||||
|
minimum: 0
|
||||||
|
maximum: 100
|
||||||
|
value: Math.round(SettingsData.dockLauncherLogoBrightness * 100)
|
||||||
|
unit: "%"
|
||||||
|
defaultValue: 50
|
||||||
|
onSliderValueChanged: newValue => SettingsData.set("dockLauncherLogoBrightness", newValue / 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsSliderRow {
|
||||||
|
settingKey: "dockLauncherLogoContrast"
|
||||||
|
tags: ["dock", "launcher", "logo", "contrast", "color"]
|
||||||
|
text: I18n.tr("Contrast")
|
||||||
|
minimum: 0
|
||||||
|
maximum: 200
|
||||||
|
value: Math.round(SettingsData.dockLauncherLogoContrast * 100)
|
||||||
|
unit: "%"
|
||||||
|
defaultValue: 100
|
||||||
|
onSliderValueChanged: newValue => SettingsData.set("dockLauncherLogoContrast", newValue / 100)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SettingsCard {
|
SettingsCard {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
iconName: "photo_size_select_large"
|
iconName: "photo_size_select_large"
|
||||||
|
|||||||
Reference in New Issue
Block a user