BarSettings: reworking drag&drop

This commit is contained in:
LemmyCook 2025-09-04 00:04:02 -04:00
parent f39dd2aa1c
commit 9e819084af

View file

@ -115,93 +115,72 @@ NBox {
} }
// Drag and Drop Widget Area // Drag and Drop Widget Area
Flow { // Replace your Flow section with this:
id: widgetFlow
Layout.fillWidth: true
Layout.fillHeight: true
Layout.minimumHeight: 65 * scaling
spacing: Style.marginS * scaling
flow: Flow.LeftToRight
Repeater { // Drag and Drop Widget Area - use Item container
model: widgetModel Item {
delegate: Rectangle { Layout.fillWidth: true
id: widgetItem Layout.fillHeight: true
required property int index Layout.minimumHeight: 65 * scaling
required property var modelData
width: widgetContent.implicitWidth + Style.marginL * scaling Flow {
height: Style.baseWidgetSize * 1.15 * scaling id: widgetFlow
radius: Style.radiusL * scaling anchors.fill: parent
color: root.getWidgetColor(modelData) spacing: Style.marginS * scaling
border.color: Color.mOutline flow: Flow.LeftToRight
border.width: Math.max(1, Style.borderS * scaling)
// Drag properties Repeater {
Drag.keys: ["widget"] model: widgetModel
Drag.active: mouseArea.drag.active delegate: Rectangle {
Drag.hotSpot.x: width / 2 id: widgetItem
Drag.hotSpot.y: height / 2 required property int index
required property var modelData
// Store the widget index for drag operations width: widgetContent.implicitWidth + Style.marginL * scaling
property int widgetIndex: index height: Style.baseWidgetSize * 1.15 * scaling
readonly property int buttonsWidth: Math.round(20 * scaling) radius: Style.radiusL * scaling
readonly property int buttonsCount: 1 + BarWidgetRegistry.widgetHasUserSettings(modelData.id) color: root.getWidgetColor(modelData)
border.color: Color.mOutline
border.width: Math.max(1, Style.borderS * scaling)
// Visual feedback during drag // Store the widget index for drag operations
states: State { property int widgetIndex: index
when: mouseArea.drag.active readonly property int buttonsWidth: Math.round(20 * scaling)
PropertyChanges { readonly property int buttonsCount: 1 + BarWidgetRegistry.widgetHasUserSettings(modelData.id)
target: widgetItem
scale: 1.1 // Visual feedback during drag
opacity: 0.9 states: State {
z: 1000 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
NText {
text: modelData.id
font.pointSize: Style.fontSizeS * scaling
color: Color.mOnPrimary
horizontalAlignment: Text.AlignHCenter
elide: Text.ElideRight
Layout.preferredWidth: 80 * scaling
} }
RowLayout { RowLayout {
id: widgetContent spacing: 0
Layout.preferredWidth: buttonsCount * buttonsWidth
anchors.centerIn: parent
spacing: Style.marginXXS * scaling Loader {
active: BarWidgetRegistry.widgetHasUserSettings(modelData.id)
NText { sourceComponent: NIconButton {
text: modelData.id icon: "settings"
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: {
// Open widget settings dialog
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 sizeRatio: 0.6
colorBorder: Color.applyOpacity(Color.mOutline, "40") colorBorder: Color.applyOpacity(Color.mOutline, "40")
colorBg: Color.mOnSurface colorBg: Color.mOnSurface
@ -209,151 +188,183 @@ NBox {
colorBgHover: Color.applyOpacity(Color.mOnPrimary, "40") colorBgHover: Color.applyOpacity(Color.mOnPrimary, "40")
colorFgHover: Color.mOnPrimary colorFgHover: Color.mOnPrimary
onClicked: { onClicked: {
removeWidget(sectionId, index) var dialog = Qt.createComponent("BarWidgetSettingsDialog.qml").createObject(root, {
"widgetIndex": index,
"widgetData": modelData,
"widgetId": modelData.id,
"parent": Overlay.overlay
})
dialog.open()
} }
} }
} }
}
// Mouse area for drag and drop NIconButton {
property int mouseXStartDrag: 0 icon: "close"
sizeRatio: 0.6
MouseArea { colorBorder: Color.applyOpacity(Color.mOutline, "40")
id: mouseArea colorBg: Color.mOnSurface
anchors.fill: parent colorFg: Color.mOnPrimary
drag.target: parent colorBgHover: Color.applyOpacity(Color.mOnPrimary, "40")
colorFgHover: Color.mOnPrimary
onPressed: mouse => { onClicked: {
// Check if the click is on the settings or close button area removeWidget(sectionId, index)
const buttonsX = widgetContent.x + widgetContent.width - (buttonsWidth * buttonsCount)
if (mouseX >= buttonsX) {
// Click is on the buttons, don't start drag
mouse.accepted = false
return
}
//Logger.log("BarSectionEditor", `Started dragging widget: ${modelData.id} at index ${index}`)
// Bring to front when starting drag
widgetItem.z = 1000
mouseXStartDrag = mouseX
}
onReleased: mouse => {
//Logger.log("BarSectionEditor", `Released widget: ${modelData.id} at index ${index}`)
// Reset z-index when drag ends
widgetItem.z = 0
console.log(mouseXStartDrag - mouse.x)
// Get the global mouse position
const globalDropX = mouseArea.mouseX + widgetItem.x + widgetFlow.x
const globalDropY = mouseArea.mouseY + widgetItem.y + widgetFlow.y
// Find which widget the drop position is closest to
let targetIndex = -1
let minDistance = Infinity
for (var i = 0; i < widgetModel.length; i++) {
if (i !== index) {
// Get the position of other widgets
const otherWidget = widgetFlow.children[i]
if (otherWidget && otherWidget.widgetIndex !== undefined) {
// Calculate the center of the other widget
const otherCenterX = otherWidget.x + otherWidget.width / 2 + widgetFlow.x
const otherCenterY = otherWidget.y + otherWidget.height / 2 + widgetFlow.y
// Calculate distance to the center of this widget
const distance = Math.sqrt(Math.pow(globalDropX - otherCenterX,
2) + Math.pow(globalDropY - otherCenterY, 2))
if (distance < minDistance) {
minDistance = distance
targetIndex = otherWidget.widgetIndex
}
}
}
}
// Only reorder if we found a valid target and it's different from current position
if (targetIndex !== -1 && targetIndex !== index) {
const fromIndex = index
const toIndex = targetIndex
reorderWidget(sectionId, fromIndex, toIndex)
} }
} }
} }
} }
} }
} }
}
// Drop zone at the beginning (positioned absolutely)
DropArea { // MouseArea outside Flow, covering the same area
id: startDropZone MouseArea {
width: 40 * scaling id: flowDragArea
height: 40 * scaling anchors.fill: parent
x: widgetFlow.x z: 999 // Above all widgets to ensure it gets events first
y: widgetFlow.y + (widgetFlow.height - height) / 2
keys: ["widget"] // Critical properties for proper event handling
z: 1001 // Above the Flow acceptedButtons: Qt.LeftButton
preventStealing: true // Prevent child items from stealing events
Rectangle { propagateComposedEvents: false // Don't propagate to children during drag
anchors.fill: parent hoverEnabled: true
color: startDropZone.containsDrag ? Color.applyOpacity(Color.mPrimary, "20") : Color.transparent
border.color: startDropZone.containsDrag ? Color.mPrimary : Color.transparent property point startPos: Qt.point(0, 0)
border.width: startDropZone.containsDrag ? 2 : 0 property bool dragStarted: false
radius: Style.radiusS * scaling property int draggedIndex: -1
} property real dragThreshold: 15 * scaling
property Item draggedWidget: null
onEntered: function (drag) {//Logger.log("BarSectionEditor", "Entered start drop zone") property point clickOffsetInWidget: Qt.point(0, 0) // Add this line
}
onPressed: mouse => {
onDropped: function (drop) { startPos = Qt.point(mouse.x, mouse.y)
//Logger.log("BarSectionEditor", "Dropped on start zone") dragStarted = false
if (drop.source && drop.source.widgetIndex !== undefined) { draggedIndex = -1
const fromIndex = drop.source.widgetIndex draggedWidget = null
const toIndex = 0 // Insert at the beginning
if (fromIndex !== toIndex) { console.log("Mouse pressed at:", mouse.x, mouse.y)
//Logger.log("BarSectionEditor", `Dropped widget from index ${fromIndex} to beginning`)
reorderWidget(sectionId, fromIndex, toIndex) // 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) {
// Drop zone at the end (positioned absolutely)
DropArea { const localX = mouse.x - widget.x
id: endDropZone const buttonsStartX = widget.width - (widget.buttonsCount * widget.buttonsWidth)
width: 40 * scaling
height: 40 * scaling if (localX < buttonsStartX) {
x: widgetFlow.x + widgetFlow.width - width draggedIndex = widget.widgetIndex
y: widgetFlow.y + (widgetFlow.height - height) / 2 draggedWidget = widget
keys: ["widget"]
z: 1001 // Above the Flow // Calculate and store where within the widget the user clicked
const clickOffsetX = mouse.x - widget.x // Distance from widget's left edge
Rectangle { const clickOffsetY = mouse.y - widget.y // Distance from widget's top edge
anchors.fill: parent clickOffsetInWidget = Qt.point(clickOffsetX, clickOffsetY)
color: endDropZone.containsDrag ? Color.applyOpacity(Color.mPrimary, "20") : Color.transparent
border.color: endDropZone.containsDrag ? Color.mPrimary : Color.transparent Logger.log("BarSectionEditor", "Selected widget:", widgetModel[i].id, "at index", i)
border.width: endDropZone.containsDrag ? 2 : 0 Logger.log("BarSectionEditor", "Widget position:", widget.x, widget.y)
radius: Style.radiusS * scaling Logger.log("BarSectionEditor", "Mouse position:", mouse.x, mouse.y)
} Logger.log("BarSectionEditor", "Click offset within widget:", clickOffsetInWidget.x, clickOffsetInWidget.y)
onEntered: function (drag) {//Logger.log("BarSectionEditor", "Entered end drop zone") // Immediately set prevent stealing to true when drag candidate is found
} preventStealing = true
break
onDropped: function (drop) { }else {
//Logger.log("BarSectionEditor", "Dropped on end zone") // Click was on buttons - allow event propagation
if (drop.source && drop.source.widgetIndex !== undefined) { mouse.accepted = false
const fromIndex = drop.source.widgetIndex return
const toIndex = widgetModel.length // Insert at the end }
if (fromIndex !== toIndex) { }
//Logger.log("BarSectionEditor", `Dropped widget from index ${fromIndex} to end`) }
reorderWidget(sectionId, fromIndex, toIndex) }
} }
}
} 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
}
}
}
}
} }
} }