Something went wrong. Try again.
The agentic engineering control plane for the posthuman future
Something went wrong. Try again.
10 kB · 223 lines
C
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224#pragma once
// MissionControlOverlay's controller. Non-singleton, QML-instantiated: the// overlay creates one and injects the model singletons via DI properties.//// ONE POINTER OWNER. A single MouseArea on the overlay owns the pointer// for the overlay's whole life and relays generalized state here. Nothing// else takes a mouse grab, so there is never a hand-off: panes and drop// zones are plain Items that hit-test THEMSELVES against the published// pointer state and claim reactively.//// Why not hand the grab off? Verified empirically (real OS drag): once an// item's exclusive grab drops, Qt re-runs target discovery on the next// held-button move and re-grabs whichever area is topmost under the// cursor. Hover still reaches other items, but a destination zone can// never receive released(). So the design that works is: never drop it.//// This class owns:// - published pointer state (scene position, pressed, dragArmed),// - the drag session (begin with the source's identity, finish with// exactly one AppModel mutation, cancel),// - drop legality (a lone pane cannot leave its tab or cross into// another workspace as a new tab — the source workspace would die),// - the drag ghost's snapshot hand-off (pool->item(paneId) →// TerminalQuickItem::captureSnapshot()).//// It never touches the scene graph: no item walking, no class-name// matching, no geometry. Containment is computed by the view that owns// the geometry (each pane, each zone, each card, each row).//// The QML side keeps pure view state: open, carousel geometry/snap, the// ghost image, highlights — all bound reactively to the properties here.
#include <QObject>#include <QPointer>#include <QPointF>#include <QSizeF>#include <QString>#include <QtQml/qqmlregistration.h>
#include "AppModel.h"#include "PanePool.h"
class MissionControlController : public QObject { Q_OBJECT QML_ELEMENT Q_DISABLE_COPY_MOVE(MissionControlController)
// DI: the model. Injected by the overlay (AppModel is a singleton; the // page-level binding passes it down so this class stays Quick-free). Q_PROPERTY(AppModel* model READ model WRITE setModel NOTIFY modelChanged) // DI: the pane pool, for the drag ghost's snapshot hand-off. Q_PROPERTY(PanePool* pool READ pool WRITE setPool NOTIFY poolChanged)
// --- published pointer state (the single owner relays into these) -------- // Current pointer position in scene (window) coordinates. Every // containment test in the view reads these; the ghost tracks them. Q_PROPERTY(qreal sceneX READ sceneX NOTIFY pointerChanged) Q_PROPERTY(qreal sceneY READ sceneY NOTIFY pointerChanged) // The position the current press landed at, so a pane can answer "was I // the press target" after the pointer has moved away. Q_PROPERTY(qreal pressSceneX READ pressSceneX NOTIFY pressChanged) Q_PROPERTY(qreal pressSceneY READ pressSceneY NOTIFY pressChanged) // A button is down. Q_PROPERTY(bool pressed READ pressed NOTIFY pressedChanged) // The press has moved past the drag threshold. Deliberately NOT cleared // on release — it is cleared on the NEXT press — so a release handler // can distinguish a click from a drag without any ordering assumption. Q_PROPERTY(bool dragArmed READ dragArmed NOTIFY dragArmedChanged)
// --- drag state (view binds read-only) ----------------------------------- // A pane drag is in flight. Q_PROPERTY(bool dragActive READ dragActive NOTIFY dragActiveChanged) // PaneId of the dragged pane, empty while idle. Q_PROPERTY(QString draggedPaneId READ draggedPaneId NOTIFY dragActiveChanged) // The pooled TerminalQuickItem asked to captureSnapshot() at beginDrag; // QML connects its snapshotReady to feed the ghost image. Null while // idle. Q_PROPERTY(QObject* snapshotTarget READ snapshotTarget NOTIFY dragActiveChanged)
// Ghost geometry: the press position within the source pane cell and // the cell's size, captured at beginDrag (the QML ghost image scales // and offsets by these). Q_PROPERTY(qreal ghostGrabX READ ghostGrabX NOTIFY dragActiveChanged) Q_PROPERTY(qreal ghostGrabY READ ghostGrabY NOTIFY dragActiveChanged) Q_PROPERTY(qreal ghostBaseW READ ghostBaseW NOTIFY dragActiveChanged) Q_PROPERTY(qreal ghostBaseH READ ghostBaseH NOTIFY dragActiveChanged)
public: explicit MissionControlController(QObject* parent = nullptr); ~MissionControlController() override = default;
[[nodiscard]] AppModel* model() const; void setModel(AppModel* model); [[nodiscard]] PanePool* pool() const; void setPool(PanePool* pool);
[[nodiscard]] qreal sceneX() const { return m_scene.x(); } [[nodiscard]] qreal sceneY() const { return m_scene.y(); } [[nodiscard]] qreal pressSceneX() const { return m_press.x(); } [[nodiscard]] qreal pressSceneY() const { return m_press.y(); } [[nodiscard]] bool pressed() const { return m_pressed; } [[nodiscard]] bool dragArmed() const { return m_dragArmed; }
[[nodiscard]] bool dragActive() const { return m_dragActive; } [[nodiscard]] QString draggedPaneId() const { return m_draggedPaneId; } [[nodiscard]] QObject* snapshotTarget() const { return m_snapshotTarget.data(); } [[nodiscard]] qreal ghostGrabX() const { return m_grab.x(); } [[nodiscard]] qreal ghostGrabY() const { return m_grab.y(); } [[nodiscard]] qreal ghostBaseW() const { return m_ghostBase.width(); } [[nodiscard]] qreal ghostBaseH() const { return m_ghostBase.height(); }
// --- pointer ingestion --------------------------------------------------- // The overlay's single pointer owner relays here in scene coordinates. // Every view-side claim happens synchronously inside the notifies these // emit, so pointerReleased can tell "nothing claimed this drop" from // "a zone already finished the drag" just by looking at dragActive. Q_INVOKABLE void pointerPressed(qreal sceneX, qreal sceneY); Q_INVOKABLE void pointerMoved(qreal sceneX, qreal sceneY); // Publishes the release, then cancels any drag still active: QML signal // delivery is synchronous, so by the time the notify returns every hot // MissionDropZone has already called finishDrag(). A drag still live // here was released over nothing legal. Q_INVOKABLE void pointerReleased(qreal sceneX, qreal sceneY);
// --- drag engine --------------------------------------------------------- // Claimed by the MissionPaneTarget whose rect contains the press point, // on the rising edge of dragArmed. grabX/grabY are the press position // within the pane cell; baseW/baseH are that cell's size (the ghost // scales from them). All plain values: this class holds no item. Q_INVOKABLE void beginDrag(const QString& paneId, int wsIndex, int tabIndex, qreal grabX, qreal grabY, qreal baseW, qreal baseH); // Claimed by the hot MissionDropZone on the falling edge of pressed. // Executes exactly one AppModel mutation, then resets. zone is // "left"|"right"|"up"|"down"|"center"|"append"|"newtab"; paneId is the // target pane ("" for append/newtab); tabIndex is -1 for newtab. Q_INVOKABLE void finishDrag(const QString& zone, const QString& paneId, int wsIndex, int tabIndex); Q_INVOKABLE void cancelDrag();
// Click claim. A MissionPaneTarget whose rect contains the press point // calls this on the falling edge of pressed when the gesture never // became a drag (dragArmed stayed false). The first claim wins (panes // tile the fit box, so at most one contains the press). The engine then // reveals that pane and asks the overlay to close; with no claim the // release was on empty background, which just closes. Routing the click // decision here (not in competing QML handlers) removes the ordering // race between "reveal the pane" and "close the overlay". Q_INVOKABLE void claimClick(const QString& paneId);
// Would this drop be refused? Zones bind their highlight to it so an // illegal target never lights up and never claims the release. paneId // is the zone's neighbour pane ("" for append/newtab): a pane dropped // onto one of its OWN zones is a no-op the model refuses, so it must // not light. The source-shape guards mirror AppModel's mutators exactly // (movePaneBeside/movePaneToTab/movePaneToNewTab/swapPanes) so a lit // zone always mutates. Q_INVOKABLE bool legalDrop(const QString& zone, const QString& paneId, int wsIndex, int tabIndex) const;
signals: void modelChanged(); void poolChanged(); void pointerChanged(); void pressChanged(); void pressedChanged(); void dragArmedChanged(); void dragActiveChanged(); // Emitted after finishDrag executed a model mutation, before the state // reset — QML may close/react. void dropExecuted(const QString& zone); // A click (pane or background) ended the gesture: the overlay closes. void closeRequested();
private: void setScene(qreal sceneX, qreal sceneY); void executeClaim(); // The deferred half of executeClaim: runs the AppModel mutation after a // render pass has processed the ghost unbind (see executeClaim). void executeQueuedDrop(); void resetDragState();
[[nodiscard]] bool isIllegalDrop(const QString& zone, const QString& paneId, int wsIndex, int tabIndex) const;
AppModel* m_model = nullptr; PanePool* m_pool = nullptr;
// Published pointer state. QPointF m_scene{0, 0}; QPointF m_press{0, 0}; bool m_pressed = false; bool m_dragArmed = false;
// Drag session. bool m_dragActive = false; QString m_draggedPaneId; QPointF m_grab{0, 0}; QSizeF m_ghostBase{0, 0}; int m_sourceWs = -1; int m_sourceTab = -1; QPointer<QObject> m_snapshotTarget;
// The best drop claim recorded during the gesture, executed on release. // Zones claim synchronously inside pressedChanged(); specificity // ordering makes the winner independent of handler order. QString m_claimZone; QString m_claimPaneId; int m_claimWs = -1; int m_claimTab = -1;
// The pane that claimed the click (a release that never armed a drag), // recorded synchronously inside pressedChanged() and resolved when the // release returns. Empty for a background click. QString m_clickPaneId;};