File indexing completed on 2024-05-19 16:34:45
0001 /* 0002 SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "scene/item.h" 0008 #include "core/renderlayer.h" 0009 #include "core/renderloop.h" 0010 #include "scene/scene.h" 0011 #include "utils/common.h" 0012 0013 namespace KWin 0014 { 0015 0016 Item::Item(Scene *scene, Item *parent) 0017 : m_scene(scene) 0018 { 0019 setParentItem(parent); 0020 connect(m_scene, &Scene::delegateRemoved, this, &Item::removeRepaints); 0021 } 0022 0023 Item::~Item() 0024 { 0025 setParentItem(nullptr); 0026 for (const auto &dirty : std::as_const(m_repaints)) { 0027 if (!dirty.isEmpty()) { 0028 m_scene->addRepaint(dirty); 0029 } 0030 } 0031 } 0032 0033 Scene *Item::scene() const 0034 { 0035 return m_scene; 0036 } 0037 0038 qreal Item::opacity() const 0039 { 0040 return m_opacity; 0041 } 0042 0043 void Item::setOpacity(qreal opacity) 0044 { 0045 if (m_opacity != opacity) { 0046 m_opacity = opacity; 0047 scheduleRepaint(boundingRect()); 0048 } 0049 } 0050 0051 int Item::z() const 0052 { 0053 return m_z; 0054 } 0055 0056 void Item::setZ(int z) 0057 { 0058 if (m_z == z) { 0059 return; 0060 } 0061 m_z = z; 0062 if (m_parentItem) { 0063 m_parentItem->markSortedChildItemsDirty(); 0064 } 0065 scheduleRepaint(boundingRect()); 0066 } 0067 0068 Item *Item::parentItem() const 0069 { 0070 return m_parentItem; 0071 } 0072 0073 void Item::setParentItem(Item *item) 0074 { 0075 if (m_parentItem == item) { 0076 return; 0077 } 0078 if (m_parentItem) { 0079 m_parentItem->removeChild(this); 0080 } 0081 m_parentItem = item; 0082 if (m_parentItem) { 0083 Q_ASSERT(m_parentItem->m_scene == m_scene); 0084 m_parentItem->addChild(this); 0085 } 0086 updateEffectiveVisibility(); 0087 } 0088 0089 void Item::addChild(Item *item) 0090 { 0091 Q_ASSERT(!m_childItems.contains(item)); 0092 0093 m_childItems.append(item); 0094 markSortedChildItemsDirty(); 0095 0096 updateBoundingRect(); 0097 scheduleRepaint(item->boundingRect().translated(item->position())); 0098 0099 Q_EMIT childAdded(item); 0100 } 0101 0102 void Item::removeChild(Item *item) 0103 { 0104 Q_ASSERT(m_childItems.contains(item)); 0105 scheduleRepaint(item->boundingRect().translated(item->position())); 0106 0107 m_childItems.removeOne(item); 0108 markSortedChildItemsDirty(); 0109 0110 updateBoundingRect(); 0111 } 0112 0113 QList<Item *> Item::childItems() const 0114 { 0115 return m_childItems; 0116 } 0117 0118 QPointF Item::position() const 0119 { 0120 return m_position; 0121 } 0122 0123 void Item::setPosition(const QPointF &point) 0124 { 0125 if (m_position != point) { 0126 scheduleRepaint(boundingRect()); 0127 m_position = point; 0128 if (m_parentItem) { 0129 m_parentItem->updateBoundingRect(); 0130 } 0131 scheduleRepaint(boundingRect()); 0132 Q_EMIT positionChanged(); 0133 } 0134 } 0135 0136 QSizeF Item::size() const 0137 { 0138 return m_size; 0139 } 0140 0141 void Item::setSize(const QSizeF &size) 0142 { 0143 if (m_size != size) { 0144 scheduleRepaint(rect()); 0145 m_size = size; 0146 updateBoundingRect(); 0147 scheduleRepaint(rect()); 0148 discardQuads(); 0149 Q_EMIT sizeChanged(); 0150 } 0151 } 0152 0153 QRectF Item::rect() const 0154 { 0155 return QRectF(QPoint(0, 0), size()); 0156 } 0157 0158 QRectF Item::boundingRect() const 0159 { 0160 return m_boundingRect; 0161 } 0162 0163 void Item::updateBoundingRect() 0164 { 0165 QRectF boundingRect = rect(); 0166 for (Item *item : std::as_const(m_childItems)) { 0167 boundingRect |= item->boundingRect().translated(item->position()); 0168 } 0169 if (m_boundingRect != boundingRect) { 0170 m_boundingRect = boundingRect; 0171 Q_EMIT boundingRectChanged(); 0172 if (m_parentItem) { 0173 m_parentItem->updateBoundingRect(); 0174 } 0175 } 0176 } 0177 0178 QVector<QRectF> Item::shape() const 0179 { 0180 return QVector<QRectF>(); 0181 } 0182 0183 QRegion Item::opaque() const 0184 { 0185 return QRegion(); 0186 } 0187 0188 QPointF Item::rootPosition() const 0189 { 0190 QPointF ret = position(); 0191 0192 Item *parent = parentItem(); 0193 while (parent) { 0194 ret += parent->position(); 0195 parent = parent->parentItem(); 0196 } 0197 0198 return ret; 0199 } 0200 0201 QMatrix4x4 Item::transform() const 0202 { 0203 return m_transform; 0204 } 0205 0206 void Item::setTransform(const QMatrix4x4 &transform) 0207 { 0208 m_transform = transform; 0209 } 0210 0211 QRegion Item::mapToGlobal(const QRegion ®ion) const 0212 { 0213 if (region.isEmpty()) { 0214 return QRegion(); 0215 } 0216 return region.translated(rootPosition().toPoint()); 0217 } 0218 0219 QRectF Item::mapToGlobal(const QRectF &rect) const 0220 { 0221 if (rect.isEmpty()) { 0222 return QRect(); 0223 } 0224 return rect.translated(rootPosition()); 0225 } 0226 0227 QRectF Item::mapFromGlobal(const QRectF &rect) const 0228 { 0229 if (rect.isEmpty()) { 0230 return QRect(); 0231 } 0232 return rect.translated(-rootPosition()); 0233 } 0234 0235 void Item::stackBefore(Item *sibling) 0236 { 0237 if (Q_UNLIKELY(!sibling)) { 0238 qCDebug(KWIN_CORE) << Q_FUNC_INFO << "requires a valid sibling"; 0239 return; 0240 } 0241 if (Q_UNLIKELY(!sibling->parentItem() || sibling->parentItem() != parentItem())) { 0242 qCDebug(KWIN_CORE) << Q_FUNC_INFO << "requires items to be siblings"; 0243 return; 0244 } 0245 if (Q_UNLIKELY(sibling == this)) { 0246 return; 0247 } 0248 0249 const int selfIndex = m_parentItem->m_childItems.indexOf(this); 0250 const int siblingIndex = m_parentItem->m_childItems.indexOf(sibling); 0251 0252 if (selfIndex == siblingIndex - 1) { 0253 return; 0254 } 0255 0256 m_parentItem->m_childItems.move(selfIndex, selfIndex > siblingIndex ? siblingIndex : siblingIndex - 1); 0257 markSortedChildItemsDirty(); 0258 0259 scheduleRepaint(boundingRect()); 0260 sibling->scheduleRepaint(sibling->boundingRect()); 0261 } 0262 0263 void Item::stackAfter(Item *sibling) 0264 { 0265 if (Q_UNLIKELY(!sibling)) { 0266 qCDebug(KWIN_CORE) << Q_FUNC_INFO << "requires a valid sibling"; 0267 return; 0268 } 0269 if (Q_UNLIKELY(!sibling->parentItem() || sibling->parentItem() != parentItem())) { 0270 qCDebug(KWIN_CORE) << Q_FUNC_INFO << "requires items to be siblings"; 0271 return; 0272 } 0273 if (Q_UNLIKELY(sibling == this)) { 0274 return; 0275 } 0276 0277 const int selfIndex = m_parentItem->m_childItems.indexOf(this); 0278 const int siblingIndex = m_parentItem->m_childItems.indexOf(sibling); 0279 0280 if (selfIndex == siblingIndex + 1) { 0281 return; 0282 } 0283 0284 m_parentItem->m_childItems.move(selfIndex, selfIndex > siblingIndex ? siblingIndex + 1 : siblingIndex); 0285 markSortedChildItemsDirty(); 0286 0287 scheduleRepaint(boundingRect()); 0288 sibling->scheduleRepaint(sibling->boundingRect()); 0289 } 0290 0291 void Item::scheduleRepaint(const QRegion ®ion) 0292 { 0293 if (isVisible()) { 0294 scheduleRepaintInternal(region); 0295 } 0296 } 0297 0298 void Item::scheduleRepaintInternal(const QRegion ®ion) 0299 { 0300 const QRegion globalRegion = mapToGlobal(region); 0301 const QList<SceneDelegate *> delegates = m_scene->delegates(); 0302 for (SceneDelegate *delegate : delegates) { 0303 const QRegion dirtyRegion = globalRegion & delegate->viewport(); 0304 if (!dirtyRegion.isEmpty()) { 0305 m_repaints[delegate] += dirtyRegion; 0306 delegate->layer()->loop()->scheduleRepaint(this); 0307 } 0308 } 0309 } 0310 0311 void Item::scheduleFrame() 0312 { 0313 if (!isVisible()) { 0314 return; 0315 } 0316 const QRect geometry = mapToGlobal(rect()).toRect(); 0317 const QList<SceneDelegate *> delegates = m_scene->delegates(); 0318 for (SceneDelegate *delegate : delegates) { 0319 if (delegate->viewport().intersects(geometry)) { 0320 delegate->layer()->loop()->scheduleRepaint(this); 0321 } 0322 } 0323 } 0324 0325 void Item::preprocess() 0326 { 0327 } 0328 0329 WindowQuadList Item::buildQuads() const 0330 { 0331 return WindowQuadList(); 0332 } 0333 0334 void Item::discardQuads() 0335 { 0336 m_quads.reset(); 0337 } 0338 0339 WindowQuadList Item::quads() const 0340 { 0341 if (!m_quads.has_value()) { 0342 m_quads = buildQuads(); 0343 } 0344 return m_quads.value(); 0345 } 0346 0347 QRegion Item::repaints(SceneDelegate *delegate) const 0348 { 0349 return m_repaints.value(delegate); 0350 } 0351 0352 void Item::resetRepaints(SceneDelegate *delegate) 0353 { 0354 m_repaints.insert(delegate, QRegion()); 0355 } 0356 0357 void Item::removeRepaints(SceneDelegate *delegate) 0358 { 0359 m_repaints.remove(delegate); 0360 } 0361 0362 bool Item::explicitVisible() const 0363 { 0364 return m_explicitVisible; 0365 } 0366 0367 bool Item::isVisible() const 0368 { 0369 return m_effectiveVisible; 0370 } 0371 0372 void Item::setVisible(bool visible) 0373 { 0374 if (m_explicitVisible != visible) { 0375 m_explicitVisible = visible; 0376 updateEffectiveVisibility(); 0377 } 0378 } 0379 0380 void Item::scheduleRepaint(const QRectF ®ion) 0381 { 0382 scheduleRepaint(QRegion(region.toAlignedRect())); 0383 } 0384 0385 bool Item::computeEffectiveVisibility() const 0386 { 0387 return m_explicitVisible && (!m_parentItem || m_parentItem->isVisible()); 0388 } 0389 0390 void Item::updateEffectiveVisibility() 0391 { 0392 const bool effectiveVisible = computeEffectiveVisibility(); 0393 if (m_effectiveVisible == effectiveVisible) { 0394 return; 0395 } 0396 0397 m_effectiveVisible = effectiveVisible; 0398 if (!m_effectiveVisible) { 0399 m_scene->addRepaint(mapToGlobal(boundingRect()).toAlignedRect()); 0400 } else { 0401 scheduleRepaintInternal(boundingRect().toAlignedRect()); 0402 } 0403 0404 for (Item *childItem : std::as_const(m_childItems)) { 0405 childItem->updateEffectiveVisibility(); 0406 } 0407 } 0408 0409 static bool compareZ(const Item *a, const Item *b) 0410 { 0411 return a->z() < b->z(); 0412 } 0413 0414 QList<Item *> Item::sortedChildItems() const 0415 { 0416 if (!m_sortedChildItems.has_value()) { 0417 QList<Item *> items = m_childItems; 0418 std::stable_sort(items.begin(), items.end(), compareZ); 0419 m_sortedChildItems = items; 0420 } 0421 return m_sortedChildItems.value(); 0422 } 0423 0424 void Item::markSortedChildItemsDirty() 0425 { 0426 m_sortedChildItems.reset(); 0427 } 0428 0429 } // namespace KWin