File indexing completed on 2024-12-01 11:10:43
0001 /* 0002 SPDX-FileCopyrightText: 2018 Michail Vourlakos <mvourlakos@gmail.com> 0003 SPDX-License-Identifier: GPL-2.0-or-later 0004 */ 0005 0006 #include "positioner.h" 0007 0008 // local 0009 #include <coretypes.h> 0010 #include "effects.h" 0011 #include "originalview.h" 0012 #include "view.h" 0013 #include "visibilitymanager.h" 0014 #include "../lattecorona.h" 0015 #include "../screenpool.h" 0016 #include "../data/screendata.h" 0017 #include "../layout/centrallayout.h" 0018 #include "../layouts/manager.h" 0019 #include "../settings/universalsettings.h" 0020 #include "../wm/abstractwindowinterface.h" 0021 0022 // Qt 0023 #include <QDebug> 0024 0025 // KDE 0026 #include <KWayland/Client/plasmashell.h> 0027 #include <KWayland/Client/surface.h> 0028 #include <KWindowSystem> 0029 0030 #define RELOCATIONSHOWINGEVENT "viewInRelocationShowing" 0031 0032 namespace Latte { 0033 namespace ViewPart { 0034 0035 Positioner::Positioner(Latte::View *parent) 0036 : QObject(parent), 0037 m_view(parent) 0038 { 0039 m_screenSyncTimer.setSingleShot(true); 0040 m_screenSyncTimer.setInterval(2000); 0041 connect(&m_screenSyncTimer, &QTimer::timeout, this, &Positioner::reconsiderScreen); 0042 0043 //! under X11 it was identified that windows many times especially under screen changes 0044 //! don't end up at the correct position and size. This timer will enforce repositionings 0045 //! and resizes every 500ms if the window hasn't end up to correct values and until this 0046 //! is achieved 0047 m_validateGeometryTimer.setSingleShot(true); 0048 m_validateGeometryTimer.setInterval(500); 0049 connect(&m_validateGeometryTimer, &QTimer::timeout, this, &Positioner::syncGeometry); 0050 0051 //! syncGeometry() function is costly, so now we make sure that is not executed too often 0052 m_syncGeometryTimer.setSingleShot(true); 0053 m_syncGeometryTimer.setInterval(150); 0054 connect(&m_syncGeometryTimer, &QTimer::timeout, this, &Positioner::immediateSyncGeometry); 0055 0056 m_corona = qobject_cast<Latte::Corona *>(m_view->corona()); 0057 0058 if (m_corona) { 0059 if (KWindowSystem::isPlatformX11()) { 0060 m_trackedWindowId = m_view->winId(); 0061 m_corona->wm()->registerIgnoredWindow(m_trackedWindowId); 0062 0063 connect(m_view, &Latte::View::forcedShown, this, [&]() { 0064 m_corona->wm()->unregisterIgnoredWindow(m_trackedWindowId); 0065 m_trackedWindowId = m_view->winId(); 0066 m_corona->wm()->registerIgnoredWindow(m_trackedWindowId); 0067 }); 0068 } else { 0069 connect(m_view, &QWindow::windowTitleChanged, this, &Positioner::updateWaylandId); 0070 connect(m_corona->wm(), &WindowSystem::AbstractWindowInterface::latteWindowAdded, this, &Positioner::updateWaylandId); 0071 } 0072 0073 connect(m_corona->layoutsManager(), &Layouts::Manager::currentLayoutIsSwitching, this, &Positioner::onCurrentLayoutIsSwitching); 0074 ///// 0075 0076 m_screenSyncTimer.setInterval(qMax(m_corona->universalSettings()->screenTrackerInterval() - 500, 1000)); 0077 connect(m_corona->universalSettings(), &UniversalSettings::screenTrackerIntervalChanged, this, [&]() { 0078 m_screenSyncTimer.setInterval(qMax(m_corona->universalSettings()->screenTrackerInterval() - 500, 1000)); 0079 }); 0080 0081 connect(m_corona, &Latte::Corona::viewLocationChanged, this, [&]() { 0082 //! check if an edge has been freed for a primary dock 0083 //! from another screen 0084 if (m_view->onPrimary()) { 0085 m_screenSyncTimer.start(); 0086 } 0087 }); 0088 } 0089 0090 init(); 0091 } 0092 0093 Positioner::~Positioner() 0094 { 0095 m_inDelete = true; 0096 slideOutDuringExit(); 0097 m_corona->wm()->unregisterIgnoredWindow(m_trackedWindowId); 0098 0099 m_screenSyncTimer.stop(); 0100 m_validateGeometryTimer.stop(); 0101 } 0102 0103 void Positioner::init() 0104 { 0105 //! connections 0106 connect(this, &Positioner::screenGeometryChanged, this, &Positioner::syncGeometry); 0107 0108 connect(this, &Positioner::hidingForRelocationStarted, this, &Positioner::updateInRelocationAnimation); 0109 connect(this, &Positioner::showingAfterRelocationFinished, this, &Positioner::updateInRelocationAnimation); 0110 connect(this, &Positioner::showingAfterRelocationFinished, this, &Positioner::syncLatteViews); 0111 connect(this, &Positioner::startupFinished, this, &Positioner::onStartupFinished); 0112 0113 connect(m_view, &Latte::View::onPrimaryChanged, this, &Positioner::syncLatteViews); 0114 0115 connect(this, &Positioner::inSlideAnimationChanged, this, [&]() { 0116 if (!inSlideAnimation()) { 0117 syncGeometry(); 0118 } 0119 }); 0120 0121 connect(this, &Positioner::isStickedOnTopEdgeChanged, this, [&]() { 0122 if (m_view->formFactor() == Plasma::Types::Vertical) { 0123 syncGeometry(); 0124 } 0125 }); 0126 0127 connect(this, &Positioner::isStickedOnBottomEdgeChanged, this, [&]() { 0128 if (m_view->formFactor() == Plasma::Types::Vertical) { 0129 syncGeometry(); 0130 } 0131 }); 0132 0133 connect(m_corona->activitiesConsumer(), &KActivities::Consumer::currentActivityChanged, this, [&]() { 0134 if (m_view->formFactor() == Plasma::Types::Vertical && m_view->layout() && m_view->layout()->isCurrent()) { 0135 syncGeometry(); 0136 } 0137 }); 0138 0139 connect(this, &Positioner::slideOffsetChanged, this, [&]() { 0140 updatePosition(m_lastAvailableScreenRect); 0141 }); 0142 0143 connect(m_view, &QQuickWindow::xChanged, this, &Positioner::validateDockGeometry); 0144 connect(m_view, &QQuickWindow::yChanged, this, &Positioner::validateDockGeometry); 0145 connect(m_view, &QQuickWindow::widthChanged, this, &Positioner::validateDockGeometry); 0146 connect(m_view, &QQuickWindow::heightChanged, this, &Positioner::validateDockGeometry); 0147 connect(m_view, &QQuickWindow::screenChanged, this, &Positioner::currentScreenChanged); 0148 connect(m_view, &QQuickWindow::screenChanged, this, &Positioner::onScreenChanged); 0149 0150 connect(m_view, &Latte::View::behaveAsPlasmaPanelChanged, this, &Positioner::syncGeometry); 0151 connect(m_view, &Latte::View::maxThicknessChanged, this, &Positioner::syncGeometry); 0152 0153 connect(m_view, &Latte::View::behaveAsPlasmaPanelChanged, this, [&]() { 0154 if (!m_view->behaveAsPlasmaPanel() && m_slideOffset != 0) { 0155 m_slideOffset = 0; 0156 syncGeometry(); 0157 } 0158 }); 0159 0160 connect(m_view, &Latte::View::offsetChanged, this, [&]() { 0161 updatePosition(m_lastAvailableScreenRect); 0162 }); 0163 0164 connect(m_view, &Latte::View::locationChanged, this, [&]() { 0165 updateFormFactor(); 0166 syncGeometry(); 0167 }); 0168 0169 connect(m_view, &Latte::View::editThicknessChanged, this, [&]() { 0170 updateCanvasGeometry(m_lastAvailableScreenRect); 0171 }); 0172 0173 connect(m_view, &Latte::View::maxLengthChanged, this, [&]() { 0174 if (m_view->behaveAsPlasmaPanel()) { 0175 syncGeometry(); 0176 } 0177 }); 0178 0179 connect(m_view, &Latte::View::normalThicknessChanged, this, [&]() { 0180 if (m_view->behaveAsPlasmaPanel()) { 0181 syncGeometry(); 0182 } 0183 }); 0184 0185 connect(m_view, &Latte::View::screenEdgeMarginEnabledChanged, this, [&]() { 0186 syncGeometry(); 0187 }); 0188 0189 connect(m_view, &Latte::View::screenEdgeMarginChanged, this, [&]() { 0190 syncGeometry(); 0191 }); 0192 0193 connect(m_view, &View::layoutChanged, this, [&]() { 0194 if (m_nextLayoutName.isEmpty() && m_view->layout() && m_view->formFactor() == Plasma::Types::Vertical) { 0195 syncGeometry(); 0196 } 0197 }); 0198 0199 connect(m_view->effects(), &Latte::ViewPart::Effects::drawShadowsChanged, this, [&]() { 0200 if (!m_view->behaveAsPlasmaPanel()) { 0201 syncGeometry(); 0202 } 0203 }); 0204 0205 connect(m_view->effects(), &Latte::ViewPart::Effects::innerShadowChanged, this, [&]() { 0206 if (m_view->behaveAsPlasmaPanel()) { 0207 syncGeometry(); 0208 } 0209 }); 0210 0211 connect(qGuiApp, &QGuiApplication::screenAdded, this, &Positioner::onScreenChanged); 0212 connect(m_corona->screenPool(), &ScreenPool::primaryScreenChanged, this, &Positioner::onScreenChanged); 0213 0214 connect(m_view, &Latte::View::visibilityChanged, this, &Positioner::initDelayedSignals); 0215 0216 initSignalingForLocationChangeSliding(); 0217 } 0218 0219 void Positioner::initDelayedSignals() 0220 { 0221 connect(m_view->visibility(), &ViewPart::VisibilityManager::isHiddenChanged, this, [&]() { 0222 if (m_view->behaveAsPlasmaPanel() && !m_view->visibility()->isHidden() && qAbs(m_slideOffset)>0) { 0223 //! ignore any checks to make sure the panel geometry is up-to-date 0224 immediateSyncGeometry(); 0225 } 0226 }); 0227 } 0228 0229 void Positioner::updateWaylandId() 0230 { 0231 QString validTitle = m_view->validTitle(); 0232 if (validTitle.isEmpty()) { 0233 return; 0234 } 0235 0236 Latte::WindowSystem::WindowId newId = m_corona->wm()->winIdFor("latte-dock", validTitle); 0237 0238 if (m_trackedWindowId != newId) { 0239 if (!m_trackedWindowId.isNull()) { 0240 m_corona->wm()->unregisterIgnoredWindow(m_trackedWindowId); 0241 } 0242 0243 m_trackedWindowId = newId; 0244 m_corona->wm()->registerIgnoredWindow(m_trackedWindowId); 0245 0246 emit winIdChanged(); 0247 } 0248 } 0249 0250 bool Positioner::inRelocationShowing() const 0251 { 0252 return m_inRelocationShowing; 0253 } 0254 0255 void Positioner::setInRelocationShowing(bool active) 0256 { 0257 if (m_inRelocationShowing == active) { 0258 return; 0259 } 0260 0261 m_inRelocationShowing = active; 0262 0263 if (m_inRelocationShowing) { 0264 m_view->visibility()->addBlockHidingEvent(RELOCATIONSHOWINGEVENT); 0265 } else { 0266 m_view->visibility()->removeBlockHidingEvent(RELOCATIONSHOWINGEVENT); 0267 } 0268 } 0269 0270 bool Positioner::isOffScreen() const 0271 { 0272 return (m_view->absoluteGeometry().x()<-500 || m_view->absoluteGeometry().y()<-500); 0273 } 0274 0275 int Positioner::currentScreenId() const 0276 { 0277 auto *latteCorona = qobject_cast<Latte::Corona *>(m_view->corona()); 0278 0279 if (latteCorona) { 0280 return latteCorona->screenPool()->id(m_screenNameToFollow); 0281 } 0282 0283 return -1; 0284 } 0285 0286 Latte::WindowSystem::WindowId Positioner::trackedWindowId() 0287 { 0288 if (KWindowSystem::isPlatformWayland() && m_trackedWindowId.toInt() <= 0) { 0289 updateWaylandId(); 0290 } 0291 0292 return m_trackedWindowId; 0293 } 0294 0295 QString Positioner::currentScreenName() const 0296 { 0297 return m_screenNameToFollow; 0298 } 0299 0300 WindowSystem::AbstractWindowInterface::Slide Positioner::slideLocation(Plasma::Types::Location location) 0301 { 0302 auto slideedge = WindowSystem::AbstractWindowInterface::Slide::None; 0303 0304 if (location == Plasma::Types::Floating && m_view->containment()) { 0305 location = m_view->containment()->location(); 0306 } 0307 0308 switch (location) { 0309 case Plasma::Types::TopEdge: 0310 slideedge = WindowSystem::AbstractWindowInterface::Slide::Top; 0311 break; 0312 0313 case Plasma::Types::RightEdge: 0314 slideedge = WindowSystem::AbstractWindowInterface::Slide::Right; 0315 break; 0316 0317 case Plasma::Types::BottomEdge: 0318 slideedge = WindowSystem::AbstractWindowInterface::Slide::Bottom; 0319 break; 0320 0321 case Plasma::Types::LeftEdge: 0322 slideedge = WindowSystem::AbstractWindowInterface::Slide::Left; 0323 break; 0324 0325 default: 0326 qDebug() << staticMetaObject.className() << "wrong location"; 0327 break; 0328 } 0329 0330 return slideedge; 0331 } 0332 0333 void Positioner::slideOutDuringExit(Plasma::Types::Location location) 0334 { 0335 if (m_view->isVisible()) { 0336 m_corona->wm()->slideWindow(*m_view, slideLocation(location)); 0337 m_view->setVisible(false); 0338 } 0339 } 0340 0341 void Positioner::slideInDuringStartup() 0342 { 0343 m_corona->wm()->slideWindow(*m_view, slideLocation(m_view->containment()->location())); 0344 } 0345 0346 void Positioner::onStartupFinished() 0347 { 0348 if (m_inStartup) { 0349 m_inStartup = false; 0350 syncGeometry(); 0351 emit isOffScreenChanged(); 0352 } 0353 } 0354 0355 void Positioner::onCurrentLayoutIsSwitching(const QString &layoutName) 0356 { 0357 if (!m_view || !m_view->layout() || m_view->layout()->name() != layoutName || !m_view->isVisible()) { 0358 return; 0359 } 0360 0361 m_inLayoutUnloading = true; 0362 slideOutDuringExit(); 0363 } 0364 0365 void Positioner::setWindowOnActivities(const Latte::WindowSystem::WindowId &wid, const QStringList &activities) 0366 { 0367 m_corona->wm()->setWindowOnActivities(wid, activities); 0368 } 0369 0370 void Positioner::syncLatteViews() 0371 { 0372 if (m_view->layout()) { 0373 //! This is needed in case the edge there are views that must be deleted 0374 //! after screen edges changes 0375 m_view->layout()->syncLatteViewsToScreens(); 0376 } 0377 } 0378 0379 void Positioner::updateContainmentScreen() 0380 { 0381 if (m_view->containment()) { 0382 m_view->containment()->reactToScreenChange(); 0383 } 0384 } 0385 0386 //! this function updates the dock's associated screen. 0387 //! updateScreenId = true, update also the m_screenNameToFollow 0388 //! updateScreenId = false, do not update the m_screenNameToFollow 0389 //! that way an explicit dock can be shown in another screen when 0390 //! there isnt a tasks dock running in the system and for that 0391 //! dock its first origin screen is stored and that way when 0392 //! that screen is reconnected the dock will return to its original 0393 //! place 0394 void Positioner::setScreenToFollow(QScreen *scr, bool updateScreenId) 0395 { 0396 if (!scr || (scr && (m_screenToFollow == scr) && (m_view->screen() == scr))) { 0397 return; 0398 } 0399 0400 qDebug() << "setScreenToFollow() called for screen:" << scr->name() << " update:" << updateScreenId; 0401 0402 m_screenToFollow = scr; 0403 0404 if (updateScreenId) { 0405 m_screenNameToFollow = scr->name(); 0406 } 0407 0408 qDebug() << "adapting to screen..."; 0409 m_view->setScreen(scr); 0410 0411 updateContainmentScreen(); 0412 0413 connect(scr, &QScreen::geometryChanged, this, &Positioner::screenGeometryChanged); 0414 syncGeometry(); 0415 m_view->updateAbsoluteGeometry(true); 0416 qDebug() << "setScreenToFollow() ended..."; 0417 0418 emit screenGeometryChanged(); 0419 emit currentScreenChanged(); 0420 } 0421 0422 //! the main function which decides if this dock is at the 0423 //! correct screen 0424 void Positioner::reconsiderScreen() 0425 { 0426 if (m_inDelete) { 0427 return; 0428 } 0429 0430 qDebug() << "reconsiderScreen() called..."; 0431 qDebug() << " Delayer "; 0432 0433 for (const auto scr : qGuiApp->screens()) { 0434 qDebug() << " D, found screen: " << scr->name(); 0435 } 0436 0437 bool screenExists{false}; 0438 QScreen *primaryScreen{m_corona->screenPool()->primaryScreen()}; 0439 0440 //!check if the associated screen is running 0441 for (const auto scr : qGuiApp->screens()) { 0442 if (m_screenNameToFollow == scr->name() 0443 || (m_view->onPrimary() && scr == primaryScreen)) { 0444 screenExists = true; 0445 } 0446 } 0447 0448 qDebug() << "dock screen exists ::: " << screenExists; 0449 0450 //! 1.a primary dock must be always on the primary screen 0451 if (m_view->onPrimary() && (m_screenNameToFollow != primaryScreen->name() 0452 || m_screenToFollow != primaryScreen 0453 || m_view->screen() != primaryScreen)) { 0454 //! case 1 0455 qDebug() << "reached case 1: of updating dock primary screen..."; 0456 setScreenToFollow(primaryScreen); 0457 } else if (!m_view->onPrimary()) { 0458 //! 2.an explicit dock must be always on the correct associated screen 0459 //! there are cases that window manager misplaces the dock, this function 0460 //! ensures that this dock will return at its correct screen 0461 for (const auto scr : qGuiApp->screens()) { 0462 if (scr && scr->name() == m_screenNameToFollow) { 0463 qDebug() << "reached case 2: updating the explicit screen for dock..."; 0464 setScreenToFollow(scr); 0465 break; 0466 } 0467 } 0468 } 0469 0470 syncGeometry(); 0471 qDebug() << "reconsiderScreen() ended..."; 0472 } 0473 0474 void Positioner::onScreenChanged(QScreen *scr) 0475 { 0476 m_screenSyncTimer.start(); 0477 0478 //! this is needed in order to update the struts on screen change 0479 //! and even though the geometry has been set correctly the offsets 0480 //! of the screen must be updated to the new ones 0481 if (m_view->visibility() && m_view->visibility()->mode() == Latte::Types::AlwaysVisible) { 0482 m_view->updateAbsoluteGeometry(true); 0483 } 0484 } 0485 0486 void Positioner::syncGeometry() 0487 { 0488 if (!(m_view->screen() && m_view->containment()) || m_inDelete || m_slideOffset!=0 || inSlideAnimation()) { 0489 return; 0490 } 0491 0492 qDebug() << "syncGeometry() called..."; 0493 0494 if (!m_syncGeometryTimer.isActive()) { 0495 m_syncGeometryTimer.start(); 0496 } 0497 } 0498 0499 void Positioner::immediateSyncGeometry() 0500 { 0501 bool found{false}; 0502 0503 qDebug() << "immediateSyncGeometry() called..."; 0504 0505 //! before updating the positioning and geometry of the dock 0506 //! we make sure that the dock is at the correct screen 0507 if (m_view->screen() != m_screenToFollow) { 0508 qDebug() << "Sync Geometry screens inconsistent!!!! "; 0509 0510 if (m_screenToFollow) { 0511 qDebug() << "Sync Geometry screens inconsistent for m_screenToFollow:" << m_screenToFollow->name() << " dock screen:" << m_view->screen()->name(); 0512 } 0513 0514 if (!m_screenSyncTimer.isActive()) { 0515 m_screenSyncTimer.start(); 0516 } 0517 } else { 0518 found = true; 0519 } 0520 0521 //! if the dock isnt at the correct screen the calculations 0522 //! are not executed 0523 if (found) { 0524 //! compute the free screen rectangle for vertical panels only once 0525 //! this way the costly QRegion computations are calculated only once 0526 //! instead of two times (both inside the resizeWindow and the updatePosition) 0527 QRegion freeRegion;; 0528 QRect maximumRect; 0529 QRect availableScreenRect = m_view->screen()->geometry(); 0530 0531 if (m_inStartup) { 0532 //! paint out-of-screen 0533 availableScreenRect = QRect(-9999, -9999, m_view->screen()->geometry().width(), m_view->screen()->geometry().height()); 0534 } 0535 0536 if (m_view->formFactor() == Plasma::Types::Vertical) { 0537 QString layoutName = m_view->layout() ? m_view->layout()->name() : QString(); 0538 auto latteCorona = qobject_cast<Latte::Corona *>(m_view->corona()); 0539 int fixedScreen = m_view->onPrimary() ? latteCorona->screenPool()->primaryScreenId() : m_view->containment()->screen(); 0540 0541 QList<Types::Visibility> ignoreModes({Latte::Types::AutoHide, 0542 Latte::Types::SidebarOnDemand, 0543 Latte::Types::SidebarAutoHide}); 0544 0545 QList<Plasma::Types::Location> ignoreEdges({Plasma::Types::LeftEdge, 0546 Plasma::Types::RightEdge}); 0547 0548 if (m_isStickedOnTopEdge && m_isStickedOnBottomEdge) { 0549 //! dont send an empty edges array because that means include all screen edges in calculations 0550 ignoreEdges << Plasma::Types::TopEdge; 0551 ignoreEdges << Plasma::Types::BottomEdge; 0552 } else { 0553 if (m_isStickedOnTopEdge) { 0554 ignoreEdges << Plasma::Types::TopEdge; 0555 } 0556 0557 if (m_isStickedOnBottomEdge) { 0558 ignoreEdges << Plasma::Types::BottomEdge; 0559 } 0560 } 0561 0562 QString activityid = m_view->layout() ? m_view->layout()->lastUsedActivity() : QString(); 0563 if (m_inStartup) { 0564 //! paint out-of-screen 0565 freeRegion = availableScreenRect; 0566 } else { 0567 freeRegion = latteCorona->availableScreenRegionWithCriteria(fixedScreen, activityid, ignoreModes, ignoreEdges); 0568 } 0569 0570 //! On startup when offscreen use offscreen screen geometry. 0571 //! This way vertical docks and panels are not showing are shrinked that 0572 //! need to be expanded after sliding-in in startup 0573 maximumRect = maximumNormalGeometry(m_inStartup ? availableScreenRect : QRect()); 0574 QRegion availableRegion = freeRegion.intersected(maximumRect); 0575 0576 availableScreenRect = freeRegion.intersected(maximumRect).boundingRect(); 0577 float area = 0; 0578 0579 //! it is used to choose which or the availableRegion rectangles will 0580 //! be the one representing dock geometry 0581 for (QRegion::const_iterator p_rect=availableRegion.begin(); p_rect!=availableRegion.end(); ++p_rect) { 0582 //! the area of each rectangle in calculated in squares of 50x50 0583 //! this is a way to avoid enourmous numbers for area value 0584 float tempArea = (float)((*p_rect).width() * (*p_rect).height()) / 2500; 0585 0586 if (tempArea > area) { 0587 availableScreenRect = (*p_rect); 0588 area = tempArea; 0589 } 0590 } 0591 0592 validateTopBottomBorders(availableScreenRect, freeRegion); 0593 m_lastAvailableScreenRegion = freeRegion; 0594 } else { 0595 m_view->effects()->setForceTopBorder(false); 0596 m_view->effects()->setForceBottomBorder(false); 0597 } 0598 0599 m_lastAvailableScreenRect = availableScreenRect; 0600 0601 m_view->effects()->updateEnabledBorders(); 0602 0603 resizeWindow(availableScreenRect); 0604 updatePosition(availableScreenRect); 0605 updateCanvasGeometry(availableScreenRect); 0606 0607 qDebug() << "syncGeometry() calculations for screen: " << m_view->screen()->name() << " _ " << m_view->screen()->geometry(); 0608 qDebug() << "syncGeometry() calculations for edge: " << m_view->location(); 0609 } 0610 0611 qDebug() << "syncGeometry() ended..."; 0612 0613 // qDebug() << "dock geometry:" << qRectToStr(geometry()); 0614 } 0615 0616 void Positioner::validateDockGeometry() 0617 { 0618 if (m_slideOffset==0 && m_view->geometry() != m_validGeometry) { 0619 m_validateGeometryTimer.start(); 0620 } 0621 } 0622 0623 QRect Positioner::canvasGeometry() 0624 { 0625 return m_canvasGeometry; 0626 } 0627 0628 void Positioner::setCanvasGeometry(const QRect &geometry) 0629 { 0630 if (m_canvasGeometry == geometry) { 0631 return; 0632 } 0633 0634 m_canvasGeometry = geometry; 0635 emit canvasGeometryChanged(); 0636 } 0637 0638 0639 //! this is used mainly from vertical panels in order to 0640 //! to get the maximum geometry that can be used from the dock 0641 //! based on their alignment type and the location dock 0642 QRect Positioner::maximumNormalGeometry(QRect screenGeometry) 0643 { 0644 QRect currentScrGeometry = screenGeometry.isEmpty() ? m_view->screen()->geometry() : screenGeometry; 0645 0646 int xPos = 0; 0647 int yPos = currentScrGeometry.y();; 0648 int maxHeight = currentScrGeometry.height(); 0649 int maxWidth = m_view->maxNormalThickness(); 0650 QRect maxGeometry; 0651 maxGeometry.setRect(0, 0, maxWidth, maxHeight); 0652 0653 switch (m_view->location()) { 0654 case Plasma::Types::LeftEdge: 0655 xPos = currentScrGeometry.x(); 0656 maxGeometry.setRect(xPos, yPos, maxWidth, maxHeight); 0657 break; 0658 0659 case Plasma::Types::RightEdge: 0660 xPos = currentScrGeometry.right() - maxWidth + 1; 0661 maxGeometry.setRect(xPos, yPos, maxWidth, maxHeight); 0662 break; 0663 0664 default: 0665 //! bypass clang warnings 0666 break; 0667 } 0668 0669 return maxGeometry; 0670 } 0671 0672 void Positioner::validateTopBottomBorders(QRect availableScreenRect, QRegion availableScreenRegion) 0673 { 0674 //! Check if the the top/bottom borders must be drawn also 0675 int edgeMargin = qMax(1, m_view->screenEdgeMargin()); 0676 0677 if (availableScreenRect.top() != m_view->screenGeometry().top()) { 0678 //! check top border 0679 int x = m_view->location() == Plasma::Types::LeftEdge ? m_view->screenGeometry().x() : m_view->screenGeometry().right() - edgeMargin + 1; 0680 QRegion fitInRegion = QRect(x, availableScreenRect.y()-1, edgeMargin, 1); 0681 QRegion subtracted = fitInRegion.subtracted(availableScreenRegion); 0682 0683 if (subtracted.isNull()) { 0684 //!FitIn rectangle fits TOTALLY in the free screen region and as such 0685 //!the top border should be drawn 0686 m_view->effects()->setForceTopBorder(true); 0687 } else { 0688 m_view->effects()->setForceTopBorder(false); 0689 } 0690 } else { 0691 m_view->effects()->setForceTopBorder(false); 0692 } 0693 0694 if (availableScreenRect.bottom() != m_view->screenGeometry().bottom()) { 0695 //! check top border 0696 int x = m_view->location() == Plasma::Types::LeftEdge ? m_view->screenGeometry().x() : m_view->screenGeometry().right() - edgeMargin + 1; 0697 QRegion fitInRegion = QRect(x, availableScreenRect.bottom()+1, edgeMargin, 1); 0698 QRegion subtracted = fitInRegion.subtracted(availableScreenRegion); 0699 0700 if (subtracted.isNull()) { 0701 //!FitIn rectangle fits TOTALLY in the free screen region and as such 0702 //!the BOTTOM border should be drawn 0703 m_view->effects()->setForceBottomBorder(true); 0704 } else { 0705 m_view->effects()->setForceBottomBorder(false); 0706 } 0707 } else { 0708 m_view->effects()->setForceBottomBorder(false); 0709 } 0710 } 0711 0712 void Positioner::updateCanvasGeometry(QRect availableScreenRect) 0713 { 0714 if (availableScreenRect.isEmpty()) { 0715 return; 0716 } 0717 0718 QRect canvas; 0719 QRect screenGeometry{m_view->screen()->geometry()}; 0720 int thickness{m_view->editThickness()}; 0721 0722 if (m_view->formFactor() == Plasma::Types::Vertical) { 0723 canvas.setWidth(thickness); 0724 canvas.setHeight(availableScreenRect.height()); 0725 } else { 0726 canvas.setWidth(screenGeometry.width()); 0727 canvas.setHeight(thickness); 0728 } 0729 0730 switch (m_view->location()) { 0731 case Plasma::Types::TopEdge: 0732 canvas.moveLeft(screenGeometry.x()); 0733 canvas.moveTop(screenGeometry.y()); 0734 break; 0735 0736 case Plasma::Types::BottomEdge: 0737 canvas.moveLeft(screenGeometry.x()); 0738 canvas.moveTop(screenGeometry.bottom() - thickness + 1); 0739 break; 0740 0741 case Plasma::Types::RightEdge: 0742 canvas.moveLeft(screenGeometry.right() - thickness + 1); 0743 canvas.moveTop(availableScreenRect.y()); 0744 break; 0745 0746 case Plasma::Types::LeftEdge: 0747 canvas.moveLeft(availableScreenRect.x()); 0748 canvas.moveTop(availableScreenRect.y()); 0749 break; 0750 0751 default: 0752 qWarning() << "wrong location, couldn't update the canvas config window geometry " << m_view->location(); 0753 } 0754 0755 setCanvasGeometry(canvas); 0756 } 0757 0758 void Positioner::updatePosition(QRect availableScreenRect) 0759 { 0760 QRect screenGeometry{availableScreenRect}; 0761 QPoint position; 0762 position = {0, 0}; 0763 0764 const auto gap = [&](int scr_length) -> int { 0765 return static_cast<int>(scr_length * m_view->offset()); 0766 }; 0767 const auto gapCentered = [&](int scr_length) -> int { 0768 return static_cast<int>(scr_length * ((1 - m_view->maxLength()) / 2) + scr_length * m_view->offset()); 0769 }; 0770 const auto gapReversed = [&](int scr_length) -> int { 0771 return static_cast<int>(scr_length - (scr_length * m_view->maxLength()) - gap(scr_length)); 0772 }; 0773 0774 int cleanThickness = m_view->normalThickness() - m_view->effects()->innerShadow(); 0775 0776 int screenEdgeMargin = m_view->behaveAsPlasmaPanel() ? m_view->screenEdgeMargin() - qAbs(m_slideOffset) : 0; 0777 0778 switch (m_view->location()) { 0779 case Plasma::Types::TopEdge: 0780 if (m_view->behaveAsPlasmaPanel()) { 0781 int y = screenGeometry.y() + screenEdgeMargin; 0782 0783 if (m_view->alignment() == Latte::Types::Left) { 0784 position = {screenGeometry.x() + gap(screenGeometry.width()), y}; 0785 } else if (m_view->alignment() == Latte::Types::Right) { 0786 position = {screenGeometry.x() + gapReversed(screenGeometry.width()) + 1, y}; 0787 } else { 0788 position = {screenGeometry.x() + gapCentered(screenGeometry.width()), y}; 0789 } 0790 } else { 0791 position = {screenGeometry.x(), screenGeometry.y()}; 0792 } 0793 0794 break; 0795 0796 case Plasma::Types::BottomEdge: 0797 if (m_view->behaveAsPlasmaPanel()) { 0798 int y = screenGeometry.y() + screenGeometry.height() - cleanThickness - screenEdgeMargin; 0799 0800 if (m_view->alignment() == Latte::Types::Left) { 0801 position = {screenGeometry.x() + gap(screenGeometry.width()), y}; 0802 } else if (m_view->alignment() == Latte::Types::Right) { 0803 position = {screenGeometry.x() + gapReversed(screenGeometry.width()) + 1, y}; 0804 } else { 0805 position = {screenGeometry.x() + gapCentered(screenGeometry.width()), y}; 0806 } 0807 } else { 0808 position = {screenGeometry.x(), screenGeometry.y() + screenGeometry.height() - m_view->height()}; 0809 } 0810 0811 break; 0812 0813 case Plasma::Types::RightEdge: 0814 if (m_view->behaveAsPlasmaPanel()) { 0815 int x = availableScreenRect.right() - cleanThickness + 1 - screenEdgeMargin; 0816 0817 if (m_view->alignment() == Latte::Types::Top) { 0818 position = {x, availableScreenRect.y() + gap(availableScreenRect.height())}; 0819 } else if (m_view->alignment() == Latte::Types::Bottom) { 0820 position = {x, availableScreenRect.y() + gapReversed(availableScreenRect.height()) + 1}; 0821 } else { 0822 position = {x, availableScreenRect.y() + gapCentered(availableScreenRect.height())}; 0823 } 0824 } else { 0825 position = {availableScreenRect.right() - m_view->width() + 1, availableScreenRect.y()}; 0826 } 0827 0828 break; 0829 0830 case Plasma::Types::LeftEdge: 0831 if (m_view->behaveAsPlasmaPanel()) { 0832 int x = availableScreenRect.x() + screenEdgeMargin; 0833 0834 if (m_view->alignment() == Latte::Types::Top) { 0835 position = {x, availableScreenRect.y() + gap(availableScreenRect.height())}; 0836 } else if (m_view->alignment() == Latte::Types::Bottom) { 0837 position = {x, availableScreenRect.y() + gapReversed(availableScreenRect.height()) + 1}; 0838 } else { 0839 position = {x, availableScreenRect.y() + gapCentered(availableScreenRect.height())}; 0840 } 0841 } else { 0842 position = {availableScreenRect.x(), availableScreenRect.y()}; 0843 } 0844 0845 break; 0846 0847 default: 0848 qWarning() << "wrong location, couldn't update the panel position" 0849 << m_view->location(); 0850 } 0851 0852 if (m_slideOffset == 0 || m_nextScreenEdge != Plasma::Types::Floating /*exactly after relocating and changing screen edge*/) { 0853 //! update valid geometry in normal positioning 0854 m_validGeometry.moveTopLeft(position); 0855 } else { 0856 //! when sliding in/out update only the relevant axis for the screen_edge in 0857 //! to not mess the calculations and the automatic geometry checkers that 0858 //! View::Positioner is using. 0859 if (m_view->formFactor() == Plasma::Types::Horizontal) { 0860 m_validGeometry.moveLeft(position.x()); 0861 } else { 0862 m_validGeometry.moveTop(position.y()); 0863 } 0864 } 0865 0866 m_view->setPosition(position); 0867 0868 if (m_view->surface()) { 0869 m_view->surface()->setPosition(position); 0870 } 0871 } 0872 0873 int Positioner::slideOffset() const 0874 { 0875 return m_slideOffset; 0876 } 0877 0878 void Positioner::setSlideOffset(int offset) 0879 { 0880 if (m_slideOffset == offset) { 0881 return; 0882 } 0883 0884 m_slideOffset = offset; 0885 emit slideOffsetChanged(); 0886 } 0887 0888 0889 void Positioner::resizeWindow(QRect availableScreenRect) 0890 { 0891 QSize screenSize = m_view->screen()->size(); 0892 QSize size = (m_view->formFactor() == Plasma::Types::Vertical) ? QSize(m_view->maxThickness(), availableScreenRect.height()) : QSize(screenSize.width(), m_view->maxThickness()); 0893 0894 if (m_view->formFactor() == Plasma::Types::Vertical) { 0895 //qDebug() << "MAXIMUM RECT :: " << maximumRect << " - AVAILABLE RECT :: " << availableRect; 0896 if (m_view->behaveAsPlasmaPanel()) { 0897 size.setWidth(m_view->normalThickness()); 0898 size.setHeight(static_cast<int>(m_view->maxLength() * availableScreenRect.height())); 0899 } 0900 } else { 0901 if (m_view->behaveAsPlasmaPanel()) { 0902 size.setWidth(static_cast<int>(m_view->maxLength() * screenSize.width())); 0903 size.setHeight(m_view->normalThickness()); 0904 } 0905 } 0906 0907 //! protect from invalid window size under wayland 0908 size.setWidth(qMax(1, size.width())); 0909 size.setHeight(qMax(1, size.height())); 0910 0911 m_validGeometry.setSize(size); 0912 0913 m_view->setMinimumSize(size); 0914 m_view->setMaximumSize(size); 0915 m_view->resize(size); 0916 0917 if (m_view->formFactor() == Plasma::Types::Horizontal) { 0918 emit windowSizeChanged(); 0919 } 0920 } 0921 0922 void Positioner::updateFormFactor() 0923 { 0924 if (!m_view->containment()) 0925 return; 0926 0927 switch (m_view->location()) { 0928 case Plasma::Types::TopEdge: 0929 case Plasma::Types::BottomEdge: 0930 m_view->containment()->setFormFactor(Plasma::Types::Horizontal); 0931 break; 0932 0933 case Plasma::Types::LeftEdge: 0934 case Plasma::Types::RightEdge: 0935 m_view->containment()->setFormFactor(Plasma::Types::Vertical); 0936 break; 0937 0938 default: 0939 qWarning() << "wrong location, couldn't update the panel position" << m_view->location(); 0940 } 0941 } 0942 0943 void Positioner::onLastRepositionApplyEvent() 0944 { 0945 m_view->effects()->setAnimationsBlocked(false); 0946 setInRelocationShowing(true); 0947 emit showingAfterRelocationFinished(); 0948 emit edgeChanged(); 0949 0950 if (m_repositionFromViewSettingsWindow) { 0951 m_repositionFromViewSettingsWindow = false; 0952 m_view->showSettingsWindow(); 0953 } 0954 } 0955 0956 void Positioner::initSignalingForLocationChangeSliding() 0957 { 0958 connect(this, &Positioner::hidingForRelocationStarted, this, &Positioner::onHideWindowsForSlidingOut); 0959 0960 //! SCREEN_EDGE 0961 connect(m_view, &View::locationChanged, this, [&]() { 0962 if (m_nextScreenEdge != Plasma::Types::Floating) { 0963 bool isrelocationlastevent = isLastHidingRelocationEvent(); 0964 immediateSyncGeometry(); 0965 m_nextScreenEdge = Plasma::Types::Floating; 0966 0967 //! make sure that View has been repositioned properly in next screen edge and show view afterwards 0968 if (isrelocationlastevent) { 0969 QTimer::singleShot(100, [this]() { 0970 onLastRepositionApplyEvent(); 0971 }); 0972 } 0973 } 0974 }); 0975 0976 //! SCREEN 0977 connect(m_view, &QQuickView::screenChanged, this, [&]() { 0978 if (!m_view || !m_nextScreen) { 0979 return; 0980 } 0981 0982 //[1] if panels are not excluded from confirmed geometry check then they are stuck in sliding out end 0983 //and they do not switch to new screen geometry 0984 //[2] under wayland view geometry may be delayed to be updated even though the screen has been updated correctly 0985 bool confirmedgeometry = KWindowSystem::isPlatformWayland() || m_view->behaveAsPlasmaPanel() || (!m_view->behaveAsPlasmaPanel() && m_nextScreen->geometry().contains(m_view->geometry().center())); 0986 0987 if (m_nextScreen 0988 && m_nextScreen == m_view->screen() 0989 && confirmedgeometry) { 0990 bool isrelocationlastevent = isLastHidingRelocationEvent(); 0991 m_nextScreen = nullptr; 0992 m_nextScreenName = ""; 0993 0994 //! make sure that View has been repositioned properly in next screen and show view afterwards 0995 if (isrelocationlastevent) { 0996 QTimer::singleShot(100, [this]() { 0997 onLastRepositionApplyEvent(); 0998 }); 0999 } 1000 } 1001 }); 1002 1003 //! LAYOUT 1004 connect(m_view, &View::layoutChanged, this, [&]() { 1005 if (!m_nextLayoutName.isEmpty() && m_view->layout()) { 1006 bool isrelocationlastevent = isLastHidingRelocationEvent(); 1007 m_nextLayoutName = ""; 1008 1009 //! make sure that View has been repositioned properly in next layout and show view afterwards 1010 if (isrelocationlastevent) { 1011 QTimer::singleShot(100, [this]() { 1012 onLastRepositionApplyEvent(); 1013 }); 1014 } 1015 } 1016 }); 1017 1018 //! APPLY CHANGES 1019 connect(this, &Positioner::hidingForRelocationFinished, this, [&]() { 1020 //! must be called only if relocation is animated 1021 if (m_repositionIsAnimated) { 1022 m_repositionIsAnimated = false; 1023 m_view->effects()->setAnimationsBlocked(true); 1024 } 1025 1026 //! LAYOUT 1027 if (!m_nextLayoutName.isEmpty()) { 1028 m_corona->layoutsManager()->moveView(m_view->layout()->name(), m_view->containment()->id(), m_nextLayoutName); 1029 } 1030 1031 //! SCREEN 1032 if (!m_nextScreenName.isEmpty()) { 1033 bool nextonprimary = (m_nextScreenName == Latte::Data::Screen::ONPRIMARYNAME); 1034 m_nextScreen = m_corona->screenPool()->primaryScreen(); 1035 1036 if (!nextonprimary) { 1037 for (const auto scr : qGuiApp->screens()) { 1038 if (scr && scr->name() == m_nextScreenName) { 1039 m_nextScreen = scr; 1040 break; 1041 } 1042 } 1043 } 1044 1045 m_view->setOnPrimary(nextonprimary); 1046 setScreenToFollow(m_nextScreen); 1047 } 1048 1049 //! SCREEN_EDGE 1050 if (m_nextScreenEdge != Plasma::Types::Floating) { 1051 m_view->setLocation(m_nextScreenEdge); 1052 } 1053 1054 //! ALIGNMENT 1055 if (m_nextAlignment != Latte::Types::NoneAlignment && m_nextAlignment != m_view->alignment()) { 1056 m_view->setAlignment(m_nextAlignment); 1057 m_nextAlignment = Latte::Types::NoneAlignment; 1058 } 1059 1060 //! SCREENSGROUP 1061 if (m_view->isOriginal()) { 1062 auto originalview = qobject_cast<Latte::OriginalView *>(m_view); 1063 originalview->setScreensGroup(m_nextScreensGroup); 1064 } 1065 }); 1066 } 1067 1068 bool Positioner::inLayoutUnloading() 1069 { 1070 return m_inLayoutUnloading; 1071 } 1072 1073 bool Positioner::inRelocationAnimation() 1074 { 1075 return ((m_nextScreenEdge != Plasma::Types::Floating) || !m_nextLayoutName.isEmpty() || !m_nextScreenName.isEmpty()); 1076 } 1077 1078 bool Positioner::inSlideAnimation() const 1079 { 1080 return m_inSlideAnimation; 1081 } 1082 1083 void Positioner::setInSlideAnimation(bool active) 1084 { 1085 if (m_inSlideAnimation == active) { 1086 return; 1087 } 1088 1089 m_inSlideAnimation = active; 1090 emit inSlideAnimationChanged(); 1091 } 1092 1093 bool Positioner::isCursorInsideView() const 1094 { 1095 return m_view->geometry().contains(QCursor::pos(m_screenToFollow)); 1096 } 1097 1098 bool Positioner::isStickedOnTopEdge() const 1099 { 1100 return m_isStickedOnTopEdge; 1101 } 1102 1103 void Positioner::setIsStickedOnTopEdge(bool sticked) 1104 { 1105 if (m_isStickedOnTopEdge == sticked) { 1106 return; 1107 } 1108 1109 m_isStickedOnTopEdge = sticked; 1110 emit isStickedOnTopEdgeChanged(); 1111 } 1112 1113 bool Positioner::isStickedOnBottomEdge() const 1114 { 1115 return m_isStickedOnBottomEdge; 1116 } 1117 1118 void Positioner::setIsStickedOnBottomEdge(bool sticked) 1119 { 1120 if (m_isStickedOnBottomEdge == sticked) { 1121 return; 1122 } 1123 1124 m_isStickedOnBottomEdge = sticked; 1125 emit isStickedOnBottomEdgeChanged(); 1126 } 1127 1128 void Positioner::updateInRelocationAnimation() 1129 { 1130 bool inrelocationanimation = inRelocationAnimation(); 1131 1132 if (m_inRelocationAnimation == inrelocationanimation) { 1133 return; 1134 } 1135 1136 m_inRelocationAnimation = inrelocationanimation; 1137 emit inRelocationAnimationChanged(); 1138 } 1139 1140 bool Positioner::isLastHidingRelocationEvent() const 1141 { 1142 int events{0}; 1143 1144 if (!m_nextLayoutName.isEmpty()) { 1145 events++; 1146 } 1147 1148 if (!m_nextScreenName.isEmpty()){ 1149 events++; 1150 } 1151 1152 if (m_nextScreenEdge != Plasma::Types::Floating) { 1153 events++; 1154 } 1155 1156 return (events <= 1); 1157 } 1158 1159 void Positioner::setNextLocation(const QString layoutName, const int screensGroup, QString screenName, int edge, int alignment) 1160 { 1161 bool isanimated{false}; 1162 bool haschanges{false}; 1163 1164 //! LAYOUT 1165 if (!layoutName.isEmpty()) { 1166 auto layout = m_view->layout(); 1167 auto origin = qobject_cast<CentralLayout *>(layout); 1168 auto destination = m_corona->layoutsManager()->synchronizer()->centralLayout(layoutName); 1169 1170 if (origin && destination && origin!=destination) { 1171 //! Needs to be updated; when the next layout is in the same Visible Workarea 1172 //! with the old one changing layouts should be instant 1173 bool inVisibleWorkarea{origin->lastUsedActivity() == destination->lastUsedActivity()}; 1174 1175 haschanges = true; 1176 m_nextLayoutName = layoutName; 1177 1178 if (!inVisibleWorkarea) { 1179 isanimated = true; 1180 } 1181 } 1182 } 1183 1184 //! SCREENSGROUP 1185 if (m_view->isOriginal()) { 1186 auto originalview = qobject_cast<Latte::OriginalView *>(m_view); 1187 //!initialize screens group 1188 m_nextScreensGroup = originalview->screensGroup(); 1189 1190 if (m_nextScreensGroup != screensGroup) { 1191 haschanges = true; 1192 m_nextScreensGroup = static_cast<Latte::Types::ScreensGroup>(screensGroup); 1193 1194 if (m_nextScreensGroup == Latte::Types::AllScreensGroup) { 1195 screenName = Latte::Data::Screen::ONPRIMARYNAME; 1196 } else if (m_nextScreensGroup == Latte::Types::AllSecondaryScreensGroup) { 1197 int scrid = originalview->expectedScreenIdFromScreenGroup(m_nextScreensGroup); 1198 1199 if (scrid != Latte::ScreenPool::NOSCREENID) { 1200 screenName = m_corona->screenPool()->connector(scrid); 1201 } 1202 } 1203 } 1204 } else { 1205 m_nextScreensGroup = Latte::Types::SingleScreenGroup; 1206 } 1207 1208 //! SCREEN 1209 if (!screenName.isEmpty()) { 1210 bool nextonprimary = (screenName == Latte::Data::Screen::ONPRIMARYNAME); 1211 1212 if ( (m_view->onPrimary() && !nextonprimary) /*primary -> explicit*/ 1213 || (!m_view->onPrimary() && nextonprimary) /*explicit -> primary*/ 1214 || (!m_view->onPrimary() && !nextonprimary && screenName != currentScreenName()) ) { /*explicit -> new_explicit*/ 1215 1216 QString nextscreenname = nextonprimary ? m_corona->screenPool()->primaryScreen()->name() : screenName; 1217 1218 if (currentScreenName() == nextscreenname) { 1219 m_view->setOnPrimary(nextonprimary); 1220 updateContainmentScreen(); 1221 } else { 1222 m_nextScreenName = screenName; 1223 isanimated = true; 1224 haschanges = true; 1225 } 1226 } 1227 } 1228 1229 //! SCREEN_EDGE 1230 if (edge != Plasma::Types::Floating) { 1231 if (edge != m_view->location()) { 1232 m_nextScreenEdge = static_cast<Plasma::Types::Location>(edge); 1233 isanimated = true; 1234 haschanges = true; 1235 } 1236 } 1237 1238 //! ALIGNMENT 1239 if (alignment != Latte::Types::NoneAlignment && m_view->alignment() != alignment) { 1240 m_nextAlignment = static_cast<Latte::Types::Alignment>(alignment); 1241 haschanges = true; 1242 } 1243 1244 if (haschanges && m_view->isOriginal()) { 1245 auto originalview = qobject_cast<Latte::OriginalView *>(m_view); 1246 originalview->setNextLocationForClones(layoutName, edge, alignment); 1247 } 1248 1249 m_repositionIsAnimated = isanimated; 1250 m_repositionFromViewSettingsWindow = m_view->settingsWindowIsShown(); 1251 1252 if (isanimated) { 1253 emit hidingForRelocationStarted(); 1254 } else if (haschanges){ 1255 emit hidingForRelocationFinished(); 1256 } 1257 } 1258 1259 } 1260 }