SWMMVis  6.0.0-alpha.4
Qt6/C++ GIS-based graphical user interface for the SWMMVis engine (6.0.0-alpha.4)
Loading...
Searching...
No Matches
layertreepanel.h
Go to the documentation of this file.
1
10#ifndef LAYERTREEPANEL_H
11#define LAYERTREEPANEL_H
12
13#include <QAbstractItemModel>
14#include <QHash>
15#include <QList>
16#include <QPointer>
17#include <QSet>
18#include <QVector>
19#include <QWidget>
20
21#include <array>
22#include <vector>
23
24class QTreeView;
25class QToolBar;
26class QLineEdit;
27class QPoint;
28class QSortFilterProxyModel;
29class MapCanvas;
34
35// Slice S3 — sublayer row type forward decl.
36namespace OpenSWMM::Render { class ISublayer; }
37
71class LayerTreeModel : public QAbstractItemModel
72{
73 Q_OBJECT
74
75public:
76
77 explicit LayerTreeModel(MapCanvas *canvas = nullptr, QObject *parent = nullptr);
78 ~LayerTreeModel() override;
79
88 [[nodiscard]] MapCanvas *canvas() const;
89
90 // QAbstractItemModel interface
91 QModelIndex index(int row, int column, const QModelIndex &parent = {}) const override;
92 QModelIndex parent(const QModelIndex &child) const override;
93 int rowCount(const QModelIndex &parent = {}) const override;
94 int columnCount(const QModelIndex &parent = {}) const override;
95 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
96 bool setData(const QModelIndex &index, const QVariant &value, int role) override;
97 Qt::ItemFlags flags(const QModelIndex &index) const override;
98 QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
99
100 // Drag-and-drop reordering (within a category only).
101 Qt::DropActions supportedDropActions() const override;
102 bool canDropMimeData(const QMimeData *data, Qt::DropAction action,
103 int row, int column, const QModelIndex &parent) const override;
104 bool dropMimeData(const QMimeData *data, Qt::DropAction action,
105 int row, int column, const QModelIndex &parent) override;
106 QMimeData *mimeData(const QModelIndexList &indexes) const override;
107 QStringList mimeTypes() const override;
108
113 [[nodiscard]] OpenSWMMVisLayer *layerForIndex(const QModelIndex &index) const;
114
118 [[nodiscard]] bool isCategoryIndex(const QModelIndex &index) const;
119
120 // ---- Slice BI-MK.LT: 3-level tree for multi-kind layers ----------------
121 //
122 // Under each `SWMMModelLayer` row the model exposes 11 sub-rows, one per
123 // Category enum value (Junctions, Outfalls, ..., RainGages). The sub-row
124 // surfaces a per-kind visibility checkbox and feeds the right-click
125 // context menu's Style submenu.
126
128 [[nodiscard]] bool isKindIndex(const QModelIndex &index) const;
129
131 [[nodiscard]] OpenSWMMVisLayer *kindParentLayer(const QModelIndex &index) const;
132
135 [[nodiscard]] int kindOrdinal(const QModelIndex &index) const;
136
137 // ---- Slice S3 (RENDERING_OUTPUT_SUBLAYERS_PLAN.md §4.1) -----------------
138 // 3rd-level sub-rows under any ISublayerHost layer that is NOT already
139 // multi-kind. SWMM2DResultsLayer adopts this — SWMMResultsLayer keeps
140 // its existing OUT.3 kind-row UX (the eligibility rule below avoids
141 // dual sub-row schemes on the same layer; the kind-vs-sublayer
142 // interaction on multi-kind hosts is a follow-up UX iteration).
143
145 [[nodiscard]] bool isSublayerIndex(const QModelIndex &index) const;
146
148 [[nodiscard]] OpenSWMMVisLayer *sublayerParentLayer(const QModelIndex &index) const;
149
151 [[nodiscard]] OpenSWMM::Render::ISublayer *
152 sublayerForIndex(const QModelIndex &index) const;
153
168 bool reorderCategory(int categoryId, int dstTreeRow);
169
171 [[nodiscard]] int categoryIdForIndex(const QModelIndex &index) const;
172
173 // Index lookup for re-selecting a row after a model reset (every
174 // reorder resets, which drops the view's selection).
175 [[nodiscard]] QModelIndex indexForCategory(int categoryId) const;
176 [[nodiscard]] QModelIndex indexForLayer(OpenSWMMVisLayer *layer) const;
177 [[nodiscard]] QModelIndex indexForSublayer(OpenSWMMVisLayer *layer,
178 OpenSWMM::Render::ISublayer *sublayer) const;
179
188
189private slots:
190 void onLayerAdded(OpenSWMMVisLayer *layer);
191 void onLayerRemoved(OpenSWMMVisLayer *layer);
192 void onLayerOrderChanged();
193 void onLayerDataChanged(OpenSWMMVisLayer *layer);
194 void onSublayerOrderChanged(OpenSWMMVisLayer *host);
195
196private:
197 struct Category {
198 int id = -1; // openswmmvis::ui::CategoryId
199 QString name;
200 QString iconAlias; // Qt resource alias under :/swmmvis/
201 QVector<OpenSWMMVisLayer *> layers; // canvas-stack order, top first
202 };
203
204 // Slice BI-MK.LT — one KindRow per (multi-kind layer, kind ordinal).
205 // Allocated in m_kindRowStorage, then std::array makes the addresses
206 // stable across model lifetime so they're safe to use as QModelIndex
207 // internalPointers. Hash is keyed on the parent layer pointer; entries
208 // exist only for layers that support multi-kind styling (today: only
209 // SWMMModelLayer with 11 kinds).
210 struct KindRow {
211 OpenSWMMVisLayer *layer = nullptr;
212 int kindOrdinal = -1;
213 };
214 static constexpr int kKindsPerSwmmModelLayer = 11;
215
216 // Slice S3 — one SublayerRow per (host layer, sublayer pointer).
217 // Storage is a std::vector reserved to the final size at rebuild time
218 // and never modified afterwards, so element addresses stay stable for
219 // the model's lifetime — same stability contract as the KindRow
220 // std::array storage.
221 struct SublayerRow {
222 OpenSWMMVisLayer *layer = nullptr;
223 OpenSWMM::Render::ISublayer *sublayer = nullptr;
224 };
225
226 void rebuildCategories();
227 void rebuildKindRows();
228 void rebuildSublayerRows();
229 int categoryOf(OpenSWMMVisLayer *layer) const;
230 int sublayerRowIndex(const void *sublayerRowPtr) const;
231
232 // QPointer, not a raw pointer: the canvas dies with its project window
233 // while this dock outlives it, and setCanvas() disconnects from the OLD
234 // canvas — on a raw pointer that was a use-after-free (SIGSEGV in
235 // QObject::disconnect from onActiveSubWindowChanged).
236 QPointer<MapCanvas> m_canvas;
237 QVector<Category> m_categories;
238 QHash<OpenSWMMVisLayer *, int> m_layerToCategory;
239 QHash<OpenSWMMVisLayer *, std::array<KindRow, kKindsPerSwmmModelLayer>>
240 m_kindRowStorage;
241 QSet<const void *> m_kindRowPtrSet; // O(1) kind-row discriminator
242 // Slice S3 — sublayer-row storage. The std::vector inside each hash
243 // node is sized once at rebuild time and never grown, so element
244 // addresses (used as QModelIndex internalPointers) are stable for the
245 // model's lifetime. m_sublayerRowPtrSet provides O(1) discrimination
246 // between sublayer-row, kind-row, layer, and category indices.
247 QHash<OpenSWMMVisLayer *, std::vector<SublayerRow>>
248 m_sublayerRowStorage;
249 QSet<const void *> m_sublayerRowPtrSet;
250
251 // Slice LTR-2026-09-19: no cached category order here. The displayed
252 // category sequence is DERIVED from the canvas stack (first appearance,
253 // top first) so the tree can never disagree with paint order — including
254 // after undo/redo. The per-project preference that places a group whose
255 // layers are all gone lives on the canvas (MapCanvas::layerGroupOrder).
256};
257
258// ---------------------------------------------------------------------------
259
273class LayerTreePanel : public QWidget
274{
275 Q_OBJECT
276
277public:
278
279 explicit LayerTreePanel(MapCanvas *canvas = nullptr, QWidget *parent = nullptr);
280 ~LayerTreePanel() override;
281
285 [[nodiscard]] OpenSWMMVisLayer *selectedLayer() const;
286
290 void setCanvas(MapCanvas *canvas);
291
292 [[nodiscard]] LayerTreeModel *model() const { return m_model; }
293
294signals:
295
300
308 void kindSelected(OpenSWMMVisLayer *layer, int kindOrdinal);
309
317 const QString &routingId = QString());
318
325
332
344 int kindOrdinal,
345 const QString &rendererId);
346
354 void plotKindObjectRequested(int kindOrdinal, const QString &objectName);
355
364
373
374public slots:
383
384private slots:
385 void onRemoveSelectedLayer();
386 void onZoomToSelectedLayer();
387 void onMoveLayerUp();
388 void onMoveLayerDown();
389 void onMoveCategoryUp();
390 void onMoveCategoryDown();
391 void onSelectionChanged();
392 void onLayerDoubleClicked(const QModelIndex &index);
393 void onContextMenuRequested(const QPoint &pos);
394 void onSearchTextChanged(const QString &text);
395
396private:
397 void setupUi();
398 void zoomToLayer(OpenSWMMVisLayer *layer);
399 QModelIndex toSourceIndex(const QModelIndex &proxyIdx) const;
400
401 // Slice LTR-2026-09-19 — reorder helpers. Layer moves are expressed in
402 // tree rows within the layer's category (the canvas keeps the stack
403 // grouped, so the sibling's canvas index is the exact target).
404 void moveSelectedLayerToRow(int targetRow);
405 void moveSelectedCategoryToRow(int targetRow);
406 // Every reorder resets the model (and expandAll() follows), which drops
407 // the selection. Remember what was current before the reset and put the
408 // selection back on the same category / layer / sublayer afterwards.
409 void rememberCurrentRow();
410 void restoreRememberedRow();
411
412 struct RememberedRow {
413 int categoryId = -1;
414 QPointer<OpenSWMMVisLayer> layer;
415 OpenSWMM::Render::ISublayer *sublayer = nullptr; // compared by address only
416 };
417 RememberedRow m_remembered;
418
419 QPointer<MapCanvas> m_canvas; // see LayerTreeModel::m_canvas
420 QTreeView *m_treeView = nullptr;
421 QLineEdit *m_searchEdit = nullptr;
422 LayerTreeModel *m_model = nullptr;
423 QSortFilterProxyModel *m_proxy = nullptr;
424
425 // Cached active analysis layers (for the context-menu check-state only).
426 QPointer<SWMMResultsLayer> m_activeResults1D;
427 QPointer<SWMM2DResultsLayer> m_activeResults2D;
428};
429
430#endif // LAYERTREEPANEL_H
QAbstractItemModel that mirrors the layer stack of a MapCanvas, grouped by category.
Definition layertreepanel.h:72
QModelIndex indexForLayer(OpenSWMMVisLayer *layer) const
Definition layertreepanel.cpp:1304
bool reorderCategory(int categoryId, int dstTreeRow)
Slice LTR-2026-09-19 — moves the category group categoryId (an openswmmvis::ui::CategoryId currently ...
Definition layertreepanel.cpp:1111
bool isSublayerIndex(const QModelIndex &index) const
Definition layertreepanel.cpp:1354
int columnCount(const QModelIndex &parent={}) const override
Definition layertreepanel.cpp:657
bool setData(const QModelIndex &index, const QVariant &value, int role) override
Definition layertreepanel.cpp:846
Qt::DropActions supportedDropActions() const override
Definition layertreepanel.cpp:1004
void setCanvas(MapCanvas *canvas)
Rebind to a different MapCanvas (called on MDI tab switch). Pass nullptr to detach (empty tree).
Definition layertreepanel.cpp:251
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
Definition layertreepanel.cpp:1161
MapCanvas * canvas() const
Definition layertreepanel.cpp:287
QModelIndex indexForSublayer(OpenSWMMVisLayer *layer, OpenSWMM::Render::ISublayer *sublayer) const
Definition layertreepanel.cpp:1313
OpenSWMMVisLayer * kindParentLayer(const QModelIndex &index) const
Definition layertreepanel.cpp:1340
void notifyHostSubOrderChanged()
Slice GUI-2026-05-30 §2 / §3 — notify the model that the sublayer or kind paint-order on a host layer...
Definition layertreepanel.cpp:1422
OpenSWMM::Render::ISublayer * sublayerForIndex(const QModelIndex &index) const
Definition layertreepanel.cpp:1368
bool isKindIndex(const QModelIndex &index) const
Definition layertreepanel.cpp:1333
QStringList mimeTypes() const override
Definition layertreepanel.cpp:997
QModelIndex indexForCategory(int categoryId) const
Definition layertreepanel.cpp:1296
OpenSWMMVisLayer * layerForIndex(const QModelIndex &index) const
Returns the OpenSWMMVisLayer for a layer-row index, or nullptr for category rows / invalid indices.
Definition layertreepanel.cpp:1274
bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const override
Definition layertreepanel.cpp:1057
Qt::ItemFlags flags(const QModelIndex &index) const override
Definition layertreepanel.cpp:930
int categoryIdForIndex(const QModelIndex &index) const
Definition layertreepanel.cpp:1289
int rowCount(const QModelIndex &parent={}) const override
Definition layertreepanel.cpp:627
QMimeData * mimeData(const QModelIndexList &indexes) const override
Definition layertreepanel.cpp:1010
OpenSWMMVisLayer * sublayerParentLayer(const QModelIndex &index) const
Definition layertreepanel.cpp:1361
bool isCategoryIndex(const QModelIndex &index) const
True if index is a category header row (no associated layer).
Definition layertreepanel.cpp:1326
~LayerTreeModel() override
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
Definition layertreepanel.cpp:662
int kindOrdinal(const QModelIndex &index) const
Definition layertreepanel.cpp:1346
QVariant headerData(int section, Qt::Orientation orientation, int role) const override
Definition layertreepanel.cpp:980
Embeddable widget containing the layer tree.
Definition layertreepanel.h:274
void setActiveResultsLayer(SWMMResultsLayer *layer)
Cache the project window's active 1D / 2D results layers so the context-menu "Set as Active Results L...
Definition layertreepanel.cpp:2140
void setActive2DResultsLayerRequested(class SWMM2DResultsLayer *layer)
void setCanvas(MapCanvas *canvas)
Rebind the panel and its model to a different canvas.
Definition layertreepanel.cpp:1461
void layerSelected(OpenSWMMVisLayer *layer)
Emitted when the user selects a layer in the tree.
void plotKindObjectRequested(int kindOrdinal, const QString &objectName)
Slice PT.1 — emitted when the user picks "Plot timeseries…" on a kind sub-row's right-click menu and ...
void layerStyleRequested(OpenSWMMVisLayer *layer)
Emitted when the user picks "Set Style…" on a layer's context menu. SWMMVis listens and opens the Sym...
void layerKindStyleRequested(OpenSWMMVisLayer *layer, int kindOrdinal, const QString &rendererId)
Slice BI-MK.LT — emitted when the user picks a Style option from a kind sub-row's right-click menu....
void setActive2DResultsLayer(SWMM2DResultsLayer *layer)
Definition layertreepanel.cpp:2145
LayerTreeModel * model() const
Definition layertreepanel.h:292
void attributeTableRequested(OpenSWMMVisLayer *layer)
Emitted when the user picks "Open Attribute Table" on a layer's context menu. SWMMVis raises the Attr...
void setActiveResultsLayerRequested(class SWMMResultsLayer *layer)
Emitted when the user picks "Set as Active Results Layer" on a results layer's context menu....
~LayerTreePanel() override
void layerPropertiesRequested(OpenSWMMVisLayer *layer, const QString &routingId=QString())
Emitted when the user requests to view properties for a layer. routingId is empty for plain "Properti...
void kindSelected(OpenSWMMVisLayer *layer, int kindOrdinal)
Emitted when the user selects a SWMM kind sub-row (e.g. "Storage"). kindOrdinal matches the SWMMModel...
void plotTimeSeriesFromOutputLayerRequested(class SWMMResultsLayer *layer)
Emitted when the user right-clicks a SWMM Output (.out) layer and picks "Plot Time Series…"....
OpenSWMMVisLayer * selectedLayer() const
Returns the currently selected layer, or nullptr.
Definition layertreepanel.cpp:1450
The central map display widget — QGIS-style hybrid rendering.
Definition mapcanvas.h:63
One toggleable visual aspect of a results / model layer.
Definition isublayer.h:87
Abstract base class for all layers displayed in the MapCanvas.
Definition openswmmvislayer.h:116
Top-level project container managing sessions, the SWMM model layer, and persistence paths for one SW...
Definition openswmmvisworkspace.h:42
Definition swmm2dresultslayer.h:550
Overlays time-stepped simulation results on the SWMM network geometry.
Definition swmmresultslayer.h:96
SWMM_FilePathRole role
Definition ioportabilitynormalizer.cpp:22
int index
Position in m_quadRegions / m_quadReports.
Definition meshgenerator.cpp:153
Definition gisrasterlayer.h:40
QVector< int > parent
union-find
Definition pslgminsize.cpp:176
int pos
Definition queryparser.cpp:36
QString text
Definition queryparser.cpp:35
QString name
Definition sectionmodelbuilders.cpp:474