v2.0.0
Loading...
Searching...
No Matches
multiviewlayout.cpp
Go to the documentation of this file.
1//=============================================================================================================
12
13//=============================================================================================================
14// INCLUDES
15//=============================================================================================================
16
17#include "multiviewlayout.h"
18
19#include <algorithm>
20#include <cmath>
21
22namespace DISP3DLIB
23{
24
25//=============================================================================================================
26// DEFINE MEMBER METHODS
27//=============================================================================================================
28
29QRect MultiViewLayout::slotRect(int slot, int numEnabled, const QSize& outputSize) const
30{
31 if (numEnabled <= 1) {
32 return QRect(0, 0, outputSize.width(), outputSize.height());
33 }
34
35 const int w = outputSize.width();
36 const int h = outputSize.height();
37
38 // ── 2 panes: side-by-side ─────────────────────────────────────────
39 if (numEnabled == 2) {
40 const int leftW = std::clamp(static_cast<int>(std::lround(w * m_splitX)),
41 m_minPanePx,
42 std::max(m_minPanePx, w - m_minPanePx));
43 const int rightW = std::max(1, w - leftW);
44 return (slot == 0) ? QRect(0, 0, leftW, h)
45 : QRect(leftW, 0, rightW, h);
46 }
47
48 // ── 3 panes: full-width top, split bottom ─────────────────────────
49 if (numEnabled == 3) {
50 const int topH = std::clamp(static_cast<int>(std::lround(h * m_splitY)),
51 m_minPanePx,
52 std::max(m_minPanePx, h - m_minPanePx));
53 const int bottomH = std::max(1, h - topH);
54 if (slot == 0) {
55 return QRect(0, 0, w, topH);
56 }
57 const int leftW = std::clamp(static_cast<int>(std::lround(w * m_splitX)),
58 m_minPanePx,
59 std::max(m_minPanePx, w - m_minPanePx));
60 const int rightW = std::max(1, w - leftW);
61 return (slot == 1) ? QRect(0, topH, leftW, bottomH)
62 : QRect(leftW, topH, rightW, bottomH);
63 }
64
65 // ── 4 panes: 2×2 grid ─────────────────────────────────────────────
66 const int leftW = std::clamp(static_cast<int>(std::lround(w * m_splitX)),
67 m_minPanePx,
68 std::max(m_minPanePx, w - m_minPanePx));
69 const int rightW = std::max(1, w - leftW);
70 const int topH = std::clamp(static_cast<int>(std::lround(h * m_splitY)),
71 m_minPanePx,
72 std::max(m_minPanePx, h - m_minPanePx));
73 const int bottomH = std::max(1, h - topH);
74
75 const int col = slot % 2;
76 const int row = slot / 2;
77 return QRect((col == 0) ? 0 : leftW,
78 (row == 0) ? 0 : topH,
79 (col == 0) ? leftW : rightW,
80 (row == 0) ? topH : bottomH);
81}
82
83//=============================================================================================================
84
86 int numEnabled,
87 const QSize& outputSize) const
88{
89 if (numEnabled <= 1) {
90 return SplitterHit::None;
91 }
92
93 const int w = outputSize.width();
94 const int h = outputSize.height();
95
96 // 2-pane: only vertical splitter
97 if (numEnabled == 2) {
98 const int splitX = std::clamp(static_cast<int>(std::lround(w * m_splitX)),
99 m_minPanePx,
100 std::max(m_minPanePx, w - m_minPanePx));
101 return (std::abs(pos.x() - splitX) <= m_hitTolerancePx)
104 }
105
106 // 3- or 4-pane
107 const int splitX = std::clamp(static_cast<int>(std::lround(w * m_splitX)),
108 m_minPanePx,
109 std::max(m_minPanePx, w - m_minPanePx));
110 const int splitY = std::clamp(static_cast<int>(std::lround(h * m_splitY)),
111 m_minPanePx,
112 std::max(m_minPanePx, h - m_minPanePx));
113
114 const bool nearHorizontal = (std::abs(pos.y() - splitY) <= m_hitTolerancePx);
115
116 // For 3-view: vertical only exists in bottom half
117 const bool inBottomHalf = (pos.y() > splitY);
118 const bool nearVertical = (std::abs(pos.x() - splitX) <= m_hitTolerancePx);
119 const bool verticalActive = nearVertical && (numEnabled != 3 || inBottomHalf);
120
121 if (verticalActive && nearHorizontal)
122 return SplitterHit::Both;
123 if (verticalActive)
125 if (nearHorizontal)
127
128 return SplitterHit::None;
129}
130
131//=============================================================================================================
132
134{
135 switch (hit) {
137 return Qt::SizeHorCursor;
139 return Qt::SizeVerCursor;
141 return Qt::SizeAllCursor;
142 default:
143 return Qt::ArrowCursor;
144 }
145}
146
147//=============================================================================================================
148
150 const QVector<int>& enabledViewports,
151 const QSize& outputSize) const
152{
153 const int numEnabled = enabledViewports.size();
154 for (int slot = 0; slot < numEnabled; ++slot) {
155 if (slotRect(slot, numEnabled, outputSize).contains(pos)) {
156 return enabledViewports[slot];
157 }
158 }
159 return -1;
160}
161
162//=============================================================================================================
163
164QRect MultiViewLayout::insetForSeparator(const QRect& paneRect,
165 int slot,
166 int numEnabled) const
167{
168 if (numEnabled <= 1) {
169 return paneRect;
170 }
171
172 QRect r = paneRect;
173
174 if (numEnabled == 2) {
175 if (slot == 0)
176 r.setWidth(std::max(1, r.width() - m_separatorLinePx));
177 return r;
178 }
179
180 if (numEnabled == 3) {
181 if (slot == 0) {
182 r.setHeight(std::max(1, r.height() - m_separatorLinePx));
183 } else if (slot == 1) {
184 r.setWidth(std::max(1, r.width() - m_separatorLinePx));
185 }
186 return r;
187 }
188
189 // 4 panes
190 const int col = slot % 2;
191 const int row = slot / 2;
192 const bool hasRightNeighbor = (col == 0) && (slot + 1 < numEnabled) && ((slot / 2) == ((slot + 1) / 2));
193 const bool hasBottomNeighbor = (row == 0) && (slot + 2 < numEnabled);
194
195 if (hasRightNeighbor)
196 r.setWidth(std::max(1, r.width() - m_separatorLinePx));
197 if (hasBottomNeighbor)
198 r.setHeight(std::max(1, r.height() - m_separatorLinePx));
199
200 return r;
201}
202
203//=============================================================================================================
204
206 const QSize& widgetSize,
207 QRect& verticalRect,
208 QRect& horizontalRect) const
209{
210 verticalRect = QRect();
211 horizontalRect = QRect();
212
213 if (numEnabled <= 1)
214 return;
215
216 const int w = std::max(1, widgetSize.width());
217 const int h = std::max(1, widgetSize.height());
218
219 const int splitX = std::clamp(static_cast<int>(std::lround(w * m_splitX)),
220 m_minPanePx,
221 std::max(m_minPanePx, w - m_minPanePx));
222
223 if (numEnabled >= 3) {
224 const int splitY = std::clamp(static_cast<int>(std::lround(h * m_splitY)),
225 m_minPanePx,
226 std::max(m_minPanePx, h - m_minPanePx));
227
228 horizontalRect = QRect(0,
229 splitY - m_separatorLinePx / 2,
230 w,
231 m_separatorLinePx);
232
233 if (numEnabled == 3) {
234 // Vertical only in bottom half
235 verticalRect = QRect(splitX - m_separatorLinePx / 2,
236 splitY,
237 m_separatorLinePx,
238 h - splitY);
239 } else {
240 // Full-height vertical
241 verticalRect = QRect(splitX - m_separatorLinePx / 2,
242 0,
243 m_separatorLinePx,
244 h);
245 }
246 } else {
247 // 2 panes: full-height vertical
248 verticalRect = QRect(splitX - m_separatorLinePx / 2,
249 0,
250 m_separatorLinePx,
251 h);
252 }
253}
254
255//=============================================================================================================
256
257void MultiViewLayout::dragSplitter(const QPoint& pos,
258 SplitterHit activeSplitter,
259 const QSize& widgetSize)
260{
261 const int w = std::max(1, widgetSize.width());
262 const int h = std::max(1, widgetSize.height());
263
264 if (activeSplitter == SplitterHit::Vertical || activeSplitter == SplitterHit::Both) {
265 const int clampedX = std::clamp(pos.x(), m_minPanePx, std::max(m_minPanePx, w - m_minPanePx));
266 m_splitX = clampSplit(static_cast<float>(clampedX) / static_cast<float>(w));
267 }
268
269 if (activeSplitter == SplitterHit::Horizontal || activeSplitter == SplitterHit::Both) {
270 const int clampedY = std::clamp(pos.y(), m_minPanePx, std::max(m_minPanePx, h - m_minPanePx));
271 m_splitY = clampSplit(static_cast<float>(clampedY) / static_cast<float>(h));
272 }
273}
274
275} // namespace DISP3DLIB
Viewport geometry, splitter logic and per-pane preset assignment for the disp3D multi-view grid.
3-D brain visualisation using the Qt RHI rendering backend.
QRect insetForSeparator(const QRect &paneRect, int slot, int numEnabled) const
SplitterHit hitTestSplitter(const QPoint &pos, int numEnabled, const QSize &outputSize) const
void separatorGeometries(int numEnabled, const QSize &widgetSize, QRect &verticalRect, QRect &horizontalRect) const
int viewportIndexAt(const QPoint &pos, const QVector< int > &enabledViewports, const QSize &outputSize) const
static Qt::CursorShape cursorForHit(SplitterHit hit)
QRect slotRect(int slot, int numEnabled, const QSize &outputSize) const
void dragSplitter(const QPoint &pos, SplitterHit activeSplitter, const QSize &widgetSize)