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