1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-31 17:02:51 -05:00

system tray: allow re-ordering tray items

This commit is contained in:
bbedward
2026-01-30 09:17:01 -05:00
parent 67d3aa9da3
commit 803bc1cb7f
3 changed files with 313 additions and 34 deletions

View File

@@ -95,6 +95,7 @@ Singleton {
property var barPinnedApps: []
property int dockLauncherPosition: 0
property var hiddenTrayIds: []
property var trayItemOrder: []
property var recentColors: []
property bool showThirdPartyPlugins: false
property string launchPrefix: ""
@@ -871,6 +872,11 @@ Singleton {
return trayId && hiddenTrayIds.indexOf(trayId) !== -1;
}
function setTrayItemOrder(order) {
trayItemOrder = order;
saveSettings();
}
function addRecentColor(color) {
const colorStr = color.toString();
let recent = recentColors.slice();

View File

@@ -50,6 +50,7 @@ var SPEC = {
barPinnedApps: { def: [] },
dockLauncherPosition: { def: 0 },
hiddenTrayIds: { def: [] },
trayItemOrder: { def: [] },
recentColors: { def: [] },
showThirdPartyPlugins: { def: false },
launchPrefix: { def: "" },

View File

@@ -45,8 +45,62 @@ Item {
return `${id}::${tooltipTitle}`;
}
readonly property var mainBarItems: allTrayItems.filter(item => !SessionData.isHiddenTrayId(root.getTrayItemKey(item)))
readonly property var hiddenBarItems: allTrayItems.filter(item => SessionData.isHiddenTrayId(root.getTrayItemKey(item)))
property int _trayOrderTrigger: 0
Connections {
target: SessionData
function onTrayItemOrderChanged() {
root._trayOrderTrigger++;
}
}
function sortByPreferredOrder(items, trigger) {
void trigger;
const savedOrder = SessionData.trayItemOrder || [];
const orderMap = new Map();
savedOrder.forEach((key, idx) => orderMap.set(key, idx));
return [...items].sort((a, b) => {
const keyA = getTrayItemKey(a);
const keyB = getTrayItemKey(b);
const orderA = orderMap.has(keyA) ? orderMap.get(keyA) : 10000 + items.indexOf(a);
const orderB = orderMap.has(keyB) ? orderMap.get(keyB) : 10000 + items.indexOf(b);
return orderA - orderB;
});
}
readonly property var allSortedTrayItems: sortByPreferredOrder(allTrayItems, _trayOrderTrigger)
readonly property var allSortedTrayItemKeys: allSortedTrayItems.map(item => getTrayItemKey(item))
readonly property var mainBarItemsRaw: allSortedTrayItems.filter(item => !SessionData.isHiddenTrayId(root.getTrayItemKey(item)))
readonly property var mainBarItems: mainBarItemsRaw.map((item, idx) => ({
key: getTrayItemKey(item),
item: item
}))
readonly property var hiddenBarItems: allSortedTrayItems.filter(item => SessionData.isHiddenTrayId(root.getTrayItemKey(item)))
function moveTrayItemInFullOrder(visibleFromIndex, visibleToIndex) {
if (visibleFromIndex === visibleToIndex || visibleFromIndex < 0 || visibleToIndex < 0)
return;
const fromKey = mainBarItems[visibleFromIndex]?.key ?? null;
const toKey = mainBarItems[visibleToIndex]?.key ?? null;
if (!fromKey || !toKey)
return;
const fullOrder = [...allSortedTrayItemKeys];
const fullFromIndex = fullOrder.indexOf(fromKey);
const fullToIndex = fullOrder.indexOf(toKey);
if (fullFromIndex < 0 || fullToIndex < 0)
return;
const movedKey = fullOrder.splice(fullFromIndex, 1)[0];
fullOrder.splice(fullToIndex, 0, movedKey);
SessionData.setTrayItemOrder(fullOrder);
}
property int draggedIndex: -1
property int dropTargetIndex: -1
property bool suppressShiftAnimation: false
readonly property bool hasHiddenItems: allTrayItems.length > mainBarItems.length
readonly property int calculatedSize: {
if (allTrayItems.length === 0)
@@ -160,23 +214,24 @@ Item {
spacing: 0
Repeater {
model: root.mainBarItems
model: ScriptModel {
values: root.mainBarItems
objectProp: "key"
}
delegate: Item {
id: delegateRoot
property var trayItem: modelData
property var trayItem: modelData.item
property string itemKey: modelData.key
property string iconSource: {
let icon = trayItem && trayItem.icon;
if (typeof icon === 'string' || icon instanceof String) {
if (icon === "") {
if (icon === "")
return "";
}
if (icon.includes("?path=")) {
const split = icon.split("?path=");
if (split.length !== 2) {
if (split.length !== 2)
return icon;
}
const name = split[0];
const path = split[1];
let fileName = name.substring(name.lastIndexOf("/") + 1);
@@ -185,9 +240,8 @@ Item {
}
return `file://${path}/${fileName}`;
}
if (icon.startsWith("/") && !icon.startsWith("file://")) {
if (icon.startsWith("/") && !icon.startsWith("file://"))
return `file://${icon}`;
}
return icon;
}
return "";
@@ -195,6 +249,51 @@ Item {
width: 24
height: root.barThickness
z: dragHandler.dragging ? 100 : 0
property real shiftOffset: {
if (root.draggedIndex < 0)
return 0;
if (index === root.draggedIndex)
return 0;
const dragIdx = root.draggedIndex;
const dropIdx = root.dropTargetIndex;
const shiftAmount = 24;
if (dropIdx < 0)
return 0;
if (dragIdx < dropIdx && index > dragIdx && index <= dropIdx)
return -shiftAmount;
if (dragIdx > dropIdx && index >= dropIdx && index < dragIdx)
return shiftAmount;
return 0;
}
transform: Translate {
x: delegateRoot.shiftOffset
Behavior on x {
enabled: !root.suppressShiftAnimation
NumberAnimation {
duration: 150
easing.type: Easing.OutCubic
}
}
}
Item {
id: dragHandler
anchors.fill: parent
property bool dragging: false
property point dragStartPos: Qt.point(0, 0)
property real dragAxisOffset: 0
property bool longPressing: false
Timer {
id: longPressTimer
interval: 400
repeat: false
onTriggered: dragHandler.longPressing = true
}
}
Rectangle {
id: visualContent
@@ -203,6 +302,13 @@ Item {
anchors.centerIn: parent
radius: Theme.cornerRadius
color: trayItemArea.containsMouse ? Theme.widgetBaseHoverColor : "transparent"
border.width: dragHandler.dragging ? 2 : 0
border.color: Theme.primary
opacity: dragHandler.dragging ? 0.8 : 1.0
transform: Translate {
x: dragHandler.dragging ? dragHandler.dragAxisOffset : 0
}
IconImage {
id: iconImg
@@ -221,9 +327,8 @@ Item {
visible: !iconImg.visible
text: {
const itemId = trayItem?.id || "";
if (!itemId) {
if (!itemId)
return "?";
}
return itemId.charAt(0).toUpperCase();
}
font.pixelSize: 10
@@ -233,24 +338,82 @@ Item {
MouseArea {
id: trayItemArea
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.LeftButton | Qt.RightButton
cursorShape: Qt.PointingHandCursor
onClicked: mouse => {
cursorShape: dragHandler.longPressing ? Qt.DragMoveCursor : Qt.PointingHandCursor
onPressed: mouse => {
if (mouse.button === Qt.LeftButton) {
dragHandler.dragStartPos = Qt.point(mouse.x, mouse.y);
longPressTimer.start();
}
}
onReleased: mouse => {
longPressTimer.stop();
const wasDragging = dragHandler.dragging;
const didReorder = wasDragging && root.dropTargetIndex >= 0 && root.dropTargetIndex !== root.draggedIndex;
if (didReorder) {
root.suppressShiftAnimation = true;
root.moveTrayItemInFullOrder(root.draggedIndex, root.dropTargetIndex);
Qt.callLater(() => root.suppressShiftAnimation = false);
}
dragHandler.longPressing = false;
dragHandler.dragging = false;
dragHandler.dragAxisOffset = 0;
root.draggedIndex = -1;
root.dropTargetIndex = -1;
if (wasDragging || mouse.button !== Qt.LeftButton)
return;
if (!delegateRoot.trayItem)
return;
if (mouse.button === Qt.LeftButton && !delegateRoot.trayItem.onlyMenu) {
if (!delegateRoot.trayItem.onlyMenu) {
delegateRoot.trayItem.activate();
return;
}
if (!delegateRoot.trayItem.hasMenu)
return;
root.menuOpen = false;
root.showForTrayItem(delegateRoot.trayItem, visualContent, parentScreen, root.isAtBottom, root.isVertical, root.axis);
}
onPositionChanged: mouse => {
if (dragHandler.longPressing && !dragHandler.dragging) {
const distance = Math.abs(mouse.x - dragHandler.dragStartPos.x);
if (distance > 5) {
dragHandler.dragging = true;
root.draggedIndex = index;
root.dropTargetIndex = index;
}
}
if (!dragHandler.dragging)
return;
const axisOffset = mouse.x - dragHandler.dragStartPos.x;
dragHandler.dragAxisOffset = axisOffset;
const itemSize = 24;
const slotOffset = Math.round(axisOffset / itemSize);
const newTargetIndex = Math.max(0, Math.min(root.mainBarItems.length - 1, index + slotOffset));
if (newTargetIndex !== root.dropTargetIndex) {
root.dropTargetIndex = newTargetIndex;
}
}
onClicked: mouse => {
if (dragHandler.dragging)
return;
if (mouse.button !== Qt.RightButton)
return;
if (!delegateRoot.trayItem?.hasMenu)
return;
root.menuOpen = false;
root.showForTrayItem(delegateRoot.trayItem, visualContent, parentScreen, root.isAtBottom, root.isVertical, root.axis);
}
}
}
}
@@ -293,23 +456,24 @@ Item {
spacing: 0
Repeater {
model: root.mainBarItems
model: ScriptModel {
values: root.mainBarItems
objectProp: "key"
}
delegate: Item {
id: delegateRoot
property var trayItem: modelData
property var trayItem: modelData.item
property string itemKey: modelData.key
property string iconSource: {
let icon = trayItem && trayItem.icon;
if (typeof icon === 'string' || icon instanceof String) {
if (icon === "") {
if (icon === "")
return "";
}
if (icon.includes("?path=")) {
const split = icon.split("?path=");
if (split.length !== 2) {
if (split.length !== 2)
return icon;
}
const name = split[0];
const path = split[1];
let fileName = name.substring(name.lastIndexOf("/") + 1);
@@ -318,9 +482,8 @@ Item {
}
return `file://${path}/${fileName}`;
}
if (icon.startsWith("/") && !icon.startsWith("file://")) {
if (icon.startsWith("/") && !icon.startsWith("file://"))
return `file://${icon}`;
}
return icon;
}
return "";
@@ -328,6 +491,51 @@ Item {
width: root.barThickness
height: 24
z: dragHandler.dragging ? 100 : 0
property real shiftOffset: {
if (root.draggedIndex < 0)
return 0;
if (index === root.draggedIndex)
return 0;
const dragIdx = root.draggedIndex;
const dropIdx = root.dropTargetIndex;
const shiftAmount = 24;
if (dropIdx < 0)
return 0;
if (dragIdx < dropIdx && index > dragIdx && index <= dropIdx)
return -shiftAmount;
if (dragIdx > dropIdx && index >= dropIdx && index < dragIdx)
return shiftAmount;
return 0;
}
transform: Translate {
y: delegateRoot.shiftOffset
Behavior on y {
enabled: !root.suppressShiftAnimation
NumberAnimation {
duration: 150
easing.type: Easing.OutCubic
}
}
}
Item {
id: dragHandler
anchors.fill: parent
property bool dragging: false
property point dragStartPos: Qt.point(0, 0)
property real dragAxisOffset: 0
property bool longPressing: false
Timer {
id: longPressTimer
interval: 400
repeat: false
onTriggered: dragHandler.longPressing = true
}
}
Rectangle {
id: visualContent
@@ -336,6 +544,13 @@ Item {
anchors.centerIn: parent
radius: Theme.cornerRadius
color: trayItemArea.containsMouse ? Theme.widgetBaseHoverColor : "transparent"
border.width: dragHandler.dragging ? 2 : 0
border.color: Theme.primary
opacity: dragHandler.dragging ? 0.8 : 1.0
transform: Translate {
y: dragHandler.dragging ? dragHandler.dragAxisOffset : 0
}
IconImage {
id: iconImg
@@ -354,9 +569,8 @@ Item {
visible: !iconImg.visible
text: {
const itemId = trayItem?.id || "";
if (!itemId) {
if (!itemId)
return "?";
}
return itemId.charAt(0).toUpperCase();
}
font.pixelSize: 10
@@ -366,24 +580,82 @@ Item {
MouseArea {
id: trayItemArea
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.LeftButton | Qt.RightButton
cursorShape: Qt.PointingHandCursor
onClicked: mouse => {
cursorShape: dragHandler.longPressing ? Qt.DragMoveCursor : Qt.PointingHandCursor
onPressed: mouse => {
if (mouse.button === Qt.LeftButton) {
dragHandler.dragStartPos = Qt.point(mouse.x, mouse.y);
longPressTimer.start();
}
}
onReleased: mouse => {
longPressTimer.stop();
const wasDragging = dragHandler.dragging;
const didReorder = wasDragging && root.dropTargetIndex >= 0 && root.dropTargetIndex !== root.draggedIndex;
if (didReorder) {
root.suppressShiftAnimation = true;
root.moveTrayItemInFullOrder(root.draggedIndex, root.dropTargetIndex);
Qt.callLater(() => root.suppressShiftAnimation = false);
}
dragHandler.longPressing = false;
dragHandler.dragging = false;
dragHandler.dragAxisOffset = 0;
root.draggedIndex = -1;
root.dropTargetIndex = -1;
if (wasDragging || mouse.button !== Qt.LeftButton)
return;
if (!delegateRoot.trayItem)
return;
if (mouse.button === Qt.LeftButton && !delegateRoot.trayItem.onlyMenu) {
if (!delegateRoot.trayItem.onlyMenu) {
delegateRoot.trayItem.activate();
return;
}
if (!delegateRoot.trayItem.hasMenu)
return;
root.menuOpen = false;
root.showForTrayItem(delegateRoot.trayItem, visualContent, parentScreen, root.isAtBottom, root.isVertical, root.axis);
}
onPositionChanged: mouse => {
if (dragHandler.longPressing && !dragHandler.dragging) {
const distance = Math.abs(mouse.y - dragHandler.dragStartPos.y);
if (distance > 5) {
dragHandler.dragging = true;
root.draggedIndex = index;
root.dropTargetIndex = index;
}
}
if (!dragHandler.dragging)
return;
const axisOffset = mouse.y - dragHandler.dragStartPos.y;
dragHandler.dragAxisOffset = axisOffset;
const itemSize = 24;
const slotOffset = Math.round(axisOffset / itemSize);
const newTargetIndex = Math.max(0, Math.min(root.mainBarItems.length - 1, index + slotOffset));
if (newTargetIndex !== root.dropTargetIndex) {
root.dropTargetIndex = newTargetIndex;
}
}
onClicked: mouse => {
if (dragHandler.dragging)
return;
if (mouse.button !== Qt.RightButton)
return;
if (!delegateRoot.trayItem?.hasMenu)
return;
root.menuOpen = false;
root.showForTrayItem(delegateRoot.trayItem, visualContent, parentScreen, root.isAtBottom, root.isVertical, root.axis);
}
}
}
}