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