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:
@@ -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();
|
||||
|
||||
@@ -50,6 +50,7 @@ var SPEC = {
|
||||
barPinnedApps: { def: [] },
|
||||
dockLauncherPosition: { def: 0 },
|
||||
hiddenTrayIds: { def: [] },
|
||||
trayItemOrder: { def: [] },
|
||||
recentColors: { def: [] },
|
||||
showThirdPartyPlugins: { def: false },
|
||||
launchPrefix: { def: "" },
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user