File indexing completed on 2024-05-19 05:32:19

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