mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-06-26 21:15:18 -04:00
refactor(WidgetsTab): port updated drag-and-drop functionality & cross section target ordering
This commit is contained in:
@@ -27,6 +27,22 @@ Item {
|
|||||||
property bool hasMultipleBars: SettingsData.barConfigs.length > 1
|
property bool hasMultipleBars: SettingsData.barConfigs.length > 1
|
||||||
property int pluginCatalogRevision: 0
|
property int pluginCatalogRevision: 0
|
||||||
|
|
||||||
|
property string highlightedId: ""
|
||||||
|
property string highlightedSection: ""
|
||||||
|
|
||||||
|
// Cross-section drag coordinator state + floating proxy avatar.
|
||||||
|
property bool dragActive: false
|
||||||
|
property string dragSourceSection: ""
|
||||||
|
property string dragTargetSection: ""
|
||||||
|
property string dragId: ""
|
||||||
|
property var dragWidgetData: null
|
||||||
|
property int targetIndex: -1
|
||||||
|
property real dragRowHeight: 72
|
||||||
|
property bool proxyVisible: false
|
||||||
|
property real proxyX: 0
|
||||||
|
property real proxyY: 0
|
||||||
|
property real proxyWidth: 0
|
||||||
|
|
||||||
DankTooltipV2 {
|
DankTooltipV2 {
|
||||||
id: sharedTooltip
|
id: sharedTooltip
|
||||||
}
|
}
|
||||||
@@ -276,6 +292,48 @@ Item {
|
|||||||
return coreWidgets;
|
return coreWidgets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
focus: true
|
||||||
|
Keys.onPressed: function (event) {
|
||||||
|
var flat = flatList();
|
||||||
|
if (flat.length === 0)
|
||||||
|
return;
|
||||||
|
var ctrl = (event.modifiers & Qt.ControlModifier) !== 0;
|
||||||
|
if (event.key === Qt.Key_Up || event.key === Qt.Key_Down) {
|
||||||
|
var dir = event.key === Qt.Key_Down ? 1 : -1;
|
||||||
|
if (ctrl) {
|
||||||
|
if (highlightedId !== "")
|
||||||
|
moveWithinSection(highlightedSection, highlightedId, dir);
|
||||||
|
} else {
|
||||||
|
var idx = -1;
|
||||||
|
for (var i = 0; i < flat.length; i++) {
|
||||||
|
if (flat[i].section === highlightedSection && flat[i].id === highlightedId) {
|
||||||
|
idx = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (idx < 0) {
|
||||||
|
var f = dir > 0 ? flat[0] : flat[flat.length - 1];
|
||||||
|
highlightedSection = f.section;
|
||||||
|
highlightedId = f.id;
|
||||||
|
} else {
|
||||||
|
idx = Math.max(0, Math.min(flat.length - 1, idx + dir));
|
||||||
|
highlightedSection = flat[idx].section;
|
||||||
|
highlightedId = flat[idx].id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
event.accepted = true;
|
||||||
|
} else if ((event.key === Qt.Key_Left || event.key === Qt.Key_Right) && ctrl) {
|
||||||
|
if (highlightedId !== "")
|
||||||
|
moveAcrossSections(highlightedSection, highlightedId, event.key === Qt.Key_Right ? 1 : -1);
|
||||||
|
event.accepted = true;
|
||||||
|
} else if (event.key === Qt.Key_Space || event.key === Qt.Key_Return) {
|
||||||
|
if (highlightedId !== "") {
|
||||||
|
toggleHighlighted();
|
||||||
|
event.accepted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: PluginService
|
target: PluginService
|
||||||
|
|
||||||
@@ -489,8 +547,200 @@ Item {
|
|||||||
setWidgetsForSection(sectionId, widgets);
|
setWidgetsForSection(sectionId, widgets);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleItemOrderChanged(sectionId, newOrder) {
|
function barKey(sectionId) {
|
||||||
setWidgetsForSection(sectionId, newOrder);
|
return sectionId === "left" ? "leftWidgets" : sectionId === "center" ? "centerWidgets" : "rightWidgets";
|
||||||
|
}
|
||||||
|
|
||||||
|
function sectionItem(sectionId) {
|
||||||
|
return sectionId === "left" ? leftSection : sectionId === "center" ? centerSection : sectionId === "right" ? rightSection : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Id-based reorder; rebuilds from authoritative objects so every prop (incl. hideWhenIdle) survives
|
||||||
|
function reorderSection(sectionId, orderedIds) {
|
||||||
|
var current = getWidgetsForSection(sectionId);
|
||||||
|
var byId = {};
|
||||||
|
current.forEach(w => {
|
||||||
|
var id = (typeof w === "string" ? w : w.id);
|
||||||
|
byId[id] = w;
|
||||||
|
});
|
||||||
|
var reordered = [];
|
||||||
|
orderedIds.forEach(id => {
|
||||||
|
if (byId[id] !== undefined)
|
||||||
|
reordered.push(byId[id]);
|
||||||
|
});
|
||||||
|
setWidgetsForSection(sectionId, reordered);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move a widget across sections (or within); committed as one atomic bar-config save
|
||||||
|
function moveWidget(fromSection, toSection, movedId, toIndex) {
|
||||||
|
if (fromSection === toSection) {
|
||||||
|
var arr = getWidgetsForSection(fromSection).slice();
|
||||||
|
var fi = arr.findIndex(w => (typeof w === "string" ? w : w.id) === movedId);
|
||||||
|
if (fi < 0)
|
||||||
|
return;
|
||||||
|
var m = arr.splice(fi, 1)[0];
|
||||||
|
arr.splice(Math.max(0, Math.min(toIndex, arr.length)), 0, m);
|
||||||
|
setWidgetsForSection(fromSection, arr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var src = getWidgetsForSection(fromSection).slice();
|
||||||
|
var fromIdx = src.findIndex(w => (typeof w === "string" ? w : w.id) === movedId);
|
||||||
|
if (fromIdx < 0)
|
||||||
|
return;
|
||||||
|
var moved = src.splice(fromIdx, 1)[0];
|
||||||
|
var dst = getWidgetsForSection(toSection).slice();
|
||||||
|
dst.splice(Math.max(0, Math.min(toIndex, dst.length)), 0, moved);
|
||||||
|
var updates = {};
|
||||||
|
updates[barKey(fromSection)] = src;
|
||||||
|
updates[barKey(toSection)] = dst;
|
||||||
|
SettingsData.updateBarConfig(selectedBarId, updates);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sectionAtY(gy) {
|
||||||
|
var sections = ["left", "center", "right"];
|
||||||
|
var nearest = "";
|
||||||
|
var nearestDist = Infinity;
|
||||||
|
for (var i = 0; i < sections.length; i++) {
|
||||||
|
var it = sectionItem(sections[i]);
|
||||||
|
if (!it)
|
||||||
|
continue;
|
||||||
|
var top = it.mapToItem(widgetsTab, 0, 0).y;
|
||||||
|
var bot = top + it.height;
|
||||||
|
if (gy >= top && gy <= bot)
|
||||||
|
return sections[i];
|
||||||
|
var d = gy < top ? (top - gy) : (gy - bot);
|
||||||
|
if (d < nearestDist) {
|
||||||
|
nearestDist = d;
|
||||||
|
nearest = sections[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nearest;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDragStarted(sectionId, id, index, widgetData, localPos) {
|
||||||
|
widgetsTab.forceActiveFocus();
|
||||||
|
highlightedSection = sectionId;
|
||||||
|
highlightedId = id;
|
||||||
|
dragActive = true;
|
||||||
|
dragSourceSection = sectionId;
|
||||||
|
dragTargetSection = sectionId;
|
||||||
|
dragId = id;
|
||||||
|
dragWidgetData = widgetData;
|
||||||
|
targetIndex = -1;
|
||||||
|
var src = sectionItem(sectionId);
|
||||||
|
dragRowHeight = src ? src.rowHeight : 72;
|
||||||
|
var origin = src ? src.mapToItem(widgetsTab, 0, 0) : {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
};
|
||||||
|
proxyX = origin.x;
|
||||||
|
proxyWidth = src ? src.width : 0;
|
||||||
|
proxyVisible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDragMoved(sectionId, localPos) {
|
||||||
|
if (!dragActive)
|
||||||
|
return;
|
||||||
|
var src = sectionItem(sectionId);
|
||||||
|
if (!src)
|
||||||
|
return;
|
||||||
|
var g = src.mapToItem(widgetsTab, localPos.x, localPos.y);
|
||||||
|
var hit = sectionAtY(g.y);
|
||||||
|
if (hit === "" || hit === dragSourceSection) {
|
||||||
|
if (dragTargetSection !== dragSourceSection) {
|
||||||
|
var prev = sectionItem(dragTargetSection);
|
||||||
|
if (prev)
|
||||||
|
prev.clearGap();
|
||||||
|
dragTargetSection = dragSourceSection;
|
||||||
|
}
|
||||||
|
src.setCrossMode(false);
|
||||||
|
targetIndex = -1;
|
||||||
|
proxyVisible = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (dragTargetSection !== hit) {
|
||||||
|
if (dragTargetSection !== dragSourceSection) {
|
||||||
|
var prevSec = sectionItem(dragTargetSection);
|
||||||
|
if (prevSec)
|
||||||
|
prevSec.clearGap();
|
||||||
|
}
|
||||||
|
dragTargetSection = hit;
|
||||||
|
}
|
||||||
|
src.setCrossMode(true);
|
||||||
|
var tgt = sectionItem(hit);
|
||||||
|
targetIndex = tgt.slotIndexForGlobalY(widgetsTab, g.y);
|
||||||
|
tgt.openGapAt(targetIndex);
|
||||||
|
proxyY = g.y - dragRowHeight / 2;
|
||||||
|
proxyVisible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDragEnded(sectionId) {
|
||||||
|
var src = sectionItem(dragSourceSection);
|
||||||
|
var crossing = dragTargetSection !== "" && dragTargetSection !== dragSourceSection;
|
||||||
|
if (crossing) {
|
||||||
|
moveWidget(dragSourceSection, dragTargetSection, dragId, targetIndex);
|
||||||
|
var tgt = sectionItem(dragTargetSection);
|
||||||
|
if (tgt)
|
||||||
|
tgt.clearGap();
|
||||||
|
if (src)
|
||||||
|
src.cancelDrag();
|
||||||
|
} else if (src) {
|
||||||
|
src.commitDrag();
|
||||||
|
}
|
||||||
|
if (src)
|
||||||
|
src.setCrossMode(false);
|
||||||
|
dragActive = false;
|
||||||
|
dragSourceSection = "";
|
||||||
|
dragTargetSection = "";
|
||||||
|
dragId = "";
|
||||||
|
dragWidgetData = null;
|
||||||
|
targetIndex = -1;
|
||||||
|
proxyVisible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function flatList() {
|
||||||
|
var out = [];
|
||||||
|
["left", "center", "right"].forEach(s => {
|
||||||
|
getWidgetsForSection(s).forEach(w => {
|
||||||
|
out.push({
|
||||||
|
"section": s,
|
||||||
|
"id": (typeof w === "string" ? w : w.id)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveWithinSection(sectionId, id, delta) {
|
||||||
|
var ids = getWidgetsForSection(sectionId).map(w => typeof w === "string" ? w : w.id);
|
||||||
|
var pos = ids.indexOf(id);
|
||||||
|
var next = pos + delta;
|
||||||
|
if (pos < 0 || next < 0 || next >= ids.length)
|
||||||
|
return;
|
||||||
|
ids.splice(pos, 1);
|
||||||
|
ids.splice(next, 0, id);
|
||||||
|
reorderSection(sectionId, ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveAcrossSections(sectionId, id, delta) {
|
||||||
|
var order = ["left", "center", "right"];
|
||||||
|
var si = order.indexOf(sectionId);
|
||||||
|
var ti = si + delta;
|
||||||
|
if (si < 0 || ti < 0 || ti >= order.length)
|
||||||
|
return;
|
||||||
|
var to = order[ti];
|
||||||
|
moveWidget(sectionId, to, id, getWidgetsForSection(to).length);
|
||||||
|
highlightedSection = to;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleHighlighted() {
|
||||||
|
if (highlightedId === "" || highlightedSection === "")
|
||||||
|
return;
|
||||||
|
var w = getWidgetsForSection(highlightedSection).find(x => (typeof x === "string" ? x : x.id) === highlightedId);
|
||||||
|
if (w === undefined)
|
||||||
|
return;
|
||||||
|
var en = (typeof w === "string") ? true : (w.enabled !== false);
|
||||||
|
handleItemEnabledChanged(highlightedSection, highlightedId, !en);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSpacerSizeChanged(sectionId, widgetIndex, newSize) {
|
function handleSpacerSizeChanged(sectionId, widgetIndex, newSize) {
|
||||||
@@ -1069,8 +1319,19 @@ Item {
|
|||||||
onItemEnabledChanged: (sectionId, itemId, enabled) => {
|
onItemEnabledChanged: (sectionId, itemId, enabled) => {
|
||||||
widgetsTab.handleItemEnabledChanged(sectionId, itemId, enabled);
|
widgetsTab.handleItemEnabledChanged(sectionId, itemId, enabled);
|
||||||
}
|
}
|
||||||
onItemOrderChanged: newOrder => {
|
highlightedId: widgetsTab.highlightedId
|
||||||
widgetsTab.handleItemOrderChanged(sectionId, newOrder);
|
highlightedSection: widgetsTab.highlightedSection
|
||||||
|
onItemOrderChanged: (sectionId, orderedIds) => {
|
||||||
|
widgetsTab.reorderSection(sectionId, orderedIds);
|
||||||
|
}
|
||||||
|
onDragStarted: (sectionId, id, index, widgetData, localPos) => {
|
||||||
|
widgetsTab.handleDragStarted(sectionId, id, index, widgetData, localPos);
|
||||||
|
}
|
||||||
|
onDragMoved: (sectionId, localPos) => {
|
||||||
|
widgetsTab.handleDragMoved(sectionId, localPos);
|
||||||
|
}
|
||||||
|
onDragEnded: sectionId => {
|
||||||
|
widgetsTab.handleDragEnded(sectionId);
|
||||||
}
|
}
|
||||||
onAddWidget: sectionId => {
|
onAddWidget: sectionId => {
|
||||||
showWidgetSelectionPopup(sectionId);
|
showWidgetSelectionPopup(sectionId);
|
||||||
@@ -1145,8 +1406,19 @@ Item {
|
|||||||
onItemEnabledChanged: (sectionId, itemId, enabled) => {
|
onItemEnabledChanged: (sectionId, itemId, enabled) => {
|
||||||
widgetsTab.handleItemEnabledChanged(sectionId, itemId, enabled);
|
widgetsTab.handleItemEnabledChanged(sectionId, itemId, enabled);
|
||||||
}
|
}
|
||||||
onItemOrderChanged: newOrder => {
|
highlightedId: widgetsTab.highlightedId
|
||||||
widgetsTab.handleItemOrderChanged(sectionId, newOrder);
|
highlightedSection: widgetsTab.highlightedSection
|
||||||
|
onItemOrderChanged: (sectionId, orderedIds) => {
|
||||||
|
widgetsTab.reorderSection(sectionId, orderedIds);
|
||||||
|
}
|
||||||
|
onDragStarted: (sectionId, id, index, widgetData, localPos) => {
|
||||||
|
widgetsTab.handleDragStarted(sectionId, id, index, widgetData, localPos);
|
||||||
|
}
|
||||||
|
onDragMoved: (sectionId, localPos) => {
|
||||||
|
widgetsTab.handleDragMoved(sectionId, localPos);
|
||||||
|
}
|
||||||
|
onDragEnded: sectionId => {
|
||||||
|
widgetsTab.handleDragEnded(sectionId);
|
||||||
}
|
}
|
||||||
onAddWidget: sectionId => {
|
onAddWidget: sectionId => {
|
||||||
showWidgetSelectionPopup(sectionId);
|
showWidgetSelectionPopup(sectionId);
|
||||||
@@ -1221,8 +1493,19 @@ Item {
|
|||||||
onItemEnabledChanged: (sectionId, itemId, enabled) => {
|
onItemEnabledChanged: (sectionId, itemId, enabled) => {
|
||||||
widgetsTab.handleItemEnabledChanged(sectionId, itemId, enabled);
|
widgetsTab.handleItemEnabledChanged(sectionId, itemId, enabled);
|
||||||
}
|
}
|
||||||
onItemOrderChanged: newOrder => {
|
highlightedId: widgetsTab.highlightedId
|
||||||
widgetsTab.handleItemOrderChanged(sectionId, newOrder);
|
highlightedSection: widgetsTab.highlightedSection
|
||||||
|
onItemOrderChanged: (sectionId, orderedIds) => {
|
||||||
|
widgetsTab.reorderSection(sectionId, orderedIds);
|
||||||
|
}
|
||||||
|
onDragStarted: (sectionId, id, index, widgetData, localPos) => {
|
||||||
|
widgetsTab.handleDragStarted(sectionId, id, index, widgetData, localPos);
|
||||||
|
}
|
||||||
|
onDragMoved: (sectionId, localPos) => {
|
||||||
|
widgetsTab.handleDragMoved(sectionId, localPos);
|
||||||
|
}
|
||||||
|
onDragEnded: sectionId => {
|
||||||
|
widgetsTab.handleDragEnded(sectionId);
|
||||||
}
|
}
|
||||||
onAddWidget: sectionId => {
|
onAddWidget: sectionId => {
|
||||||
showWidgetSelectionPopup(sectionId);
|
showWidgetSelectionPopup(sectionId);
|
||||||
@@ -1280,4 +1563,59 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Floating drag avatar, outside the DankFlickable clip so it paints over the inter-card gap.
|
||||||
|
Item {
|
||||||
|
id: dragProxy
|
||||||
|
|
||||||
|
visible: widgetsTab.proxyVisible
|
||||||
|
x: widgetsTab.proxyX
|
||||||
|
y: widgetsTab.proxyY
|
||||||
|
width: widgetsTab.proxyWidth
|
||||||
|
height: widgetsTab.dragRowHeight
|
||||||
|
z: 9999
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 2
|
||||||
|
radius: Theme.cornerRadius + 6
|
||||||
|
color: Theme.secondaryContainer
|
||||||
|
border.color: Theme.primary
|
||||||
|
border.width: 2
|
||||||
|
scale: 1.02
|
||||||
|
opacity: 0.95
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "drag_indicator"
|
||||||
|
size: Theme.iconSize - 4
|
||||||
|
color: Theme.primary
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Theme.spacingM + 8
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
id: proxyIcon
|
||||||
|
name: (widgetsTab.dragWidgetData && widgetsTab.dragWidgetData.icon) ? widgetsTab.dragWidgetData.icon : "widgets"
|
||||||
|
size: Theme.iconSize
|
||||||
|
color: Theme.primary
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Theme.spacingM * 2 + 40
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: (widgetsTab.dragWidgetData && widgetsTab.dragWidgetData.text) ? widgetsTab.dragWidgetData.text : ""
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
elide: Text.ElideRight
|
||||||
|
anchors.left: proxyIcon.right
|
||||||
|
anchors.leftMargin: Theme.spacingM
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: Theme.spacingM
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ Column {
|
|||||||
}
|
}
|
||||||
|
|
||||||
signal itemEnabledChanged(string sectionId, string itemId, bool enabled)
|
signal itemEnabledChanged(string sectionId, string itemId, bool enabled)
|
||||||
signal itemOrderChanged(var newOrder)
|
signal itemOrderChanged(string sectionId, var orderedIds)
|
||||||
signal addWidget(string sectionId)
|
signal addWidget(string sectionId)
|
||||||
signal removeWidget(string sectionId, int widgetIndex)
|
signal removeWidget(string sectionId, int widgetIndex)
|
||||||
signal spacerSizeChanged(string sectionId, int widgetIndex, int newSize)
|
signal spacerSizeChanged(string sectionId, int widgetIndex, int newSize)
|
||||||
@@ -38,19 +38,121 @@ Column {
|
|||||||
signal overflowSettingChanged(string sectionId, int widgetIndex, string settingName, var value)
|
signal overflowSettingChanged(string sectionId, int widgetIndex, string settingName, var value)
|
||||||
signal hideWhenIdleChanged(string sectionId, int widgetIndex, bool enabled)
|
signal hideWhenIdleChanged(string sectionId, int widgetIndex, bool enabled)
|
||||||
|
|
||||||
function cloneWidgetData(widget) {
|
// Cross-section drag coordination with WidgetsTab (positions are section-local)
|
||||||
var result = {
|
signal dragStarted(string sectionId, string id, int index, var widgetData, var localPos)
|
||||||
"id": widget.id,
|
signal dragMoved(string sectionId, var localPos)
|
||||||
"enabled": widget.enabled
|
signal dragEnded(string sectionId)
|
||||||
};
|
|
||||||
var keys = ["size", "selectedGpuIndex", "pciId", "mountPath", "diskUsageMode", "minimumWidth", "showSwap", "showInGb", "mediaSize", "clockCompactMode", "focusedWindowSize", "focusedWindowCompactMode", "runningAppsCompactMode", "keyboardLayoutNameCompactMode", "keyboardLayoutNameShowIcon", "runningAppsGroupByApp", "runningAppsCurrentWorkspace", "runningAppsCurrentMonitor", "showNetworkIcon", "showBluetoothIcon", "showAudioIcon", "showAudioPercent", "showVpnIcon", "showBrightnessIcon", "showBrightnessPercent", "showMicIcon", "showMicPercent", "showBatteryIcon", "showBatteryPercent", "showBatteryPercentOnlyOnBattery", "showBatteryTime", "showBatteryTimeOnlyOnBattery", "showPrinterIcon", "showScreenSharingIcon", "showIdleInhibitorIcon", "showDoNotDisturbIcon", "controlCenterGroupOrder", "barMaxVisibleApps", "barMaxVisibleRunningApps", "barShowOverflowBadge", "trayUseInlineExpansion", "trayPopupSingleLine", "trayAutoOverflow", "trayMaxVisibleItems"];
|
property string highlightedId: ""
|
||||||
for (var i = 0; i < keys.length; i++) {
|
property string highlightedSection: ""
|
||||||
if (widget[keys[i]] !== undefined)
|
|
||||||
result[keys[i]] = widget[keys[i]];
|
// Absolute-Y spring drag state (mirrors DankDashTab); gapIndex is the phantom drop slot
|
||||||
}
|
property var workingOrder: []
|
||||||
return result;
|
property int draggingIndex: -1
|
||||||
|
property string draggingId: ""
|
||||||
|
property var dragStartOrder: []
|
||||||
|
property int gapIndex: -1
|
||||||
|
property bool crossSectionActive: false
|
||||||
|
|
||||||
|
readonly property real rowHeight: 72
|
||||||
|
readonly property real rowSpacing: Theme.spacingS
|
||||||
|
|
||||||
|
readonly property real totalHeight: {
|
||||||
|
const n = items.length;
|
||||||
|
let base = n * (rowHeight + rowSpacing);
|
||||||
|
if (gapIndex >= 0)
|
||||||
|
base += (rowHeight + rowSpacing);
|
||||||
|
return Math.max(0, base - rowSpacing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resetWorkingOrder() {
|
||||||
|
const arr = [];
|
||||||
|
for (var i = 0; i < items.length; i++)
|
||||||
|
arr.push(i);
|
||||||
|
workingOrder = arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
function slotYForIndex(i) {
|
||||||
|
var pos = workingOrder.indexOf(i);
|
||||||
|
if (pos < 0)
|
||||||
|
pos = i;
|
||||||
|
var y = pos * (rowHeight + rowSpacing);
|
||||||
|
if (gapIndex >= 0 && pos >= gapIndex)
|
||||||
|
y += (rowHeight + rowSpacing);
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
function slotIndexForY(localY) {
|
||||||
|
var idx = Math.round(localY / (rowHeight + rowSpacing));
|
||||||
|
return Math.max(0, Math.min(idx, items.length));
|
||||||
|
}
|
||||||
|
|
||||||
|
function slotIndexForGlobalY(rootItem, gy) {
|
||||||
|
var p = reorderArea.mapFromItem(rootItem, 0, gy);
|
||||||
|
return slotIndexForY(p.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
function beginDrag(i) {
|
||||||
|
draggingIndex = i;
|
||||||
|
draggingId = (items[i] && items[i].id) ? items[i].id : "";
|
||||||
|
dragStartOrder = workingOrder.slice();
|
||||||
|
crossSectionActive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateDragTarget(centerY) {
|
||||||
|
if (draggingIndex < 0)
|
||||||
|
return;
|
||||||
|
var pos = Math.floor(centerY / (rowHeight + rowSpacing));
|
||||||
|
pos = Math.max(0, Math.min(pos, items.length - 1));
|
||||||
|
var arr = workingOrder.slice();
|
||||||
|
var d = arr.indexOf(draggingIndex);
|
||||||
|
if (d < 0 || d === pos)
|
||||||
|
return;
|
||||||
|
arr.splice(d, 1);
|
||||||
|
arr.splice(pos, 0, draggingIndex);
|
||||||
|
workingOrder = arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setCrossMode(active) {
|
||||||
|
if (crossSectionActive === active)
|
||||||
|
return;
|
||||||
|
crossSectionActive = active;
|
||||||
|
if (active)
|
||||||
|
workingOrder = dragStartOrder.slice();
|
||||||
|
}
|
||||||
|
|
||||||
|
function openGapAt(idx) {
|
||||||
|
gapIndex = Math.max(0, Math.min(idx, items.length));
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearGap() {
|
||||||
|
gapIndex = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function commitDrag() {
|
||||||
|
if (draggingIndex < 0)
|
||||||
|
return;
|
||||||
|
const changed = JSON.stringify(workingOrder) !== JSON.stringify(dragStartOrder);
|
||||||
|
const orderedIds = workingOrder.map(i => items[i].id);
|
||||||
|
draggingIndex = -1;
|
||||||
|
draggingId = "";
|
||||||
|
crossSectionActive = false;
|
||||||
|
gapIndex = -1;
|
||||||
|
if (changed)
|
||||||
|
itemOrderChanged(sectionId, orderedIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelDrag() {
|
||||||
|
draggingIndex = -1;
|
||||||
|
draggingId = "";
|
||||||
|
crossSectionActive = false;
|
||||||
|
gapIndex = -1;
|
||||||
|
resetWorkingOrder();
|
||||||
|
}
|
||||||
|
|
||||||
|
onItemsChanged: resetWorkingOrder()
|
||||||
|
Component.onCompleted: resetWorkingOrder()
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: implicitHeight
|
height: implicitHeight
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
@@ -74,11 +176,19 @@ Column {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Item {
|
||||||
id: itemsList
|
id: reorderArea
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingS
|
height: root.totalHeight
|
||||||
|
|
||||||
|
Behavior on height {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.expressiveDurations.normal
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: Theme.expressiveCurves.expressiveDefaultSpatial
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: root.items
|
model: root.items
|
||||||
@@ -86,22 +196,75 @@ Column {
|
|||||||
delegate: Item {
|
delegate: Item {
|
||||||
id: delegateItem
|
id: delegateItem
|
||||||
|
|
||||||
property bool held: dragArea.pressed
|
readonly property int rowIndex: index
|
||||||
property real originalY: y
|
readonly property bool dragging: root.draggingIndex === rowIndex
|
||||||
|
readonly property bool highlighted: root.highlightedId !== "" && root.highlightedId === modelData.id && root.highlightedSection === root.sectionId
|
||||||
|
|
||||||
width: itemsList.width
|
width: reorderArea.width
|
||||||
height: Math.max(70, textColumn.implicitHeight + 32)
|
height: root.rowHeight
|
||||||
z: held ? 2 : 1
|
z: dragging ? 100 : (highlighted ? 3 : 1)
|
||||||
|
opacity: (dragging && root.crossSectionActive) ? 0 : 1
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: delegateItem
|
||||||
|
property: "y"
|
||||||
|
value: root.slotYForIndex(delegateItem.rowIndex)
|
||||||
|
when: !delegateItem.dragging
|
||||||
|
restoreMode: Binding.RestoreNone
|
||||||
|
}
|
||||||
|
|
||||||
|
onYChanged: {
|
||||||
|
if (!dragging)
|
||||||
|
return;
|
||||||
|
root.dragMoved(root.sectionId, delegateItem.mapToItem(root, delegateItem.width / 2, delegateItem.height / 2));
|
||||||
|
if (!root.crossSectionActive)
|
||||||
|
root.updateDragTarget(y + height / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on y {
|
||||||
|
enabled: !delegateItem.dragging
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.expressiveDurations.expressiveDefaultSpatial
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: Theme.expressiveCurves.expressiveFastSpatial
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: itemBackground
|
id: itemBackground
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: 2
|
anchors.margins: 2
|
||||||
radius: Theme.cornerRadius
|
scale: delegateItem.dragging ? 1.02 : 1.0
|
||||||
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.8)
|
transformOrigin: Item.Center
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
radius: delegateItem.dragging ? Theme.cornerRadius + 6 : Theme.cornerRadius
|
||||||
border.width: 0
|
color: delegateItem.dragging ? Theme.secondaryContainer : Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.8)
|
||||||
|
border.color: delegateItem.dragging ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
||||||
|
border.width: delegateItem.dragging ? 2 : 0
|
||||||
|
|
||||||
|
Behavior on scale {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Behavior on radius {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Behavior on border.color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
name: "drag_indicator"
|
name: "drag_indicator"
|
||||||
@@ -866,41 +1029,36 @@ Column {
|
|||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
width: 60
|
anchors.right: actionButtons.left
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.SizeVerCursor
|
cursorShape: delegateItem.dragging ? Qt.ClosedHandCursor : Qt.OpenHandCursor
|
||||||
drag.target: held ? delegateItem : undefined
|
drag.target: delegateItem
|
||||||
drag.axis: Drag.YAxis
|
drag.axis: Drag.YAxis
|
||||||
drag.minimumY: -delegateItem.height
|
drag.minimumY: -2000
|
||||||
drag.maximumY: itemsList.height
|
drag.maximumY: 4000
|
||||||
|
drag.smoothed: false
|
||||||
preventStealing: true
|
preventStealing: true
|
||||||
onPressed: {
|
onPressed: {
|
||||||
delegateItem.z = 2;
|
root.beginDrag(delegateItem.rowIndex);
|
||||||
delegateItem.originalY = delegateItem.y;
|
root.dragStarted(root.sectionId, modelData.id, delegateItem.rowIndex, modelData, delegateItem.mapToItem(root, delegateItem.width / 2, delegateItem.height / 2));
|
||||||
}
|
|
||||||
onReleased: {
|
|
||||||
delegateItem.z = 1;
|
|
||||||
if (drag.active) {
|
|
||||||
var newIndex = Math.round(delegateItem.y / (delegateItem.height + itemsList.spacing));
|
|
||||||
newIndex = Math.max(0, Math.min(newIndex, root.items.length - 1));
|
|
||||||
if (newIndex !== index) {
|
|
||||||
var newItems = root.items.slice();
|
|
||||||
var draggedItem = newItems.splice(index, 1)[0];
|
|
||||||
newItems.splice(newIndex, 0, draggedItem);
|
|
||||||
root.itemOrderChanged(newItems.map(item => root.cloneWidgetData(item)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
delegateItem.x = 0;
|
|
||||||
delegateItem.y = delegateItem.originalY;
|
|
||||||
}
|
}
|
||||||
|
onReleased: root.dragEnded(root.sectionId)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Behavior on y {
|
Rectangle {
|
||||||
enabled: !dragArea.held && !dragArea.drag.active
|
anchors.fill: parent
|
||||||
|
anchors.margins: -2
|
||||||
|
radius: Theme.cornerRadius + 2
|
||||||
|
color: "transparent"
|
||||||
|
border.width: 2
|
||||||
|
border.color: Theme.primary
|
||||||
|
opacity: delegateItem.highlighted && !delegateItem.dragging ? 0.6 : 0
|
||||||
|
visible: opacity > 0.01
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1878,7 +2036,7 @@ Column {
|
|||||||
setting: "showDoNotDisturbIcon"
|
setting: "showDoNotDisturbIcon"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
property var controlCenterGroups: defaultControlCenterGroups
|
property var controlCenterGroups: defaultControlCenterGroups
|
||||||
property int draggedControlCenterGroupIndex: -1
|
property int draggedControlCenterGroupIndex: -1
|
||||||
|
|||||||
Reference in New Issue
Block a user