diff --git a/Makefile b/Makefile index 1b214cc..84be693 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,9 @@ debug: ui: ifeq ($(HAS_QT6_QML),yes) - cmake -S ui -B ui/build -DCMAKE_BUILD_TYPE=Release + @if [ ! -f ui/build/Makefile ]; then \ + cmake -S ui -B ui/build -DCMAKE_BUILD_TYPE=Release; \ + fi cmake --build ui/build -j else @echo "Skipping UI build: Qt6 Qml not found" diff --git a/ui/qml/OutlineView.qml b/ui/qml/OutlineView.qml index ca5a43e..86787db 100644 --- a/ui/qml/OutlineView.qml +++ b/ui/qml/OutlineView.qml @@ -554,6 +554,7 @@ ListView { Layout.preferredHeight: outline.baseFontSize + 8 radius: 3 readonly property bool active: gripMouse.pressed + || gripMouse.drag.active color: active ? theme.highlightColor : (gripMouse.containsMouse ? theme.alternateBaseColor : "transparent") @@ -566,16 +567,29 @@ ListView { opacity: gripMouse.containsMouse || gripLabel.active ? 1.0 : 0.4 } - // Invisible proxy that carries the drag payload. Starting a - // Qt Automatic drag on it hands the gesture to the window's - // drag manager, independent of this (destructible) delegate. + // Invisible proxy that carries the drag payload. Binding + // Drag.active to the MouseArea's drag.active and using + // dragType Automatic makes Qt start an OS-level drag by + // itself (no startDrag() call) when the drag threshold is + // crossed. The OS drag is scene/window-level, so it is + // delivered to DropAreas and survives the view springing to + // another file mid-drag. Item { id: dragProxy - anchors.fill: parent + visible: false + width: 1; height: 1 + Drag.active: gripMouse.drag.active Drag.dragType: Drag.Automatic Drag.supportedActions: Qt.MoveAction Drag.proposedAction: Qt.MoveAction Drag.keys: ["ork-section"] + Drag.onActiveChanged: { + if (!Drag.active && outline.dragActive) { + // Drag ended. A drop on the outline committed via + // the DropArea; otherwise clean up here. + outline.commitDrop() + } + } } MouseArea { @@ -585,30 +599,26 @@ ListView { cursorShape: gripLabel.active ? Qt.ClosedHandCursor : Qt.OpenHandCursor preventStealing: true - property bool started: false - onPressed: started = false - onPositionChanged: (mouse) => { - if (started) return - started = true - // Record the drag source in the model (survives a - // later file switch) and begin the OS drag. For a - // Drag.Automatic drag, startDrag() activates it — - // do NOT set Drag.active manually first. + // Dragging this (invisible, unanchored) proxy provides + // the drag.active signal the proxy's Drag binds to. + drag.target: dragProxy + drag.threshold: 4 + onPressed: { + // Record the drag source and payload up front; the + // Automatic drag auto-starts once the threshold is + // crossed. docModel.beginDrag(del.index) outline.dragActive = true dragProxy.Drag.mimeData = { "text/plain": docModel.dragSourceDoc() + "\t" + docModel.dragSourceId() } - dragProxy.Drag.startDrag() } onReleased: { - // startDrag() blocks until the OS drag finishes, so - // by here the drop (if any) has been handled by a - // DropArea. Clear any drag state that a drop didn't. - if (outline.dragActive) + // A plain click (no drag) never activated the drag: + // clear the state we set in onPressed. + if (outline.dragActive && !drag.active) outline.commitDrop() - started = false } } } @@ -1291,20 +1301,21 @@ ListView { } } - // Inline body editor. Replaces the rendered segments while - // editing. Seeded with the raw body text; a toolbar inserts a - // timestamp at the cursor; Save writes via setBody (one 9P - // write, server reparses), Cancel/Escape discards. - ColumnLayout { - id: bodyEditColumn - visible: del.editingBody + // Inline body editor. Loaded lazily (only when the user opens + // the editor) so the TextArea is not instantiated per row — + // avoiding a Breeze style instantiation error and saving memory. + Loader { + id: bodyEditLoader + active: del.editingBody Layout.fillWidth: true - spacing: 4 + sourceComponent: ColumnLayout { + id: bodyEditColumn + spacing: 4 - function saveBody() { - docModel.setBody(del.index, bodyEditor.text) - del.editingBody = false - } + function saveBody() { + docModel.setBody(del.index, bodyEditor.text) + del.editingBody = false + } // Bordered scroll frame holding the editor. Uses the // Flickable + TextArea.flickable idiom (not ScrollView), @@ -1388,6 +1399,7 @@ ListView { onClicked: bodyEditColumn.saveBody() } } + } } } }