File indexing completed on 2024-12-01 11:10:44
0001 /* 0002 SPDX-FileCopyrightText: 2016 Smith AR <audoban@openmailbox.org> 0003 SPDX-FileCopyrightText: 2016 Michail Vourlakos <mvourlakos@gmail.com> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "visibilitymanager.h" 0009 0010 // local 0011 #include "positioner.h" 0012 #include "view.h" 0013 #include "helpers/floatinggapwindow.h" 0014 #include "helpers/screenedgeghostwindow.h" 0015 #include "windowstracker/currentscreentracker.h" 0016 #include "../apptypes.h" 0017 #include "../lattecorona.h" 0018 #include "../screenpool.h" 0019 #include "../layouts/manager.h" 0020 #include "../wm/abstractwindowinterface.h" 0021 0022 // Qt 0023 #include <QDebug> 0024 0025 // KDE 0026 #include <KWindowSystem> 0027 #include <KWayland/Client/plasmashell.h> 0028 #include <KWayland/Client/surface.h> 0029 0030 //! Hide Timer can create cases that when it is low it does not allow the 0031 //! view to be show. For example !compositing+kwin_edges+hide inteval<50ms 0032 //! FIXED: As it appears because we dont hide any view anymore before its sliding in 0033 //! animation has ended that probably allows to set the hide minimum interval to zero 0034 //! without any further issues, such as to not show the view even though the 0035 //! user is touching the screen edge 0036 const int HIDEMINIMUMINTERVAL = 0; 0037 //! After calling SidebarAutoHide panel to show for example through Sidebar button 0038 //! or global shortcuts we make sure bar will be shown enough time 0039 //! in order for the user to observe its contents 0040 const int SIDEBARAUTOHIDEMINIMUMSHOW = 1000; 0041 0042 0043 namespace Latte { 0044 namespace ViewPart { 0045 0046 //! BEGIN: VisiblityManager implementation 0047 const QRect VisibilityManager::ISHIDDENMASK = QRect(-1, -1, 1, 1); 0048 0049 VisibilityManager::VisibilityManager(PlasmaQuick::ContainmentView *view) 0050 : QObject(view) 0051 { 0052 qDebug() << "VisibilityManager creating..."; 0053 0054 m_latteView = qobject_cast<Latte::View *>(view); 0055 m_corona = qobject_cast<Latte::Corona *>(view->corona()); 0056 m_wm = m_corona->wm(); 0057 0058 connect(this, &VisibilityManager::hidingIsBlockedChanged, this, &VisibilityManager::onHidingIsBlockedChanged); 0059 0060 connect(this, &VisibilityManager::slideOutFinished, this, &VisibilityManager::updateHiddenState); 0061 connect(this, &VisibilityManager::slideInFinished, this, &VisibilityManager::updateHiddenState); 0062 0063 connect(this, &VisibilityManager::enableKWinEdgesChanged, this, &VisibilityManager::updateKWinEdgesSupport); 0064 connect(this, &VisibilityManager::modeChanged, this, &VisibilityManager::updateKWinEdgesSupport); 0065 connect(this, &VisibilityManager::modeChanged, this, &VisibilityManager::updateSidebarState); 0066 0067 connect(this, &VisibilityManager::isFloatingGapWindowEnabledChanged, this, &VisibilityManager::onIsFloatingGapWindowEnabledChanged); 0068 0069 connect(this, &VisibilityManager::mustBeShown, this, [&]() { 0070 if (m_latteView && !m_latteView->isVisible()) { 0071 m_latteView->setVisible(true); 0072 } 0073 }); 0074 0075 if (m_latteView) { 0076 connect(m_latteView, &Latte::View::eventTriggered, this, &VisibilityManager::viewEventManager); 0077 connect(m_latteView, &Latte::View::behaveAsPlasmaPanelChanged , this, &VisibilityManager::updateKWinEdgesSupport); 0078 connect(m_latteView, &Latte::View::byPassWMChanged, this, &VisibilityManager::updateKWinEdgesSupport); 0079 0080 connect(m_latteView, &Latte::View::inEditModeChanged, this, &VisibilityManager::initViewFlags); 0081 0082 //! Frame Extents 0083 connect(m_latteView, &Latte::View::headThicknessGapChanged, this, &VisibilityManager::onHeadThicknessChanged); 0084 connect(m_latteView, &Latte::View::locationChanged, this, [&]() { 0085 if (!m_latteView->behaveAsPlasmaPanel()) { 0086 //! Resend frame extents because their geometry has changed 0087 const bool forceUpdate{true}; 0088 publishFrameExtents(forceUpdate); 0089 } 0090 }); 0091 0092 connect(m_latteView, &Latte::View::typeChanged, this, [&]() { 0093 if (m_latteView->inEditMode()) { 0094 //! Resend frame extents because type has changed 0095 const bool forceUpdate{true}; 0096 publishFrameExtents(forceUpdate); 0097 } 0098 }); 0099 0100 connect(m_latteView, &Latte::View::forcedShown, this, [&]() { 0101 //! Resend frame extents to compositor otherwise because compositor cleared 0102 //! them with no reason when the user is closing an activity 0103 const bool forceUpdate{true}; 0104 publishFrameExtents(forceUpdate); 0105 }); 0106 0107 connect(this, &VisibilityManager::modeChanged, this, [&]() { 0108 emit m_latteView->availableScreenRectChangedFrom(m_latteView); 0109 }); 0110 0111 //! Send frame extents on startup, this is really necessary when recreating a view. 0112 //! Such a case is when toggling byPassWM and a view is recreated after disabling editing mode 0113 const bool forceUpdate{true}; 0114 publishFrameExtents(forceUpdate); 0115 } 0116 0117 m_timerShow.setSingleShot(true); 0118 m_timerHide.setSingleShot(true); 0119 0120 connect(&m_timerShow, &QTimer::timeout, this, [&]() { 0121 if (m_isHidden || m_isBelowLayer) { 0122 // qDebug() << "must be shown"; 0123 emit mustBeShown(); 0124 } 0125 }); 0126 connect(&m_timerHide, &QTimer::timeout, this, [&]() { 0127 if (!hidingIsBlocked() && !m_isHidden && !m_isBelowLayer && !m_dragEnter) { 0128 if (m_isFloatingGapWindowEnabled) { 0129 //! first check if mouse is inside the floating gap 0130 checkMouseInFloatingArea(); 0131 } else { 0132 //! immediate call 0133 emit mustBeHide(); 0134 } 0135 } 0136 }); 0137 0138 m_timerPublishFrameExtents.setInterval(1500); 0139 m_timerPublishFrameExtents.setSingleShot(true); 0140 connect(&m_timerPublishFrameExtents, &QTimer::timeout, this, [&]() { publishFrameExtents(); }); 0141 0142 m_timerBlockStrutsUpdate.setInterval(1000); 0143 m_timerBlockStrutsUpdate.setSingleShot(true); 0144 connect(&m_timerBlockStrutsUpdate, &QTimer::timeout, this, [&]() { updateStrutsBasedOnLayoutsAndActivities(); }); 0145 0146 restoreConfig(); 0147 0148 //! connect save values after they have been restored 0149 connect(this, &VisibilityManager::enableKWinEdgesChanged, this, &VisibilityManager::saveConfig); 0150 connect(this, &VisibilityManager::modeChanged, this, &VisibilityManager::saveConfig); 0151 connect(this, &VisibilityManager::raiseOnDesktopChanged, this, &VisibilityManager::saveConfig); 0152 connect(this, &VisibilityManager::raiseOnActivityChanged, this, &VisibilityManager::saveConfig); 0153 connect(this, &VisibilityManager::timerShowChanged, this, &VisibilityManager::saveConfig); 0154 connect(this, &VisibilityManager::timerHideChanged, this, &VisibilityManager::saveConfig); 0155 } 0156 0157 VisibilityManager::~VisibilityManager() 0158 { 0159 qDebug() << "VisibilityManager deleting..."; 0160 m_wm->removeViewStruts(*m_latteView); 0161 0162 if (m_edgeGhostWindow) { 0163 m_edgeGhostWindow->deleteLater(); 0164 } 0165 0166 if (m_floatingGapWindow) { 0167 m_floatingGapWindow->deleteLater(); 0168 } 0169 } 0170 0171 //! Struts 0172 int VisibilityManager::strutsThickness() const 0173 { 0174 return m_strutsThickness; 0175 } 0176 0177 void VisibilityManager::setStrutsThickness(int thickness) 0178 { 0179 if (m_strutsThickness == thickness) { 0180 return; 0181 } 0182 0183 m_strutsThickness = thickness; 0184 emit strutsThicknessChanged(); 0185 } 0186 0187 Types::Visibility VisibilityManager::mode() const 0188 { 0189 return m_mode; 0190 } 0191 0192 void VisibilityManager::initViewFlags() 0193 { 0194 if ((m_mode == Types::WindowsCanCover || m_mode == Types::WindowsAlwaysCover) && (!m_latteView->inEditMode())) { 0195 setViewOnBackLayer(); 0196 } else { 0197 setViewOnFrontLayer(); 0198 } 0199 } 0200 0201 void VisibilityManager::setViewOnBackLayer() 0202 { 0203 m_wm->setViewExtraFlags(m_latteView, false, Types::WindowsAlwaysCover); 0204 setIsBelowLayer(true); 0205 } 0206 0207 void VisibilityManager::setViewOnFrontLayer() 0208 { 0209 m_wm->setViewExtraFlags(m_latteView, true); 0210 setIsBelowLayer(false); 0211 if (KWindowSystem::isPlatformX11()) { 0212 m_latteView->raise(); 0213 } 0214 } 0215 0216 void VisibilityManager::setMode(Latte::Types::Visibility mode) 0217 { 0218 if (m_mode == mode) { 0219 return; 0220 } 0221 0222 qDebug() << "Updating visibility mode to :::: " << mode; 0223 0224 Q_ASSERT_X(mode != Types::None, staticMetaObject.className(), "set visibility to Types::None"); 0225 0226 // clear mode 0227 for (auto &c : m_connections) { 0228 disconnect(c); 0229 } 0230 0231 int base{0}; 0232 0233 m_publishedStruts = QRect(); 0234 0235 if (m_mode == Types::AlwaysVisible) { 0236 //! remove struts for old always visible mode 0237 m_wm->removeViewStruts(*m_latteView); 0238 } 0239 0240 m_timerShow.stop(); 0241 m_timerHide.stop(); 0242 m_mode = mode; 0243 0244 initViewFlags(); 0245 0246 if (mode != Types::AlwaysVisible && mode != Types::WindowsGoBelow) { 0247 m_connections[0] = connect(m_wm, &WindowSystem::AbstractWindowInterface::currentDesktopChanged, this, [&] { 0248 if (m_raiseOnDesktopChange) { 0249 raiseViewTemporarily(); 0250 } 0251 }); 0252 m_connections[1] = connect(m_wm, &WindowSystem::AbstractWindowInterface::currentActivityChanged, this, [&]() { 0253 if (m_raiseOnActivityChange) { 0254 raiseViewTemporarily(); 0255 } else { 0256 updateHiddenState(); 0257 } 0258 }); 0259 0260 base = 2; 0261 } 0262 0263 switch (m_mode) { 0264 case Types::AlwaysVisible: { 0265 if (m_latteView->containment() && m_latteView->screen()) { 0266 updateStrutsBasedOnLayoutsAndActivities(); 0267 } 0268 0269 m_connections[base] = connect(this, &VisibilityManager::strutsThicknessChanged, &VisibilityManager::updateStrutsAfterTimer); 0270 0271 // disabling this call because it was creating too many struts calls and ??? 0272 // could create reduced responsiveness for DynamicStruts Scenario(for example ?? 0273 // when dragging active window from a floating dock/panel) ??? 0274 m_connections[base+1] = connect(m_latteView, &Latte::View::absoluteGeometryChanged, this, &VisibilityManager::updateStrutsAfterTimer); 0275 0276 m_connections[base+2] = connect(m_corona->activitiesConsumer(), &KActivities::Consumer::currentActivityChanged, this, [&]() { 0277 if (m_corona && m_corona->layoutsManager()->memoryUsage() == MemoryUsage::MultipleLayouts) { 0278 updateStrutsBasedOnLayoutsAndActivities(true); 0279 } 0280 }); 0281 0282 //! respect canSetStrut that must be disabled under x11 when an alwaysvisible screen edge is common between two or more screens 0283 m_connections[base+3] = connect(m_corona->screenPool(), &Latte::ScreenPool::screenGeometryChanged, this, &VisibilityManager::updateStrutsAfterTimer); 0284 0285 m_connections[base+4] = connect(m_latteView, &Latte::View::activitiesChanged, this, [&]() { 0286 updateStrutsBasedOnLayoutsAndActivities(true); 0287 }); 0288 0289 raiseView(true); 0290 break; 0291 } 0292 0293 case Types::AutoHide: { 0294 m_connections[base] = connect(this, &VisibilityManager::containsMouseChanged, this, [&]() { 0295 raiseView(m_containsMouse); 0296 }); 0297 0298 raiseView(m_containsMouse); 0299 break; 0300 } 0301 0302 case Types::DodgeActive: { 0303 m_connections[base] = connect(this, &VisibilityManager::containsMouseChanged 0304 , this, &VisibilityManager::dodgeActive); 0305 m_connections[base+1] = connect(m_latteView->windowsTracker()->currentScreen(), &TrackerPart::CurrentScreenTracker::activeWindowTouchingChanged 0306 , this, &VisibilityManager::dodgeActive); 0307 0308 dodgeActive(); 0309 break; 0310 } 0311 0312 case Types::DodgeMaximized: { 0313 m_connections[base] = connect(this, &VisibilityManager::containsMouseChanged 0314 , this, &VisibilityManager::dodgeMaximized); 0315 m_connections[base+1] = connect(m_latteView->windowsTracker()->currentScreen(), &TrackerPart::CurrentScreenTracker::activeWindowMaximizedChanged 0316 , this, &VisibilityManager::dodgeMaximized); 0317 0318 dodgeMaximized(); 0319 break; 0320 } 0321 0322 case Types::DodgeAllWindows: { 0323 m_connections[base] = connect(this, &VisibilityManager::containsMouseChanged 0324 , this, &VisibilityManager::dodgeAllWindows); 0325 0326 m_connections[base+1] = connect(m_latteView->windowsTracker()->currentScreen(), &TrackerPart::CurrentScreenTracker::existsWindowTouchingChanged 0327 , this, &VisibilityManager::dodgeAllWindows); 0328 0329 dodgeAllWindows(); 0330 break; 0331 } 0332 0333 case Types::WindowsGoBelow: 0334 break; 0335 0336 case Types::WindowsCanCover: 0337 m_connections[base] = connect(this, &VisibilityManager::containsMouseChanged, this, [&]() { 0338 raiseView(m_containsMouse); 0339 }); 0340 0341 raiseView(m_containsMouse); 0342 break; 0343 0344 case Types::WindowsAlwaysCover: 0345 break; 0346 0347 case Types::SidebarOnDemand: 0348 m_connections[base] = connect(m_latteView, &Latte::View::inEditModeChanged, this, [&]() { 0349 if (!m_latteView->inEditMode()) { 0350 m_isRequestedShownSidebarOnDemand = false; 0351 updateHiddenState(); 0352 } 0353 }); 0354 0355 m_isRequestedShownSidebarOnDemand = false; 0356 updateHiddenState(); 0357 break; 0358 0359 case Types::SidebarAutoHide: 0360 m_connections[base] = connect(this, &VisibilityManager::containsMouseChanged, this, [&]() { 0361 if (!m_latteView->inEditMode()) { 0362 updateHiddenState(); 0363 } 0364 }); 0365 0366 m_connections[base+1] = connect(m_latteView, &Latte::View::inEditModeChanged, this, [&]() { 0367 if (m_latteView->inEditMode() && !m_isHidden) { 0368 updateHiddenState(); 0369 } 0370 }); 0371 0372 toggleHiddenState(); 0373 break; 0374 0375 default: 0376 break; 0377 } 0378 0379 emit modeChanged(); 0380 } 0381 0382 void VisibilityManager::updateStrutsAfterTimer() 0383 { 0384 bool execute = !m_timerBlockStrutsUpdate.isActive(); 0385 0386 m_timerBlockStrutsUpdate.start(); 0387 0388 if (execute) { 0389 updateStrutsBasedOnLayoutsAndActivities(); 0390 } 0391 } 0392 0393 void VisibilityManager::updateSidebarState() 0394 { 0395 bool cursidebarstate = ((m_mode == Types::SidebarOnDemand) 0396 || (m_mode == Types::SidebarAutoHide)); 0397 0398 if (m_isSidebar == cursidebarstate) { 0399 return; 0400 } 0401 0402 m_isSidebar == cursidebarstate; 0403 emit isSidebarChanged(); 0404 0405 } 0406 0407 void VisibilityManager::updateStrutsBasedOnLayoutsAndActivities(bool forceUpdate) 0408 { 0409 bool inMultipleLayoutsAndCurrent = (m_corona->layoutsManager()->memoryUsage() == MemoryUsage::MultipleLayouts 0410 && m_latteView->layout() && !m_latteView->positioner()->inRelocationAnimation() 0411 && m_latteView->layout()->isCurrent()); 0412 0413 if (m_strutsThickness>0 && canSetStrut() && (m_corona->layoutsManager()->memoryUsage() == MemoryUsage::SingleLayout || inMultipleLayoutsAndCurrent)) { 0414 QRect computedStruts = acceptableStruts(); 0415 if (m_publishedStruts != computedStruts || forceUpdate) { 0416 //! Force update is needed when very important events happen in DE and there is a chance 0417 //! that previously even though struts where sent the DE did not accept them. 0418 //! Such a case is when STOPPING an Activity and windows faulty become invisible even 0419 //! though they should not. In such case setting struts when the windows are hidden 0420 //! the struts do not take any effect 0421 m_publishedStruts = computedStruts; 0422 m_wm->setViewStruts(*m_latteView, m_publishedStruts, m_latteView->location()); 0423 } 0424 } else { 0425 m_publishedStruts = QRect(); 0426 m_wm->removeViewStruts(*m_latteView); 0427 } 0428 } 0429 0430 bool VisibilityManager::canSetStrut() const 0431 { 0432 if (m_latteView->positioner()->isOffScreen()) { 0433 return false; 0434 } 0435 0436 if (!KWindowSystem::isPlatformX11() || m_wm->isKWinRunning()) { 0437 // we always trust wayland and kwin to provide proper struts 0438 return true; 0439 } 0440 0441 if (qGuiApp->screens().count() < 2) { 0442 return true; 0443 } 0444 0445 /*Alternative DEs*/ 0446 0447 const QRect thisScreen = m_latteView->screen()->geometry(); 0448 0449 // Extended struts against a screen edge near to another screen are really harmful, so windows maximized under the panel is a lesser pain 0450 // TODO: force "windows can cover" in those cases? 0451 for (QScreen *screen : qGuiApp->screens()) { 0452 if (!screen || m_latteView->screen() == screen) { 0453 continue; 0454 } 0455 0456 const QRect otherScreen = screen->geometry(); 0457 0458 switch (m_latteView->location()) { 0459 case Plasma::Types::TopEdge: 0460 if (otherScreen.bottom() <= thisScreen.top()) { 0461 return false; 0462 } 0463 break; 0464 case Plasma::Types::BottomEdge: 0465 if (otherScreen.top() >= thisScreen.bottom()) { 0466 return false; 0467 } 0468 break; 0469 case Plasma::Types::RightEdge: 0470 if (otherScreen.left() >= thisScreen.right()) { 0471 return false; 0472 } 0473 break; 0474 case Plasma::Types::LeftEdge: 0475 if (otherScreen.right() <= thisScreen.left()) { 0476 return false; 0477 } 0478 break; 0479 default: 0480 return false; 0481 } 0482 } 0483 0484 return true; 0485 } 0486 0487 QRect VisibilityManager::acceptableStruts() 0488 { 0489 QRect calcs; 0490 0491 switch (m_latteView->location()) { 0492 case Plasma::Types::TopEdge: { 0493 calcs = QRect(m_latteView->x(), m_latteView->screenGeometry().top(), m_latteView->width(), m_strutsThickness); 0494 break; 0495 } 0496 0497 case Plasma::Types::BottomEdge: { 0498 int y = m_latteView->screenGeometry().bottom() - m_strutsThickness + 1 /* +1, is needed in order to not leave a gap at screen_edge*/; 0499 calcs = QRect(m_latteView->x(), y, m_latteView->width(), m_strutsThickness); 0500 break; 0501 } 0502 0503 case Plasma::Types::LeftEdge: { 0504 calcs = QRect(m_latteView->screenGeometry().left(), m_latteView->y(), m_strutsThickness, m_latteView->height()); 0505 break; 0506 } 0507 0508 case Plasma::Types::RightEdge: { 0509 int x = m_latteView->screenGeometry().right() - m_strutsThickness + 1 /* +1, is needed in order to not leave a gap at screen_edge*/; 0510 calcs = QRect(x, m_latteView->y(), m_strutsThickness, m_latteView->height()); 0511 break; 0512 } 0513 } 0514 0515 return calcs; 0516 } 0517 0518 bool VisibilityManager::raiseOnDesktop() const 0519 { 0520 return m_raiseOnDesktopChange; 0521 } 0522 0523 void VisibilityManager::setRaiseOnDesktop(bool enable) 0524 { 0525 if (enable == m_raiseOnDesktopChange) 0526 return; 0527 0528 m_raiseOnDesktopChange = enable; 0529 emit raiseOnDesktopChanged(); 0530 } 0531 0532 bool VisibilityManager::raiseOnActivity() const 0533 { 0534 return m_raiseOnActivityChange; 0535 } 0536 0537 void VisibilityManager::setRaiseOnActivity(bool enable) 0538 { 0539 if (enable == m_raiseOnActivityChange) 0540 return; 0541 0542 m_raiseOnActivityChange = enable; 0543 emit raiseOnActivityChanged(); 0544 } 0545 0546 bool VisibilityManager::isBelowLayer() const 0547 { 0548 return m_isBelowLayer; 0549 } 0550 0551 void VisibilityManager::setIsBelowLayer(bool below) 0552 { 0553 if (m_isBelowLayer == below) { 0554 return; 0555 } 0556 0557 m_isBelowLayer = below; 0558 0559 updateGhostWindowState(); 0560 0561 emit isBelowLayerChanged(); 0562 } 0563 0564 bool VisibilityManager::isHidden() const 0565 { 0566 return m_isHidden; 0567 } 0568 0569 void VisibilityManager::setIsHidden(bool isHidden) 0570 { 0571 if (m_isHidden == isHidden) 0572 return; 0573 0574 m_isHidden = isHidden; 0575 updateGhostWindowState(); 0576 0577 emit isHiddenChanged(); 0578 } 0579 0580 bool VisibilityManager::isShownFully() const 0581 { 0582 return m_isShownFully; 0583 } 0584 0585 void VisibilityManager::setIsShownFully(bool fully) 0586 { 0587 if (m_isShownFully == fully) { 0588 return; 0589 } 0590 0591 m_isShownFully = fully; 0592 emit isShownFullyChanged(); 0593 } 0594 0595 bool VisibilityManager::hidingIsBlocked() const 0596 { 0597 return (m_blockHidingEvents.count() > 0); 0598 } 0599 0600 bool VisibilityManager::isFloatingGapWindowEnabled() const 0601 { 0602 return m_isFloatingGapWindowEnabled; 0603 } 0604 0605 void VisibilityManager::setIsFloatingGapWindowEnabled(bool enabled) 0606 { 0607 if (m_isFloatingGapWindowEnabled == enabled) { 0608 return; 0609 } 0610 0611 m_isFloatingGapWindowEnabled = enabled; 0612 emit isFloatingGapWindowEnabledChanged(); 0613 } 0614 0615 bool VisibilityManager::hasBlockHidingEvent(const QString &type) 0616 { 0617 return (!type.isEmpty() && m_blockHidingEvents.contains(type)); 0618 } 0619 0620 void VisibilityManager::addBlockHidingEvent(const QString &type) 0621 { 0622 if (m_blockHidingEvents.contains(type) || type.isEmpty()) { 0623 return; 0624 } 0625 //qDebug() << " org.kde.late {{ ++++ adding block hiding event :: " << type; 0626 0627 bool prevHidingIsBlocked = hidingIsBlocked(); 0628 0629 m_blockHidingEvents << type; 0630 0631 if (prevHidingIsBlocked != hidingIsBlocked()) { 0632 emit hidingIsBlockedChanged(); 0633 } 0634 } 0635 0636 void VisibilityManager::removeBlockHidingEvent(const QString &type) 0637 { 0638 if (!m_blockHidingEvents.contains(type) || type.isEmpty()) { 0639 return; 0640 } 0641 //qDebug() << " org.kde.latte {{ ---- remove block hiding event :: " << type; 0642 0643 bool prevHidingIsBlocked = hidingIsBlocked(); 0644 0645 m_blockHidingEvents.removeAll(type); 0646 0647 if (prevHidingIsBlocked != hidingIsBlocked()) { 0648 emit hidingIsBlockedChanged(); 0649 } 0650 } 0651 0652 void VisibilityManager::onHidingIsBlockedChanged() 0653 { 0654 if (hidingIsBlocked()) { 0655 m_timerHide.stop(); 0656 emit mustBeShown(); 0657 } else { 0658 updateHiddenState(); 0659 } 0660 } 0661 0662 void VisibilityManager::onHeadThicknessChanged() 0663 { 0664 if (!m_timerPublishFrameExtents.isActive()) { 0665 m_timerPublishFrameExtents.start(); 0666 } 0667 } 0668 0669 void VisibilityManager::publishFrameExtents(bool forceUpdate) 0670 { 0671 if (m_frameExtentsHeadThicknessGap != m_latteView->headThicknessGap() 0672 || m_frameExtentsLocation != m_latteView->location() 0673 || forceUpdate) { 0674 0675 m_frameExtentsLocation = m_latteView->location(); 0676 m_frameExtentsHeadThicknessGap = m_latteView->headThicknessGap(); 0677 0678 if (KWindowSystem::isPlatformX11() && m_latteView->devicePixelRatio()!=1.0) { 0679 //!Fix for X11 Global Scale 0680 m_frameExtentsHeadThicknessGap = qRound(m_frameExtentsHeadThicknessGap * m_latteView->devicePixelRatio()); 0681 } 0682 0683 QMargins frameExtents(0, 0, 0, 0); 0684 0685 if (m_latteView->location() == Plasma::Types::LeftEdge) { 0686 frameExtents.setRight(m_frameExtentsHeadThicknessGap); 0687 } else if (m_latteView->location() == Plasma::Types::TopEdge) { 0688 frameExtents.setBottom(m_frameExtentsHeadThicknessGap); 0689 } else if (m_latteView->location() == Plasma::Types::RightEdge) { 0690 frameExtents.setLeft(m_frameExtentsHeadThicknessGap); 0691 } else { 0692 frameExtents.setTop(m_frameExtentsHeadThicknessGap); 0693 } 0694 0695 bool bypasswm{m_latteView->byPassWM() && KWindowSystem::isPlatformX11()}; 0696 0697 qDebug() << " -> Frame Extents :: " << m_frameExtentsLocation << " __ " << " extents :: " << frameExtents << " bypasswm :: " << bypasswm; 0698 0699 if (!frameExtents.isNull() && !m_latteView->behaveAsPlasmaPanel() && !bypasswm) { 0700 //! When a view returns its frame extents to zero then that triggers a compositor 0701 //! strange behavior that moves/hides the view totally and freezes entire Latte 0702 //! this is why we have blocked that setting 0703 m_wm->setFrameExtents(m_latteView, frameExtents); 0704 } else if (m_latteView->behaveAsPlasmaPanel() || bypasswm) { 0705 QMargins panelExtents(0, 0, 0, 0); 0706 m_wm->setFrameExtents(m_latteView, panelExtents); 0707 emit frameExtentsCleared(); 0708 } 0709 } 0710 } 0711 0712 int VisibilityManager::timerShow() const 0713 { 0714 return m_timerShow.interval(); 0715 } 0716 0717 void VisibilityManager::setTimerShow(int msec) 0718 { 0719 if (m_timerShow.interval() == msec) { 0720 return; 0721 } 0722 0723 m_timerShow.setInterval(msec); 0724 emit timerShowChanged(); 0725 } 0726 0727 int VisibilityManager::timerHide() const 0728 { 0729 return m_timerHideInterval; 0730 } 0731 0732 void VisibilityManager::setTimerHide(int msec) 0733 { 0734 int interval = qMax(HIDEMINIMUMINTERVAL, msec); 0735 0736 if (m_timerHideInterval == interval) { 0737 return; 0738 } 0739 0740 m_timerHideInterval = interval; 0741 m_timerHide.setInterval(interval); 0742 emit timerHideChanged(); 0743 } 0744 0745 bool VisibilityManager::isSidebar() const 0746 { 0747 return m_mode == Latte::Types::SidebarOnDemand || m_mode == Latte::Types::SidebarAutoHide; 0748 } 0749 0750 bool VisibilityManager::supportsKWinEdges() const 0751 { 0752 return (m_edgeGhostWindow != nullptr); 0753 } 0754 0755 void VisibilityManager::updateGhostWindowState() 0756 { 0757 if (supportsKWinEdges()) { 0758 bool inCurrentLayout = (m_corona->layoutsManager()->memoryUsage() == MemoryUsage::SingleLayout || 0759 (m_corona->layoutsManager()->memoryUsage() == MemoryUsage::MultipleLayouts 0760 && m_latteView->layout() && !m_latteView->positioner()->inRelocationAnimation() 0761 && m_latteView->layout()->isCurrent())); 0762 0763 if (inCurrentLayout) { 0764 if (m_mode == Latte::Types::WindowsCanCover) { 0765 m_wm->setActiveEdge(m_edgeGhostWindow, m_isBelowLayer && !m_containsMouse); 0766 } else { 0767 bool activated = (m_isHidden && !windowContainsMouse()); 0768 0769 m_wm->setActiveEdge(m_edgeGhostWindow, activated); 0770 } 0771 } else { 0772 m_wm->setActiveEdge(m_edgeGhostWindow, false); 0773 } 0774 } 0775 } 0776 0777 void VisibilityManager::hide() 0778 { 0779 if (KWindowSystem::isPlatformX11()) { 0780 m_latteView->setVisible(false); 0781 } 0782 } 0783 0784 void VisibilityManager::show() 0785 { 0786 if (KWindowSystem::isPlatformX11()) { 0787 m_latteView->setVisible(true); 0788 } 0789 } 0790 0791 void VisibilityManager::toggleHiddenState() 0792 { 0793 if (!m_latteView->inEditMode()) { 0794 if (isSidebar()) { 0795 // if (m_blockHidingEvents.contains(Q_FUNC_INFO)) { 0796 // removeBlockHidingEvent(Q_FUNC_INFO); 0797 // } 0798 0799 if (m_mode == Latte::Types::SidebarOnDemand) { 0800 m_isRequestedShownSidebarOnDemand = !m_isRequestedShownSidebarOnDemand; 0801 updateHiddenState(); 0802 } else if (m_mode == Latte::Types::SidebarAutoHide) { 0803 if (m_isHidden) { 0804 emit mustBeShown(); 0805 startTimerHide(SIDEBARAUTOHIDEMINIMUMSHOW + m_timerHideInterval); 0806 } else { 0807 emit mustBeHide(); 0808 } 0809 } 0810 } else { 0811 /* if (!m_isHidden && !m_blockHidingEvents.contains(Q_FUNC_INFO)) { 0812 addBlockHidingEvent(Q_FUNC_INFO); 0813 } else if (m_isHidden) { 0814 removeBlockHidingEvent(Q_FUNC_INFO); 0815 }*/ 0816 } 0817 } 0818 } 0819 0820 void VisibilityManager::updateHiddenState() 0821 { 0822 if (m_dragEnter) 0823 return; 0824 0825 switch (m_mode) { 0826 case Types::AutoHide: 0827 case Types::WindowsCanCover: 0828 raiseView(m_containsMouse); 0829 break; 0830 0831 case Types::DodgeActive: 0832 dodgeActive(); 0833 break; 0834 0835 case Types::DodgeMaximized: 0836 dodgeMaximized(); 0837 break; 0838 0839 case Types::DodgeAllWindows: 0840 dodgeAllWindows(); 0841 break; 0842 0843 case Types::SidebarOnDemand: 0844 raiseView(m_isRequestedShownSidebarOnDemand); 0845 break; 0846 0847 case Types::SidebarAutoHide: 0848 raiseView(m_latteView->inEditMode() || (m_containsMouse && !m_isHidden)); 0849 break; 0850 0851 default: 0852 break; 0853 } 0854 } 0855 0856 void VisibilityManager::raiseView(bool raise) 0857 { 0858 if (m_mode == Latte::Types::SidebarOnDemand) { 0859 if (raise && m_isHidden) { 0860 emit mustBeShown(); 0861 } else if (!raise && !m_isHidden && !m_dragEnter && !hidingIsBlocked()) { 0862 emit mustBeHide(); 0863 } 0864 return; 0865 } 0866 0867 if (raise) { 0868 m_timerHide.stop(); 0869 0870 if (!m_timerShow.isActive()) { 0871 m_timerShow.start(); 0872 } 0873 } else if (!m_dragEnter && !hidingIsBlocked()) { 0874 m_timerShow.stop(); 0875 0876 if (m_hideNow) { 0877 m_hideNow = false; 0878 emit mustBeHide(); 0879 } else if (!m_timerHide.isActive()) { 0880 startTimerHide(); 0881 } 0882 } 0883 } 0884 0885 void VisibilityManager::raiseViewTemporarily() 0886 { 0887 if (m_raiseTemporarily) 0888 return; 0889 0890 m_raiseTemporarily = true; 0891 m_timerHide.stop(); 0892 m_timerShow.stop(); 0893 0894 if (m_isHidden) 0895 emit mustBeShown(); 0896 0897 QTimer::singleShot(qBound(1800, 2 * m_timerHide.interval(), 3000), this, [&]() { 0898 m_raiseTemporarily = false; 0899 m_hideNow = true; 0900 updateHiddenState(); 0901 }); 0902 } 0903 0904 bool VisibilityManager::isValidMode() const 0905 { 0906 return (m_mode != Types::None && m_mode != Types::NormalWindow); 0907 } 0908 0909 void VisibilityManager::applyActivitiesToHiddenWindows(const QStringList &activities) 0910 { 0911 if (m_edgeGhostWindow) { 0912 m_wm->setWindowOnActivities(m_edgeGhostWindow->trackedWindowId(), activities); 0913 } 0914 0915 if (m_floatingGapWindow) { 0916 m_wm->setWindowOnActivities(m_floatingGapWindow->trackedWindowId(), activities); 0917 } 0918 } 0919 0920 void VisibilityManager::startTimerHide(const int &msec) 0921 { 0922 if (msec == 0) { 0923 int secs = m_timerHideInterval; 0924 0925 if (!KWindowSystem::compositingActive()) { 0926 //! this is needed in order to give view time to show and 0927 //! for floating case to give time to user to reach the view with its mouse 0928 secs = qMax(m_timerHideInterval, m_latteView->screenEdgeMargin() > 0 ? 700 : 200); 0929 } 0930 0931 m_timerHide.start(secs); 0932 } else { 0933 m_timerHide.start(msec); 0934 } 0935 } 0936 0937 void VisibilityManager::dodgeActive() 0938 { 0939 if (m_raiseTemporarily) 0940 return; 0941 0942 //!don't send false raiseView signal when containing mouse 0943 if (m_containsMouse) { 0944 raiseView(true); 0945 return; 0946 } 0947 0948 raiseView(!m_latteView->windowsTracker()->currentScreen()->activeWindowTouching()); 0949 } 0950 0951 void VisibilityManager::dodgeMaximized() 0952 { 0953 if (m_raiseTemporarily) 0954 return; 0955 0956 //!don't send false raiseView signal when containing mouse 0957 if (m_containsMouse) { 0958 raiseView(true); 0959 return; 0960 } 0961 0962 raiseView(!m_latteView->windowsTracker()->currentScreen()->activeWindowMaximized()); 0963 } 0964 0965 void VisibilityManager::dodgeAllWindows() 0966 { 0967 if (m_raiseTemporarily) 0968 return; 0969 0970 if (m_containsMouse) { 0971 raiseView(true); 0972 return; 0973 } 0974 0975 bool windowIntersects{m_latteView->windowsTracker()->currentScreen()->activeWindowTouching() || m_latteView->windowsTracker()->currentScreen()->existsWindowTouching()}; 0976 0977 raiseView(!windowIntersects); 0978 } 0979 0980 void VisibilityManager::saveConfig() 0981 { 0982 if (!m_latteView->containment()) { 0983 return; 0984 } 0985 0986 auto config = m_latteView->containment()->config(); 0987 0988 config.writeEntry("enableKWinEdges", m_enableKWinEdgesFromUser); 0989 config.writeEntry("timerShow", m_timerShow.interval()); 0990 config.writeEntry("timerHide", m_timerHideInterval); 0991 config.writeEntry("raiseOnDesktopChange", m_raiseOnDesktopChange); 0992 config.writeEntry("raiseOnActivityChange", m_raiseOnActivityChange); 0993 config.writeEntry("visibility", static_cast<int>(m_mode)); 0994 0995 } 0996 0997 void VisibilityManager::restoreConfig() 0998 { 0999 auto config = m_latteView->containment()->config(); 1000 setTimerHide(qMax(HIDEMINIMUMINTERVAL, config.readEntry("timerHide", 700))); 1001 setTimerShow(config.readEntry("timerShow", 0)); 1002 setEnableKWinEdges(config.readEntry("enableKWinEdges", true)); 1003 setRaiseOnDesktop(config.readEntry("raiseOnDesktopChange", false)); 1004 setRaiseOnActivity(config.readEntry("raiseOnActivityChange", false)); 1005 1006 setMode((Types::Visibility)(config.readEntry("visibility", (int)(Types::DodgeActive)))); 1007 } 1008 1009 bool VisibilityManager::containsMouse() const 1010 { 1011 return m_containsMouse; 1012 } 1013 1014 void VisibilityManager::setContainsMouse(bool contains) 1015 { 1016 if (m_containsMouse == contains) { 1017 return; 1018 } 1019 1020 m_containsMouse = contains; 1021 emit containsMouseChanged(); 1022 } 1023 1024 bool VisibilityManager::windowContainsMouse() 1025 { 1026 return m_containsMouse || (m_edgeGhostWindow && m_edgeGhostWindow->containsMouse()); 1027 } 1028 1029 void VisibilityManager::checkMouseInFloatingArea() 1030 { 1031 if (m_isFloatingGapWindowEnabled) { 1032 if (!m_floatingGapWindow) { 1033 createFloatingGapWindow(); 1034 } 1035 1036 m_floatingGapWindow->callAsyncContainsMouse(); 1037 } 1038 } 1039 1040 void VisibilityManager::viewEventManager(QEvent *ev) 1041 { 1042 switch (ev->type()) { 1043 case QEvent::Enter: 1044 setContainsMouse(true); 1045 break; 1046 1047 case QEvent::Leave: 1048 m_dragEnter = false; 1049 setContainsMouse(false); 1050 break; 1051 1052 case QEvent::DragEnter: 1053 m_dragEnter = true; 1054 1055 if (m_isHidden && !isSidebar()) { 1056 emit mustBeShown(); 1057 } 1058 1059 break; 1060 1061 case QEvent::DragLeave: 1062 case QEvent::Drop: 1063 m_dragEnter = false; 1064 updateHiddenState(); 1065 break; 1066 1067 default: 1068 break; 1069 } 1070 } 1071 1072 //! KWin Edges Support functions 1073 bool VisibilityManager::enableKWinEdges() const 1074 { 1075 return m_enableKWinEdgesFromUser; 1076 } 1077 1078 void VisibilityManager::setEnableKWinEdges(bool enable) 1079 { 1080 if (m_enableKWinEdgesFromUser == enable) { 1081 return; 1082 } 1083 1084 m_enableKWinEdgesFromUser = enable; 1085 1086 emit enableKWinEdgesChanged(); 1087 } 1088 1089 void VisibilityManager::updateKWinEdgesSupport() 1090 { 1091 if ((m_mode == Types::AutoHide 1092 || m_mode == Types::DodgeActive 1093 || m_mode == Types::DodgeAllWindows 1094 || m_mode == Types::DodgeMaximized) 1095 && !m_latteView->byPassWM()) { 1096 1097 if (m_enableKWinEdgesFromUser || m_latteView->behaveAsPlasmaPanel()) { 1098 createEdgeGhostWindow(); 1099 } else if (!m_enableKWinEdgesFromUser) { 1100 deleteEdgeGhostWindow(); 1101 } 1102 } else if (m_mode == Types::WindowsCanCover) { 1103 createEdgeGhostWindow(); 1104 } else { 1105 deleteEdgeGhostWindow(); 1106 } 1107 } 1108 1109 void VisibilityManager::onIsFloatingGapWindowEnabledChanged() 1110 { 1111 if (m_isFloatingGapWindowEnabled) { 1112 createFloatingGapWindow(); 1113 } else { 1114 deleteFloatingGapWindow(); 1115 } 1116 } 1117 1118 void VisibilityManager::createEdgeGhostWindow() 1119 { 1120 if (!m_edgeGhostWindow) { 1121 m_edgeGhostWindow = new ScreenEdgeGhostWindow(m_latteView); 1122 1123 connect(m_edgeGhostWindow, &ScreenEdgeGhostWindow::containsMouseChanged, this, [ = ](bool contains) { 1124 if (contains) { 1125 raiseView(true); 1126 } else { 1127 m_timerShow.stop(); 1128 updateGhostWindowState(); 1129 } 1130 }); 1131 1132 connect(m_edgeGhostWindow, &ScreenEdgeGhostWindow::dragEntered, this, [&]() { 1133 if (m_isHidden) { 1134 emit mustBeShown(); 1135 } 1136 }); 1137 1138 m_connectionsKWinEdges[0] = connect(m_wm, &WindowSystem::AbstractWindowInterface::currentActivityChanged, 1139 this, [&]() { 1140 bool inCurrentLayout = (m_corona->layoutsManager()->memoryUsage() == MemoryUsage::SingleLayout || 1141 (m_corona->layoutsManager()->memoryUsage() == MemoryUsage::MultipleLayouts 1142 && m_latteView->layout() && !m_latteView->positioner()->inRelocationAnimation() 1143 && m_latteView->layout()->isCurrent())); 1144 1145 if (m_edgeGhostWindow) { 1146 if (inCurrentLayout) { 1147 m_wm->setActiveEdge(m_edgeGhostWindow, m_isHidden); 1148 } else { 1149 m_wm->setActiveEdge(m_edgeGhostWindow, false); 1150 } 1151 } 1152 }); 1153 1154 emit supportsKWinEdgesChanged(); 1155 } 1156 } 1157 1158 void VisibilityManager::deleteEdgeGhostWindow() 1159 { 1160 if (m_edgeGhostWindow) { 1161 m_edgeGhostWindow->deleteLater(); 1162 m_edgeGhostWindow = nullptr; 1163 1164 for (auto &c : m_connectionsKWinEdges) { 1165 disconnect(c); 1166 } 1167 1168 emit supportsKWinEdgesChanged(); 1169 } 1170 } 1171 1172 void VisibilityManager::createFloatingGapWindow() 1173 { 1174 if (!m_floatingGapWindow) { 1175 m_floatingGapWindow = new FloatingGapWindow(m_latteView); 1176 1177 connect(m_floatingGapWindow, &FloatingGapWindow::asyncContainsMouseChanged, this, [ = ](bool contains) { 1178 if (contains) { 1179 if (m_isFloatingGapWindowEnabled && !m_isHidden) { 1180 //! immediate call after contains mouse checks for mouse in sensitive floating areas 1181 updateHiddenState(); 1182 } 1183 } else { 1184 if (m_isFloatingGapWindowEnabled && !m_isHidden) { 1185 //! immediate call after contains mouse checks for mouse in sensitive floating areas 1186 emit mustBeHide(); 1187 } 1188 } 1189 }); 1190 } 1191 } 1192 1193 void VisibilityManager::deleteFloatingGapWindow() 1194 { 1195 if (m_floatingGapWindow) { 1196 m_floatingGapWindow->deleteLater(); 1197 m_floatingGapWindow = nullptr; 1198 } 1199 } 1200 1201 bool VisibilityManager::supportsFloatingGap() const 1202 { 1203 return (m_floatingGapWindow != nullptr); 1204 } 1205 1206 1207 //! END: VisibilityManager implementation 1208 1209 } 1210 }