diff --git a/app/view/view.cpp b/app/view/view.cpp index 4e31f5b7..e70c6617 100644 --- a/app/view/view.cpp +++ b/app/view/view.cpp @@ -1,1318 +1,1349 @@ /* * Copyright 2016 Smith AR * Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "view.h" // local #include "contextmenu.h" #include "effects.h" #include "positioner.h" #include "visibilitymanager.h" #include "settings/primaryconfigview.h" #include "settings/secondaryconfigview.h" #include "../indicator/factory.h" #include "../lattecorona.h" #include "../layout/genericlayout.h" #include "../layouts/manager.h" #include "../plasma/extended/theme.h" #include "../screenpool.h" #include "../settings/universalsettings.h" #include "../shortcuts/globalshortcuts.h" #include "../shortcuts/shortcutstracker.h" #include "../../liblatte2/extras.h" // Qt #include #include #include #include #include #include #include // KDe #include #include #include #include #include // Plasma #include #include #include namespace Latte { //! both alwaysVisible and byPassWM are passed through corona because //! during the view window creation containment hasn't been set, but these variables //! are needed in order for window flags to be set correctly View::View(Plasma::Corona *corona, QScreen *targetScreen, bool byPassWM) : PlasmaQuick::ContainmentView(corona), m_contextMenu(new ViewPart::ContextMenu(this)), m_effects(new ViewPart::Effects(this)) { //! needs to be created after Effects because it catches some of its signals //! and avoid a crash from View::winId() at the same time m_positioner = new ViewPart::Positioner(this); setTitle(corona->kPackage().metadata().name()); setIcon(qGuiApp->windowIcon()); setResizeMode(QuickViewSharedEngine::SizeRootObjectToView); setColor(QColor(Qt::transparent)); setDefaultAlphaBuffer(true); setClearBeforeRendering(true); const auto flags = Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::NoDropShadowWindowHint | Qt::WindowDoesNotAcceptFocus; if (byPassWM) { setFlags(flags | Qt::BypassWindowManagerHint); } else { setFlags(flags); } if (targetScreen) m_positioner->setScreenToFollow(targetScreen); else m_positioner->setScreenToFollow(qGuiApp->primaryScreen()); connect(this, &View::containmentChanged , this, [ &, byPassWM]() { qDebug() << "dock view c++ containment changed 1..."; if (!this->containment()) return; qDebug() << "dock view c++ containment changed 2..."; //! First load default values from file restoreConfig(); //! Afterwards override that values in case during creation something different is needed setByPassWM(byPassWM); //! Check the screen assigned to this dock reconsiderScreen(); //! needs to be created before visibility creation because visibility uses it if (!m_windowsTracker) { m_windowsTracker = new ViewPart::WindowsTracker(this); emit windowsTrackerChanged(); } if (!m_visibility) { m_visibility = new ViewPart::VisibilityManager(this); connect(m_visibility, &ViewPart::VisibilityManager::isHiddenChanged, this, [&]() { if (m_visibility->isHidden()) { deactivateApplets(); } }); emit visibilityChanged(); } if (!m_indicator) { m_indicator = new ViewPart::Indicator(this); emit indicatorChanged(); } connect(this->containment(), SIGNAL(statusChanged(Plasma::Types::ItemStatus)), SLOT(statusChanged(Plasma::Types::ItemStatus))); }, Qt::DirectConnection); m_corona = qobject_cast(this->corona()); if (m_corona) { connect(m_corona, &Latte::Corona::viewLocationChanged, this, &View::dockLocationChanged); } } View::~View() { m_inDelete = true; //! clear Layout connections m_visibleHackTimer1.stop(); m_visibleHackTimer2.stop(); for (auto &c : connectionsLayout) { disconnect(c); } //! unload indicators if (m_indicator) { m_indicator->unloadIndicators(); } disconnect(m_corona, &Latte::Corona::availableScreenRectChangedFrom, this, &View::availableScreenRectChangedFrom); disconnect(containment(), SIGNAL(statusChanged(Plasma::Types::ItemStatus)), this, SLOT(statusChanged(Plasma::Types::ItemStatus))); qDebug() << "dock view deleting..."; rootContext()->setContextProperty(QStringLiteral("dock"), nullptr); rootContext()->setContextProperty(QStringLiteral("layoutsManager"), nullptr); rootContext()->setContextProperty(QStringLiteral("shortcutsEngine"), nullptr); rootContext()->setContextProperty(QStringLiteral("themeExtended"), nullptr); rootContext()->setContextProperty(QStringLiteral("universalSettings"), nullptr); //! this disconnect does not free up connections correctly when //! latteView is deleted. A crash for this example is the following: //! switch to Alternative Session and disable compositing, //! the signal creating the crash was probably from deleted //! windows. //! this->disconnect(); if (m_configView) { m_configView->deleteLater(); } if (m_contextMenu) { delete m_contextMenu; } //needs to be deleted before Effects because it catches some of its signals if (m_positioner) { delete m_positioner; } if (m_effects) { delete m_effects; } if (m_indicator) { delete m_indicator; } if (m_visibility) { delete m_visibility; } if (m_windowsTracker) { delete m_windowsTracker; } } void View::init() { connect(this, &QQuickWindow::xChanged, this, &View::xChanged); connect(this, &QQuickWindow::xChanged, this, &View::updateAbsoluteGeometry); connect(this, &QQuickWindow::yChanged, this, &View::yChanged); connect(this, &QQuickWindow::yChanged, this, &View::updateAbsoluteGeometry); connect(this, &QQuickWindow::widthChanged, this, &View::widthChanged); connect(this, &QQuickWindow::widthChanged, this, &View::updateAbsoluteGeometry); connect(this, &QQuickWindow::heightChanged, this, &View::heightChanged); connect(this, &QQuickWindow::heightChanged, this, &View::updateAbsoluteGeometry); connect(m_corona, &Latte::Corona::availableScreenRectChangedFrom, this, &View::availableScreenRectChangedFrom); connect(this, &View::byPassWMChanged, this, &View::saveConfig); connect(this, &View::isPreferredForShortcutsChanged, this, &View::saveConfig); connect(this, &View::onPrimaryChanged, this, &View::saveConfig); connect(this, &View::typeChanged, this, &View::saveConfig); connect(this, &View::normalThicknessChanged, this, [&]() { emit m_corona->availableScreenRectChangedFrom(this); }); connect(m_effects, &ViewPart::Effects::innerShadowChanged, this, [&]() { emit m_corona->availableScreenRectChangedFrom(this); }); connect(m_positioner, &ViewPart::Positioner::onHideWindowsForSlidingOut, this, &View::hideWindowsForSlidingOut); connect(m_positioner, &ViewPart::Positioner::screenGeometryChanged, this, &View::screenGeometryChanged); connect(m_positioner, &ViewPart::Positioner::windowSizeChanged, this, [&]() { emit m_corona->availableScreenRectChangedFrom(this); }); connect(m_contextMenu, &ViewPart::ContextMenu::menuChanged, this, &View::contextMenuIsShownChanged); connect(m_corona->indicatorFactory(), &Latte::Indicator::Factory::pluginsUpdated, this, &View::reloadSource); //! View sends this signal in order to avoid crashes from ViewPart::Indicator when the view is recreated connect(m_corona->indicatorFactory(), &Latte::Indicator::Factory::customPluginsChanged, this, &View::customPluginsChanged); connect(m_corona, &Latte::Corona::availableScreenRectChanged, this, &View::availableScreenRectChangedForViewParts); ///!!!!! rootContext()->setContextProperty(QStringLiteral("latteView"), this); if (m_corona) { rootContext()->setContextProperty(QStringLiteral("layoutsManager"), m_corona->layoutsManager()); rootContext()->setContextProperty(QStringLiteral("shortcutsEngine"), m_corona->globalShortcuts()->shortcutsTracker()); rootContext()->setContextProperty(QStringLiteral("themeExtended"), m_corona->themeExtended()); rootContext()->setContextProperty(QStringLiteral("universalSettings"), m_corona->universalSettings()); } setSource(corona()->kPackage().filePath("lattedockui")); m_positioner->syncGeometry(); qDebug() << "SOURCE:" << source(); } void View::reloadSource() { if (m_layout && containment()) { if (settingsWindowIsShown()) { m_configView->deleteLater(); } engine()->clearComponentCache(); m_layout->recreateView(containment(), settingsWindowIsShown()); } } bool View::inDelete() const { return m_inDelete; } void View::disconnectSensitiveSignals() { disconnect(m_corona, &Latte::Corona::availableScreenRectChangedFrom, this, &View::availableScreenRectChangedFrom); setLayout(nullptr); if (m_windowsTracker) { // m_windowsTracker->setEnabled(false); } } void View::availableScreenRectChangedFrom(View *origin) { if (m_inDelete || origin == this) return; if (formFactor() == Plasma::Types::Vertical) { m_positioner->syncGeometry(); } } void View::setupWaylandIntegration() { if (m_shellSurface) return; if (Latte::Corona *c = qobject_cast(corona())) { using namespace KWayland::Client; PlasmaShell *interface {c->waylandCoronaInterface()}; if (!interface) return; Surface *s{Surface::fromWindow(this)}; if (!s) return; m_shellSurface = interface->createSurface(s, this); qDebug() << "WAYLAND dock window surface was created..."; m_shellSurface->setSkipTaskbar(true); m_shellSurface->setRole(PlasmaShellSurface::Role::Panel); m_shellSurface->setPanelBehavior(PlasmaShellSurface::PanelBehavior::WindowsGoBelow); } } KWayland::Client::PlasmaShellSurface *View::surface() { return m_shellSurface; } //! the main function which decides if this dock is at the //! correct screen void View::reconsiderScreen() { m_positioner->reconsiderScreen(); } void View::copyView() { m_layout->copyView(containment()); } void View::removeView() { if (m_layout && m_layout->viewsCount() > 1) { QAction *removeAct = this->containment()->actions()->action(QStringLiteral("remove")); if (removeAct) { removeAct->trigger(); } } } bool View::settingsWindowIsShown() { auto configView = qobject_cast(m_configView); return (configView != nullptr); } void View::showSettingsWindow() { if (!settingsWindowIsShown()) { emit m_visibility->mustBeShown(); showConfigurationInterface(containment()); applyActivitiesToWindows(); } } PlasmaQuick::ConfigView *View::configView() { return m_configView; } void View::showConfigurationInterface(Plasma::Applet *applet) { if (!applet || !applet->containment()) return; Plasma::Containment *c = qobject_cast(applet); if (m_configView && c && c->isContainment() && c == this->containment()) { if (m_configView->isVisible()) { m_configView->hide(); } else { m_configView->show(); } return; } else if (m_configView) { if (m_configView->applet() == applet) { m_configView->show(); if (KWindowSystem::isPlatformX11()) { m_configView->requestActivate(); } return; } else { m_configView->hide(); } } bool delayConfigView = false; if (c && containment() && c->isContainment() && c->id() == this->containment()->id()) { m_configView = new ViewPart::PrimaryConfigView(c, this); delayConfigView = true; } else { m_configView = new PlasmaQuick::ConfigView(applet); } m_configView.data()->init(); if (!delayConfigView) { m_configView->show(); } else { //add a timer for showing the configuration window the first time it is //created in order to give the containment's layouts the time to //calculate the window's height QTimer::singleShot(150, [this]() { if (m_configView) { m_configView->show(); } }); } } QRect View::localGeometry() const { return m_localGeometry; } void View::setLocalGeometry(const QRect &geometry) { if (m_localGeometry == geometry) { return; } m_localGeometry = geometry; emit localGeometryChanged(); updateAbsoluteGeometry(); } void View::updateAbsoluteGeometry(bool bypassChecks) { //! there was a -1 in height and width here. The reason of this //! if I remember correctly was related to multi-screen but I cant //! remember exactly the reason, something related to right edge in //! multi screen environment. BUT this was breaking the entire AlwaysVisible //! experience with struts. Removing them in order to restore correct //! behavior and keeping this comment in order to check for //! multi-screen breakage QRect absGeometry {x() + m_localGeometry.x(), y() + m_localGeometry.y() , m_localGeometry.width(), m_localGeometry.height()}; if (m_absoluteGeometry == absGeometry && !bypassChecks) { return; } if (m_absoluteGeometry != absGeometry) { m_absoluteGeometry = absGeometry; emit absoluteGeometryChanged(m_absoluteGeometry); } //! this is needed in order to update correctly the screenGeometries if (visibility() && corona() && visibility()->mode() == Types::AlwaysVisible) { //! main use of BYPASSCKECKS is from Positioner when the view changes screens emit m_corona->availableScreenRectChangedFrom(this); emit m_corona->availableScreenRegionChangedFrom(this); } } void View::statusChanged(Plasma::Types::ItemStatus status) { if (containment()) { if (containment()->status() >= Plasma::Types::NeedsAttentionStatus && containment()->status() != Plasma::Types::HiddenStatus) { setBlockHiding(true); } else if (!containment()->isUserConfiguring()){ setBlockHiding(false); } } } Types::ViewType View::type() const { return m_type; } void View::setType(Types::ViewType type) { if (m_type == type) { return; } m_type = type; emit typeChanged(); } bool View::alternativesIsShown() const { return m_alternativesIsShown; } void View::setAlternativesIsShown(bool show) { if (m_alternativesIsShown == show) { return; } m_alternativesIsShown = show; setBlockHiding(show); emit alternativesIsShownChanged(); } bool View::containsDrag() const { return m_containsDrag; } void View::setContainsDrag(bool contains) { if (m_containsDrag == contains) { return; } m_containsDrag = contains; emit containsDragChanged(); } bool View::containsMouse() const { return m_containsMouse; } bool View::contextMenuIsShown() const { if (!m_contextMenu) { return false; } return m_contextMenu->menu(); } int View::currentThickness() const { if (formFactor() == Plasma::Types::Vertical) { return m_effects->mask().isNull() ? width() : m_effects->mask().width() - m_effects->innerShadow(); } else { return m_effects->mask().isNull() ? height() : m_effects->mask().height() - m_effects->innerShadow(); } } int View::normalThickness() const { return m_normalThickness; } void View::setNormalThickness(int thickness) { if (m_normalThickness == thickness) { return; } m_normalThickness = thickness; emit normalThicknessChanged(); } bool View::byPassWM() const { return m_byPassWM; } void View::setByPassWM(bool bypass) { if (m_byPassWM == bypass) { return; } m_byPassWM = bypass; emit byPassWMChanged(); } bool View::behaveAsPlasmaPanel() const { return m_behaveAsPlasmaPanel; } void View::setBehaveAsPlasmaPanel(bool behavior) { if (m_behaveAsPlasmaPanel == behavior) { return; } m_behaveAsPlasmaPanel = behavior; emit behaveAsPlasmaPanelChanged(); } bool View::inEditMode() const { return m_inEditMode; } void View::setInEditMode(bool edit) { if (m_inEditMode == edit) { return; } m_inEditMode = edit; emit inEditModeChanged(); } bool View::isPreferredForShortcuts() const { return m_isPreferredForShortcuts; } void View::setIsPreferredForShortcuts(bool preferred) { if (m_isPreferredForShortcuts == preferred) { return; } m_isPreferredForShortcuts = preferred; emit isPreferredForShortcutsChanged(); if (m_isPreferredForShortcuts && m_layout) { emit m_layout->preferredViewForShortcutsChanged(this); } } bool View::latteTasksArePresent() const { return m_latteTasksArePresent; } void View::setLatteTasksArePresent(bool present) { if (m_latteTasksArePresent == present) { return; } m_latteTasksArePresent = present; emit latteTasksArePresentChanged(); } +bool View::touchingBottomViewAndIsBusy() const +{ + return m_touchingBottomViewAndIsBusy; +} + +void View::setTouchingBottomViewAndIsBusy(bool touchAndBusy) +{ + if (m_touchingBottomViewAndIsBusy == touchAndBusy) { + return; + } + + m_touchingBottomViewAndIsBusy = touchAndBusy; + + emit touchingBottomViewAndIsBusyChanged(); +} + +bool View::touchingTopViewAndIsBusy() const +{ + return m_touchingTopViewAndIsBusy; +} + +void View::setTouchingTopViewAndIsBusy(bool touchAndBusy) +{ + if (m_touchingTopViewAndIsBusy == touchAndBusy) { + return; + } + + m_touchingTopViewAndIsBusy = touchAndBusy; + emit touchingTopViewAndIsBusyChanged(); +} + void View::preferredViewForShortcutsChangedSlot(Latte::View *view) { if (view != this) { setIsPreferredForShortcuts(false); } } bool View::onPrimary() const { return m_onPrimary; } void View::setOnPrimary(bool flag) { if (m_onPrimary == flag) { return; } m_onPrimary = flag; emit onPrimaryChanged(); } float View::maxLength() const { return m_maxLength; } void View::setMaxLength(float length) { if (m_maxLength == length) { return; } m_maxLength = length; emit maxLengthChanged(); } int View::editThickness() const { return m_editThickness; } void View::setEditThickness(int thickness) { if (m_editThickness == thickness) { return; } m_editThickness = thickness; emit editThicknessChanged(); } int View::maxThickness() const { return m_maxThickness; } void View::setMaxThickness(int thickness) { if (m_maxThickness == thickness) return; m_maxThickness = thickness; emit maxThicknessChanged(); } int View::alignment() const { return m_alignment; } void View::setAlignment(int alignment) { Types::Alignment align = static_cast(alignment); if (m_alignment == alignment) { return; } m_alignment = align; emit alignmentChanged(); } QRect View::absoluteGeometry() const { return m_absoluteGeometry; } QRect View::screenGeometry() const { if (this->screen()) { QRect geom = this->screen()->geometry(); return geom; } return QRect(); } int View::offset() const { return m_offset; } void View::setOffset(int offset) { if (m_offset == offset) { return; } m_offset = offset; emit offsetChanged(); } int View::fontPixelSize() const { return m_fontPixelSize; } void View::setFontPixelSize(int size) { if (m_fontPixelSize == size) { return; } m_fontPixelSize = size; emit fontPixelSizeChanged(); } bool View::isOnAllActivities() const { return m_activities.isEmpty() || m_activities[0] == "0"; } bool View::isOnActivity(const QString &activity) const { return isOnAllActivities() || m_activities.contains(activity); } QStringList View::activities() const { return m_activities; } void View::applyActivitiesToWindows() { if (m_visibility && m_layout) { m_windowsTracker->setWindowOnActivities(*this, m_activities); if (m_configView) { m_windowsTracker->setWindowOnActivities(*m_configView, m_activities); auto configView = qobject_cast(m_configView); if (configView && configView->secondaryWindow()) { m_windowsTracker->setWindowOnActivities(*configView->secondaryWindow(), m_activities); } } if (m_visibility->supportsKWinEdges()) { m_visibility->applyActivitiesToHiddenWindows(m_activities); } } } Layout::GenericLayout *View::layout() const { return m_layout; } void View::setLayout(Layout::GenericLayout *layout) { if (m_layout == layout) { return; } // clear mode for (auto &c : connectionsLayout) { disconnect(c); } m_layout = layout; if (m_layout) { connectionsLayout << connect(containment(), &Plasma::Applet::destroyedChanged, m_layout, &Layout::GenericLayout::destroyedChanged); connectionsLayout << connect(containment(), &Plasma::Applet::locationChanged, m_corona, &Latte::Corona::viewLocationChanged); connectionsLayout << connect(containment(), &Plasma::Containment::appletAlternativesRequested, m_corona, &Latte::Corona::showAlternativesForApplet, Qt::QueuedConnection); if (m_corona->layoutsManager()->memoryUsage() == Types::MultipleLayouts) { connectionsLayout << connect(containment(), &Plasma::Containment::appletCreated, m_layout, &Layout::GenericLayout::appletCreated); } connectionsLayout << connect(m_positioner, &Latte::ViewPart::Positioner::edgeChanged, m_layout, &Layout::GenericLayout::viewEdgeChanged); //! Sometimes the activity isnt completely ready, by adding a delay //! we try to catch up QTimer::singleShot(100, [this]() { if (m_layout && m_visibility) { m_activities = m_layout->appliedActivities(); qDebug() << "DOCK VIEW FROM LAYOUT ::: " << m_layout->name() << " - activities: " << m_activities; applyActivitiesToWindows(); emit activitiesChanged(); } }); connectionsLayout << connect(m_layout, &Layout::GenericLayout::preferredViewForShortcutsChanged, this, &View::preferredViewForShortcutsChangedSlot); connectionsLayout << connect(m_layout, &Layout::GenericLayout::lastConfigViewForChanged, this, &View::configViewCreatedFor); Latte::Corona *latteCorona = qobject_cast(this->corona()); if (latteCorona->layoutsManager()->memoryUsage() == Types::MultipleLayouts) { connectionsLayout << connect(latteCorona->activitiesConsumer(), &KActivities::Consumer::runningActivitiesChanged, this, [&]() { if (m_layout && m_visibility) { m_activities = m_layout->appliedActivities(); qDebug() << "DOCK VIEW FROM LAYOUT (runningActivitiesChanged) ::: " << m_layout->name() << " - activities: " << m_activities; applyActivitiesToWindows(); emit activitiesChanged(); } }); connectionsLayout << connect(m_layout, &Layout::GenericLayout::activitiesChanged, this, [&]() { if (m_layout) { m_activities = m_layout->appliedActivities(); applyActivitiesToWindows(); emit activitiesChanged(); } }); connectionsLayout << connect(latteCorona->layoutsManager(), &Layouts::Manager::layoutsChanged, this, [&]() { if (m_layout) { m_activities = m_layout->appliedActivities(); applyActivitiesToWindows(); emit activitiesChanged(); } }); //! BEGIN OF KWIN HACK //! IMPORTANT ::: Fixing KWin Faulty Behavior that KWin hides ALL Views when an Activity stops //! with no reason!! m_visibleHackTimer1.setInterval(400); m_visibleHackTimer2.setInterval(2500); m_visibleHackTimer1.setSingleShot(true); m_visibleHackTimer2.setSingleShot(true); connectionsLayout << connect(this, &QWindow::visibleChanged, this, [&]() { if (m_layout && !inDelete() & !isVisible()) { m_visibleHackTimer1.start(); m_visibleHackTimer2.start(); } }); connectionsLayout << connect(&m_visibleHackTimer1, &QTimer::timeout, this, [&]() { if (m_layout && !inDelete() & !isVisible()) { show(); applyActivitiesToWindows(); //qDebug() << "View:: Enforce reshow from timer 1..."; emit activitiesChanged(); emit forcedShown(); } else { //qDebug() << "View:: No needed reshow from timer 1..."; } }); connectionsLayout << connect(&m_visibleHackTimer2, &QTimer::timeout, this, [&]() { if (m_layout && !inDelete() && !isVisible()) { show(); applyActivitiesToWindows(); //qDebug() << "View:: Enforce reshow from timer 2..."; emit activitiesChanged(); emit forcedShown(); } else { //qDebug() << "View:: No needed reshow from timer 2..."; } }); //! END OF KWIN HACK } emit layoutChanged(); } else { m_activities.clear(); } } void View::moveToLayout(QString layoutName) { if (!m_layout) { return; } QList containments = m_layout->unassignFromLayout(this); Latte::Corona *latteCorona = qobject_cast(this->corona()); if (latteCorona && containments.size() > 0) { Layout::GenericLayout *newLayout = latteCorona->layoutsManager()->synchronizer()->layout(layoutName); if (newLayout) { newLayout->assignToLayout(this, containments); } } } void View::setBlockHiding(bool block) { if (!block) { auto *configView = qobject_cast(m_configView); if (m_alternativesIsShown || (configView && configView->sticker() && configView->isVisible())) { return; } if (m_visibility) { m_visibility->setBlockHiding(false); } } else { if (m_visibility) { m_visibility->setBlockHiding(true); } } } void View::configViewCreatedFor(Latte::View *view) { if (view!=this && m_configView) { //! for each layout only one dock should show its configuration windows //! otherwise we could reach a point that because a settings window //! is below another Latte View its options are not reachable auto configDialog = qobject_cast(m_configView); if (configDialog) { configDialog->hideConfigWindow(); } } } void View::hideWindowsForSlidingOut() { setBlockHiding(false); if (m_configView) { auto configDialog = qobject_cast(m_configView); if (configDialog) { configDialog->hideConfigWindow(); } } } //! remove latte tasks plasmoid void View::removeTasksPlasmoid() { if (!tasksPresent() || !containment()) { return; } for (const Plasma::Applet *applet : containment()->applets()) { KPluginMetaData meta = applet->kPackage().metadata(); if (meta.pluginId() == "org.kde.latte.plasmoid") { QAction *closeApplet = applet->actions()->action(QStringLiteral("remove")); if (closeApplet) { closeApplet->trigger(); //! remove only the first found return; } } } } //! check if the tasks plasmoid exist in the dock bool View::tasksPresent() { if (!this->containment()) { return false; } for (const Plasma::Applet *applet : this->containment()->applets()) { const auto &provides = KPluginMetaData::readStringList(applet->pluginMetaData().rawData(), QStringLiteral("X-Plasma-Provides")); if (provides.contains(QLatin1String("org.kde.plasma.multitasking"))) { return true; } } return false; } //!check if the plasmoid with _name_ exists in the midedata bool View::mimeContainsPlasmoid(QMimeData *mimeData, QString name) { if (!mimeData) { return false; } if (mimeData->hasFormat(QStringLiteral("text/x-plasmoidservicename"))) { QString data = mimeData->data(QStringLiteral("text/x-plasmoidservicename")); const QStringList appletNames = data.split('\n', QString::SkipEmptyParts); for (const QString &appletName : appletNames) { if (appletName == name) return true; } } return false; } ViewPart::Effects *View::effects() const { return m_effects; } ViewPart::Indicator *View::indicator() const { return m_indicator; } ViewPart::Positioner *View::positioner() const { return m_positioner; } ViewPart::VisibilityManager *View::visibility() const { return m_visibility; } ViewPart::WindowsTracker *View::windowsTracker() const { return m_windowsTracker; } bool View::event(QEvent *e) { if (!m_inDelete) { emit eventTriggered(e); switch (e->type()) { case QEvent::Enter: m_containsMouse = true; if (m_configView) { ViewPart::PrimaryConfigView *primaryConfigView = qobject_cast(m_configView); if (primaryConfigView) { if (primaryConfigView->secondaryWindow()) { ViewPart::SecondaryConfigView *secConfigView = qobject_cast(primaryConfigView->secondaryWindow()); if (secConfigView) { secConfigView->requestActivate(); } } primaryConfigView->requestActivate(); } } break; case QEvent::Leave: m_containsMouse = false; setContainsDrag(false); engine()->trimComponentCache(); break; case QEvent::DragEnter: setContainsDrag(true); break; case QEvent::DragLeave: case QEvent::Drop: setContainsDrag(false); break; case QEvent::MouseButtonPress: if (auto mouseEvent = dynamic_cast(e)) { emit mousePressed(mouseEvent->pos(), mouseEvent->button()); } break; case QEvent::MouseButtonRelease: if (auto mouseEvent = dynamic_cast(e)) { emit mouseReleased(mouseEvent->pos(), mouseEvent->button()); } break; /* case QEvent::DragMove: qDebug() << "DRAG MOVING>>>>>>"; break;*/ case QEvent::PlatformSurface: if (auto pe = dynamic_cast(e)) { switch (pe->surfaceEventType()) { case QPlatformSurfaceEvent::SurfaceCreated: setupWaylandIntegration(); if (m_shellSurface) { m_positioner->syncGeometry(); m_effects->updateShadows(); } break; case QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed: if (m_shellSurface) { delete m_shellSurface; m_shellSurface = nullptr; qDebug() << "WAYLAND dock window surface was deleted..."; m_effects->clearShadows(); } break; } } break; case QEvent::Show: m_corona->wm()->setViewExtraFlags(*this); break; default: break; } } return ContainmentView::event(e); } void View::deactivateApplets() { if (!containment()) { return; } for (const auto applet : containment()->applets()) { PlasmaQuick::AppletQuickItem *ai = applet->property("_plasma_graphicObject").value(); if (ai) { ai->setExpanded(false); } } } void View::toggleAppletExpanded(const int id) { if (!containment()) { return; } for (const auto applet : containment()->applets()) { if (applet->id() == id) { PlasmaQuick::AppletQuickItem *ai = applet->property("_plasma_graphicObject").value(); if (ai) { if (!ai->isActivationTogglesExpanded()) { ai->setActivationTogglesExpanded(true); } emit applet->activated(); } } } } QVariantList View::containmentActions() { QVariantList actions; /*if (containment()->corona()->immutability() != Plasma::Types::Mutable) { return actions; }*/ //FIXME: the trigger string it should be better to be supported this way //const QString trigger = Plasma::ContainmentActions::eventToString(event); const QString trigger = "RightButton;NoModifier"; Plasma::ContainmentActions *plugin = this->containment()->containmentActions().value(trigger); if (!plugin) { return actions; } if (plugin->containment() != this->containment()) { plugin->setContainment(this->containment()); // now configure it KConfigGroup cfg(this->containment()->corona()->config(), "ActionPlugins"); cfg = KConfigGroup(&cfg, QString::number(this->containment()->containmentType())); KConfigGroup pluginConfig = KConfigGroup(&cfg, trigger); plugin->restore(pluginConfig); } for (QAction *ac : plugin->contextualActions()) { actions << QVariant::fromValue(ac); } return actions; } void View::disableGrabItemBehavior() { setMouseGrabEnabled(false); } void View::restoreGrabItemBehavior() { if (mouseGrabberItem()) { mouseGrabberItem()->ungrabMouse(); } } bool View::isHighestPriorityView() { if (m_layout) { return this == m_layout->highestPriorityView(); } return false; } //!BEGIN overriding context menus behavior void View::mousePressEvent(QMouseEvent *event) { bool result = m_contextMenu->mousePressEvent(event); emit contextMenuIsShownChanged(); if (result) { PlasmaQuick::ContainmentView::mousePressEvent(event); } } //!END overriding context menus behavior //!BEGIN configuration functions void View::saveConfig() { if (!this->containment()) return; auto config = this->containment()->config(); config.writeEntry("onPrimary", onPrimary()); config.writeEntry("byPassWM", byPassWM()); config.writeEntry("isPreferredForShortcuts", isPreferredForShortcuts()); config.writeEntry("viewType", (int)m_type); config.sync(); } void View::restoreConfig() { if (!this->containment()) return; auto config = this->containment()->config(); m_onPrimary = config.readEntry("onPrimary", true); m_byPassWM = config.readEntry("byPassWM", false); m_isPreferredForShortcuts = config.readEntry("isPreferredForShortcuts", false); //! Send changed signals at the end in order to be sure that saveConfig //! wont rewrite default/invalid values emit onPrimaryChanged(); emit byPassWMChanged(); } //!END configuration functions } //!END namespace diff --git a/app/view/view.h b/app/view/view.h index 1ab23b30..0983f0e3 100644 --- a/app/view/view.h +++ b/app/view/view.h @@ -1,353 +1,368 @@ /* * Copyright 2016 Smith AR * Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef VIEW_H #define VIEW_H // local #include "effects.h" #include "positioner.h" #include "visibilitymanager.h" #include "indicator/indicator.h" #include "settings/primaryconfigview.h" #include "windowstracker/windowstracker.h" #include "../shortcuts/globalshortcuts.h" #include "../layout/genericlayout.h" #include "../plasma/quick/containmentview.h" #include "../plasma/quick/configview.h" #include "../../liblatte2/types.h" // C++ #include // Qt #include #include #include #include #include #include namespace Plasma { class Types; class Corona; class Containment; } namespace KWayland { namespace Client { class PlasmaShellSurface; } } namespace Latte { class Corona; class GenericLayout; namespace ViewPart { class ContextMenu; } } namespace Latte { class View : public PlasmaQuick::ContainmentView { Q_OBJECT Q_PROPERTY(Latte::Types::ViewType type READ type WRITE setType NOTIFY typeChanged) Q_PROPERTY(bool alternativesIsShown READ alternativesIsShown NOTIFY alternativesIsShownChanged) Q_PROPERTY(bool behaveAsPlasmaPanel READ behaveAsPlasmaPanel WRITE setBehaveAsPlasmaPanel NOTIFY behaveAsPlasmaPanelChanged) Q_PROPERTY(bool byPassWM READ byPassWM WRITE setByPassWM NOTIFY byPassWMChanged) Q_PROPERTY(bool containsDrag READ containsDrag NOTIFY containsDragChanged) Q_PROPERTY(bool contextMenuIsShown READ contextMenuIsShown NOTIFY contextMenuIsShownChanged) //! Because Latte uses animations, changing to edit mode it may be different than //! when the isUserConfiguring changes value Q_PROPERTY(bool inEditMode READ inEditMode WRITE setInEditMode NOTIFY inEditModeChanged) Q_PROPERTY(bool isPreferredForShortcuts READ isPreferredForShortcuts WRITE setIsPreferredForShortcuts NOTIFY isPreferredForShortcutsChanged) Q_PROPERTY(bool latteTasksArePresent READ latteTasksArePresent WRITE setLatteTasksArePresent NOTIFY latteTasksArePresentChanged) Q_PROPERTY(bool onPrimary READ onPrimary WRITE setOnPrimary NOTIFY onPrimaryChanged) + //! values to be used from Smart surrounding Views + Q_PROPERTY(bool touchingBottomViewAndIsBusy READ touchingBottomViewAndIsBusy WRITE setTouchingBottomViewAndIsBusy NOTIFY touchingBottomViewAndIsBusyChanged) + Q_PROPERTY(bool touchingTopViewAndIsBusy READ touchingTopViewAndIsBusy WRITE setTouchingTopViewAndIsBusy NOTIFY touchingTopViewAndIsBusyChanged) + Q_PROPERTY(int alignment READ alignment WRITE setAlignment NOTIFY alignmentChanged) Q_PROPERTY(int fontPixelSize READ fontPixelSize WRITE setFontPixelSize NOTIFY fontPixelSizeChanged) Q_PROPERTY(int x READ x NOTIFY xChanged) Q_PROPERTY(int y READ y NOTIFY yChanged) Q_PROPERTY(int width READ width NOTIFY widthChanged) Q_PROPERTY(int height READ height NOTIFY heightChanged) Q_PROPERTY(int editThickness READ editThickness WRITE setEditThickness NOTIFY editThicknessChanged) Q_PROPERTY(int maxThickness READ maxThickness WRITE setMaxThickness NOTIFY maxThicknessChanged) Q_PROPERTY(int normalThickness READ normalThickness WRITE setNormalThickness NOTIFY normalThicknessChanged) Q_PROPERTY(int offset READ offset WRITE setOffset NOTIFY offsetChanged) Q_PROPERTY(float maxLength READ maxLength WRITE setMaxLength NOTIFY maxLengthChanged) Q_PROPERTY(Latte::Layout::GenericLayout *layout READ layout WRITE setLayout NOTIFY layoutChanged) Q_PROPERTY(Latte::ViewPart::Effects *effects READ effects NOTIFY effectsChanged) Q_PROPERTY(Latte::ViewPart::Indicator *indicator READ indicator NOTIFY indicatorChanged) Q_PROPERTY(Latte::ViewPart::Positioner *positioner READ positioner NOTIFY positionerChanged) Q_PROPERTY(Latte::ViewPart::VisibilityManager *visibility READ visibility NOTIFY visibilityChanged) Q_PROPERTY(Latte::ViewPart::WindowsTracker *windowsTracker READ windowsTracker NOTIFY windowsTrackerChanged) Q_PROPERTY(QRect absoluteGeometry READ absoluteGeometry NOTIFY absoluteGeometryChanged) Q_PROPERTY(QRect localGeometry READ localGeometry WRITE setLocalGeometry NOTIFY localGeometryChanged) Q_PROPERTY(QRect screenGeometry READ screenGeometry NOTIFY screenGeometryChanged) public: View(Plasma::Corona *corona, QScreen *targetScreen = nullptr, bool byPassWM = false); virtual ~View(); void init(); Types::ViewType type() const; void setType(Types::ViewType type); bool alternativesIsShown() const; void setAlternativesIsShown(bool show); bool inDelete() const; bool onPrimary() const; void setOnPrimary(bool flag); int currentThickness() const; bool behaveAsPlasmaPanel() const; void setBehaveAsPlasmaPanel(bool behavior); bool containsDrag() const; bool containsMouse() const; bool contextMenuIsShown() const; bool byPassWM() const; void setByPassWM(bool bypass); bool inEditMode() const; void setInEditMode(bool edit); bool isPreferredForShortcuts() const; void setIsPreferredForShortcuts(bool preferred); bool latteTasksArePresent() const; void setLatteTasksArePresent(bool present); + bool touchingBottomViewAndIsBusy() const; + void setTouchingBottomViewAndIsBusy(bool touchAndBusy); + + bool touchingTopViewAndIsBusy() const; + void setTouchingTopViewAndIsBusy(bool touchAndBusy); + float maxLength() const; void setMaxLength(float length); int fontPixelSize() const; void setFontPixelSize(int size); int editThickness() const; void setEditThickness(int thickness); int maxThickness() const; void setMaxThickness(int thickness); int normalThickness() const; void setNormalThickness(int thickness); int offset() const; void setOffset(int offset); int alignment() const; void setAlignment(int alignment); QRect absoluteGeometry() const; QRect screenGeometry() const; QRect localGeometry() const; void setLocalGeometry(const QRect &geometry); bool isOnActivity(const QString &activity) const; bool isOnAllActivities() const; QStringList activities() const; bool settingsWindowIsShown(); void showSettingsWindow(); PlasmaQuick::ConfigView *configView(); ViewPart::Effects *effects() const; ViewPart::Indicator *indicator() const; ViewPart::Positioner *positioner() const; ViewPart::VisibilityManager *visibility() const; ViewPart::WindowsTracker *windowsTracker() const; Layout::GenericLayout *layout() const; void setLayout(Layout::GenericLayout *layout); KWayland::Client::PlasmaShellSurface *surface(); void reconsiderScreen(); //! these are signals that create crashes, such a example is the availableScreenRectChanged from corona //! when its containment is destroyed void disconnectSensitiveSignals(); public slots: Q_INVOKABLE void copyView(); Q_INVOKABLE void removeView(); Q_INVOKABLE QVariantList containmentActions(); Q_INVOKABLE void deactivateApplets(); Q_INVOKABLE void moveToLayout(QString layoutName); Q_INVOKABLE void removeTasksPlasmoid(); Q_INVOKABLE void setBlockHiding(bool block); Q_INVOKABLE void toggleAppletExpanded(const int id); Q_INVOKABLE bool mimeContainsPlasmoid(QMimeData *mimeData, QString name); Q_INVOKABLE bool tasksPresent(); void updateAbsoluteGeometry(bool bypassChecks = false); Q_INVOKABLE void disableGrabItemBehavior(); Q_INVOKABLE void restoreGrabItemBehavior(); Q_INVOKABLE bool isHighestPriorityView(); protected slots: void showConfigurationInterface(Plasma::Applet *applet) override; protected: bool event(QEvent *ev) override; void mousePressEvent(QMouseEvent *event) override; signals: void eventTriggered(QEvent *ev); void mousePressed(const QPoint pos, const int button); void mouseReleased(const QPoint pos, const int button); void activitiesChanged(); void alternativesIsShownChanged(); void alignmentChanged(); void behaveAsPlasmaPanelChanged(); void byPassWMChanged(); void configWindowGeometryChanged(); // is called from config windows void containsDragChanged(); void contextMenuIsShownChanged(); void dockLocationChanged(); void editThicknessChanged(); void effectsChanged(); void fontPixelSizeChanged(); void forcedShown(); //[workaround] forced shown to avoid a KWin issue that hides windows when closing activities void widthChanged(); void heightChanged(); void inEditModeChanged(); void indicatorChanged(); void isPreferredForShortcutsChanged(); void latteTasksArePresentChanged(); void layoutChanged(); void localGeometryChanged(); void maxLengthChanged(); void maxThicknessChanged(); void normalThicknessChanged(); void offsetChanged(); void onPrimaryChanged(); void positionerChanged(); void screenGeometryChanged(); + void touchingBottomViewAndIsBusyChanged(); + void touchingTopViewAndIsBusyChanged(); void typeChanged(); void visibilityChanged(); void windowsTrackerChanged(); void xChanged(); void yChanged(); void absoluteGeometryChanged(const QRect &geometry); //! pass on signals to children in order to avoid crashes when View is recreated or destroyed void availableScreenRectChangedForViewParts(); void customPluginsChanged(); private slots: void availableScreenRectChangedFrom(View *origin); void configViewCreatedFor(Latte::View *view); void hideWindowsForSlidingOut(); void preferredViewForShortcutsChangedSlot(Latte::View *view); void reloadSource(); void statusChanged(Plasma::Types::ItemStatus); void restoreConfig(); void saveConfig(); private: void applyActivitiesToWindows(); void initSignalingForLocationChangeSliding(); void setupWaylandIntegration(); void updateAppletContainsMethod(); void setContainsDrag(bool contains); private: Plasma::Containment *containmentById(uint id); bool m_alternativesIsShown{false}; bool m_behaveAsPlasmaPanel{false}; bool m_byPassWM{true}; bool m_containsDrag{false}; bool m_containsMouse{false}; bool m_inDelete{false}; bool m_inEditMode{false}; bool m_isPreferredForShortcuts{false}; bool m_latteTasksArePresent{false}; bool m_onPrimary{true}; + bool m_touchingBottomViewAndIsBusy{false}; + bool m_touchingTopViewAndIsBusy{false}; + int m_fontPixelSize{ -1}; int m_editThickness{24}; int m_maxThickness{24}; int m_normalThickness{24}; int m_offset{0}; float m_maxLength{1}; Types::Alignment m_alignment{Types::Center}; Types::ViewType m_type{Types::DockView}; QRect m_localGeometry; QRect m_absoluteGeometry; QStringList m_activities; //! HACK: Timers in order to handle KWin faulty //! behavior that hides Views when closing Activities //! with no actual reason QTimer m_visibleHackTimer1; QTimer m_visibleHackTimer2; Layout::GenericLayout *m_layout{nullptr}; QPointer m_configView; QPointer m_contextMenu; QPointer m_effects; QPointer m_indicator; QPointer m_positioner; QPointer m_visibility; QPointer m_windowsTracker; //! Connections to release and bound for the assigned layout QList connectionsLayout; QPointer m_corona; KWayland::Client::PlasmaShellSurface *m_shellSurface{nullptr}; }; } #endif diff --git a/containment/package/contents/ui/VisibilityManager.qml b/containment/package/contents/ui/VisibilityManager.qml index 3e5d7216..7edf0986 100644 --- a/containment/package/contents/ui/VisibilityManager.qml +++ b/containment/package/contents/ui/VisibilityManager.qml @@ -1,763 +1,792 @@ /* * Copyright 2016 Smith AR * Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ import QtQuick 2.1 import QtQuick.Window 2.2 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.plasmoid 2.0 import org.kde.latte 0.2 as Latte Item{ id: manager anchors.fill: parent property QtObject window property bool debugMagager: Qt.application.arguments.indexOf("--mask") >= 0 property bool blockUpdateMask: false property bool inForceHiding: false //is used when the docks are forced in hiding e.g. when changing layouts property bool normalState : false // this is being set from updateMaskArea property bool previousNormalState : false // this is only for debugging purposes property bool panelIsBiggerFromIconSize: root.useThemePanel && (root.themePanelThickness >= (root.iconSize + root.thickMargin)) property int animationSpeed: Latte.WindowSystem.compositingActive ? (editModeVisual.inEditMode ? editModeVisual.speed * 0.8 : root.appliedDurationTime * 1.4 * units.longDuration) : 0 property bool inSlidingIn: false //necessary because of its init structure property alias inSlidingOut: slidingAnimationAutoHiddenOut.running property bool inTempHiding: false property int length: root.isVertical ? Screen.height : Screen.width //screenGeometry.height : screenGeometry.width property int slidingOutToPos: ((plasmoid.location===PlasmaCore.Types.LeftEdge)||(plasmoid.location===PlasmaCore.Types.TopEdge)) ? -thicknessNormal : thicknessNormal; property int thicknessAutoHidden: Latte.WindowSystem.compositingActive ? 2 : 1 property int thicknessMid: (1 + (0.65 * (root.maxZoomFactor-1)))*(root.iconSize+root.thickMargins+extraThickMask) //needed in some animations property int thicknessNormal: Math.max(root.iconSize + root.thickMargins + extraThickMask + 1, root.realPanelSize + root.panelShadow) property int thicknessZoom: ((root.iconSize+root.thickMargins+extraThickMask) * root.maxZoomFactor) + 2 //it is used to keep thickness solid e.g. when iconSize changes from auto functions property int thicknessMidOriginal: Math.max(thicknessNormalOriginal,extraThickMask + (1 + (0.65 * (root.maxZoomFactor-1)))*(root.maxIconSize+root.maxThickMargin)) //needed in some animations property int thicknessNormalOriginal: root.maxIconSize + (root.maxThickMargin * 2) //this way we always have the same thickness published at all states /*property int thicknessNormalOriginal: !root.behaveAsPlasmaPanel || root.editMode ? thicknessNormalOriginalValue : root.realPanelSize + root.panelShadow*/ property int thicknessNormalOriginalValue: root.maxIconSize + (root.maxThickMargin * 2) + extraThickMask + 1 property int thicknessZoomOriginal: Math.max( ((root.maxIconSize+(root.maxThickMargin * 2)) * root.maxZoomFactor) + extraThickMask + 2, root.realPanelSize + root.panelShadow, (Latte.WindowSystem.compositingActive ? thicknessEditMode + root.editShadow : thicknessEditMode)) //! is used from Panel in edit mode in order to provide correct masking property int thicknessEditMode: thicknessNormalOriginalValue + editModeVisual.settingsThickness //! is used to increase the mask thickness readonly property int marginBetweenContentsAndRuler: root.editMode ? 10 : 0 property int extraThickMask: marginBetweenContentsAndRuler + Math.max(indicatorsExtraThickMask, shadowsExtraThickMask) //! this is set from indicators when they need extra thickness mask size readonly property int indicatorsExtraThickMask: indicators.info.extraMaskThickness property int shadowsExtraThickMask: { if (Latte.WindowSystem.isPlatformWayland) { return 0; } //! 45% of max shadow size in px. var shadowMaxNeededMargin = 0.45 * root.appShadowSizeOriginal; var shadowOpacity = (plasmoid.configuration.shadowOpacity) / 100; //! +40% of shadow opacity in percentage shadowOpacity = shadowOpacity + shadowOpacity*0.4; //! This way we are trying to calculate how many pixels are needed in order for the shadow //! to be drawn correctly without being cut of from View::mask() under X11 shadowMaxNeededMargin = (shadowMaxNeededMargin * shadowOpacity); //! give some more space when items shadows are enabled and extremely big if (root.enableShadows && root.maxThickMargin < shadowMaxNeededMargin) { return shadowMaxNeededMargin - root.maxThickMargin; } return 0; } Binding{ target: latteView property:"maxThickness" //! prevents updating window geometry during closing window in wayland and such fixes a crash when: latteView && !inTempHiding && !inForceHiding value: thicknessZoomOriginal } property bool validIconSize: (root.iconSize===root.maxIconSize || root.iconSize === root.automaticIconSizeBasedSize) property bool inPublishingState: validIconSize && !inSlidingIn && !inSlidingOut && !inTempHiding && !inForceHiding Binding{ target: latteView property:"normalThickness" when: latteView && inPublishingState value: thicknessNormalOriginal } Binding{ target: latteView property:"editThickness" when: latteView value: thicknessEditMode } Binding{ target: latteView property: "type" when: latteView value: root.viewType } Binding{ target: latteView property: "behaveAsPlasmaPanel" when: latteView value: root.editMode ? false : root.behaveAsPlasmaPanel } Binding{ target: latteView property: "fontPixelSize" when: theme value: theme.defaultFont.pixelSize } Binding{ target: latteView property:"inEditMode" when: latteView value: root.editMode } Binding{ target: latteView property:"latteTasksArePresent" when: latteView value: latteApplet !== null } Binding{ target: latteView property: "maxLength" when: latteView value: root.inConfigureAppletsMode ? 1 : plasmoid.configuration.maxLength/100 } Binding{ target: latteView property: "offset" when: latteView value: plasmoid.configuration.offset } Binding{ target: latteView property: "alignment" when: latteView value: root.panelAlignment } + Binding{ + target: latteView + property: "touchingTopViewAndIsBusy" + when: latteView + value: { + var isTouchingTopScreenEdge = (latteView.y === latteView.screenGeometry.y); + var hasTopBorder = ((latteView.effects && (latteView.effects.enabledBorders & PlasmaCore.FrameSvg.TopBorder)) > 0); + + return root.isVertical && !isTouchingTopScreenEdge && !hasTopBorder && root.forcePanelForBusyBackground; + } + } + + Binding{ + target: latteView + property: "touchingBottomViewAndIsBusy" + when: latteView + value: { + var latteBottom = latteView.y + latteView.height; + var screenBottom = latteView.screenGeometry.y + latteView.screenGeometry.height; + var isTouchingBottomScreenEdge = (latteBottom === screenBottom); + + var hasBottomBorder = ((latteView.effects && (latteView.effects.enabledBorders & PlasmaCore.FrameSvg.BottomBorder)) > 0); + + return root.isVertical && !isTouchingBottomScreenEdge && !hasBottomBorder && root.forcePanelForBusyBackground; + } + } + + //! View::Effects bindings Binding{ target: latteView && latteView.effects ? latteView.effects : null property: "backgroundOpacity" when: latteView && latteView.effects value: root.currentPanelTransparency } Binding{ target: latteView && latteView.effects ? latteView.effects : null property: "drawEffects" when: latteView && latteView.effects value: Latte.WindowSystem.compositingActive && (((root.blurEnabled && root.useThemePanel) || (root.blurEnabled && root.forceSolidPanel && Latte.WindowSystem.compositingActive)) && (!root.inStartup || inForceHiding || inTempHiding)) } Binding{ target: latteView && latteView.effects ? latteView.effects : null property: "drawShadows" when: latteView && latteView.effects value: root.drawShadowsExternal && (!root.inStartup || inForceHiding || inTempHiding) } Binding{ target: latteView && latteView.effects ? latteView.effects : null property:"editShadow" when: latteView && latteView.effects value: root.editShadow } Binding{ target: latteView && latteView.effects ? latteView.effects : null property:"innerShadow" when: latteView && latteView.effects value: { if (editModeVisual.editAnimationEnded && !root.behaveAsPlasmaPanel) { return root.editShadow; } else { return root.panelShadow; } } } Binding{ target: latteView && latteView.effects ? latteView.effects : null property: "settingsMaskSubtracted" when: latteView && latteView.effects value: { if (Latte.WindowSystem.compositingActive && root.editMode && editModeVisual.editAnimationEnded && (root.animationsNeedBothAxis === 0 || root.zoomFactor===1) ) { return true; } else { return false; } } } + //! View::WindowsTracker bindings Binding{ target: latteView && latteView.windowsTracker ? latteView.windowsTracker : null property: "enabled" when: latteView && latteView.windowsTracker && latteView.visibility value: (latteView && latteView.visibility && !(latteView.visibility.mode === Latte.Types.AlwaysVisible /* Visibility */ || latteView.visibility.mode === Latte.Types.WindowsGoBelow || latteView.visibility.mode === Latte.Types.AutoHide)) || root.appletsNeedWindowsTracking > 0 /*Applets Neew Windows Tracking */ || root.dragActiveWindowEnabled /*Dragging Active Window(Empty Areas)*/ || ((root.backgroundOnlyOnMaximized /*Dynamic Background */ || plasmoid.configuration.solidBackgroundForMaximized || root.disablePanelShadowMaximized || root.windowColors !== Latte.Types.NoneWindowColors)) } Connections{ target:root onPanelShadowChanged: updateMaskArea(); onPanelThickMarginHighChanged: updateMaskArea(); } Connections{ target: layoutsManager onCurrentLayoutIsSwitching: { if (Latte.WindowSystem.compositingActive && latteView && latteView.layout && latteView.layout.name === layoutName) { manager.inTempHiding = true; manager.inForceHiding = true; root.clearZoom(); manager.slotMustBeHide(); } } } Connections{ target: themeExtended ? themeExtended : null onRoundnessChanged: latteView.effects.forceMaskRedraw(); onThemeChanged: latteView.effects.forceMaskRedraw(); } onNormalStateChanged: { if (normalState) { root.updateAutomaticIconSize(); root.updateSizeForAppletsInFill(); } } onThicknessZoomOriginalChanged: { updateMaskArea(); } function slotContainsMouseChanged() { if(latteView.visibility.containsMouse) { updateMaskArea(); if (slidingAnimationAutoHiddenOut.running && !inTempHiding && !inForceHiding) { slotMustBeShown(); } } } function slotMustBeShown() { // console.log("show..."); if (!slidingAnimationAutoHiddenIn.running && !inTempHiding && !inForceHiding){ slidingAnimationAutoHiddenIn.init(); } } function slotMustBeHide() { //! prevent sliding-in on startup if the dodge modes have sent a hide signal if (inStartupTimer.running && root.inStartup) { root.inStartup = false; } // console.log("hide...."); if((!slidingAnimationAutoHiddenOut.running && !latteView.visibility.blockHiding && !latteView.visibility.containsMouse) || inForceHiding) { slidingAnimationAutoHiddenOut.init(); } } //! functions used for sliding out/in during location/screen changes function slotHideDockDuringLocationChange() { inTempHiding = true; blockUpdateMask = true; slotMustBeHide(); } function slotShowDockAfterLocationChange() { slidingAnimationAutoHiddenIn.init(); } function sendHideDockDuringLocationChangeFinished(){ blockUpdateMask = false; latteView.positioner.hideDockDuringLocationChangeFinished(); } function sendSlidingOutAnimationEnded() { latteView.visibility.hide(); latteView.visibility.isHidden = true; if (visibilityManager.debugMagager) { console.log("hiding animation ended..."); } sendHideDockDuringLocationChangeFinished(); } ///test maskArea function updateMaskArea() { if (!latteView || blockUpdateMask) { return; } var localX = 0; var localY = 0; normalState = ((root.animationsNeedBothAxis === 0) && (root.animationsNeedLength === 0)) || (latteView.visibility.isHidden && !latteView.visibility.containsMouse && root.animationsNeedThickness == 0); // debug maskArea criteria if (debugMagager) { console.log(root.animationsNeedBothAxis + ", " + root.animationsNeedLength + ", " + root.animationsNeedThickness + ", " + latteView.visibility.isHidden); if (previousNormalState !== normalState) { console.log("normal state changed to:" + normalState); previousNormalState = normalState; } } var tempLength = root.isHorizontal ? width : height; var tempThickness = root.isHorizontal ? height : width; var space = 0; if (Latte.WindowSystem.compositingActive) { if (root.useThemePanel){ space = root.totalPanelEdgeSpacing + root.panelMarginLength + 1; } else { space = root.totalPanelEdgeSpacing + 1; } } else { space = root.totalPanelEdgeSpacing + root.panelMarginLength; } var noCompositingEdit = !Latte.WindowSystem.compositingActive && root.editMode; if (Latte.WindowSystem.compositingActive || noCompositingEdit) { if (normalState) { //console.log("entered normal state..."); //count panel length //used when !compositing and in editMode if (noCompositingEdit) { tempLength = root.isHorizontal ? root.width : root.height; } else { if(root.isHorizontal) { tempLength = plasmoid.configuration.panelPosition === Latte.Types.Justify ? layoutsContainer.width + space : layoutsContainer.mainLayout.width + space; } else { tempLength = plasmoid.configuration.panelPosition === Latte.Types.Justify ? layoutsContainer.height + space : layoutsContainer.mainLayout.height + space; } } tempThickness = thicknessNormal; if (root.animationsNeedThickness > 0) { tempThickness = Latte.WindowSystem.compositingActive ? thicknessZoom : thicknessNormal; } if (latteView.visibility.isHidden && !slidingAnimationAutoHiddenOut.running ) { tempThickness = thicknessAutoHidden; } //configure x,y based on plasmoid position and root.panelAlignment(Alignment) if ((plasmoid.location === PlasmaCore.Types.BottomEdge) || (plasmoid.location === PlasmaCore.Types.TopEdge)) { if (plasmoid.location === PlasmaCore.Types.BottomEdge) { localY = latteView.visibility.isHidden && latteView.visibility.supportsKWinEdges ? latteView.height + tempThickness : latteView.height - tempThickness; } else if (plasmoid.location === PlasmaCore.Types.TopEdge) { localY = latteView.visibility.isHidden && latteView.visibility.supportsKWinEdges ? -tempThickness : 0; } if (noCompositingEdit) { localX = 0; } else if (plasmoid.configuration.panelPosition === Latte.Types.Justify) { localX = (latteView.width/2) - tempLength/2 + root.offset; } else if (root.panelAlignment === Latte.Types.Left) { localX = root.offset; } else if (root.panelAlignment === Latte.Types.Center) { localX = (latteView.width/2) - tempLength/2 + root.offset; } else if (root.panelAlignment === Latte.Types.Right) { localX = latteView.width - layoutsContainer.mainLayout.width - space - root.offset; } } else if ((plasmoid.location === PlasmaCore.Types.LeftEdge) || (plasmoid.location === PlasmaCore.Types.RightEdge)){ if (plasmoid.location === PlasmaCore.Types.LeftEdge) { localX = latteView.visibility.isHidden && latteView.visibility.supportsKWinEdges ? -tempThickness : 0; } else if (plasmoid.location === PlasmaCore.Types.RightEdge) { localX = latteView.visibility.isHidden && latteView.visibility.supportsKWinEdges ? latteView.width + tempThickness : latteView.width - tempThickness; } if (noCompositingEdit) { localY = 0; } else if (plasmoid.configuration.panelPosition === Latte.Types.Justify) { localY = (latteView.height/2) - tempLength/2 + root.offset; } else if (root.panelAlignment === Latte.Types.Top) { localY = root.offset; } else if (root.panelAlignment === Latte.Types.Center) { localY = (latteView.height/2) - tempLength/2 + root.offset; } else if (root.panelAlignment === Latte.Types.Bottom) { localY = latteView.height - layoutsContainer.mainLayout.height - space - root.offset; } } } else { if(root.isHorizontal) tempLength = Screen.width; //screenGeometry.width; else tempLength = Screen.height; //screenGeometry.height; //grow only on length and not thickness if(root.animationsNeedLength>0 && root.animationsNeedBothAxis === 0) { //this is used to fix a bug with shadow showing when the animation of edit mode //is triggered tempThickness = editModeVisual.editAnimationEnded ? thicknessEditMode + root.editShadow : thicknessEditMode if (latteView.visibility.isHidden && !slidingAnimationAutoHiddenOut.running ) { tempThickness = thicknessAutoHidden; } else if (root.animationsNeedThickness > 0) { tempThickness = thicknessZoomOriginal; } } else{ //use all thickness space if (latteView.visibility.isHidden && !slidingAnimationAutoHiddenOut.running ) { tempThickness = Latte.WindowSystem.compositingActive ? thicknessAutoHidden : thicknessNormalOriginal; } else { tempThickness = thicknessZoomOriginal; } } //configure the x,y position based on thickness if(plasmoid.location === PlasmaCore.Types.RightEdge) localX = Math.max(0,latteView.width - tempThickness); else if(plasmoid.location === PlasmaCore.Types.BottomEdge) localY = Math.max(0,latteView.height - tempThickness); } } // end of compositing calculations var maskArea = latteView.effects.mask; if (Latte.WindowSystem.compositingActive) { var maskLength = maskArea.width; //in Horizontal if (root.isVertical) { maskLength = maskArea.height; } var maskThickness = maskArea.height; //in Horizontal if (root.isVertical) { maskThickness = maskArea.width; } } else if (!noCompositingEdit){ //! no compositing case var overridesHidden = latteView.visibility.isHidden && !latteView.visibility.supportsKWinEdges; if (!overridesHidden) { localX = latteView.effects.rect.x; localY = latteView.effects.rect.y; } else { if (plasmoid.location === PlasmaCore.Types.BottomEdge) { localX = latteView.effects.rect.x; localY = root.height - thicknessAutoHidden; } else if (plasmoid.location === PlasmaCore.Types.TopEdge) { localX = latteView.effects.rect.x; localY = 0; } else if (plasmoid.location === PlasmaCore.Types.LeftEdge) { localX = 0; localY = latteView.effects.rect.y; } else if (plasmoid.location === PlasmaCore.Types.RightEdge) { localX = root.width - thicknessAutoHidden; localY = latteView.effects.rect.y; } } if (root.isHorizontal) { tempThickness = overridesHidden ? thicknessAutoHidden : latteView.effects.rect.height; tempLength = latteView.effects.rect.width; } else { tempThickness = overridesHidden ? thicknessAutoHidden : latteView.effects.rect.width; tempLength = latteView.effects.rect.height; } } // console.log("Not updating mask..."); if( maskArea.x !== localX || maskArea.y !== localY || maskLength !== tempLength || maskThickness !== tempThickness) { // console.log("Updating mask..."); var newMaskArea = Qt.rect(-1,-1,0,0); newMaskArea.x = localX; newMaskArea.y = localY; if (isHorizontal) { newMaskArea.width = tempLength; newMaskArea.height = tempThickness; } else { newMaskArea.width = tempThickness; newMaskArea.height = tempLength; } if (!Latte.WindowSystem.compositingActive) { latteView.effects.mask = newMaskArea; } else { if (latteView.behaveAsPlasmaPanel && !root.editMode) { latteView.effects.mask = Qt.rect(0,0,root.width,root.height); } else { latteView.effects.mask = newMaskArea; } } } var validIconSize = (root.iconSize===root.maxIconSize || root.iconSize === root.automaticIconSizeBasedSize); //console.log("reached updating geometry ::: "+dock.maskArea); if(inPublishingState && (normalState || root.editMode)) { var tempGeometry = Qt.rect(latteView.effects.mask.x, latteView.effects.mask.y, latteView.effects.mask.width, latteView.effects.mask.height); //the shadows size must be removed from the maskArea //before updating the localDockGeometry if (!latteView.behaveAsPlasmaPanel || root.editMode) { var fixedThickness = root.editMode ? root.iconSize + root.thickMargins : root.realPanelThickness; if (plasmoid.formFactor === PlasmaCore.Types.Vertical) { tempGeometry.width = fixedThickness; } else { tempGeometry.height = fixedThickness; } if (plasmoid.location === PlasmaCore.Types.BottomEdge) { tempGeometry.y = latteView.height - fixedThickness; } else if (plasmoid.location === PlasmaCore.Types.RightEdge) { tempGeometry.x = latteView.width - fixedThickness; } //set the boundaries for latteView local geometry //qBound = qMax(min, qMin(value, max)). tempGeometry.x = Math.max(0, Math.min(tempGeometry.x, latteView.width)); tempGeometry.y = Math.max(0, Math.min(tempGeometry.y, latteView.height)); tempGeometry.width = Math.min(tempGeometry.width, latteView.width); tempGeometry.height = Math.min(tempGeometry.height, latteView.height); } //console.log("update geometry ::: "+tempGeometry); latteView.localGeometry = tempGeometry; } } Loader{ anchors.fill: parent active: root.debugMode sourceComponent: Item{ anchors.fill:parent Rectangle{ id: windowBackground anchors.fill: parent border.color: "red" border.width: 1 color: "transparent" } Rectangle{ x: latteView ? latteView.effects.mask.x : -1 y: latteView ? latteView.effects.mask.y : -1 height: latteView ? latteView.effects.mask.height : 0 width: latteView ? latteView.effects.mask.width : 0 border.color: "green" border.width: 1 color: "transparent" } } } /***Hiding/Showing Animations*****/ //////////////// Animations - Slide In - Out SequentialAnimation{ id: slidingAnimationAutoHiddenOut ScriptAction{ script: { root.isHalfShown = true; } } PropertyAnimation { target: layoutsContainer property: root.isVertical ? "x" : "y" to: { if (Latte.WindowSystem.compositingActive) { return slidingOutToPos; } else { if ((plasmoid.location===PlasmaCore.Types.LeftEdge)||(plasmoid.location===PlasmaCore.Types.TopEdge)) { return slidingOutToPos + 1; } else { return slidingOutToPos - 1; } } } duration: manager.animationSpeed easing.type: Easing.InQuad } ScriptAction{ script: { latteView.visibility.isHidden = true; } } onStarted: { if (manager.debugMagager) { console.log("hiding animation started..."); } } onStopped: { //! Trying to move the ending part of the signals at the end of editing animation if (!manager.inTempHiding) { manager.updateMaskArea(); } else { if (!editModeVisual.inEditMode) { manager.sendSlidingOutAnimationEnded(); } } latteView.visibility.slideOutFinished(); } function init() { if (!latteView.visibility.blockHiding) { start(); } } } SequentialAnimation{ id: slidingAnimationAutoHiddenIn PauseAnimation{ duration: manager.inTempHiding && animationsEnabled ? 500 : 0 } PropertyAnimation { target: layoutsContainer property: root.isVertical ? "x" : "y" to: 0 duration: manager.animationSpeed easing.type: Easing.OutQuad } ScriptAction{ script: { root.isHalfShown = false; root.inStartup = false; } } onStarted: { latteView.visibility.show(); if (manager.debugMagager) { console.log("showing animation started..."); } } onStopped: { inSlidingIn = false; if (manager.inTempHiding) { manager.inTempHiding = false; updateAutomaticIconSize(); } manager.inTempHiding = false; updateAutomaticIconSize(); if (manager.debugMagager) { console.log("showing animation ended..."); } latteView.visibility.slideInFinished(); if (!Latte.WindowSystem.compositingActive) { //! this is needed in order to update dock absolute geometry correctly in the end updateMaskArea(); } } function init() { // if (!latteView.visibility.blockHiding) inSlidingIn = true; if (slidingAnimationAutoHiddenOut.running) { slidingAnimationAutoHiddenOut.stop(); } latteView.visibility.isHidden = false; updateMaskArea(); start(); } } }