From 4c2250ba3cd47d920ed0365346014b7902edc45a Mon Sep 17 00:00:00 2001 From: Levi Neely Date: Tue, 6 Oct 2026 11:06:07 +0200 Subject: [PATCH] Fix drag start properly; lazy-load body editor; stop make reconfigure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three fixes: Drag: the previous attempt removed Drag.active=true before startDrag(), but startDrag() *requires* an active drag — so it errored "startDrag() drag must be active" and no drag began (the no-drop cursor). Use the idiomatic automatic-drag pattern instead: bind the proxy's Drag.active to the grip MouseArea's drag.active and set drag.target to the proxy; with dragType Automatic, Qt starts the OS drag itself when the threshold is crossed — no startDrag() call. Lifecycle is driven off Drag.onActiveChanged / onReleased. Body editor: wrap it in a Loader (active: editingBody) so the TextArea is only instantiated when the user opens the editor, not once per row. This avoids the per-row Breeze "Unable to assign TextArea to QQuickTextInput" instantiation error and is lighter. Makefile: the ui target ran `cmake -S ui -B ui/build` on every invocation, reconfiguring the tree each time (which could leave the QML cache stale relative to edited .qml). Configure only when ui/build/Makefile is absent; otherwise just `cmake --build`. GUI builds clean. --- Makefile | 4 ++- ui/qml/OutlineView.qml | 74 ++++++++++++++++++++++++------------------ 2 files changed, 46 insertions(+), 32 deletions(-) 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() } } + } } } }