File indexing completed on 2024-11-10 04:57:21
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 "customtile.h" 0011 #include "core/output.h" 0012 #include "tilemanager.h" 0013 0014 namespace KWin 0015 { 0016 0017 QDebug operator<<(QDebug debug, const CustomTile *tile) 0018 { 0019 QDebugStateSaver saver(debug); 0020 debug.nospace(); 0021 if (tile) { 0022 debug << tile->metaObject()->className() << '(' << static_cast<const void *>(tile); 0023 debug << tile->relativeGeometry() << tile->layoutDirection(); 0024 debug << ')'; 0025 } else { 0026 debug << "Tile(0x0)"; 0027 } 0028 return debug; 0029 } 0030 0031 CustomTile::CustomTile(TileManager *tiling, CustomTile *parentItem) 0032 : Tile(tiling, parentItem) 0033 { 0034 setQuickTileMode(QuickTileFlag::Custom); 0035 m_geometryLock = true; 0036 } 0037 0038 CustomTile *CustomTile::createChildAt(const QRectF &relativeGeometry, LayoutDirection layoutDirection, int position) 0039 { 0040 CustomTile *tile = new CustomTile(manager(), this); 0041 connect(tile, &CustomTile::layoutModified, this, &CustomTile::layoutModified); 0042 tile->setRelativeGeometry(relativeGeometry); 0043 tile->setLayoutDirection(layoutDirection); 0044 manager()->model()->beginInsertTile(tile, position); 0045 insertChild(position, tile); 0046 manager()->model()->endInsertTile(); 0047 return tile; 0048 } 0049 0050 void CustomTile::setRelativeGeometry(const QRectF &geom) 0051 { 0052 if (relativeGeometry() == geom) { 0053 return; 0054 } 0055 0056 QRectF finalGeom = geom.intersected(QRectF(0, 0, 1, 1)); 0057 finalGeom.setWidth(std::max(finalGeom.width(), minimumSize().width())); 0058 finalGeom.setHeight(std::max(finalGeom.height(), minimumSize().height())); 0059 // We couldn't set the minimum size and still remain in boundaries, do nothing 0060 if (finalGeom.right() > 1 || finalGeom.bottom() > 1) { 0061 return; 0062 } 0063 0064 auto *parentT = static_cast<CustomTile *>(parentTile()); 0065 0066 if (!m_geometryLock && parentT && parentT->layoutDirection() != LayoutDirection::Floating) { 0067 m_geometryLock = true; 0068 if (finalGeom.left() != relativeGeometry().left()) { 0069 Tile *tile = nextTileAt(Qt::LeftEdge); 0070 if (tile) { 0071 QRectF tileGeom = tile->relativeGeometry(); 0072 tileGeom.setRight(finalGeom.left()); 0073 tile->setRelativeGeometry(tileGeom); 0074 // The other tile gometry may be not what we set due to size constraints 0075 finalGeom.setLeft(tile->relativeGeometry().right()); 0076 } else { 0077 // We are at the left border of the screen, we are always at 0 0078 finalGeom.setLeft(0); 0079 } 0080 } 0081 if (finalGeom.top() != relativeGeometry().top()) { 0082 auto *tile = nextTileAt(Qt::TopEdge); 0083 if (tile) { 0084 auto tileGeom = tile->relativeGeometry(); 0085 tileGeom.setBottom(finalGeom.top()); 0086 tile->setRelativeGeometry(tileGeom); 0087 finalGeom.setTop(tile->relativeGeometry().bottom()); 0088 } else { 0089 // We are at the top border of the screen, we are always at 0 0090 finalGeom.setTop(0); 0091 } 0092 } 0093 if (finalGeom.right() != relativeGeometry().right()) { 0094 auto *tile = nextTileAt(Qt::RightEdge); 0095 if (tile) { 0096 auto tileGeom = tile->relativeGeometry(); 0097 tileGeom.setLeft(finalGeom.right()); 0098 tile->setRelativeGeometry(tileGeom); 0099 finalGeom.setRight(tile->relativeGeometry().left()); 0100 } else { 0101 // We are at the right border of the screen, we are always at 1 0102 finalGeom.setRight(1); 0103 } 0104 } 0105 if (finalGeom.bottom() != relativeGeometry().bottom()) { 0106 auto *tile = nextTileAt(Qt::BottomEdge); 0107 if (tile) { 0108 auto tileGeom = tile->relativeGeometry(); 0109 tileGeom.setTop(finalGeom.bottom()); 0110 tile->setRelativeGeometry(tileGeom); 0111 finalGeom.setBottom(tile->relativeGeometry().top()); 0112 } else { 0113 // We are at the bottom border of the screen, we are always at 1 0114 finalGeom.setBottom(1); 0115 } 0116 } 0117 m_geometryLock = false; 0118 } else if (parentT && parentT->layoutDirection() == LayoutDirection::Floating) { 0119 finalGeom = geom.intersected(parentT->relativeGeometry()); 0120 } 0121 0122 const auto childrenT = childTiles(); 0123 0124 // Resize all the children to fit in new size 0125 for (auto t : childrenT) { 0126 auto childGeom = t->relativeGeometry(); 0127 childGeom = childGeom.intersected(finalGeom); 0128 if (layoutDirection() == LayoutDirection::Horizontal) { 0129 childGeom.setTop(finalGeom.top()); 0130 childGeom.setBottom(finalGeom.bottom()); 0131 } else if (layoutDirection() == LayoutDirection::Vertical) { 0132 childGeom.setLeft(finalGeom.left()); 0133 childGeom.setRight(finalGeom.right()); 0134 } 0135 0136 t->setRelativeGeometry(childGeom); 0137 } 0138 0139 if (!childrenT.isEmpty()) { 0140 auto childGeom = childrenT.first()->relativeGeometry(); 0141 if (layoutDirection() == LayoutDirection::Horizontal) { 0142 childGeom.setLeft(finalGeom.left()); 0143 } else if (layoutDirection() == LayoutDirection::Vertical) { 0144 childGeom.setTop(finalGeom.top()); 0145 } 0146 childrenT.first()->setRelativeGeometry(childGeom); 0147 } 0148 0149 // Make sure the last child item fills the area 0150 // TODO: ensure all children don't go below minimum size/resize all children 0151 if (!m_geometryLock && !childrenT.isEmpty() && layoutDirection() != LayoutDirection::Floating) { 0152 auto childGeom = childrenT.last()->relativeGeometry(); 0153 childGeom.setRight(finalGeom.right()); 0154 childGeom.setBottom(finalGeom.bottom()); 0155 childrenT.last()->setRelativeGeometry(childGeom); 0156 } 0157 0158 Tile::setRelativeGeometry(finalGeom); 0159 if (parentT) { 0160 Q_EMIT parentT->layoutModified(); 0161 } 0162 m_geometryLock = false; 0163 } 0164 0165 bool CustomTile::supportsResizeGravity(KWin::Gravity gravity) 0166 { 0167 if (layoutDirection() == LayoutDirection::Floating) { 0168 return gravity != Gravity::None; 0169 } 0170 0171 return Tile::supportsResizeGravity(gravity); 0172 } 0173 0174 void CustomTile::split(KWin::Tile::LayoutDirection newDirection) 0175 { 0176 auto *parentT = static_cast<CustomTile *>(parentTile()); 0177 0178 // If we are m_rootLayoutTile always create childrens, not siblings 0179 if (parentT && (parentT->childCount() < 2 || parentT->layoutDirection() == newDirection)) { 0180 // Add a new cell to the current layout 0181 setLayoutDirection(newDirection); 0182 QRectF newGeo; 0183 if (newDirection == LayoutDirection::Floating) { 0184 newGeo = QRectF(relativeGeometry().left() + 0.1, relativeGeometry().top() + 0.1, 0.3, 0.2); 0185 newGeo.setLeft(std::max(newGeo.left(), parentT->relativeGeometry().left())); 0186 newGeo.setTop(std::max(newGeo.top(), parentT->relativeGeometry().top())); 0187 newGeo.setRight(std::min(newGeo.right(), parentT->relativeGeometry().right())); 0188 newGeo.setBottom(std::min(newGeo.bottom(), parentT->relativeGeometry().bottom())); 0189 } else if (newDirection == LayoutDirection::Horizontal) { 0190 newGeo = relativeGeometry(); 0191 newGeo.setWidth(relativeGeometry().width() / 2); 0192 Tile::setRelativeGeometry(newGeo); 0193 newGeo.moveLeft(newGeo.x() + newGeo.width()); 0194 } else if (newDirection == LayoutDirection::Vertical) { 0195 newGeo = relativeGeometry(); 0196 newGeo.setHeight(relativeGeometry().height() / 2); 0197 Tile::setRelativeGeometry(newGeo); 0198 newGeo.moveTop(newGeo.y() + newGeo.height()); 0199 } 0200 0201 parentT->createChildAt(newGeo, layoutDirection(), row() + 1); 0202 } else { 0203 // Do a new layout and put tiles inside 0204 setLayoutDirection(newDirection); 0205 auto newGeo = relativeGeometry(); 0206 if (newDirection == LayoutDirection::Floating) { 0207 // Do a new layout with one floating tile inside 0208 auto startGeom = relativeGeometry(); 0209 if (!childTiles().isEmpty()) { 0210 startGeom = childTiles().last()->relativeGeometry(); 0211 } 0212 newGeo = QRectF(startGeom.left() + 0.05, startGeom.top() + 0.05, 0.3, 0.25); 0213 newGeo.setLeft(std::max(newGeo.left(), relativeGeometry().left())); 0214 newGeo.setTop(std::max(newGeo.top(), relativeGeometry().top())); 0215 newGeo.setRight(std::min(newGeo.right(), relativeGeometry().right())); 0216 newGeo.setBottom(std::min(newGeo.bottom(), relativeGeometry().bottom())); 0217 createChildAt(newGeo, newDirection, childCount()); 0218 } else if (newDirection == LayoutDirection::Horizontal) { 0219 // Do a new layout with 2 cells inside this one 0220 newGeo.setWidth(relativeGeometry().width() / 2); 0221 createChildAt(newGeo, newDirection, childCount()); 0222 newGeo.moveLeft(newGeo.x() + newGeo.width()); 0223 createChildAt(newGeo, newDirection, childCount()); 0224 } else if (newDirection == LayoutDirection::Vertical) { 0225 // Do a new layout with 2 cells inside this one 0226 newGeo.setHeight(relativeGeometry().height() / 2); 0227 createChildAt(newGeo, newDirection, childCount()); 0228 newGeo.moveTop(newGeo.y() + newGeo.height()); 0229 createChildAt(newGeo, newDirection, childCount()); 0230 } 0231 } 0232 } 0233 0234 void CustomTile::moveByPixels(const QPointF &delta) 0235 { 0236 if (static_cast<CustomTile *>(parentTile())->layoutDirection() != LayoutDirection::Floating) { 0237 return; 0238 } 0239 0240 const auto outGeom = manager()->output()->geometry(); 0241 const auto relativeMove = QPointF(delta.x() / outGeom.width(), delta.y() / outGeom.height()); 0242 auto geom = relativeGeometry(); 0243 geom.moveTo(geom.topLeft() + relativeMove); 0244 0245 setRelativeGeometry(geom); 0246 } 0247 0248 void CustomTile::remove() 0249 { 0250 auto *parentT = static_cast<CustomTile *>(parentTile()); 0251 if (!parentT) { 0252 return; 0253 } 0254 0255 auto *prev = previousSibling(); 0256 auto *next = nextSibling(); 0257 0258 manager()->model()->beginRemoveTile(this); 0259 parentT->removeChild(this); 0260 manager()->model()->endRemoveTile(); 0261 manager()->tileRemoved(this); 0262 0263 if (parentT->layoutDirection() == LayoutDirection::Horizontal) { 0264 if (prev && next) { 0265 auto geom = prev->relativeGeometry(); 0266 geom.setRight(relativeGeometry().center().x()); 0267 prev->setRelativeGeometry(geom); 0268 geom = next->relativeGeometry(); 0269 geom.setLeft(relativeGeometry().center().x()); 0270 next->setRelativeGeometry(geom); 0271 } else if (prev) { 0272 auto geom = prev->relativeGeometry(); 0273 geom.setRight(relativeGeometry().right()); 0274 prev->setRelativeGeometry(geom); 0275 } else if (next) { 0276 auto geom = next->relativeGeometry(); 0277 geom.setLeft(relativeGeometry().left()); 0278 next->setRelativeGeometry(geom); 0279 } 0280 } else if (parentT->layoutDirection() == LayoutDirection::Vertical) { 0281 if (prev && next) { 0282 auto geom = prev->relativeGeometry(); 0283 geom.setBottom(relativeGeometry().center().y()); 0284 prev->setRelativeGeometry(geom); 0285 geom = next->relativeGeometry(); 0286 geom.setTop(relativeGeometry().center().y()); 0287 next->setRelativeGeometry(geom); 0288 } else if (prev) { 0289 auto geom = prev->relativeGeometry(); 0290 geom.setBottom(relativeGeometry().bottom()); 0291 prev->setRelativeGeometry(geom); 0292 } else if (next) { 0293 auto geom = next->relativeGeometry(); 0294 geom.setTop(relativeGeometry().top()); 0295 next->setRelativeGeometry(geom); 0296 } 0297 } 0298 0299 // On linear layouts remove the last one and promote the layout as leaf 0300 if (parentT->layoutDirection() != Tile::LayoutDirection::Floating && parentT->childCount() == 1) { 0301 auto *lastTile = static_cast<CustomTile *>(parentT->childTile(0)); 0302 if (lastTile->childCount() == 0) { 0303 lastTile->remove(); 0304 } 0305 } 0306 0307 delete this; 0308 } 0309 0310 CustomTile *CustomTile::nextTileAt(Qt::Edge edge) const 0311 { 0312 auto *parentT = static_cast<CustomTile *>(parentTile()); 0313 0314 // TODO: implement geometry base searching for floating? 0315 if (!parentT || parentT->layoutDirection() == LayoutDirection::Floating) { 0316 return nullptr; 0317 } 0318 0319 Tile *sibling = nullptr; 0320 0321 const int index = row(); 0322 int layoutRows; 0323 int layoutColumns; 0324 switch (parentT->layoutDirection()) { 0325 case LayoutDirection::Vertical: 0326 layoutRows = std::max(1, parentT->childCount()); 0327 layoutColumns = 1; 0328 break; 0329 case LayoutDirection::Horizontal: 0330 default: 0331 layoutColumns = std::max(1, parentT->childCount()); 0332 layoutRows = 1; 0333 break; 0334 } 0335 int row = index / layoutColumns; 0336 int column = index % layoutColumns; 0337 0338 switch (edge) { 0339 case Qt::LeftEdge: 0340 if (column > 0) { 0341 sibling = previousSibling(); 0342 } 0343 break; 0344 case Qt::TopEdge: 0345 if (row > 0) { 0346 sibling = parentT->childTiles()[layoutColumns * (row - 1) + column]; 0347 } 0348 break; 0349 case Qt::RightEdge: 0350 if (column < layoutColumns - 1) { 0351 sibling = nextSibling(); 0352 } 0353 break; 0354 case Qt::BottomEdge: 0355 if (row < layoutRows - 1) { 0356 const int newIndex = layoutColumns * (row + 1) + column; 0357 if (newIndex < parentT->childCount()) { 0358 sibling = parentT->childTiles()[newIndex]; 0359 } 0360 } 0361 break; 0362 } 0363 0364 if (sibling) { 0365 return static_cast<CustomTile *>(sibling); 0366 } else { 0367 return parentT->nextTileAt(edge); 0368 } 0369 } 0370 0371 void CustomTile::setLayoutDirection(Tile::LayoutDirection dir) 0372 { 0373 if (m_layoutDirection == dir) { 0374 return; 0375 } 0376 0377 m_layoutDirection = dir; 0378 Q_EMIT layoutDirectionChanged(dir); 0379 } 0380 0381 Tile::LayoutDirection CustomTile::layoutDirection() const 0382 { 0383 return m_layoutDirection; 0384 } 0385 0386 RootTile::RootTile(TileManager *tiling) 0387 : CustomTile(tiling, nullptr) 0388 { 0389 setParent(tiling); 0390 setRelativeGeometry({0, 0, 1, 1}); 0391 } 0392 0393 } // namespace KWin 0394 0395 #include "moc_customtile.cpp"