KWin
Loading...
Searching...
No Matches
customtile.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 2022 Marco Martin <mart@kde.org>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
9
10#include "customtile.h"
11#include "core/output.h"
12#include "tilemanager.h"
13
14namespace KWin
15{
16
17QDebug operator<<(QDebug debug, const CustomTile *tile)
18{
19 QDebugStateSaver saver(debug);
20 debug.nospace();
21 if (tile) {
22 debug << tile->metaObject()->className() << '(' << static_cast<const void *>(tile);
23 debug << tile->relativeGeometry() << tile->layoutDirection();
24 debug << ')';
25 } else {
26 debug << "Tile(0x0)";
27 }
28 return debug;
29}
30
32 : Tile(tiling, parentItem)
33{
35 m_geometryLock = true;
36}
37
38CustomTile *CustomTile::createChildAt(const QRectF &relativeGeometry, LayoutDirection layoutDirection, int position)
39{
40 CustomTile *tile = new CustomTile(manager(), this);
44 manager()->model()->beginInsertTile(tile, position);
45 insertChild(position, tile);
46 manager()->model()->endInsertTile();
47 return tile;
48}
49
50void CustomTile::setRelativeGeometry(const QRectF &geom)
51{
52 if (relativeGeometry() == geom) {
53 return;
54 }
55
56 QRectF finalGeom = geom.intersected(QRectF(0, 0, 1, 1));
57 finalGeom.setWidth(std::max(finalGeom.width(), minimumSize().width()));
58 finalGeom.setHeight(std::max(finalGeom.height(), minimumSize().height()));
59 // We couldn't set the minimum size and still remain in boundaries, do nothing
60 if (finalGeom.right() > 1 || finalGeom.bottom() > 1) {
61 return;
62 }
63
64 auto *parentT = static_cast<CustomTile *>(parentTile());
65
66 if (!m_geometryLock && parentT && parentT->layoutDirection() != LayoutDirection::Floating) {
67 m_geometryLock = true;
68 if (finalGeom.left() != relativeGeometry().left()) {
69 Tile *tile = nextTileAt(Qt::LeftEdge);
70 if (tile) {
71 QRectF tileGeom = tile->relativeGeometry();
72 tileGeom.setRight(finalGeom.left());
73 tile->setRelativeGeometry(tileGeom);
74 // The other tile gometry may be not what we set due to size constraints
75 finalGeom.setLeft(tile->relativeGeometry().right());
76 } else {
77 // We are at the left border of the screen, we are always at 0
78 finalGeom.setLeft(0);
79 }
80 }
81 if (finalGeom.top() != relativeGeometry().top()) {
82 auto *tile = nextTileAt(Qt::TopEdge);
83 if (tile) {
84 auto tileGeom = tile->relativeGeometry();
85 tileGeom.setBottom(finalGeom.top());
86 tile->setRelativeGeometry(tileGeom);
87 finalGeom.setTop(tile->relativeGeometry().bottom());
88 } else {
89 // We are at the top border of the screen, we are always at 0
90 finalGeom.setTop(0);
91 }
92 }
93 if (finalGeom.right() != relativeGeometry().right()) {
94 auto *tile = nextTileAt(Qt::RightEdge);
95 if (tile) {
96 auto tileGeom = tile->relativeGeometry();
97 tileGeom.setLeft(finalGeom.right());
98 tile->setRelativeGeometry(tileGeom);
99 finalGeom.setRight(tile->relativeGeometry().left());
100 } else {
101 // We are at the right border of the screen, we are always at 1
102 finalGeom.setRight(1);
103 }
104 }
105 if (finalGeom.bottom() != relativeGeometry().bottom()) {
106 auto *tile = nextTileAt(Qt::BottomEdge);
107 if (tile) {
108 auto tileGeom = tile->relativeGeometry();
109 tileGeom.setTop(finalGeom.bottom());
110 tile->setRelativeGeometry(tileGeom);
111 finalGeom.setBottom(tile->relativeGeometry().top());
112 } else {
113 // We are at the bottom border of the screen, we are always at 1
114 finalGeom.setBottom(1);
115 }
116 }
117 m_geometryLock = false;
118 } else if (parentT && parentT->layoutDirection() == LayoutDirection::Floating) {
119 finalGeom = geom.intersected(parentT->relativeGeometry());
120 }
121
122 const auto childrenT = childTiles();
123
124 // Resize all the children to fit in new size
125 for (auto t : childrenT) {
126 auto childGeom = t->relativeGeometry();
127 childGeom = childGeom.intersected(finalGeom);
129 childGeom.setTop(finalGeom.top());
130 childGeom.setBottom(finalGeom.bottom());
132 childGeom.setLeft(finalGeom.left());
133 childGeom.setRight(finalGeom.right());
134 }
135
136 t->setRelativeGeometry(childGeom);
137 }
138
139 if (!childrenT.isEmpty()) {
140 auto childGeom = childrenT.first()->relativeGeometry();
142 childGeom.setLeft(finalGeom.left());
144 childGeom.setTop(finalGeom.top());
145 }
146 childrenT.first()->setRelativeGeometry(childGeom);
147 }
148
149 // Make sure the last child item fills the area
150 // TODO: ensure all children don't go below minimum size/resize all children
151 if (!m_geometryLock && !childrenT.isEmpty() && layoutDirection() != LayoutDirection::Floating) {
152 auto childGeom = childrenT.last()->relativeGeometry();
153 childGeom.setRight(finalGeom.right());
154 childGeom.setBottom(finalGeom.bottom());
155 childrenT.last()->setRelativeGeometry(childGeom);
156 }
157
158 Tile::setRelativeGeometry(finalGeom);
159 if (parentT) {
160 Q_EMIT parentT->layoutModified();
161 }
162 m_geometryLock = false;
163}
164
166{
168 return gravity != Gravity::None;
169 }
170
171 return Tile::supportsResizeGravity(gravity);
172}
173
175{
176 auto *parentT = static_cast<CustomTile *>(parentTile());
177
178 // If we are m_rootLayoutTile always create childrens, not siblings
179 if (parentT && (parentT->childCount() < 2 || parentT->layoutDirection() == newDirection)) {
180 // Add a new cell to the current layout
181 setLayoutDirection(newDirection);
182 QRectF newGeo;
183 if (newDirection == LayoutDirection::Floating) {
184 newGeo = QRectF(relativeGeometry().left() + 0.1, relativeGeometry().top() + 0.1, 0.3, 0.2);
185 newGeo.setLeft(std::max(newGeo.left(), parentT->relativeGeometry().left()));
186 newGeo.setTop(std::max(newGeo.top(), parentT->relativeGeometry().top()));
187 newGeo.setRight(std::min(newGeo.right(), parentT->relativeGeometry().right()));
188 newGeo.setBottom(std::min(newGeo.bottom(), parentT->relativeGeometry().bottom()));
189 } else if (newDirection == LayoutDirection::Horizontal) {
190 newGeo = relativeGeometry();
191 newGeo.setWidth(relativeGeometry().width() / 2);
193 newGeo.moveLeft(newGeo.x() + newGeo.width());
194 } else if (newDirection == LayoutDirection::Vertical) {
195 newGeo = relativeGeometry();
196 newGeo.setHeight(relativeGeometry().height() / 2);
198 newGeo.moveTop(newGeo.y() + newGeo.height());
199 }
200
201 parentT->createChildAt(newGeo, layoutDirection(), row() + 1);
202 } else {
203 // Do a new layout and put tiles inside
204 setLayoutDirection(newDirection);
205 auto newGeo = relativeGeometry();
206 if (newDirection == LayoutDirection::Floating) {
207 // Do a new layout with one floating tile inside
208 auto startGeom = relativeGeometry();
209 if (!childTiles().isEmpty()) {
210 startGeom = childTiles().last()->relativeGeometry();
211 }
212 newGeo = QRectF(startGeom.left() + 0.05, startGeom.top() + 0.05, 0.3, 0.25);
213 newGeo.setLeft(std::max(newGeo.left(), relativeGeometry().left()));
214 newGeo.setTop(std::max(newGeo.top(), relativeGeometry().top()));
215 newGeo.setRight(std::min(newGeo.right(), relativeGeometry().right()));
216 newGeo.setBottom(std::min(newGeo.bottom(), relativeGeometry().bottom()));
217 createChildAt(newGeo, newDirection, childCount());
218 } else if (newDirection == LayoutDirection::Horizontal) {
219 // Do a new layout with 2 cells inside this one
220 newGeo.setWidth(relativeGeometry().width() / 2);
221 createChildAt(newGeo, newDirection, childCount());
222 newGeo.moveLeft(newGeo.x() + newGeo.width());
223 createChildAt(newGeo, newDirection, childCount());
224 } else if (newDirection == LayoutDirection::Vertical) {
225 // Do a new layout with 2 cells inside this one
226 newGeo.setHeight(relativeGeometry().height() / 2);
227 createChildAt(newGeo, newDirection, childCount());
228 newGeo.moveTop(newGeo.y() + newGeo.height());
229 createChildAt(newGeo, newDirection, childCount());
230 }
231 }
232}
233
234void CustomTile::moveByPixels(const QPointF &delta)
235{
237 return;
238 }
239
240 const auto outGeom = manager()->output()->geometry();
241 const auto relativeMove = QPointF(delta.x() / outGeom.width(), delta.y() / outGeom.height());
242 auto geom = relativeGeometry();
243 geom.moveTo(geom.topLeft() + relativeMove);
244
246}
247
249{
250 auto *parentT = static_cast<CustomTile *>(parentTile());
251 if (!parentT) {
252 return;
253 }
254
255 auto *prev = previousSibling();
256 auto *next = nextSibling();
257
258 manager()->model()->beginRemoveTile(this);
259 parentT->removeChild(this);
260 manager()->model()->endRemoveTile();
261 manager()->tileRemoved(this);
262
263 if (parentT->layoutDirection() == LayoutDirection::Horizontal) {
264 if (prev && next) {
265 auto geom = prev->relativeGeometry();
266 geom.setRight(relativeGeometry().center().x());
267 prev->setRelativeGeometry(geom);
268 geom = next->relativeGeometry();
269 geom.setLeft(relativeGeometry().center().x());
270 next->setRelativeGeometry(geom);
271 } else if (prev) {
272 auto geom = prev->relativeGeometry();
273 geom.setRight(relativeGeometry().right());
274 prev->setRelativeGeometry(geom);
275 } else if (next) {
276 auto geom = next->relativeGeometry();
277 geom.setLeft(relativeGeometry().left());
278 next->setRelativeGeometry(geom);
279 }
280 } else if (parentT->layoutDirection() == LayoutDirection::Vertical) {
281 if (prev && next) {
282 auto geom = prev->relativeGeometry();
283 geom.setBottom(relativeGeometry().center().y());
284 prev->setRelativeGeometry(geom);
285 geom = next->relativeGeometry();
286 geom.setTop(relativeGeometry().center().y());
287 next->setRelativeGeometry(geom);
288 } else if (prev) {
289 auto geom = prev->relativeGeometry();
290 geom.setBottom(relativeGeometry().bottom());
291 prev->setRelativeGeometry(geom);
292 } else if (next) {
293 auto geom = next->relativeGeometry();
294 geom.setTop(relativeGeometry().top());
295 next->setRelativeGeometry(geom);
296 }
297 }
298
299 // On linear layouts remove the last one and promote the layout as leaf
300 if (parentT->layoutDirection() != Tile::LayoutDirection::Floating && parentT->childCount() == 1) {
301 auto *lastTile = static_cast<CustomTile *>(parentT->childTile(0));
302 if (lastTile->childCount() == 0) {
303 lastTile->remove();
304 }
305 }
306
307 delete this;
308}
309
311{
312 auto *parentT = static_cast<CustomTile *>(parentTile());
313
314 // TODO: implement geometry base searching for floating?
315 if (!parentT || parentT->layoutDirection() == LayoutDirection::Floating) {
316 return nullptr;
317 }
318
319 Tile *sibling = nullptr;
320
321 const int index = row();
322 int layoutRows;
323 int layoutColumns;
324 switch (parentT->layoutDirection()) {
326 layoutRows = std::max(1, parentT->childCount());
327 layoutColumns = 1;
328 break;
330 default:
331 layoutColumns = std::max(1, parentT->childCount());
332 layoutRows = 1;
333 break;
334 }
335 int row = index / layoutColumns;
336 int column = index % layoutColumns;
337
338 switch (edge) {
339 case Qt::LeftEdge:
340 if (column > 0) {
341 sibling = previousSibling();
342 }
343 break;
344 case Qt::TopEdge:
345 if (row > 0) {
346 sibling = parentT->childTiles()[layoutColumns * (row - 1) + column];
347 }
348 break;
349 case Qt::RightEdge:
350 if (column < layoutColumns - 1) {
351 sibling = nextSibling();
352 }
353 break;
354 case Qt::BottomEdge:
355 if (row < layoutRows - 1) {
356 const int newIndex = layoutColumns * (row + 1) + column;
357 if (newIndex < parentT->childCount()) {
358 sibling = parentT->childTiles()[newIndex];
359 }
360 }
361 break;
362 }
363
364 if (sibling) {
365 return static_cast<CustomTile *>(sibling);
366 } else {
367 return parentT->nextTileAt(edge);
368 }
369}
370
372{
373 if (m_layoutDirection == dir) {
374 return;
375 }
376
377 m_layoutDirection = dir;
378 Q_EMIT layoutDirectionChanged(dir);
379}
380
382{
383 return m_layoutDirection;
384}
385
387 : CustomTile(tiling, nullptr)
388{
389 setParent(tiling);
390 setRelativeGeometry({0, 0, 1, 1});
391}
392
393} // namespace KWin
394
395#include "moc_customtile.cpp"
CustomTile * nextTileAt(Qt::Edge edge) const
KWin::Tile::LayoutDirection layoutDirection
Definition customtile.h:22
Q_INVOKABLE void remove()
void setLayoutDirection(Tile::LayoutDirection dir)
Q_INVOKABLE void split(KWin::Tile::LayoutDirection newDirection)
bool supportsResizeGravity(KWin::Gravity gravity) override
CustomTile * createChildAt(const QRectF &relativeGeometry, LayoutDirection direction, int position)
void setRelativeGeometry(const QRectF &geom) override
CustomTile(TileManager *tiling, CustomTile *parentItem=nullptr)
void layoutDirectionChanged(Tile::LayoutDirection direction)
Q_INVOKABLE void moveByPixels(const QPointF &delta)
QRect geometry
Definition output.h:134
RootTile(TileManager *tiling)
static QSizeF minimumSize()
Definition tile.h:122
int row() const
Definition tile.cpp:394
void setQuickTileMode(QuickTileMode mode)
Definition tile.cpp:200
Tile * nextSibling() const
Definition tile.cpp:403
LayoutDirection
Definition tile.h:40
TileManager * manager() const
Definition tile.cpp:389
virtual bool supportsResizeGravity(Gravity gravity)
Definition tile.cpp:50
Tile * parentTile() const
Definition tile.cpp:376
int childCount() const
Definition tile.cpp:362
Tile * previousSibling() const
Definition tile.cpp:413
virtual void setRelativeGeometry(const QRectF &geom)
Definition tile.cpp:95
QList< Tile * > childTiles() const
Definition tile.cpp:349
QRectF relativeGeometry
Definition tile.h:28
void insertChild(int position, Tile *item)
Definition tile.cpp:308
Output * output() const
void tileRemoved(KWin::Tile *tile)
TileModel * model
Definition tilemanager.h:41
Gravity
Definition globals.h:150
QDebug & operator<<(QDebug &s, const KWin::DrmConnector *obj)