Fix drag start properly; lazy-load body editor; stop make reconfigure

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.
This commit is contained in:
Levi Neely 2026-10-06 11:06:07 +02:00
parent b2efe6ea27
commit 4c2250ba3c
2 changed files with 46 additions and 32 deletions

View File

@ -24,7 +24,9 @@ debug:
ui: ui:
ifeq ($(HAS_QT6_QML),yes) 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 cmake --build ui/build -j
else else
@echo "Skipping UI build: Qt6 Qml not found" @echo "Skipping UI build: Qt6 Qml not found"

View File

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