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 &region) 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 &region)
0292 {
0293     if (isVisible()) {
0294         scheduleRepaintInternal(region);
0295     }
0296 }
0297 
0298 void Item::scheduleRepaintInternal(const QRegion &region)
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 &region)
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