File indexing completed on 2024-11-10 04:57:22
0001 /* 0002 KWin - the KDE window manager 0003 This file is part of the KDE project. 0004 0005 SPDX-FileCopyrightText: 2022 Marco Martin <mart@kde.org> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include "tile.h" 0011 #include "core/output.h" 0012 #include "tilemanager.h" 0013 #include "virtualdesktops.h" 0014 #include "window.h" 0015 #include "workspace.h" 0016 0017 #include <cmath> 0018 0019 namespace KWin 0020 { 0021 0022 QSizeF Tile::s_minimumSize = QSizeF(0.15, 0.15); 0023 0024 Tile::Tile(TileManager *tiling, Tile *parent) 0025 : QObject(parent) 0026 , m_parentTile(parent) 0027 , m_tiling(tiling) 0028 { 0029 if (m_parentTile) { 0030 m_padding = m_parentTile->padding(); 0031 } 0032 connect(Workspace::self(), &Workspace::configChanged, this, &Tile::windowGeometryChanged); 0033 } 0034 0035 Tile::~Tile() 0036 { 0037 for (auto *t : std::as_const(m_children)) { 0038 // Prevents access upon child tiles destruction 0039 t->m_parentTile = nullptr; 0040 } 0041 if (m_parentTile) { 0042 m_parentTile->removeChild(this); 0043 } 0044 for (auto *w : std::as_const(m_windows)) { 0045 Tile *tile = m_tiling->bestTileForPosition(w->moveResizeGeometry().center()); 0046 w->setTile(tile); 0047 } 0048 } 0049 0050 bool Tile::supportsResizeGravity(Gravity gravity) 0051 { 0052 if (!m_parentTile) { 0053 return false; 0054 } 0055 0056 switch (gravity) { 0057 case Gravity::Left: 0058 return m_relativeGeometry.left() > 0.0; 0059 case Gravity::Top: 0060 return m_relativeGeometry.top() > 0.0; 0061 case Gravity::Right: 0062 return m_relativeGeometry.right() < 1.0; 0063 case Gravity::Bottom: 0064 return m_relativeGeometry.bottom() < 1.0; 0065 case Gravity::TopLeft: 0066 return m_relativeGeometry.top() > 0.0 && m_relativeGeometry.left() > 0.0; 0067 case Gravity::TopRight: 0068 return m_relativeGeometry.top() > 0.0 && m_relativeGeometry.right() < 1.0; 0069 case Gravity::BottomLeft: 0070 return m_relativeGeometry.bottom() < 1.0 && m_relativeGeometry.left() > 0.0; 0071 case Gravity::BottomRight: 0072 return m_relativeGeometry.bottom() < 1.0 && m_relativeGeometry.right() < 1.0; 0073 case Gravity::None: 0074 default: 0075 return false; 0076 } 0077 } 0078 0079 void Tile::setGeometryFromWindow(const QRectF &geom) 0080 { 0081 setGeometryFromAbsolute(geom + QMarginsF(m_padding, m_padding, m_padding, m_padding)); 0082 } 0083 0084 void Tile::setGeometryFromAbsolute(const QRectF &geom) 0085 { 0086 const QRectF outGeom = m_tiling->output()->geometryF(); 0087 const QRectF relGeom((geom.x() - outGeom.x()) / outGeom.width(), 0088 (geom.y() - outGeom.y()) / outGeom.height(), 0089 geom.width() / outGeom.width(), 0090 geom.height() / outGeom.height()); 0091 0092 setRelativeGeometry(relGeom); 0093 } 0094 0095 void Tile::setRelativeGeometry(const QRectF &geom) 0096 { 0097 QRectF constrainedGeom = geom; 0098 constrainedGeom.setWidth(std::max(constrainedGeom.width(), s_minimumSize.width())); 0099 constrainedGeom.setHeight(std::max(constrainedGeom.height(), s_minimumSize.height())); 0100 0101 if (m_relativeGeometry == constrainedGeom) { 0102 return; 0103 } 0104 0105 m_relativeGeometry = constrainedGeom; 0106 0107 Q_EMIT relativeGeometryChanged(); 0108 Q_EMIT absoluteGeometryChanged(); 0109 Q_EMIT windowGeometryChanged(); 0110 0111 for (auto *w : std::as_const(m_windows)) { 0112 w->moveResize(windowGeometry()); 0113 } 0114 } 0115 0116 QRectF Tile::relativeGeometry() const 0117 { 0118 return m_relativeGeometry; 0119 } 0120 0121 QRectF Tile::absoluteGeometry() const 0122 { 0123 const QRectF geom = m_tiling->output()->geometryF(); 0124 return QRectF(std::round(geom.x() + m_relativeGeometry.x() * geom.width()), 0125 std::round(geom.y() + m_relativeGeometry.y() * geom.height()), 0126 std::round(m_relativeGeometry.width() * geom.width()), 0127 std::round(m_relativeGeometry.height() * geom.height())); 0128 } 0129 0130 QRectF Tile::absoluteGeometryInScreen() const 0131 { 0132 const QRectF geom = m_tiling->output()->geometryF(); 0133 return QRectF(std::round(m_relativeGeometry.x() * geom.width()), 0134 std::round(m_relativeGeometry.y() * geom.height()), 0135 std::round(m_relativeGeometry.width() * geom.width()), 0136 std::round(m_relativeGeometry.height() * geom.height())); 0137 } 0138 0139 QRectF Tile::windowGeometry() const 0140 { 0141 // Apply half padding between tiles and full against the screen edges 0142 QMarginsF effectiveMargins; 0143 effectiveMargins.setLeft(m_relativeGeometry.left() > 0.0 ? m_padding / 2.0 : m_padding); 0144 effectiveMargins.setTop(m_relativeGeometry.top() > 0.0 ? m_padding / 2.0 : m_padding); 0145 effectiveMargins.setRight(m_relativeGeometry.right() < 1.0 ? m_padding / 2.0 : m_padding); 0146 effectiveMargins.setBottom(m_relativeGeometry.bottom() < 1.0 ? m_padding / 2.0 : m_padding); 0147 0148 const auto geom = absoluteGeometry(); 0149 return geom.intersected(workspace()->clientArea(MaximizeArea, m_tiling->output(), VirtualDesktopManager::self()->currentDesktop())) - effectiveMargins; 0150 } 0151 0152 QRectF Tile::maximizedWindowGeometry() const 0153 { 0154 const auto geom = absoluteGeometry(); 0155 return geom.intersected(workspace()->clientArea(MaximizeArea, m_tiling->output(), VirtualDesktopManager::self()->currentDesktop())); 0156 } 0157 0158 bool Tile::isLayout() const 0159 { 0160 // Items with a single child are not allowed, unless the root which is *always* layout 0161 return m_children.count() > 0 || !m_parentTile; 0162 } 0163 0164 bool Tile::canBeRemoved() const 0165 { 0166 // The root tile can *never* be removed 0167 return m_parentTile; 0168 } 0169 0170 qreal Tile::padding() const 0171 { 0172 // Assume padding is all the same 0173 return m_padding; 0174 } 0175 0176 void Tile::setPadding(qreal padding) 0177 { 0178 if (m_padding == padding) { 0179 return; 0180 } 0181 0182 m_padding = padding; 0183 0184 for (auto *t : std::as_const(m_children)) { 0185 t->setPadding(padding); 0186 } 0187 for (auto *w : std::as_const(m_windows)) { 0188 w->moveResize(windowGeometry()); 0189 } 0190 0191 Q_EMIT paddingChanged(padding); 0192 Q_EMIT windowGeometryChanged(); 0193 } 0194 0195 QuickTileMode Tile::quickTileMode() const 0196 { 0197 return m_quickTileMode; 0198 } 0199 0200 void Tile::setQuickTileMode(QuickTileMode mode) 0201 { 0202 m_quickTileMode = mode; 0203 } 0204 0205 void Tile::resizeFromGravity(Gravity gravity, int x_root, int y_root) 0206 { 0207 if (!m_parentTile) { 0208 return; 0209 } 0210 0211 const QRectF outGeom = m_tiling->output()->geometryF(); 0212 const QPointF relativePos = QPointF((x_root - outGeom.x()) / outGeom.width(), (y_root - outGeom.y()) / outGeom.height()); 0213 QRectF newGeom = m_relativeGeometry; 0214 0215 switch (gravity) { 0216 case Gravity::TopLeft: 0217 newGeom.setTopLeft(relativePos - QPointF(m_padding / outGeom.width(), m_padding / outGeom.height())); 0218 break; 0219 case Gravity::BottomRight: 0220 newGeom.setBottomRight(relativePos + QPointF(m_padding / outGeom.width(), m_padding / outGeom.height())); 0221 break; 0222 case Gravity::BottomLeft: 0223 newGeom.setBottomLeft(relativePos + QPointF(-m_padding / outGeom.width(), m_padding / outGeom.height())); 0224 break; 0225 case Gravity::TopRight: 0226 newGeom.setTopRight(relativePos + QPointF(m_padding / outGeom.width(), -m_padding / outGeom.height())); 0227 break; 0228 case Gravity::Top: 0229 newGeom.setTop(relativePos.y() - m_padding / outGeom.height()); 0230 break; 0231 case Gravity::Bottom: 0232 newGeom.setBottom(relativePos.y() + m_padding / outGeom.height()); 0233 break; 0234 case Gravity::Left: 0235 newGeom.setLeft(relativePos.x() - m_padding / outGeom.width()); 0236 break; 0237 case Gravity::Right: 0238 newGeom.setRight(relativePos.x() + m_padding / outGeom.width()); 0239 break; 0240 case Gravity::None: 0241 Q_UNREACHABLE(); 0242 break; 0243 } 0244 0245 setRelativeGeometry(newGeom); 0246 } 0247 0248 void Tile::resizeByPixels(qreal delta, Qt::Edge edge) 0249 { 0250 if (!m_parentTile) { 0251 return; 0252 } 0253 0254 const auto outGeom = m_tiling->output()->geometryF(); 0255 auto newGeom = m_relativeGeometry; 0256 0257 switch (edge) { 0258 case Qt::LeftEdge: { 0259 qreal relativeDelta = delta / outGeom.width(); 0260 newGeom.setLeft(newGeom.left() + relativeDelta); 0261 break; 0262 } 0263 case Qt::TopEdge: { 0264 qreal relativeDelta = delta / outGeom.height(); 0265 newGeom.setTop(newGeom.top() + relativeDelta); 0266 break; 0267 } 0268 case Qt::RightEdge: { 0269 qreal relativeDelta = delta / outGeom.width(); 0270 newGeom.setRight(newGeom.right() + relativeDelta); 0271 break; 0272 } 0273 case Qt::BottomEdge: { 0274 qreal relativeDelta = delta / outGeom.height(); 0275 newGeom.setBottom(newGeom.bottom() + relativeDelta); 0276 break; 0277 } 0278 } 0279 setRelativeGeometry(newGeom); 0280 } 0281 0282 void Tile::addWindow(Window *window) 0283 { 0284 if (!m_windows.contains(window)) { 0285 window->moveResize(windowGeometry()); 0286 m_windows.append(window); 0287 window->setTile(this); 0288 Q_EMIT windowAdded(window); 0289 Q_EMIT windowsChanged(); 0290 } 0291 } 0292 0293 void Tile::removeWindow(Window *window) 0294 { 0295 // We already ensure there is a single copy of window in m_windows 0296 if (m_windows.removeOne(window)) { 0297 window->setTile(nullptr); 0298 Q_EMIT windowRemoved(window); 0299 Q_EMIT windowsChanged(); 0300 } 0301 } 0302 0303 QList<KWin::Window *> Tile::windows() const 0304 { 0305 return m_windows; 0306 } 0307 0308 void Tile::insertChild(int position, Tile *item) 0309 { 0310 Q_ASSERT(position >= 0); 0311 const bool wasEmpty = m_children.isEmpty(); 0312 item->setParent(this); 0313 0314 m_children.insert(std::clamp<qsizetype>(position, 0, m_children.length()), item); 0315 0316 if (wasEmpty) { 0317 Q_EMIT isLayoutChanged(true); 0318 for (auto *w : std::as_const(m_windows)) { 0319 Tile *tile = m_tiling->bestTileForPosition(w->moveResizeGeometry().center()); 0320 w->setTile(tile); 0321 } 0322 } 0323 0324 Q_EMIT childTilesChanged(); 0325 } 0326 0327 void Tile::destroyChild(Tile *tile) 0328 { 0329 removeChild(tile); 0330 delete tile; 0331 } 0332 0333 void Tile::removeChild(Tile *child) 0334 { 0335 const bool wasEmpty = m_children.isEmpty(); 0336 const int idx = m_children.indexOf(child); 0337 m_children.removeAll(child); 0338 if (m_children.isEmpty() && !wasEmpty) { 0339 Q_EMIT isLayoutChanged(false); 0340 } 0341 if (idx > -1) { 0342 for (int i = idx; i < m_children.count(); ++i) { 0343 Q_EMIT m_children[i]->rowChanged(i); 0344 } 0345 } 0346 Q_EMIT childTilesChanged(); 0347 } 0348 0349 QList<Tile *> Tile::childTiles() const 0350 { 0351 return m_children; 0352 } 0353 0354 Tile *Tile::childTile(int row) 0355 { 0356 if (row < 0 || row >= m_children.size()) { 0357 return nullptr; 0358 } 0359 return m_children.value(row); 0360 } 0361 0362 int Tile::childCount() const 0363 { 0364 return m_children.count(); 0365 } 0366 0367 QList<Tile *> Tile::descendants() const 0368 { 0369 QList<Tile *> tiles; 0370 for (auto *t : std::as_const(m_children)) { 0371 tiles << t << t->descendants(); 0372 } 0373 return tiles; 0374 } 0375 0376 Tile *Tile::parentTile() const 0377 { 0378 return m_parentTile; 0379 } 0380 0381 void Tile::visitDescendants(std::function<void(const Tile *child)> callback) const 0382 { 0383 callback(this); 0384 for (const Tile *child : m_children) { 0385 child->visitDescendants(callback); 0386 } 0387 } 0388 0389 TileManager *Tile::manager() const 0390 { 0391 return m_tiling; 0392 } 0393 0394 int Tile::row() const 0395 { 0396 if (m_parentTile) { 0397 return m_parentTile->m_children.indexOf(this); 0398 } 0399 0400 return -1; 0401 } 0402 0403 Tile *Tile::nextSibling() const 0404 { 0405 const int r = row(); 0406 if (!m_parentTile || row() >= m_parentTile->childCount() - 1) { 0407 return nullptr; 0408 } else { 0409 return m_parentTile->childTiles()[r + 1]; 0410 } 0411 } 0412 0413 Tile *Tile::previousSibling() const 0414 { 0415 const int r = row(); 0416 if (r <= 0 || !m_parentTile) { 0417 return nullptr; 0418 } else { 0419 return m_parentTile->childTiles().at(r - 1); 0420 } 0421 } 0422 0423 } // namespace KWin 0424 0425 #include "moc_tile.cpp"