From 4a45e73125be7976e63254e702afc7625f9c59f7 Mon Sep 17 00:00:00 2001 From: LemmyCook Date: Thu, 4 Sep 2025 00:27:38 -0400 Subject: [PATCH] BarSettings: better D&D --- .../SettingsPanel/Extras/BarSectionEditor.qml | 522 ++++++++++-------- Modules/SettingsPanel/Tabs/BarTab.qml | 45 +- 2 files changed, 315 insertions(+), 252 deletions(-) diff --git a/Modules/SettingsPanel/Extras/BarSectionEditor.qml b/Modules/SettingsPanel/Extras/BarSectionEditor.qml index 0702347..7bfd14a 100644 --- a/Modules/SettingsPanel/Extras/BarSectionEditor.qml +++ b/Modules/SettingsPanel/Extras/BarSectionEditor.qml @@ -117,254 +117,316 @@ NBox { // Drag and Drop Widget Area // Replace your Flow section with this: -// Drag and Drop Widget Area - use Item container -Item { - Layout.fillWidth: true - Layout.fillHeight: true - Layout.minimumHeight: 65 * scaling + // Drag and Drop Widget Area - use Item container + Item { + Layout.fillWidth: true + Layout.fillHeight: true + Layout.minimumHeight: 65 * scaling - Flow { - id: widgetFlow - anchors.fill: parent - spacing: Style.marginS * scaling - flow: Flow.LeftToRight + Flow { + id: widgetFlow + anchors.fill: parent + spacing: Style.marginS * scaling + flow: Flow.LeftToRight - Repeater { - model: widgetModel - delegate: Rectangle { - id: widgetItem - required property int index - required property var modelData + Repeater { + model: widgetModel + delegate: Rectangle { + id: widgetItem + required property int index + required property var modelData - width: widgetContent.implicitWidth + Style.marginL * scaling - height: Style.baseWidgetSize * 1.15 * scaling - radius: Style.radiusL * scaling - color: root.getWidgetColor(modelData) - border.color: Color.mOutline - border.width: Math.max(1, Style.borderS * scaling) + width: widgetContent.implicitWidth + Style.marginL * scaling + height: Style.baseWidgetSize * 1.15 * scaling + radius: Style.radiusL * scaling + color: root.getWidgetColor(modelData) + border.color: Color.mOutline + border.width: Math.max(1, Style.borderS * scaling) - // Store the widget index for drag operations - property int widgetIndex: index - readonly property int buttonsWidth: Math.round(20 * scaling) - readonly property int buttonsCount: 1 + BarWidgetRegistry.widgetHasUserSettings(modelData.id) + // Store the widget index for drag operations + property int widgetIndex: index + readonly property int buttonsWidth: Math.round(20 * scaling) + readonly property int buttonsCount: 1 + BarWidgetRegistry.widgetHasUserSettings(modelData.id) - // Visual feedback during drag - states: State { - when: flowDragArea.draggedIndex === index - PropertyChanges { - target: widgetItem - scale: 1.1 - opacity: 0.9 - z: 1000 - } - } + // Visual feedback during drag + states: State { + when: flowDragArea.draggedIndex === index + PropertyChanges { + target: widgetItem + scale: 1.1 + opacity: 0.9 + z: 1000 + } + } - RowLayout { - id: widgetContent - anchors.centerIn: parent - spacing: Style.marginXXS * scaling + RowLayout { + id: widgetContent + anchors.centerIn: parent + spacing: Style.marginXXS * scaling - NText { - text: modelData.id - font.pointSize: Style.fontSizeS * scaling - color: Color.mOnPrimary - horizontalAlignment: Text.AlignHCenter - elide: Text.ElideRight - Layout.preferredWidth: 80 * scaling - } + NText { + text: modelData.id + font.pointSize: Style.fontSizeS * scaling + color: Color.mOnPrimary + horizontalAlignment: Text.AlignHCenter + elide: Text.ElideRight + Layout.preferredWidth: 80 * scaling + } - RowLayout { - spacing: 0 - Layout.preferredWidth: buttonsCount * buttonsWidth - - Loader { - active: BarWidgetRegistry.widgetHasUserSettings(modelData.id) - sourceComponent: NIconButton { - icon: "settings" - sizeRatio: 0.6 - colorBorder: Color.applyOpacity(Color.mOutline, "40") - colorBg: Color.mOnSurface - colorFg: Color.mOnPrimary - colorBgHover: Color.applyOpacity(Color.mOnPrimary, "40") - colorFgHover: Color.mOnPrimary - onClicked: { - var dialog = Qt.createComponent("BarWidgetSettingsDialog.qml").createObject(root, { - "widgetIndex": index, - "widgetData": modelData, - "widgetId": modelData.id, - "parent": Overlay.overlay - }) - dialog.open() + RowLayout { + spacing: 0 + Layout.preferredWidth: buttonsCount * buttonsWidth + + Loader { + active: BarWidgetRegistry.widgetHasUserSettings(modelData.id) + sourceComponent: NIconButton { + icon: "settings" + sizeRatio: 0.6 + colorBorder: Color.applyOpacity(Color.mOutline, "40") + colorBg: Color.mOnSurface + colorFg: Color.mOnPrimary + colorBgHover: Color.applyOpacity(Color.mOnPrimary, "40") + colorFgHover: Color.mOnPrimary + onClicked: { + var dialog = Qt.createComponent("BarWidgetSettingsDialog.qml").createObject(root, { + "widgetIndex": index, + "widgetData": modelData, + "widgetId": modelData.id, + "parent": Overlay.overlay + }) + dialog.open() + } + } + } + + NIconButton { + icon: "close" + sizeRatio: 0.6 + colorBorder: Color.applyOpacity(Color.mOutline, "40") + colorBg: Color.mOnSurface + colorFg: Color.mOnPrimary + colorBgHover: Color.applyOpacity(Color.mOnPrimary, "40") + colorFgHover: Color.mOnPrimary + onClicked: { + removeWidget(sectionId, index) + } } } } - - NIconButton { - icon: "close" - sizeRatio: 0.6 - colorBorder: Color.applyOpacity(Color.mOutline, "40") - colorBg: Color.mOnSurface - colorFg: Color.mOnPrimary - colorBgHover: Color.applyOpacity(Color.mOnPrimary, "40") - colorFgHover: Color.mOnPrimary - onClicked: { - removeWidget(sectionId, index) - } - } } } } - } - } -// MouseArea outside Flow, covering the same area - MouseArea { - id: flowDragArea - anchors.fill: parent - z: 999 // Above all widgets to ensure it gets events first - - // Critical properties for proper event handling - acceptedButtons: Qt.LeftButton - preventStealing: true // Prevent child items from stealing events - propagateComposedEvents: false // Don't propagate to children during drag - hoverEnabled: true - - property point startPos: Qt.point(0, 0) - property bool dragStarted: false - property int draggedIndex: -1 - property real dragThreshold: 15 * scaling - property Item draggedWidget: null - property point clickOffsetInWidget: Qt.point(0, 0) // Add this line - - onPressed: mouse => { - startPos = Qt.point(mouse.x, mouse.y) - dragStarted = false - draggedIndex = -1 - draggedWidget = null - - console.log("Mouse pressed at:", mouse.x, mouse.y) - - // Find which widget was clicked - for (var i = 0; i < widgetModel.length; i++) { - const widget = widgetFlow.children[i] - if (widget && widget.widgetIndex !== undefined) { - if (mouse.x >= widget.x && mouse.x <= widget.x + widget.width && - mouse.y >= widget.y && mouse.y <= widget.y + widget.height) { - - const localX = mouse.x - widget.x - const buttonsStartX = widget.width - (widget.buttonsCount * widget.buttonsWidth) - - if (localX < buttonsStartX) { - draggedIndex = widget.widgetIndex - draggedWidget = widget - - // Calculate and store where within the widget the user clicked - const clickOffsetX = mouse.x - widget.x // Distance from widget's left edge - const clickOffsetY = mouse.y - widget.y // Distance from widget's top edge - clickOffsetInWidget = Qt.point(clickOffsetX, clickOffsetY) - - Logger.log("BarSectionEditor", "Selected widget:", widgetModel[i].id, "at index", i) - Logger.log("BarSectionEditor", "Widget position:", widget.x, widget.y) - Logger.log("BarSectionEditor", "Mouse position:", mouse.x, mouse.y) - Logger.log("BarSectionEditor", "Click offset within widget:", clickOffsetInWidget.x, clickOffsetInWidget.y) - - // Immediately set prevent stealing to true when drag candidate is found - preventStealing = true - break - }else { - // Click was on buttons - allow event propagation - mouse.accepted = false - return - } + // MouseArea outside Flow, covering the same area + MouseArea { + id: flowDragArea + anchors.fill: parent + z: 999 // Above all widgets to ensure it gets events first + + // Critical properties for proper event handling + acceptedButtons: Qt.LeftButton + preventStealing: false // Prevent child items from stealing events + propagateComposedEvents: draggedIndex != -1 // Don't propagate to children during drag + hoverEnabled: draggedIndex != -1 + + property point startPos: Qt.point(0, 0) + property bool dragStarted: false + property int draggedIndex: -1 + property real dragThreshold: 15 * scaling + property Item draggedWidget: null + property point clickOffsetInWidget: Qt.point(0, 0) + property point originalWidgetPos: Qt.point(0, 0) // ADD THIS: Store original position + + onPressed: mouse => { + startPos = Qt.point(mouse.x, mouse.y) + dragStarted = false + draggedIndex = -1 + draggedWidget = null + + // Find which widget was clicked + for (var i = 0; i < widgetModel.length; i++) { + const widget = widgetFlow.children[i] + if (widget && widget.widgetIndex !== undefined) { + if (mouse.x >= widget.x && mouse.x <= widget.x + widget.width && mouse.y >= widget.y + && mouse.y <= widget.y + widget.height) { + + const localX = mouse.x - widget.x + const buttonsStartX = widget.width - (widget.buttonsCount * widget.buttonsWidth) + + if (localX < buttonsStartX) { + draggedIndex = widget.widgetIndex + draggedWidget = widget + + // Calculate and store where within the widget the user clicked + const clickOffsetX = mouse.x - widget.x + const clickOffsetY = mouse.y - widget.y + clickOffsetInWidget = Qt.point(clickOffsetX, clickOffsetY) + + // STORE ORIGINAL POSITION + originalWidgetPos = Qt.point(widget.x, widget.y) + + // Immediately set prevent stealing to true when drag candidate is found + preventStealing = true + break + } else { + // Click was on buttons - allow event propagation + mouse.accepted = false + return + } + } + } + } + } + + onPositionChanged: mouse => { + if (draggedIndex !== -1) { + const deltaX = mouse.x - startPos.x + const deltaY = mouse.y - startPos.y + const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY) + + if (!dragStarted && distance > dragThreshold) { + dragStarted = true + //Logger.log("BarSectionEditor", "Drag started") + + // Enable visual feedback + if (draggedWidget) { + draggedWidget.z = 1000 + } + } + + if (dragStarted && draggedWidget) { + // Adjust position to account for where within the widget the user clicked + draggedWidget.x = mouse.x - clickOffsetInWidget.x + draggedWidget.y = mouse.y - clickOffsetInWidget.y + } + } + } + + onReleased: mouse => { + if (dragStarted && draggedWidget) { + // Find drop target using improved logic + let targetIndex = -1 + let minDistance = Infinity + const mouseX = mouse.x + const mouseY = mouse.y + + // Check if we should insert at the beginning + let insertAtBeginning = true + let insertAtEnd = true + + // Check if the dragged item is already the last item + let isLastItem = true + for (var k = 0; k < widgetModel.length; k++) { + if (k !== draggedIndex && k > draggedIndex) { + isLastItem = false + break + } + } + + for (var i = 0; i < widgetModel.length; i++) { + if (i !== draggedIndex) { + const widget = widgetFlow.children[i] + if (widget && widget.widgetIndex !== undefined) { + const centerX = widget.x + widget.width / 2 + const centerY = widget.y + widget.height / 2 + const distance = Math.sqrt(Math.pow(mouseX - centerX, 2) + Math.pow(mouseY - centerY, 2)) + + // Check if mouse is to the right of this widget + if (mouseX > widget.x + widget.width / 2) { + insertAtBeginning = false + } + // Check if mouse is to the left of this widget + if (mouseX < widget.x + widget.width / 2) { + insertAtEnd = false + } + + if (distance < minDistance) { + minDistance = distance + targetIndex = widget.widgetIndex + } + } + } + } + + // If dragging the last item to the right, don't reorder + if (isLastItem && insertAtEnd) { + insertAtEnd = false + targetIndex = -1 + Logger.log("BarSectionEditor", "Last item dropped to right - no reordering needed") + } + + // Determine final target index based on position + let finalTargetIndex = targetIndex + + if (insertAtBeginning && widgetModel.length > 1) { + // Insert at the very beginning (position 0) + finalTargetIndex = 0 + Logger.log("BarSectionEditor", "Inserting at beginning") + } else if (insertAtEnd && widgetModel.length > 1) { + // Insert at the very end + let maxIndex = -1 + for (var j = 0; j < widgetModel.length; j++) { + if (j !== draggedIndex) { + maxIndex = Math.max(maxIndex, j) + } + } + finalTargetIndex = maxIndex + Logger.log("BarSectionEditor", "Inserting at end, target:", finalTargetIndex) + } else if (targetIndex !== -1) { + // Normal case - determine if we should insert before or after the target + const targetWidget = widgetFlow.children[targetIndex] + if (targetWidget) { + const targetCenterX = targetWidget.x + targetWidget.width / 2 + if (mouseX > targetCenterX) { + // Mouse is to the right of target center, insert after + Logger.log("BarSectionEditor", "Inserting after widget at index:", targetIndex) + } else { + // Mouse is to the left of target center, insert before + finalTargetIndex = targetIndex + Logger.log("BarSectionEditor", "Inserting before widget at index:", targetIndex) + } + } + } + + Logger.log("BarSectionEditor", "Final drop target index:", finalTargetIndex) + + // Check if reordering is needed + if (finalTargetIndex !== -1 && finalTargetIndex !== draggedIndex) { + // Reordering will happen - reset position for the Flow to handle + draggedWidget.x = 0 + draggedWidget.y = 0 + draggedWidget.z = 0 + reorderWidget(sectionId, draggedIndex, finalTargetIndex) + } else { + // No reordering - restore original position + draggedWidget.x = originalWidgetPos.x + draggedWidget.y = originalWidgetPos.y + draggedWidget.z = 0 + Logger.log("BarSectionEditor", "No reordering - restoring original position") + } + } else if (draggedIndex !== -1 && !dragStarted) { + + // This was a click without drag - could add click handling here if needed + } + + // Reset everything + dragStarted = false + draggedIndex = -1 + draggedWidget = null + preventStealing = false // Allow normal event propagation again + originalWidgetPos = Qt.point(0, 0) // Reset stored position + } + + // Handle case where mouse leaves the area during drag + onExited: { + if (dragStarted && draggedWidget) { + // Restore original position when mouse leaves area + draggedWidget.x = originalWidgetPos.x + draggedWidget.y = originalWidgetPos.y + draggedWidget.z = 0 } } } } - - onPositionChanged: mouse => { - if (draggedIndex !== -1) { - const deltaX = mouse.x - startPos.x - const deltaY = mouse.y - startPos.y - const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY) - - //Logger.log("BarSectionEditor", "Position changed - distance:", distance.toFixed(2)) - - if (!dragStarted && distance > dragThreshold) { - dragStarted = true - Logger.log("BarSectionEditor", "Drag started") - - // Enable visual feedback - if (draggedWidget) { - draggedWidget.z = 1000 - } - } - - if (dragStarted && draggedWidget) { - // Adjust position to account for where within the widget the user clicked - draggedWidget.x = mouse.x - clickOffsetInWidget.x - draggedWidget.y = mouse.y - clickOffsetInWidget.y - } - } - } - - onReleased: mouse => { - if (dragStarted && draggedWidget) { - // Find drop target using current mouse position - let targetIndex = -1 - let minDistance = Infinity - - for (var i = 0; i < widgetModel.length; i++) { - if (i !== draggedIndex) { - const widget = widgetFlow.children[i] - if (widget && widget.widgetIndex !== undefined) { - const centerX = widget.x + widget.width / 2 - const centerY = widget.y + widget.height / 2 - const distance = Math.sqrt(Math.pow(mouse.x - centerX, 2) + Math.pow(mouse.y - centerY, 2)) - - if (distance < minDistance) { - minDistance = distance - targetIndex = widget.widgetIndex - } - } - } - } - - Logger.log("BarSectionEditor", "Drop target index:", targetIndex) - - // Reset widget position and z-order - draggedWidget.x = 0 - draggedWidget.y = 0 - draggedWidget.z = 0 - - if (targetIndex !== -1 && targetIndex !== draggedIndex) { - reorderWidget(sectionId, draggedIndex, targetIndex) - } - } else if (draggedIndex !== -1 && !dragStarted) { - // This was a click without drag - simulate click on the widget - // Find the clicked widget and trigger appropriate action - const widget = draggedWidget - if (widget) { - // Could add click handling here if needed - } - } - - // Reset everything - dragStarted = false - draggedIndex = -1 - draggedWidget = null - preventStealing = false // Allow normal event propagation again - } - - // Handle case where mouse leaves the area during drag - onExited: { - if (dragStarted && draggedWidget) { - // Reset position but keep drag state until release - draggedWidget.x = 0 - draggedWidget.y = 0 - draggedWidget.z = 0 - } - } - } -} } } diff --git a/Modules/SettingsPanel/Tabs/BarTab.qml b/Modules/SettingsPanel/Tabs/BarTab.qml index 566228d..e1bf63a 100644 --- a/Modules/SettingsPanel/Tabs/BarTab.qml +++ b/Modules/SettingsPanel/Tabs/BarTab.qml @@ -163,10 +163,10 @@ ColumnLayout { sectionId: "left" widgetModel: Settings.data.bar.widgets.left availableWidgets: availableWidgets - onAddWidget: (widgetId, section) => addWidgetToSection(widgetId, section) - onRemoveWidget: (section, index) => removeWidgetFromSection(section, index) - onReorderWidget: (section, fromIndex, toIndex) => reorderWidgetInSection(section, fromIndex, toIndex) - onUpdateWidgetSettings: (section, index, settings) => updateWidgetSettingsInSection(section, index, settings) + onAddWidget: (widgetId, section) => _addWidgetToSection(widgetId, section) + onRemoveWidget: (section, index) => _removeWidgetFromSection(section, index) + onReorderWidget: (section, fromIndex, toIndex) => _reorderWidgetInSection(section, fromIndex, toIndex) + onUpdateWidgetSettings: (section, index, settings) => _updateWidgetSettingsInSection(section, index, settings) } // Center Section @@ -175,10 +175,10 @@ ColumnLayout { sectionId: "center" widgetModel: Settings.data.bar.widgets.center availableWidgets: availableWidgets - onAddWidget: (widgetId, section) => addWidgetToSection(widgetId, section) - onRemoveWidget: (section, index) => removeWidgetFromSection(section, index) - onReorderWidget: (section, fromIndex, toIndex) => reorderWidgetInSection(section, fromIndex, toIndex) - onUpdateWidgetSettings: (section, index, settings) => updateWidgetSettingsInSection(section, index, settings) + onAddWidget: (widgetId, section) => _addWidgetToSection(widgetId, section) + onRemoveWidget: (section, index) => _removeWidgetFromSection(section, index) + onReorderWidget: (section, fromIndex, toIndex) => _reorderWidgetInSection(section, fromIndex, toIndex) + onUpdateWidgetSettings: (section, index, settings) => _updateWidgetSettingsInSection(section, index, settings) } // Right Section @@ -187,10 +187,10 @@ ColumnLayout { sectionId: "right" widgetModel: Settings.data.bar.widgets.right availableWidgets: availableWidgets - onAddWidget: (widgetId, section) => addWidgetToSection(widgetId, section) - onRemoveWidget: (section, index) => removeWidgetFromSection(section, index) - onReorderWidget: (section, fromIndex, toIndex) => reorderWidgetInSection(section, fromIndex, toIndex) - onUpdateWidgetSettings: (section, index, settings) => updateWidgetSettingsInSection(section, index, settings) + onAddWidget: (widgetId, section) => _addWidgetToSection(widgetId, section) + onRemoveWidget: (section, index) => _removeWidgetFromSection(section, index) + onReorderWidget: (section, fromIndex, toIndex) => _reorderWidgetInSection(section, fromIndex, toIndex) + onUpdateWidgetSettings: (section, index, settings) => _updateWidgetSettingsInSection(section, index, settings) } } } @@ -202,14 +202,9 @@ ColumnLayout { } // --------------------------------- - // Helper functions - function updateWidgetSettingsInSection(section, index, settings) { - // Update the widget settings in the Settings data - Settings.data.bar.widgets[section][index] = settings - //Logger.log("BarTab", `Updated widget settings for ${settings.id} in ${section} section`) - } - - function addWidgetToSection(widgetId, section) { + // Signal functions + // --------------------------------- + function _addWidgetToSection(widgetId, section) { var newWidget = { "id": widgetId } @@ -226,7 +221,7 @@ ColumnLayout { Settings.data.bar.widgets[section].push(newWidget) } - function removeWidgetFromSection(section, index) { + function _removeWidgetFromSection(section, index) { if (index >= 0 && index < Settings.data.bar.widgets[section].length) { var newArray = Settings.data.bar.widgets[section].slice() newArray.splice(index, 1) @@ -234,7 +229,7 @@ ColumnLayout { } } - function reorderWidgetInSection(section, fromIndex, toIndex) { + function _reorderWidgetInSection(section, fromIndex, toIndex) { if (fromIndex >= 0 && fromIndex < Settings.data.bar.widgets[section].length && toIndex >= 0 && toIndex < Settings.data.bar.widgets[section].length) { @@ -249,6 +244,12 @@ ColumnLayout { } } + function _updateWidgetSettingsInSection(section, index, settings) { + // Update the widget settings in the Settings data + Settings.data.bar.widgets[section][index] = settings + //Logger.log("BarTab", `Updated widget settings for ${settings.id} in ${section} section`) + } + // Base list model for all combo boxes ListModel { id: availableWidgets