diff --git a/app/layout/genericlayout.cpp b/app/layout/genericlayout.cpp index 07b8ca36..b2429838 100644 --- a/app/layout/genericlayout.cpp +++ b/app/layout/genericlayout.cpp @@ -1,1672 +1,1672 @@ /* * Copyright 2019 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 "genericlayout.h" // local #include "abstractlayout.h" #include "storage.h" #include "../apptypes.h" #include "../lattecorona.h" #include "../screenpool.h" #include "../layouts/importer.h" #include "../layouts/manager.h" #include "../layouts/synchronizer.h" #include "../shortcuts/shortcutstracker.h" #include "../view/view.h" #include "../view/positioner.h" // Qt #include #include // Plasma #include #include #include // KDE #include namespace Latte { namespace Layout { GenericLayout::GenericLayout(QObject *parent, QString layoutFile, QString assignedName) : AbstractLayout (parent, layoutFile, assignedName), m_storage(new Storage(this)) { } GenericLayout::~GenericLayout() { } Type GenericLayout::type() const { return Type::Generic; } void GenericLayout::unloadContainments() { if (!m_corona) { return; } qDebug() << "Layout - " + name() + " : [unloadContainments]" << "containments ::: " << m_containments.size() << " ,latteViews in memory ::: " << m_latteViews.size() << " ,hidden latteViews in memory ::: " << m_waitingLatteViews.size(); for (const auto view : m_latteViews) { view->disconnectSensitiveSignals(); } for (const auto view : m_waitingLatteViews) { view->disconnectSensitiveSignals(); } m_unloadedContainmentsIds.clear(); QList systrays; //!identify systrays and unload them first for (const auto containment : m_containments) { if (Plasma::Applet *parentApplet = qobject_cast(containment->parent())) { systrays.append(containment); } } while (!systrays.isEmpty()) { Plasma::Containment *systray = systrays.at(0); m_unloadedContainmentsIds << QString::number(systray->id()); systrays.removeFirst(); m_containments.removeAll(systray); delete systray; } while (!m_containments.isEmpty()) { Plasma::Containment *containment = m_containments.at(0); m_unloadedContainmentsIds << QString::number(containment->id()); m_containments.removeFirst(); delete containment; } } void GenericLayout::unloadLatteViews() { if (!m_corona) { return; } qDebug() << "Layout - " + name() + " : [unloadLatteViews]" << "containments ::: " << m_containments.size() << " ,latteViews in memory ::: " << m_latteViews.size() << " ,hidden latteViews in memory ::: " << m_waitingLatteViews.size(); //!disconnect signals in order to avoid crashes when the layout is unloading disconnect(this, &GenericLayout::viewsCountChanged, m_corona, &Plasma::Corona::availableScreenRectChanged); disconnect(this, &GenericLayout::viewsCountChanged, m_corona, &Plasma::Corona::availableScreenRegionChanged); disconnect(m_corona->activitiesConsumer(), &KActivities::Consumer::currentActivityChanged, this, &GenericLayout::updateLastUsedActivity); for (const auto view : m_latteViews) { view->disconnectSensitiveSignals(); } for (const auto view : m_waitingLatteViews) { view->disconnectSensitiveSignals(); } qDeleteAll(m_latteViews); qDeleteAll(m_waitingLatteViews); m_latteViews.clear(); m_waitingLatteViews.clear(); } bool GenericLayout::blockAutomaticLatteViewCreation() const { return m_blockAutomaticLatteViewCreation; } void GenericLayout::setBlockAutomaticLatteViewCreation(bool block) { if (m_blockAutomaticLatteViewCreation == block) { return; } m_blockAutomaticLatteViewCreation = block; } bool GenericLayout::isActive() const { if (!m_corona) { return false; } GenericLayout *generic = m_corona->layoutsManager()->synchronizer()->layout(m_layoutName); if (generic) { return true; } else { return false; } } bool GenericLayout::isCurrent() const { if (!m_corona) { return false; } return name() == m_corona->layoutsManager()->currentLayoutName(); } bool GenericLayout::isInternalContainment(Plasma::Applet *applet) const { if (!applet) { return false; } for (const auto containment : m_containments) { Plasma::Applet *parentApplet = qobject_cast(containment->parent()); if (parentApplet && parentApplet == applet) { return true; } } return false; } Plasma::Containment *GenericLayout::internalContainmentOf(Plasma::Applet *applet) const { if (!applet) { return nullptr; } if (isInternalContainment(applet)) { for (const auto containment : m_containments) { Plasma::Applet *parentApplet = qobject_cast(containment->parent()); if (parentApplet && parentApplet == applet) { return containment; } } } return nullptr; } int GenericLayout::viewsCount(int screen) const { if (!m_corona) { return 0; } QScreen *scr = m_corona->screenPool()->screenForId(screen); int views{0}; for (const auto view : m_latteViews) { if (view && view->screen() == scr && !view->containment()->destroyed()) { ++views; } } return views; } int GenericLayout::viewsCount(QScreen *screen) const { if (!m_corona) { return 0; } int views{0}; for (const auto view : m_latteViews) { if (view && view->screen() == screen && !view->containment()->destroyed()) { ++views; } } return views; } int GenericLayout::viewsCount() const { if (!m_corona) { return 0; } int views{0}; for (const auto view : m_latteViews) { if (view && view->containment() && !view->containment()->destroyed()) { ++views; } } return views; } QList GenericLayout::qmlFreeEdges(int screen) const { if (!m_corona) { const QList emptyEdges; return emptyEdges; } const auto edges = freeEdges(screen); QList edgesInt; for (const Plasma::Types::Location &edge : edges) { edgesInt.append(static_cast(edge)); } return edgesInt; } QList GenericLayout::freeEdges(QScreen *scr) const { using Plasma::Types; QList edges{Types::BottomEdge, Types::LeftEdge, Types::TopEdge, Types::RightEdge}; if (!m_corona) { return edges; } for (const auto view : m_latteViews) { if (view && view->positioner()->currentScreenName() == scr->name()) { edges.removeOne(view->location()); } } return edges; } QList GenericLayout::freeEdges(int screen) const { using Plasma::Types; QList edges{Types::BottomEdge, Types::LeftEdge, Types::TopEdge, Types::RightEdge}; if (!m_corona) { return edges; } QScreen *scr = m_corona->screenPool()->screenForId(screen); for (const auto view : m_latteViews) { if (view && scr && view->positioner()->currentScreenName() == scr->name()) { edges.removeOne(view->location()); } } return edges; } int GenericLayout::viewsWithTasks() const { if (!m_corona) { return 0; } int result = 0; for (const auto view : m_latteViews) { - if (view->tasksPresent()) { + if (view->extendedInterface()->hasLatteTasks() || view->extendedInterface()->hasPlasmaTasks()) { result++; } } return result; } QStringList GenericLayout::unloadedContainmentsIds() { return m_unloadedContainmentsIds; } Latte::Corona *GenericLayout::corona() { return m_corona; } Types::ViewType GenericLayout::latteViewType(uint containmentId) const { for (const auto view : m_latteViews) { if (view->containment() && view->containment()->id() == containmentId) { return view->type(); } } return Types::DockView; } Latte::View *GenericLayout::highestPriorityView() { QList views = sortedLatteViews(); return (views.count() > 0 ? views[0] : nullptr); } Latte::View *GenericLayout::lastConfigViewFor() { return m_lastConfigViewFor; } void GenericLayout::setLastConfigViewFor(Latte::View *view) { if (m_lastConfigViewFor == view) { return; } m_lastConfigViewFor = view; emit lastConfigViewForChanged(view); } Latte::View *GenericLayout::viewForContainment(Plasma::Containment *containment) { if (m_containments.contains(containment) && m_latteViews.contains(containment)) { return m_latteViews[containment]; } return nullptr; } QList GenericLayout::latteViews() { return m_latteViews.values(); } QList GenericLayout::sortedLatteViews(QList views) { QList sortedViews = views.isEmpty() ? latteViews() : views; qDebug() << " -------- "; for (int i = 0; i < sortedViews.count(); ++i) { qDebug() << i << ". " << sortedViews[i]->screen()->name() << " - " << sortedViews[i]->location(); } //! sort the views based on screens and edges priorities //! views on primary screen have higher priority and //! for views in the same screen the priority goes to //! Bottom,Left,Top,Right for (int i = 0; i < sortedViews.size(); ++i) { for (int j = 0; j < sortedViews.size() - i - 1; ++j) { if (viewAtLowerScreenPriority(sortedViews[j], sortedViews[j + 1]) || (sortedViews[j]->screen() == sortedViews[j + 1]->screen() && viewAtLowerEdgePriority(sortedViews[j], sortedViews[j + 1]))) { Latte::View *temp = sortedViews[j + 1]; sortedViews[j + 1] = sortedViews[j]; sortedViews[j] = temp; } } } Latte::View *highestPriorityView{nullptr}; for (int i = 0; i < sortedViews.size(); ++i) { if (sortedViews[i]->isPreferredForShortcuts()) { highestPriorityView = sortedViews[i]; sortedViews.removeAt(i); break; } } if (highestPriorityView) { sortedViews.prepend(highestPriorityView); } qDebug() << " -------- sorted -----"; for (int i = 0; i < sortedViews.count(); ++i) { qDebug() << i << ". " << sortedViews[i]->isPreferredForShortcuts() << " - " << sortedViews[i]->screen()->name() << " - " << sortedViews[i]->location(); } return sortedViews; } bool GenericLayout::viewAtLowerScreenPriority(Latte::View *test, Latte::View *base) { if (!base || ! test) { return true; } if (base->screen() == test->screen()) { return false; } else if (base->screen() != qGuiApp->primaryScreen() && test->screen() == qGuiApp->primaryScreen()) { return false; } else if (base->screen() == qGuiApp->primaryScreen() && test->screen() != qGuiApp->primaryScreen()) { return true; } else { int basePriority = -1; int testPriority = -1; for (int i = 0; i < qGuiApp->screens().count(); ++i) { if (base->screen() == qGuiApp->screens()[i]) { basePriority = i; } if (test->screen() == qGuiApp->screens()[i]) { testPriority = i; } } if (testPriority <= basePriority) { return true; } else { return false; } } qDebug() << "viewAtLowerScreenPriority : shouldn't had reached here..."; return false; } bool GenericLayout::viewAtLowerEdgePriority(Latte::View *test, Latte::View *base) { if (!base || ! test) { return true; } QList edges{Plasma::Types::RightEdge, Plasma::Types::TopEdge, Plasma::Types::LeftEdge, Plasma::Types::BottomEdge}; int testPriority = -1; int basePriority = -1; for (int i = 0; i < edges.count(); ++i) { if (edges[i] == base->location()) { basePriority = i; } if (edges[i] == test->location()) { testPriority = i; } } if (testPriority < basePriority) { return true; } else { return false; } } bool GenericLayout::viewDataAtLowerScreenPriority(const ViewData &test, const ViewData &base) const { if (test.onPrimary && base.onPrimary) { return false; } else if (!base.onPrimary && test.onPrimary) { return false; } else if (base.onPrimary && !test.onPrimary) { return true; } else { return test.screenId <= base.screenId; } } bool GenericLayout::viewDataAtLowerStatePriority(const ViewData &test, const ViewData &base) const { if (test.active == base.active) { return false; } else if (!base.active && test.active) { return false; } else if (base.active && !test.active) { return true; } return false; } bool GenericLayout::viewDataAtLowerEdgePriority(const ViewData &test, const ViewData &base) const { QList edges{Plasma::Types::RightEdge, Plasma::Types::TopEdge, Plasma::Types::LeftEdge, Plasma::Types::BottomEdge}; int testPriority = -1; int basePriority = -1; for (int i = 0; i < edges.count(); ++i) { if (edges[i] == base.location) { basePriority = i; } if (edges[i] == test.location) { testPriority = i; } } if (testPriority < basePriority) { return true; } else { return false; } } QList GenericLayout::sortedViewsData(const QList &viewsData) { QList sortedData = viewsData; //! sort the views based on screens and edges priorities //! views on primary screen have higher priority and //! for views in the same screen the priority goes to //! Bottom,Left,Top,Right for (int i = 0; i < sortedData.size(); ++i) { for (int j = 0; j < sortedData.size() - i - 1; ++j) { if (viewDataAtLowerStatePriority(sortedData[j], sortedData[j + 1]) || viewDataAtLowerScreenPriority(sortedData[j], sortedData[j + 1]) || (!viewDataAtLowerScreenPriority(sortedData[j], sortedData[j + 1]) && viewDataAtLowerEdgePriority(sortedData[j], sortedData[j + 1])) ) { ViewData temp = sortedData[j + 1]; sortedData[j + 1] = sortedData[j]; sortedData[j] = temp; } } } return sortedData; } const QList *GenericLayout::containments() { return &m_containments; } QList GenericLayout::viewsWithPlasmaShortcuts() { QList views; if (!m_corona) { return views; } QList appletsWithShortcuts = m_corona->globalShortcuts()->shortcutsTracker()->appletsWithPlasmaShortcuts(); for (const auto &appletId : appletsWithShortcuts) { for (const auto view : m_latteViews) { bool found{false}; for (const auto applet : view->containment()->applets()) { if (appletId == applet->id()) { if (!views.contains(view)) { views.append(view); found = true; break; } } } if (found) { break; } } } return views; } //! Containments Actions void GenericLayout::addContainment(Plasma::Containment *containment) { if (!containment || m_containments.contains(containment)) { return; } bool containmentInLayout{false}; if (m_corona->layoutsManager()->memoryUsage() == MemoryUsage::SingleLayout) { m_containments.append(containment); containmentInLayout = true; } else if (m_corona->layoutsManager()->memoryUsage() == MemoryUsage::MultipleLayouts) { QString layoutId = containment->config().readEntry("layoutId", QString()); if (!layoutId.isEmpty() && (layoutId == m_layoutName)) { m_containments.append(containment); containmentInLayout = true; } } if (containmentInLayout) { if (!blockAutomaticLatteViewCreation()) { addView(containment); } else { qDebug() << "delaying LatteView creation for containment :: " << containment->id(); } connect(containment, &QObject::destroyed, this, &GenericLayout::containmentDestroyed); } } void GenericLayout::appletCreated(Plasma::Applet *applet) { //! In Multiple Layout the orphaned systrays must be assigned to layouts //! when the user adds them KConfigGroup appletSettings = applet->containment()->config().group("Applets").group(QString::number(applet->id())).group("Configuration"); int systrayId = appletSettings.readEntry("SystrayContainmentId", -1); if (systrayId != -1) { uint sId = (uint)systrayId; for (const auto containment : m_corona->containments()) { if (containment->id() == sId) { containment->config().writeEntry("layoutId", m_layoutName); } addContainment(containment); } } } void GenericLayout::containmentDestroyed(QObject *cont) { if (!m_corona) { return; } Plasma::Containment *containment = static_cast(cont); if (containment) { int containmentIndex = m_containments.indexOf(containment); if (containmentIndex >= 0) { m_containments.removeAt(containmentIndex); } qDebug() << "Layout " << name() << " :: containment destroyed!!!!"; auto view = m_latteViews.take(containment); if (!view) { view = m_waitingLatteViews.take(containment); } if (view) { view->disconnectSensitiveSignals(); view->deleteLater(); emit viewEdgeChanged(); emit viewsCountChanged(); } } } void GenericLayout::destroyedChanged(bool destroyed) { if (!m_corona) { return; } qDebug() << "dock containment destroyed changed!!!!"; Plasma::Containment *sender = qobject_cast(QObject::sender()); if (!sender) { return; } if (destroyed) { m_waitingLatteViews[sender] = m_latteViews.take(static_cast(sender)); } else { m_latteViews[sender] = m_waitingLatteViews.take(static_cast(sender)); } emit viewEdgeChanged(); emit viewsCountChanged(); } void GenericLayout::renameLayout(QString newName) { if (!m_corona || m_corona->layoutsManager()->memoryUsage() != MemoryUsage::MultipleLayouts) { return; } if (m_layoutFile != Layouts::Importer::layoutFilePath(newName)) { setFile(Layouts::Importer::layoutFilePath(newName)); } setName(newName); for (const auto containment : m_containments) { qDebug() << "Cont ID :: " << containment->id(); containment->config().writeEntry("layoutId", m_layoutName); } } void GenericLayout::addNewView() { if (!m_corona) { return; } m_corona->addViewForLayout(name()); emit viewEdgeChanged(); } void GenericLayout::addView(Plasma::Containment *containment, bool forceOnPrimary, int explicitScreen, Layout::ViewsMap *occupied) { qDebug() << "Layout :::: " << m_layoutName << " ::: addView was called... m_containments :: " << m_containments.size(); if (!containment || !m_corona || !containment->kPackage().isValid()) { qWarning() << "the requested containment plugin can not be located or loaded"; return; } qDebug() << "step 1..."; if (!m_storage->isLatteContainment(containment)) return; qDebug() << "step 2..."; for (auto *dock : m_latteViews) { if (dock->containment() == containment) return; } qDebug() << "step 3..."; QScreen *nextScreen{qGuiApp->primaryScreen()}; bool onPrimary = containment->config().readEntry("onPrimary", true); int id = containment->screen(); if (id == -1 && explicitScreen == -1) { id = containment->lastScreen(); } if (onPrimary) { id = m_corona->screenPool()->primaryScreenId(); } else if (explicitScreen > -1) { id = explicitScreen; } Plasma::Types::Location edge = containment->location(); QString connector = m_corona->screenPool()->hasId(id) ? m_corona->screenPool()->connector(id) : ""; qDebug() << "Adding view - containment id:" << containment->id() << " ,screen :" << id << " - " << connector << " ,onprimary:" << onPrimary << " - " << " edge:" << edge << " ,screenName:" << qGuiApp->primaryScreen()->name() << " ,forceOnPrimary:" << forceOnPrimary; if (occupied && m_corona->screenPool()->hasId(id) && (*occupied).contains(connector) && (*occupied)[connector].contains(edge)) { qDebug() << "Rejected : adding view because the edge is already occupied by a higher priority view ! : " << (*occupied)[connector][edge]; return; } if (id >= 0 && !onPrimary && !forceOnPrimary) { qDebug() << "Add view - connector : " << connector; bool found{false}; if (m_corona->screenPool()->hasId(id)) { for (const auto scr : qGuiApp->screens()) { if (scr && scr->name() == connector) { found = true; nextScreen = scr; break; } } } if (!found) { qDebug() << "Rejected : adding explicit view, screen not available ! : " << connector; return; } //! explicit dock can not be added at explicit screen when that screen is the same with //! primary screen and that edge is already occupied by a primary dock if (nextScreen == qGuiApp->primaryScreen() && primaryDockOccupyEdge(containment->location())) { qDebug() << "Rejected : adding explicit view, primary dock occupies edge at screen ! : " << connector; return; } } if (id >= 0 && onPrimary) { qDebug() << "add dock - connector : " << connector; for (const Plasma::Containment *testContainment : m_latteViews.keys()) { int testScreenId = testContainment->screen(); if (testScreenId == -1) { testScreenId = testContainment->lastScreen(); } bool testOnPrimary = testContainment->config().readEntry("onPrimary", true); Plasma::Types::Location testLocation = static_cast((int)testContainment->config().readEntry("location", (int)Plasma::Types::BottomEdge)); if (!testOnPrimary && m_corona->screenPool()->primaryScreenId() == testScreenId && testLocation == containment->location()) { qDebug() << "Rejected explicit latteView and removing it in order add an onPrimary with higher priority at screen: " << connector; auto viewToDelete = m_latteViews.take(testContainment); viewToDelete->disconnectSensitiveSignals(); viewToDelete->deleteLater(); } } } qDebug() << "Adding view passed ALL checks" << " ,onPrimary:" << onPrimary << " ,screen:" << nextScreen->name() << " !!!"; //! it is used to set the correct flag during the creation //! of the window... This of course is also used during //! recreations of the window between different visibility modes auto mode = static_cast(containment->config().readEntry("visibility", static_cast(Types::DodgeActive))); bool byPassWM{false}; if (mode == Types::AlwaysVisible || mode == Types::WindowsGoBelow || mode == Types::WindowsCanCover || mode == Types::WindowsAlwaysCover) { byPassWM = false; } else { byPassWM = containment->config().readEntry("byPassWM", false); } auto latteView = new Latte::View(m_corona, nextScreen, byPassWM); latteView->init(containment); latteView->setContainment(containment); //! force this special dock case to become primary //! even though it isnt if (forceOnPrimary) { qDebug() << "Enforcing onPrimary:true as requested for LatteView..."; latteView->setOnPrimary(true); } latteView->setLayout(this); //! Qt 5.9 creates a crash for this in wayland, that is why the check is used //! but on the other hand we need this for copy to work correctly and show //! the copied dock under X11 //if (!KWindowSystem::isPlatformWayland()) { latteView->show(); //} m_latteViews[containment] = latteView; emit viewsCountChanged(); } void GenericLayout::toggleHiddenState(QString screenName, Plasma::Types::Location edge) { if (!m_corona) { return; } QString validScreenName = qGuiApp->primaryScreen()->name(); if (!screenName.isEmpty()) { validScreenName = screenName; } int viewsOnEdge{0}; for(const auto view : latteViews()) { if (view->positioner()->currentScreenName() == validScreenName && view->location() == edge) { viewsOnEdge++; } } if (viewsOnEdge >= 1) { for(const auto view : latteViews()) { if (view->positioner()->currentScreenName() == validScreenName && view->location() == edge) { if (viewsOnEdge == 1 || (viewsOnEdge >1 && view->visibility() && view->visibility()->mode() == Latte::Types::SideBar)) { view->visibility()->toggleHiddenState(); return; } } } } } bool GenericLayout::initToCorona(Latte::Corona *corona) { if (m_corona) { return false; } m_corona = corona; for (const auto containment : m_corona->containments()) { if (m_corona->layoutsManager()->memoryUsage() == MemoryUsage::SingleLayout) { addContainment(containment); } else if (m_corona->layoutsManager()->memoryUsage() == MemoryUsage::MultipleLayouts) { QString layoutId = containment->config().readEntry("layoutId", QString()); if (!layoutId.isEmpty() && (layoutId == m_layoutName)) { addContainment(containment); } } } qDebug() << "Layout ::::: " << name() << " added containments ::: " << m_containments.size(); updateLastUsedActivity(); //! signals connect(m_corona->activitiesConsumer(), &KActivities::Consumer::currentActivityChanged, this, &GenericLayout::updateLastUsedActivity); connect(m_corona, &Plasma::Corona::containmentAdded, this, &GenericLayout::addContainment); //!connect signals after adding the containment connect(this, &GenericLayout::viewsCountChanged, m_corona, &Plasma::Corona::availableScreenRectChanged); connect(this, &GenericLayout::viewsCountChanged, m_corona, &Plasma::Corona::availableScreenRegionChanged); emit viewsCountChanged(); return true; } void GenericLayout::updateLastUsedActivity() { if (!m_corona) { return; } if (!m_lastUsedActivity.isEmpty() && !m_corona->layoutsManager()->synchronizer()->activities().contains(m_lastUsedActivity)) { clearLastUsedActivity(); } QString currentId = m_corona->activitiesConsumer()->currentActivity(); QStringList appliedActivitiesIds = appliedActivities(); if (m_lastUsedActivity != currentId && appliedActivitiesIds.contains(currentId)) { m_lastUsedActivity = currentId; emit lastUsedActivityChanged(); } } void GenericLayout::assignToLayout(Latte::View *latteView, QList containments) { if (!m_corona) { return; } if (latteView) { m_latteViews[latteView->containment()] = latteView; m_containments << containments; for (const auto containment : containments) { containment->config().writeEntry("layoutId", name()); if (latteView->containment() != containment) { //! assign signals only to systrays //! the View::setLayout() is responsible for the View::Containment signals connect(containment, &QObject::destroyed, this, &GenericLayout::containmentDestroyed); connect(containment, &Plasma::Applet::destroyedChanged, this, &GenericLayout::destroyedChanged); connect(containment, &Plasma::Containment::appletCreated, this, &GenericLayout::appletCreated); } } latteView->setLayout(this); emit viewsCountChanged(); } //! sync the original layout file for integrity if (m_corona && m_corona->layoutsManager()->memoryUsage() == MemoryUsage::MultipleLayouts) { m_storage->syncToLayoutFile(false); } } QList GenericLayout::unassignFromLayout(Latte::View *latteView) { QList containments; if (!m_corona) { return containments; } containments << latteView->containment(); for (const auto containment : m_containments) { Plasma::Applet *parentApplet = qobject_cast(containment->parent()); //! add systrays from that latteView if (parentApplet && parentApplet->containment() && parentApplet->containment() == latteView->containment()) { containments << containment; //! unassign signals only to systrays //! the View::setLayout() is responsible for the View::Containment signals disconnect(containment, &QObject::destroyed, this, &GenericLayout::containmentDestroyed); disconnect(containment, &Plasma::Applet::destroyedChanged, this, &GenericLayout::destroyedChanged); disconnect(containment, &Plasma::Containment::appletCreated, this, &GenericLayout::appletCreated); } } for (const auto containment : containments) { m_containments.removeAll(containment); } if (containments.size() > 0) { m_latteViews.remove(latteView->containment()); } //! sync the original layout file for integrity if (m_corona && m_corona->layoutsManager()->memoryUsage() == MemoryUsage::MultipleLayouts) { m_storage->syncToLayoutFile(false); } return containments; } void GenericLayout::recreateView(Plasma::Containment *containment, bool delayed) { if (!m_corona || m_viewsToRecreate.contains(containment) || !containment || !m_latteViews.contains(containment)) { return; } int delay = delayed ? 350 : 0; m_viewsToRecreate << containment; //! give the time to config window to close itself first and then recreate the dock //! step:1 remove the latteview QTimer::singleShot(delay, [this, containment]() { auto view = m_latteViews[containment]; view->disconnectSensitiveSignals(); //! step:2 add the new latteview connect(view, &QObject::destroyed, this, [this, containment]() { auto view = m_latteViews.take(containment); QTimer::singleShot(250, this, [this, containment]() { if (!m_latteViews.contains(containment)) { qDebug() << "recreate - step 2: adding dock for containment:" << containment->id(); addView(containment); m_viewsToRecreate.removeAll(containment); } }); }); view->deleteLater(); }); } bool GenericLayout::latteViewExists(Plasma::Containment *containment) { if (!m_corona) { return false; } return m_latteViews.keys().contains(containment); } QList GenericLayout::availableEdgesForView(QScreen *scr, Latte::View *forView) const { using Plasma::Types; QList edges{Types::BottomEdge, Types::LeftEdge, Types::TopEdge, Types::RightEdge}; if (!m_corona) { return edges; } for (const auto view : m_latteViews) { //! make sure that availabe edges takes into account only views that should be excluded, //! this is why the forView should not be excluded if (view && view != forView && view->positioner()->currentScreenName() == scr->name()) { edges.removeOne(view->location()); } } return edges; } bool GenericLayout::explicitDockOccupyEdge(int screen, Plasma::Types::Location location) const { if (!m_corona) { return false; } for (const auto containment : m_containments) { if (m_storage->isLatteContainment(containment)) { bool onPrimary = containment->config().readEntry("onPrimary", true); int id = containment->lastScreen(); Plasma::Types::Location contLocation = containment->location(); if (!onPrimary && id == screen && contLocation == location) { return true; } } } return false; } bool GenericLayout::primaryDockOccupyEdge(Plasma::Types::Location location) const { if (!m_corona) { return false; } for (const auto containment : m_containments) { if (m_storage->isLatteContainment(containment)) { bool onPrimary{false}; if (m_latteViews.contains(containment)) { onPrimary = m_latteViews[containment]->onPrimary(); } else { onPrimary = containment->config().readEntry("onPrimary", true); } Plasma::Types::Location contLocation = containment->location(); if (onPrimary && contLocation == location) { return true; } } } return false; } bool GenericLayout::mapContainsId(const Layout::ViewsMap *map, uint viewId) const { for(const auto &scr : map->keys()) { for(const auto &edge : (*map)[scr].keys()) { if ((*map)[scr][edge].contains(viewId)) { return true; } } } return false; } //! screen name, location, containmentId Layout::ViewsMap GenericLayout::validViewsMap(Layout::ViewsMap *occupiedMap) { //! Shared Views occupy the screen edge first //! Primary Views occupy the screen edge if Shared Views do not exist already on that screen edge //! Explicity Views occypy the screen edge if Shared Views and Primary Views do not exist already on that screen edge Layout::ViewsMap map; if (!m_corona) { return map; } if (occupiedMap != nullptr) { map = (*occupiedMap); } QString prmScreenName = qGuiApp->primaryScreen()->name(); //! first step: primary docks must be placed in primary screen free edges for (const auto containment : m_containments) { if (m_storage->isLatteContainment(containment)) { int screenId = 0; //! valid screen id if (latteViewExists(containment)) { screenId = m_latteViews[containment]->positioner()->currentScreenId(); } else { screenId = containment->screen(); if (screenId == -1) { screenId = containment->lastScreen(); } } bool onPrimary{true}; //! valid onPrimary flag if (latteViewExists(containment)) { onPrimary = m_latteViews[containment]->onPrimary(); } else { onPrimary = containment->config().readEntry("onPrimary", true); } //! valid location Plasma::Types::Location location = containment->location(); if (onPrimary && (!occupiedMap || !(*occupiedMap)[prmScreenName].contains(location))) { map[prmScreenName][location] << containment->id(); } } } Layout::ViewsMap explicitMap; //! second step: explicit docks must be placed in their screens if the screen edge is free for (const auto containment : m_containments) { if (m_storage->isLatteContainment(containment)) { int screenId = 0; //! valid screen id if (latteViewExists(containment)) { screenId = m_latteViews[containment]->positioner()->currentScreenId(); } else { screenId = containment->screen(); if (screenId == -1) { screenId = containment->lastScreen(); } } bool onPrimary{true}; //! valid onPrimary flag if (latteViewExists(containment)) { onPrimary = m_latteViews[containment]->onPrimary(); } else { onPrimary = containment->config().readEntry("onPrimary", true); } //! valid location Plasma::Types::Location location = containment->location(); if (!onPrimary) { QString expScreenName = m_corona->screenPool()->connector(screenId); if (m_corona->screenPool()->screenExists(screenId) && !map[expScreenName].contains(location)) { explicitMap[expScreenName][location] << containment->id(); } } } } for(const QString &expScreenName : explicitMap.keys()) { for(const Plasma::Types::Location &expLocation : explicitMap[expScreenName].keys()) { map[expScreenName][expLocation] << explicitMap[expScreenName][expLocation]; } } return map; } //! the central functions that updates loading/unloading latteviews //! concerning screen changed (for multi-screen setups mainly) void GenericLayout::syncLatteViewsToScreens(Layout::ViewsMap *occupiedMap) { if (!m_corona) { return; } qDebug() << "START of SyncLatteViewsToScreens ...."; qDebug() << "LAYOUT ::: " << name(); qDebug() << "screen count changed -+-+ " << qGuiApp->screens().size(); Layout::ViewsMap viewsMap = validViewsMap(occupiedMap); if (occupiedMap != nullptr) { qDebug() << "Occupied map used :: " << *occupiedMap; } QString prmScreenName = qGuiApp->primaryScreen()->name(); qDebug() << "PRIMARY SCREEN :: " << prmScreenName; qDebug() << "LATTEVIEWS MAP :: " << viewsMap; //! add views for (const auto containment : m_containments) { int screenId = containment->screen(); if (screenId == -1) { screenId = containment->lastScreen(); } if (!latteViewExists(containment) && mapContainsId(&viewsMap, containment->id())) { qDebug() << "syncLatteViewsToScreens: view must be added... for containment:" << containment->id() << " at screen:" << m_corona->screenPool()->connector(screenId); addView(containment); } } //! remove views QList viewsToDelete; for (auto view : m_latteViews) { auto containment = view->containment(); if (containment && !mapContainsId(&viewsMap, containment->id())) { viewsToDelete << containment; } } while(!viewsToDelete.isEmpty()) { auto containment = viewsToDelete.takeFirst(); auto view = m_latteViews.take(containment); qDebug() << "syncLatteViewsToScreens: view must be deleted... for containment:" << containment->id() << " at screen:" << view->positioner()->currentScreenName(); view->disconnectSensitiveSignals(); view->deleteLater(); } //! reconsider views for (const auto view : m_latteViews) { if (view->containment() && mapContainsId(&viewsMap, view->containment()->id())) { //! if the dock will not be deleted its a very good point to reconsider //! if the screen in which is running is the correct one qDebug() << "syncLatteViewsToScreens: view must consider its screen... for containment:" << view->containment()->id() << " at screen:" << view->positioner()->currentScreenName(); view->reconsiderScreen(); } } qDebug() << "end of, syncLatteViewsToScreens ...."; } QList GenericLayout::containmentSystrays(Plasma::Containment *containment) const { QList trays; if (m_storage->isLatteContainment(containment)) { auto applets = containment->config().group("Applets"); for (const auto &applet : applets.groupList()) { KConfigGroup appletSettings = applets.group(applet).group("Configuration"); int tSysId = appletSettings.readEntry("SystrayContainmentId", -1); if (tSysId != -1) { trays << tSysId; } } } return trays; } QString GenericLayout::reportHtml(const ScreenPool *screenPool) { //qDebug() << "DBUS CALL ::: " << identifier << " - " << value; auto locationText = [this](const int &location) { switch (location) { case Plasma::Types::BottomEdge: return i18nc("bottom edge", "Bottom"); case Plasma::Types::LeftEdge: return i18nc("left edge", "Left"); case Plasma::Types::TopEdge: return i18nc("top edge", "Top"); case Plasma::Types::RightEdge: return i18nc("right edge", "Right"); } return QString(); }; auto idsLineStr = [this](const QList list) { QString line; for(int i=0; i"; if (activeViews == 0) { report += " -- "; } else { report += "" + QString::number(activeViews) +""; } report += ""; //! latte containment ids, systrays QHash> systrays; QList assignedSystrays; QList orphanSystrays; if (isActive()) { //! organize systrays for (const auto containment : m_containments) { QList trays = containmentSystrays(containment); if (trays.count() > 0) { systrays[containment->id()] = trays; assignedSystrays << trays; } } //! orphan systrays for (const auto containment : m_containments) { if (!m_storage->isLatteContainment(containment) && !assignedSystrays.contains(containment->id())) { orphanSystrays << containment->id(); } } } else { m_storage->systraysInformation(systrays, assignedSystrays, orphanSystrays); } report += ""; report += "" + i18n("Orphan Systrays:") +""; if (orphanSystrays.count() == 0) { report += " -- "; } else { report += "" + idsLineStr(orphanSystrays) +""; } report += ""; report += ""; report += ""; report += "" + "" + "" + "" + ""; report += ""; QList viewsData; if (isActive()) { //! collect viewData results for (const auto containment : m_containments) { if (m_storage->isLatteContainment(containment)) { ViewData vData; vData.id = containment->id(); vData.active = latteViewExists(containment); vData.location = containment->location(); //! onPrimary / Screen Id int screenId = -1; bool onPrimary = true; if (latteViewExists(containment)) { screenId = m_latteViews[containment]->positioner()->currentScreenId(); onPrimary = m_latteViews[containment]->onPrimary(); } else { screenId = containment->screen(); onPrimary = containment->config().readEntry("onPrimary", true); if (screenId == -1) { screenId = containment->lastScreen(); } } vData.onPrimary = onPrimary; vData.screenId = screenId; vData.systrays = containmentSystrays(containment); viewsData << vData; } } } else { viewsData = m_storage->viewsData(systrays); } //! sort views data viewsData = sortedViewsData(viewsData); QStringList unknownScreens; //! print viewData results for (int i=0; i"; } report += ""; //! screen QString screenStr = "[" + i18nc("primary screen","Primary") + "]"; if (viewsData[i].active && viewsData[i].onPrimary) { screenStr = "" + screenStr + ""; } if (!viewsData[i].onPrimary) { if (!screenPool->hasId(viewsData[i].screenId)) { screenStr = "[" + QString::number(viewsData[i].screenId) + "]"; unknownScreens << QString("[" + QString::number(viewsData[i].screenId) + "]"); } else { screenStr = screenPool->connector(viewsData[i].screenId); } } if(viewsData[i].active) { screenStr = "" + screenStr + ""; } report += ""; //! edge QString edgeStr = locationText(viewsData[i].location); if(viewsData[i].active) { edgeStr = "" + edgeStr + ""; } report += "" ; //! active QString activeStr = " -- "; if(viewsData[i].active) { activeStr = "" + i18n("Yes") + ""; } report += "" ; //! systrays QString systraysStr = " -- "; if (viewsData[i].systrays.count() > 0) { systraysStr = idsLineStr(viewsData[i].systrays); } if(viewsData[i].active) { systraysStr = "" + systraysStr + ""; } report += ""; report += ""; } report += "
" + i18nc("view id","ID") + "" + i18n("Screen") + "" + i18nc("screen edge","Edge") + "" + i18nc("active dock/panel","Active") + "" + i18n("Systrays") + "

" + idStr + "" + screenStr + "" + edgeStr + "" + activeStr + "" + systraysStr + "
"; report += "

"; QStringList errorsList; bool broken = m_storage->layoutIsBroken(errorsList); if (!broken && unknownScreens.count() == 0) { report += "" + i18n("No errors were identified for this layout...") + "
"; } else { report += "" + i18n("Errors:") + "
"; } if (broken){ for(int i=0; i
"; } } if (unknownScreens.count() > 0) { report += "" + i18n("Unknown screens: ") + unknownScreens.join(", ") + "
"; } return report; } QList GenericLayout::viewsScreens() { QList screens; if (isActive()) { for (const auto containment : m_containments) { if (m_storage->isLatteContainment(containment)) { int screenId = -1; //! valid screen id if (latteViewExists(containment)) { screenId = m_latteViews[containment]->positioner()->currentScreenId(); } else { screenId = containment->screen(); if (screenId == -1) { screenId = containment->lastScreen(); } } if (screenId!=-1 &&!screens.contains(screenId)) { screens << screenId; } } } return screens; } else { return m_storage->viewsScreens(); } } //! STORAGE bool GenericLayout::isWritable() const { return m_storage->isWritable(); } void GenericLayout::lock() { m_storage->lock(); } void GenericLayout::unlock() { m_storage->unlock(); } void GenericLayout::syncToLayoutFile(bool removeLayoutId) { m_storage->syncToLayoutFile(removeLayoutId); } void GenericLayout::copyView(Plasma::Containment *containment) { m_storage->copyView(containment); emit viewEdgeChanged(); } void GenericLayout::importToCorona() { m_storage->importToCorona(); } bool GenericLayout::layoutIsBroken() const { QStringList errors; return m_storage->layoutIsBroken(errors); } } } diff --git a/app/shortcuts/globalshortcuts.cpp b/app/shortcuts/globalshortcuts.cpp index 31f67281..18623205 100644 --- a/app/shortcuts/globalshortcuts.cpp +++ b/app/shortcuts/globalshortcuts.cpp @@ -1,593 +1,593 @@ /* * 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 "globalshortcuts.h" // local #include "modifiertracker.h" #include "shortcutstracker.h" #include "../lattecorona.h" #include "../layout/centrallayout.h" #include "../layouts/manager.h" #include "../layouts/synchronizer.h" #include "../settings/dialogs/settingsdialog.h" #include "../settings/universalsettings.h" #include "../view/containmentinterface.h" #include "../view/view.h" // C++ #include // Qt #include #include #include #include #include // KDE #include #include #include #include // Plasma #include #include #define SHORTCUTBLOCKHIDINGTYPE "globalshortcuts::blockHiding()" namespace Latte { const int APPLETEXECUTIONDELAY = 400; GlobalShortcuts::GlobalShortcuts(QObject *parent) : QObject(parent) { m_corona = qobject_cast(parent); m_modifierTracker = new ShortcutsPart::ModifierTracker(this); m_shortcutsTracker = new ShortcutsPart::ShortcutsTracker(this); if (m_corona) { init(); } m_hideViewsTimer.setSingleShot(true); if (QX11Info::isPlatformX11()) { //in X11 the timer is a poller that checks to see if the modifier keys //from user global shortcut have been released m_hideViewsTimer.setInterval(300); } else { //on wayland in acting just as simple timer that hides the view afterwards m_hideViewsTimer.setInterval(2500); } connect(&m_hideViewsTimer, &QTimer::timeout, this, &GlobalShortcuts::hideViewsTimerSlot); } GlobalShortcuts::~GlobalShortcuts() { if (m_modifierTracker) { m_modifierTracker->deleteLater(); } if (m_shortcutsTracker) { m_shortcutsTracker->deleteLater(); } } void GlobalShortcuts::init() { KActionCollection *generalActions = new KActionCollection(m_corona); //show-hide the main view in the primary screen QAction *showAction = generalActions->addAction(QStringLiteral("show latte view")); showAction->setText(i18n("Show Latte Dock/Panel")); showAction->setShortcut(QKeySequence(Qt::META + '`')); KGlobalAccel::setGlobalShortcut(showAction, QKeySequence(Qt::META + '`')); connect(showAction, &QAction::triggered, this, [this]() { showViews(); }); //show-cycle between Latte settings windows QAction *settingsAction = generalActions->addAction(QStringLiteral("show view settings")); settingsAction->setText(i18n("Cycle Through Dock/Panel Settings Windows")); KGlobalAccel::setGlobalShortcut(settingsAction, QKeySequence(Qt::META + Qt::Key_A)); connect(settingsAction, &QAction::triggered, this, [this] { m_modifierTracker->cancelMetaPressed(); showSettings(); }); //show the layouts editor QAction *layoutsAction = generalActions->addAction(QStringLiteral("show latte global settings")); layoutsAction->setText(i18n("Show Latte Global Settings")); layoutsAction->setShortcut(QKeySequence(Qt::META + Qt::Key_W)); KGlobalAccel::setGlobalShortcut(layoutsAction, QKeySequence(Qt::META + Qt::Key_W)); connect(layoutsAction, &QAction::triggered, this, [this]() { m_modifierTracker->cancelMetaPressed(); m_corona->layoutsManager()->showLatteSettingsDialog(Settings::Dialog::PreferencesPage, true); }); KActionCollection *taskbarActions = new KActionCollection(m_corona); //activate actions [1-9] for (int i = 1; i < 10; ++i) { const int entryNumber = i; const Qt::Key key = static_cast(Qt::Key_0 + i); QAction *action = taskbarActions->addAction(QStringLiteral("activate entry %1").arg(QString::number(entryNumber))); action->setText(i18n("Activate Entry %1", entryNumber)); action->setShortcut(QKeySequence(Qt::META + key)); KGlobalAccel::setGlobalShortcut(action, QKeySequence(Qt::META + key)); connect(action, &QAction::triggered, this, [this, i] { // qDebug() << "meta action..."; m_modifierTracker->cancelMetaPressed(); activateEntry(i, static_cast(Qt::META)); }); } //! Array that is used to register correctly actions for task index>=10 and <19 std::array keysAboveTen{ Qt::Key_0, Qt::Key_Z, Qt::Key_X, Qt::Key_C, Qt::Key_V, Qt::Key_B, Qt::Key_N, Qt::Key_M, Qt::Key_Comma, Qt::Key_Period }; //activate actions [10-19] for (int i = 10; i < 20; ++i) { QAction *action = taskbarActions->addAction(QStringLiteral("activate entry %1").arg(QString::number(i))); action->setText(i18n("Activate Entry %1", i)); action->setShortcut(QKeySequence(Qt::META + keysAboveTen[i - 10])); KGlobalAccel::setGlobalShortcut(action, QKeySequence(Qt::META + keysAboveTen[i - 10])); connect(action, &QAction::triggered, this, [this, i] { m_modifierTracker->cancelMetaPressed(); activateEntry(i, static_cast(Qt::META)); }); } //new instance actions [1-9] for (int i = 1; i < 10; ++i) { const int entryNumber = i; const Qt::Key key = static_cast(Qt::Key_0 + i); QAction *action = taskbarActions->addAction(QStringLiteral("new instance for entry %1").arg(QString::number(entryNumber))); action->setText(i18n("New Instance for Entry %1", entryNumber)); KGlobalAccel::setGlobalShortcut(action, QKeySequence(Qt::META + Qt::CTRL + key)); connect(action, &QAction::triggered, this, [this, i] { // qDebug() << "meta + ctrl + action..."; m_modifierTracker->cancelMetaPressed(); activateEntry(i, static_cast(Qt::CTRL)); }); } //new instance actions [10-19] for (int i = 10; i < 20; ++i) { QAction *action = taskbarActions->addAction(QStringLiteral("new instance for entry %1").arg(QString::number(i))); action->setText(i18n("New Instance for Entry %1", i)); KGlobalAccel::setGlobalShortcut(action, QKeySequence(Qt::META + Qt::CTRL + keysAboveTen[i - 10])); connect(action, &QAction::triggered, this, [this, i] { m_modifierTracker->cancelMetaPressed(); activateEntry(i, static_cast(Qt::CTRL)); }); } m_singleMetaAction = new QAction(this); m_singleMetaAction->setShortcut(QKeySequence(Qt::META)); connect(m_corona->universalSettings(), &UniversalSettings::metaPressAndHoldEnabledChanged , this, [&]() { if (!m_corona->universalSettings()->metaPressAndHoldEnabled()) { m_modifierTracker->blockModifierTracking(Qt::Key_Super_L); m_modifierTracker->blockModifierTracking(Qt::Key_Super_R); } else { m_modifierTracker->unblockModifierTracking(Qt::Key_Super_L); m_modifierTracker->unblockModifierTracking(Qt::Key_Super_R); } }); //display shortcut badges while holding Meta connect(m_modifierTracker, &ShortcutsPart::ModifierTracker::metaModifierPressed, this, [&]() { m_metaShowedViews = true; showViews(); }); } ShortcutsPart::ShortcutsTracker *GlobalShortcuts::shortcutsTracker() const { return m_shortcutsTracker; } Latte::View *GlobalShortcuts::highestApplicationLauncherView(const QList &views) const { if (views.isEmpty()) { return nullptr; } Latte::View *highestPriorityView{nullptr}; for (const auto view : views) { if (view->extendedInterface()->applicationLauncherHasGlobalShortcut()) { highestPriorityView = view; break; } } if (!highestPriorityView) { for (const auto view : views) { if (view->extendedInterface()->containsApplicationLauncher()) { highestPriorityView = view; break; } } } return highestPriorityView; } //! Activate launcher menu through dbus interface void GlobalShortcuts::activateLauncherMenu() { if (m_metaShowedViews) { return; } QList sortedViews; CentralLayout *currentLayout = m_corona->layoutsManager()->currentLayout(); if (currentLayout) { sortedViews = currentLayout->sortedLatteViews(); } Latte::View *highestPriorityView = highestApplicationLauncherView(sortedViews); if (highestPriorityView) { if (highestPriorityView->visibility()->isHidden() && highestPriorityView->extendedInterface()->applicationLauncherInPopup()) { m_lastInvokedAction = m_singleMetaAction; highestPriorityView->visibility()->addBlockHidingEvent(SHORTCUTBLOCKHIDINGTYPE); //! delay the execution in order to show first the view QTimer::singleShot(APPLETEXECUTIONDELAY, [this, highestPriorityView]() { highestPriorityView->extendedInterface()->toggleAppletExpanded(highestPriorityView->extendedInterface()->applicationLauncherId()); highestPriorityView->visibility()->removeBlockHidingEvent(SHORTCUTBLOCKHIDINGTYPE); }); } else { highestPriorityView->extendedInterface()->toggleAppletExpanded(highestPriorityView->extendedInterface()->applicationLauncherId()); } } } bool GlobalShortcuts::activatePlasmaTaskManager(const Latte::View *view, int index, Qt::Key modifier, bool *delayedExecution) { bool activation{modifier == static_cast(Qt::META)}; bool newInstance{!activation}; if (view->visibility()->isHidden()) { //! delay the execution in order to show first the view QTimer::singleShot(APPLETEXECUTIONDELAY, [this, view, index, activation]() { if (activation) { view->extendedInterface()->activatePlasmaTask(index); } else { view->extendedInterface()->newInstanceForPlasmaTask(index); } }); *delayedExecution = true; return true; } else { *delayedExecution = false; return (activation ? view->extendedInterface()->activatePlasmaTask(index) : view->extendedInterface()->newInstanceForPlasmaTask(index)); } } bool GlobalShortcuts::activateLatteEntry(Latte::View *view, int index, Qt::Key modifier, bool *delayedExecution) { bool activation{modifier == static_cast(Qt::META)}; bool newInstance{!activation}; int appletId = view->extendedInterface()->appletIdForIndex(index); bool hasPopUp {(appletId>-1 && view->extendedInterface()->appletIsExpandable(appletId))}; if (view->visibility()->isHidden() && hasPopUp) { //! delay the execution in order to show first the view QTimer::singleShot(APPLETEXECUTIONDELAY, [this, view, index, activation]() { if (activation) { view->extendedInterface()->activateEntry(index); } else { view->extendedInterface()->newInstanceForEntry(index); } }); *delayedExecution = true; return true; } else { *delayedExecution = false; return (activation ? view->extendedInterface()->activateEntry(index) : view->extendedInterface()->newInstanceForEntry(index)); } } bool GlobalShortcuts::activateEntryForView(Latte::View *view, int index, Qt::Key modifier) { if (!view) { return false; } bool delayed{false}; - bool executed = ((!view->latteTasksArePresent() && view->tasksPresent() && - activatePlasmaTaskManager(view, index, modifier, &delayed)) + bool executed = ((!view->extendedInterface()->hasLatteTasks() && view->extendedInterface()->hasPlasmaTasks() + && activatePlasmaTaskManager(view, index, modifier, &delayed)) || activateLatteEntry(view, index, modifier, &delayed)); if (executed) { if (!m_hideViews.contains(view)) { m_hideViews.append(view); } if (delayed) { view->visibility()->addBlockHidingEvent(SHORTCUTBLOCKHIDINGTYPE); m_hideViewsTimer.start(); } return true; } return false; } //! Activate task manager entry void GlobalShortcuts::activateEntry(int index, Qt::Key modifier) { m_lastInvokedAction = dynamic_cast(sender()); QList sortedViews; CentralLayout *currentLayout = m_corona->layoutsManager()->currentLayout(); if (currentLayout) { sortedViews = currentLayout->sortedLatteViews(); } Latte::View *highest{nullptr}; for (const auto view : sortedViews) { if (view->isPreferredForShortcuts()) { highest = view; break; } } if (highest) { activateEntryForView(highest, index, modifier); } else { for (const auto view : sortedViews) { if (activateEntryForView(view, index, modifier)) { return; } } } } //! update badge for specific view item void GlobalShortcuts::updateViewItemBadge(QString identifier, QString value) { CentralLayout *currentLayout = m_corona->layoutsManager()->currentLayout(); QList views; if (currentLayout) { views = currentLayout->latteViews(); } // update badges in all Latte Tasks plasmoids for (const auto &view : views) { view->extendedInterface()->updateBadgeForLatteTask(identifier, value); } } void GlobalShortcuts::showViews() { m_lastInvokedAction = dynamic_cast(sender()); if (!m_lastInvokedAction) { // when holding Meta m_lastInvokedAction = m_singleMetaAction; } QList sortedViews; CentralLayout *currentLayout = m_corona->layoutsManager()->currentLayout(); if (currentLayout) { sortedViews = currentLayout->sortedLatteViews(); } Latte::View *viewWithTasks{nullptr}; Latte::View *viewWithMeta{nullptr}; bool hasPreferredForShortcutsView{false}; for (const auto view : sortedViews) { if (view->isPreferredForShortcuts()) { hasPreferredForShortcutsView = true; break; } } for(const auto view : sortedViews) { if (!viewWithTasks && (!hasPreferredForShortcutsView || view->isPreferredForShortcuts())) { viewWithTasks = view; break; } } if (m_corona->universalSettings()->kwin_metaForwardedToLatte()) { viewWithMeta = highestApplicationLauncherView(sortedViews); } bool viewFound{false}; if (!m_hideViewsTimer.isActive()) { m_hideViews.clear(); } //! show view that contains tasks plasmoid if (viewWithTasks) { viewFound = true; bool showMeta = (viewWithMeta && (viewWithMeta == viewWithTasks)); viewWithTasks->extendedInterface()->showShortcutBadges(true, showMeta); if (!m_hideViewsTimer.isActive()) { m_hideViews.append(viewWithTasks); viewWithTasks->visibility()->addBlockHidingEvent(SHORTCUTBLOCKHIDINGTYPE); } } //! show view that contains only meta if (viewWithMeta && viewWithMeta != viewWithTasks && viewWithMeta->extendedInterface()->showOnlyMeta()) { viewFound = true; if (!m_hideViewsTimer.isActive()) { m_hideViews.append(viewWithMeta); viewWithMeta->visibility()->addBlockHidingEvent(SHORTCUTBLOCKHIDINGTYPE); } } //! show all the rest views that contain plasma shortcuts QList viewsWithShortcuts; if (currentLayout) { viewsWithShortcuts = currentLayout->viewsWithPlasmaShortcuts(); } if (viewsWithShortcuts.count() > 0) { viewFound = true; if (!m_hideViewsTimer.isActive()) { for(const auto view : viewsWithShortcuts) { if (view != viewWithTasks && view != viewWithMeta) { if (view->extendedInterface()->showShortcutBadges(false, false)) { m_hideViews.append(view); view->visibility()->addBlockHidingEvent(SHORTCUTBLOCKHIDINGTYPE); } } } } } if (viewFound) { if (!m_hideViewsTimer.isActive()) { m_hideViewsTimer.start(); } else { m_hideViewsTimer.stop(); hideViewsTimerSlot(); } } } bool GlobalShortcuts::viewsToHideAreValid() { for(const auto view : m_hideViews) { if (!m_corona->layoutsManager()->synchronizer()->latteViewExists(view)) { return false; } } return true; } void GlobalShortcuts::showSettings() { QList sortedViews; CentralLayout *currentLayout = m_corona->layoutsManager()->currentLayout(); if (currentLayout) { sortedViews = currentLayout->sortedLatteViews(); } //! find which is the next view to show its settings if (sortedViews.count() > 0) { int openSettings = -1; //! find last view that showed its config view for (int i = 0; i < sortedViews.size(); ++i) { if (sortedViews[i] == currentLayout->lastConfigViewFor()) { openSettings = i; break; } } if (openSettings >= 0 && sortedViews.count() > 1) { //! check if there is a view with opened settings window openSettings = sortedViews[openSettings]->settingsWindowIsShown() ? openSettings + 1 : openSettings; if (openSettings >= sortedViews.size()) { openSettings = 0; } sortedViews[openSettings]->showSettingsWindow(); } else { sortedViews[0]->showSettingsWindow(); } } } void GlobalShortcuts::hideViewsTimerSlot() { if (!m_lastInvokedAction || m_hideViews.count() == 0) { return; } auto initParameters = [this]() { m_lastInvokedAction = Q_NULLPTR; if (viewsToHideAreValid()) { for(const auto latteView : m_hideViews) { latteView->visibility()->removeBlockHidingEvent(SHORTCUTBLOCKHIDINGTYPE); latteView->extendedInterface()->hideShortcutBadges(); if (latteView->visibility()->mode() == Latte::Types::SideBar && !latteView->visibility()->isHidden()) { latteView->visibility()->toggleHiddenState(); } } } m_hideViews.clear(); m_metaShowedViews = false; }; // qDebug() << "MEMORY ::: " << m_hideViews.count() << " _ " << m_viewItemsCalled.count() << " _ " << m_showShortcutBadgesMethods.count(); if (QX11Info::isPlatformX11()) { if (!m_modifierTracker->sequenceModifierPressed(m_lastInvokedAction->shortcut())) { initParameters(); return; } else { m_hideViewsTimer.start(); } } else { // TODO: This is needs to be fixed in wayland initParameters(); } } } #include "moc_globalshortcuts.cpp" diff --git a/app/view/indicator/indicator.cpp b/app/view/indicator/indicator.cpp index 63366d07..92d23da8 100644 --- a/app/view/indicator/indicator.cpp +++ b/app/view/indicator/indicator.cpp @@ -1,459 +1,460 @@ /* * Copyright 2019 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 "indicator.h" // local #include #include "indicatorinfo.h" +#include "../containmentinterface.h" #include "../view.h" #include "../../lattecorona.h" #include "../../indicator/factory.h" // Qt #include // KDE #include #include #include #include namespace Latte { namespace ViewPart { Indicator::Indicator(Latte::View *parent) : QObject(parent), m_view(parent), m_info(new IndicatorPart::Info(this)), m_resources(new IndicatorPart::Resources(this)) { m_corona = qobject_cast(m_view->corona()); loadConfig(); connect(this, &Indicator::enabledChanged, this, &Indicator::saveConfig); connect(this, &Indicator::pluginChanged, this, &Indicator::saveConfig); - connect(m_view, &Latte::View::latteTasksArePresentChanged, this, &Indicator::latteTasksArePresentChanged); + connect(m_view->extendedInterface(), &ContainmentInterface::hasLatteTasksChanged, this, &Indicator::latteTasksArePresentChanged); connect(m_view, &Latte::View::indicatorPluginChanged, [this](const QString &indicatorId) { if (m_corona && m_corona->indicatorFactory()->isCustomType(indicatorId)) { emit customPluginsChanged(); } }); connect(m_view, &Latte::View::indicatorPluginRemoved, [this](const QString &indicatorId) { if (m_corona && m_type == indicatorId && !m_corona->indicatorFactory()->pluginExists(indicatorId)) { setType("org.kde.latte.default"); } if (m_corona && m_corona->indicatorFactory()->isCustomType(indicatorId)) { emit customPluginsChanged(); } }); load(m_type); loadPlasmaComponent(); } Indicator::~Indicator() { unloadIndicators(); if (m_component) { m_component->deleteLater(); } if (m_configLoader) { m_configLoader->deleteLater(); } if (m_configuration) { m_configuration->deleteLater(); } if (m_info) { m_info->deleteLater(); } } bool Indicator::enabled() const { return m_enabled; } void Indicator::setEnabled(bool enabled) { if (m_enabled == enabled) { return; } m_enabled = enabled; emit enabledChanged(); } bool Indicator::enabledForApplets() const { return m_enabledForApplets; } void Indicator::setEnabledForApplets(bool enabled) { if (m_enabledForApplets == enabled) { return; } m_enabledForApplets = enabled; emit enabledForAppletsChanged(); } bool Indicator::isCustomIndicator() const { return m_corona->indicatorFactory()->isCustomType(type()); } bool Indicator::latteTasksArePresent() { - return m_view->latteTasksArePresent(); + return m_view->extendedInterface()->hasLatteTasks(); } bool Indicator::providesConfigUi() const { return m_providesConfigUi; } void Indicator::setProvidesConfigUi(bool provides) { if (m_providesConfigUi == provides) { return; } m_providesConfigUi = provides; emit providesConfigUiChanged(); } bool Indicator::pluginIsReady() { return m_pluginIsReady; } void Indicator::setPluginIsReady(bool ready) { if (m_pluginIsReady == ready) { return; } m_pluginIsReady = ready; emit pluginIsReadyChanged(); } QString Indicator::type() const { return m_type; } void Indicator::setType(QString type) { if (m_type == type) { return; } load(type); } QString Indicator::customType() const { return m_customType; } void Indicator::setCustomType(QString type) { if (m_customType == type) { return; } m_customType = type; emit customPluginChanged(); } int Indicator::customPluginsCount() const { return m_corona->indicatorFactory()->customPluginsCount(); } QString Indicator::uiPath() const { return m_corona->indicatorFactory()->uiPath(m_type); } QStringList Indicator::customPluginIds() const { return m_corona->indicatorFactory()->customPluginIds(); } QStringList Indicator::customPluginNames() const { return m_corona->indicatorFactory()->customPluginNames(); } QStringList Indicator::customLocalPluginIds() const { return m_corona->indicatorFactory()->customLocalPluginIds(); } IndicatorPart::Info *Indicator::info() const { return m_info; } IndicatorPart::Resources *Indicator::resources() const { return m_resources; } QQmlComponent *Indicator::component() const { return m_component; } QQmlComponent *Indicator::plasmaComponent() const { return m_plasmaComponent; } QObject *Indicator::configuration() const { return m_configuration; } void Indicator::load(QString type) { KPluginMetaData metadata = m_corona->indicatorFactory()->metadata(type); if (metadata.isValid()) { bool state{m_enabled}; //! remove all previous indicators setPluginIsReady(false); m_metadata = metadata; m_type = type; QString path = m_metadata.fileName(); m_pluginPath = path.remove("metadata.desktop"); if (m_corona && m_corona->indicatorFactory()->isCustomType(type)) { setCustomType(type); } updateScheme(); updateComponent(); emit pluginChanged(); //! create all indicators with the new type setPluginIsReady(true); } else if (type!="org.kde.latte.default") { qDebug() << " Indicator metadata are not valid : " << type; setType("org.kde.latte.default"); } } void Indicator::updateComponent() { auto prevComponent = m_component; QString uiPath = m_metadata.value("X-Latte-MainScript"); if (!uiPath.isEmpty()) { uiPath = m_pluginPath + "package/" + uiPath; m_component = new QQmlComponent(m_view->engine(), uiPath); } if (prevComponent) { prevComponent->deleteLater(); } } void Indicator::loadPlasmaComponent() { auto prevComponent = m_plasmaComponent; KPluginMetaData metadata = m_corona->indicatorFactory()->metadata("org.kde.latte.plasmatabstyle"); QString uiPath = metadata.value("X-Latte-MainScript"); if (!uiPath.isEmpty()) { QString path = metadata.fileName(); path = path.remove("metadata.desktop"); uiPath = path + "package/" + uiPath; m_plasmaComponent = new QQmlComponent(m_view->engine(), uiPath); } if (prevComponent) { prevComponent->deleteLater(); } emit plasmaComponentChanged(); } void Indicator::configUiFor(QString type, QQuickItem *parent) { if (!parent) { return; } if (m_lastCreatedConfigUi) { delete m_lastCreatedConfigUi; m_lastCreatedConfigUi = nullptr; } auto prevConfigUi = m_lastCreatedConfigUi; KPluginMetaData metadata; if (m_metadata.pluginId() == type) { metadata = m_metadata; } else { metadata = m_corona->indicatorFactory()->metadata(type); } if (metadata.isValid()) { QString uiPath = metadata.value("X-Latte-ConfigUi"); if (!uiPath.isEmpty()) { m_lastCreatedConfigUi = new KDeclarative::QmlObjectSharedEngine(parent); m_lastCreatedConfigUi->setTranslationDomain(QLatin1String("latte_indicator_") + m_metadata.pluginId()); m_lastCreatedConfigUi->setInitializationDelayed(true); uiPath = m_pluginPath + "package/" + uiPath; m_lastCreatedConfigUi->setSource(QUrl::fromLocalFile(uiPath)); m_lastCreatedConfigUi->rootContext()->setContextProperty(QStringLiteral("dialog"), parent); m_lastCreatedConfigUi->rootContext()->setContextProperty(QStringLiteral("indicator"), this); m_lastCreatedConfigUi->completeInitialization(); QQuickItem *qmlItem = qobject_cast(m_lastCreatedConfigUi->rootObject()); if (qmlItem) { qmlItem->setParentItem(parent); setProvidesConfigUi(true); } } else { setProvidesConfigUi(false); } } } void Indicator::releaseConfigUi() { if (m_lastCreatedConfigUi) { delete m_lastCreatedConfigUi; m_lastCreatedConfigUi = nullptr; } } void Indicator::unloadIndicators() { setPluginIsReady(false); } void Indicator::updateScheme() { auto prevConfigLoader = m_configLoader; auto prevConfiguration = m_configuration; QString xmlPath = m_metadata.value("X-Latte-ConfigXml"); if (!xmlPath.isEmpty()) { QFile file(m_pluginPath + "package/" + xmlPath); m_configLoader = new KConfigLoader(m_view->containment()->config().group("Indicator").group(m_metadata.pluginId()), &file); m_configuration = new KDeclarative::ConfigPropertyMap(m_configLoader, this); } else { m_configLoader = nullptr; m_configuration = nullptr; } if (prevConfigLoader) { prevConfigLoader->deleteLater(); } if (prevConfiguration) { prevConfiguration->deleteLater(); } } void Indicator::addIndicator() { QFileDialog *fileDialog = new QFileDialog(nullptr , i18nc("add indicator", "Add Indicator") , QDir::homePath() , QStringLiteral("indicator.latte")); fileDialog->setFileMode(QFileDialog::AnyFile); fileDialog->setAcceptMode(QFileDialog::AcceptOpen); fileDialog->setDefaultSuffix("indicator.latte"); QStringList filters; filters << QString(i18nc("add indicator file", "Latte Indicator") + "(*.indicator.latte)"); fileDialog->setNameFilters(filters); connect(fileDialog, &QFileDialog::finished, fileDialog, &QFileDialog::deleteLater); connect(fileDialog, &QFileDialog::fileSelected, this, [&](const QString & file) { qDebug() << "Trying to import indicator file ::: " << file; m_corona->indicatorFactory()->importIndicatorFile(file); }); fileDialog->open(); } void Indicator::downloadIndicator() { //! call asynchronously in order to not crash when view settings window //! loses focus and it closes QTimer::singleShot(0, [this]() { m_corona->indicatorFactory()->downloadIndicator(); }); } void Indicator::removeIndicator(QString pluginId) { //! call asynchronously in order to not crash when view settings window //! loses focus and it closes QTimer::singleShot(0, [this, pluginId]() { m_corona->indicatorFactory()->removeIndicator(pluginId); }); } void Indicator::loadConfig() { auto config = m_view->containment()->config().group("Indicator"); m_customType = config.readEntry("customType", QString()); m_enabled = config.readEntry("enabled", true); m_type = config.readEntry("type", "org.kde.latte.default"); } void Indicator::saveConfig() { auto config = m_view->containment()->config().group("Indicator"); config.writeEntry("customType", m_customType); config.writeEntry("enabled", m_enabled); config.writeEntry("type", m_type); } } } diff --git a/app/view/view.cpp b/app/view/view.cpp index c3bfc761..393e5196 100644 --- a/app/view/view.cpp +++ b/app/view/view.cpp @@ -1,1628 +1,1573 @@ /* * 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 "../apptypes.h" #include "../lattecorona.h" #include "../declarativeimports/interfaces.h" #include "../indicator/factory.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" // Qt #include #include #include #include #include #include #include // KDe #include #include #include #include #include // Plasma #include #include #include #define BLOCKHIDINGDRAGTYPE "View::ContainsDrag()" #define BLOCKHIDINGNEEDSATTENTIONTYPE "View::Containment::NeedsAttentionState()" #define BLOCKHIDINGREQUESTSINPUTTYPE "View::Containment::RequestsInputState()" 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)), m_interface(new ViewPart::ContainmentInterface(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::NoDropShadowWindowHint | Qt::WindowDoesNotAcceptFocus; if (byPassWM) { setFlags(flags | Qt::BypassWindowManagerHint); } else { setFlags(flags); } if (targetScreen) m_positioner->setScreenToFollow(targetScreen); else m_positioner->setScreenToFollow(qGuiApp->primaryScreen()); m_releaseGrabTimer.setInterval(400); m_releaseGrabTimer.setSingleShot(true); connect(&m_releaseGrabTimer, &QTimer::timeout, this, &View::releaseGrab); connect(m_contextMenu, &ViewPart::ContextMenu::menuChanged, this, &View::updateTransientWindowsTracking); connect(this, &View::containmentChanged , this, [ &, byPassWM]() { qDebug() << "dock view c++ containment changed 1..."; if (!this->containment()) return; qDebug() << "dock view c++ containment changed 2..."; setTitle(validTitle()); //! 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()) { m_interface->deactivateApplets(); } }); connect(m_visibility, &ViewPart::VisibilityManager::containsMouseChanged, this, &View::updateTransientWindowsTracking); connect(m_visibility, &ViewPart::VisibilityManager::frameExtentsCleared, this, [&]() { if (behaveAsPlasmaPanel()) { //! recreate view because otherwise compositor frame extents implementation //! is triggering a crazy behavior of moving/hiding the view and freezing Latte //! in some cases. reloadSource(); } }); 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(); } disconnectSensitiveSignals(); disconnect(containment(), SIGNAL(statusChanged(Plasma::Types::ItemStatus)), this, SLOT(statusChanged(Plasma::Types::ItemStatus))); qDebug() << "dock view deleting..."; //! 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) { delete m_configView; } 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_interface) { delete m_interface; } if (m_visibility) { delete m_visibility; } if (m_windowsTracker) { delete m_windowsTracker; } } void View::init(Plasma::Containment *plasma_containment) { 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(this, &View::activitiesChanged, this, &View::applyActivitiesToWindows); connect(this, &View::localGeometryChanged, this, [&]() { updateAbsoluteGeometry(); }); connect(this, &View::screenEdgeMarginEnabledChanged, this, [&]() { updateAbsoluteGeometry(); }); //! used in order to disconnect it when it should NOT be called because it creates crashes connect(this, &View::availableScreenRectChangedFrom, m_corona, &Latte::Corona::availableScreenRectChangedFrom); connect(this, &View::availableScreenRegionChangedFrom, m_corona, &Latte::Corona::availableScreenRegionChangedFrom); connect(m_corona, &Latte::Corona::availableScreenRectChangedFrom, this, &View::availableScreenRectChangedFromSlot); connect(m_corona, &Latte::Corona::verticalUnityViewHasFocus, this, &View::topViewAlwaysOnTop); 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 availableScreenRectChangedFrom(this); }); connect(m_effects, &ViewPart::Effects::innerShadowChanged, this, [&]() { emit 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 availableScreenRectChangedFrom(this); }); connect(m_contextMenu, &ViewPart::ContextMenu::menuChanged, this, &View::contextMenuIsShownChanged); connect(m_interface, &ViewPart::ContainmentInterface::hasExpandedAppletChanged, this, &View::verticalUnityViewHasFocus); //! View sends this signal in order to avoid crashes from ViewPart::Indicator when the view is recreated connect(m_corona->indicatorFactory(), &Latte::Indicator::Factory::indicatorChanged, this, [&](const QString &indicatorId) { emit indicatorPluginChanged(indicatorId); }); connect(this, &View::indicatorPluginChanged, this, [&](const QString &indicatorId) { if (m_indicator && m_indicator->isCustomIndicator() && m_indicator->type() == indicatorId) { reloadSource(); } }); connect(m_corona->indicatorFactory(), &Latte::Indicator::Factory::indicatorRemoved, this, &View::indicatorPluginRemoved); connect(m_corona->universalSettings(), &Latte::UniversalSettings::hiddenConfigurationWindowsAreDeletedChanged, this, &View::hiddenConfigurationWindowsAreDeletedChanged); //! Assign app interfaces in be accessible through containment graphic item QQuickItem *containmentGraphicItem = qobject_cast(plasma_containment->property("_plasma_graphicObject").value()); if (containmentGraphicItem) { containmentGraphicItem->setProperty("_latte_globalShortcuts_object", QVariant::fromValue(m_corona->globalShortcuts()->shortcutsTracker())); containmentGraphicItem->setProperty("_latte_layoutsManager_object", QVariant::fromValue(m_corona->layoutsManager())); containmentGraphicItem->setProperty("_latte_themeExtended_object", QVariant::fromValue(m_corona->themeExtended())); containmentGraphicItem->setProperty("_latte_universalSettings_object", QVariant::fromValue(m_corona->universalSettings())); containmentGraphicItem->setProperty("_latte_view_object", QVariant::fromValue(this)); Latte::Interfaces *ifacesGraphicObject = qobject_cast(containmentGraphicItem->property("_latte_view_interfacesobject").value()); if (ifacesGraphicObject) { ifacesGraphicObject->updateView(); setInterfacesGraphicObj(ifacesGraphicObject); } } 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::hiddenConfigurationWindowsAreDeleted() const { return m_corona->universalSettings()->hiddenConfigurationWindowsAreDeleted(); } bool View::inDelete() const { return m_inDelete; } bool View::inReadyState() const { return (m_layout != nullptr); } void View::disconnectSensitiveSignals() { m_initLayoutTimer.stop(); disconnect(this, &View::availableScreenRectChangedFrom, m_corona, &Latte::Corona::availableScreenRectChangedFrom); disconnect(this, &View::availableScreenRegionChangedFrom, m_corona, &Latte::Corona::availableScreenRegionChangedFrom); disconnect(m_corona, &Latte::Corona::availableScreenRectChangedFrom, this, &View::availableScreenRectChangedFromSlot); disconnect(m_corona, &Latte::Corona::verticalUnityViewHasFocus, this, &View::topViewAlwaysOnTop); setLayout(nullptr); } void View::availableScreenRectChangedFromSlot(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..."; if (m_visibility) { m_visibility->initViewFlags(); } } } 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) { m_inDelete = true; QAction *removeAct = this->containment()->actions()->action(QStringLiteral("remove")); if (removeAct) { removeAct->trigger(); } } } bool View::settingsWindowIsShown() { auto cview = qobject_cast(m_configView); if (hiddenConfigurationWindowsAreDeleted()) { return (cview != nullptr); } else if (cview) { return cview->isVisible(); } return false; } 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(); } QString View::validTitle() const { if (!containment()) { return QString(); } return QString("#view#" + QString::number(containment()->id())); } 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 = m_localGeometry; absGeometry.moveLeft(x() + m_localGeometry.x()); absGeometry.moveTop(y() + m_localGeometry.y()); if (behaveAsPlasmaPanel()) { int currentScreenEdgeMargin = m_screenEdgeMarginEnabled ? qMax(0, m_screenEdgeMargin) : 0; if (location() == Plasma::Types::BottomEdge) { absGeometry.moveTop(screenGeometry().bottom() - currentScreenEdgeMargin - m_normalThickness); } else if (location() == Plasma::Types::TopEdge) { absGeometry.moveTop(screenGeometry().top() + currentScreenEdgeMargin); } else if (location() == Plasma::Types::LeftEdge) { absGeometry.moveLeft(screenGeometry().left() + currentScreenEdgeMargin); } else if (location() == Plasma::Types::RightEdge) { absGeometry.moveLeft(screenGeometry().right() - currentScreenEdgeMargin - m_normalThickness); } } 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 availableScreenRectChangedFrom(this); emit availableScreenRegionChangedFrom(this); } } void View::statusChanged(Plasma::Types::ItemStatus status) { if (!containment()) { return; } if (status == Plasma::Types::NeedsAttentionStatus) { m_visibility->addBlockHidingEvent(BLOCKHIDINGNEEDSATTENTIONTYPE); m_visibility->initViewFlags(); } else if (status == Plasma::Types::AcceptingInputStatus) { m_visibility->removeBlockHidingEvent(BLOCKHIDINGNEEDSATTENTIONTYPE); setFlags(flags() & ~Qt::WindowDoesNotAcceptFocus); KWindowSystem::forceActiveWindow(winId()); } else { updateTransientWindowsTracking(); m_visibility->removeBlockHidingEvent(BLOCKHIDINGNEEDSATTENTIONTYPE); m_visibility->initViewFlags(); } } void View::addTransientWindow(QWindow *window) { if (!m_transientWindows.contains(window) && !window->title().startsWith("#debugwindow#")) { m_transientWindows.append(window); QString winPtrStr = "0x" + QString::number((qulonglong)window,16); m_visibility->addBlockHidingEvent(winPtrStr); connect(window, &QWindow::visibleChanged, this, &View::removeTransientWindow); } } void View::removeTransientWindow(const bool &visible) { QWindow *window = static_cast(QObject::sender()); if (window && !visible) { QString winPtrStr = "0x" + QString::number((qulonglong)window,16); m_visibility->removeBlockHidingEvent(winPtrStr); disconnect(window, &QWindow::visibleChanged, this, &View::removeTransientWindow); m_transientWindows.removeAll(window); updateTransientWindowsTracking(); } } void View::updateTransientWindowsTracking() { for(QWindow *window: qApp->topLevelWindows()) { if (window->transientParent() == this){ if (window->isVisible()) { addTransientWindow(window); break; } } } } 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; emit alternativesIsShownChanged(); } bool View::containsDrag() const { return m_containsDrag; } void View::setContainsDrag(bool contains) { if (m_containsDrag == contains) { return; } m_containsDrag = contains; if (m_containsDrag) { m_visibility->addBlockHidingEvent(BLOCKHIDINGDRAGTYPE); } else { m_visibility->removeBlockHidingEvent(BLOCKHIDINGDRAGTYPE); } 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(); } int View::headThicknessGap() const { return m_headThicknessGap; } void View::setHeadThicknessGap(int thickness) { if (m_headThicknessGap == thickness) { return; } m_headThicknessGap = thickness; emit headThicknessGapChanged(); } 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::isFloatingWindow() const { return m_behaveAsPlasmaPanel && m_screenEdgeMarginEnabled && (m_screenEdgeMargin>0); } 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::inSettingsAdvancedMode() const { if (m_configView) { auto configView = qobject_cast(m_configView); if (configView) { return configView->inAdvancedMode(); } } return false; } bool View::isTouchingBottomViewAndIsBusy() const { return m_isTouchingBottomViewAndIsBusy; } void View::setIsTouchingBottomViewAndIsBusy(bool touchAndBusy) { if (m_isTouchingBottomViewAndIsBusy == touchAndBusy) { return; } m_isTouchingBottomViewAndIsBusy = touchAndBusy; emit isTouchingBottomViewAndIsBusyChanged(); } bool View::isTouchingTopViewAndIsBusy() const { return m_isTouchingTopViewAndIsBusy; } void View::setIsTouchingTopViewAndIsBusy(bool touchAndBusy) { if (m_isTouchingTopViewAndIsBusy == touchAndBusy) { return; } m_isTouchingTopViewAndIsBusy = touchAndBusy; emit isTouchingTopViewAndIsBusyChanged(); } 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(); } float View::offset() const { return m_offset; } void View::setOffset(float offset) { if (m_offset == offset) { return; } m_offset = offset; emit offsetChanged(); } bool View::screenEdgeMarginEnabled() const { return m_screenEdgeMarginEnabled; } void View::setScreenEdgeMarginEnabled(bool enabled) { if (m_screenEdgeMarginEnabled == enabled) { return; } m_screenEdgeMarginEnabled = enabled; emit screenEdgeMarginEnabledChanged(); } int View::screenEdgeMargin() const { return m_screenEdgeMargin; } void View::setScreenEdgeMargin(int margin) { if (m_screenEdgeMargin == margin) { return; } m_screenEdgeMargin = margin; emit screenEdgeMarginChanged(); } 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 { QStringList running; QStringList runningAll = m_corona->activitiesConsumer()->runningActivities(); for(int i=0; isetWindowOnActivities(*this, runningActivities); //! config windows if (m_configView) { m_windowsTracker->setWindowOnActivities(*m_configView, runningActivities); auto configView = qobject_cast(m_configView); if (configView && configView->secondaryWindow()) { m_windowsTracker->setWindowOnActivities(*configView->secondaryWindow(), runningActivities); } } //! hidden windows if (m_visibility->supportsKWinEdges()) { m_visibility->applyActivitiesToHiddenWindows(runningActivities); } } } void View::showHiddenViewFromActivityStopping() { if (m_layout && m_visibility && !inDelete() && !isVisible() && !m_visibility->isHidden()) { show(); if (m_effects) { m_effects->updateEnabledBorders(); } //qDebug() << "View:: Enforce reshow from timer 1..."; emit forcedShown(); } else if (m_layout && isVisible()) { m_inDelete = false; //qDebug() << "View:: No needed reshow from timer 1..."; } } 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() == MemoryUsage::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 m_initLayoutTimer.setInterval(100); m_initLayoutTimer.setSingleShot(true); connectionsLayout << connect(&m_initLayoutTimer, &QTimer::timeout, this, [&]() { if (m_layout && m_visibility) { setActivities(m_layout->appliedActivities()); qDebug() << "DOCK VIEW FROM LAYOUT ::: " << m_layout->name() << " - activities: " << m_activities; } }); m_initLayoutTimer.start(); connectionsLayout << connect(m_layout, &Layout::GenericLayout::preferredViewForShortcutsChanged, this, &View::preferredViewForShortcutsChangedSlot); connectionsLayout << connect(m_layout, &Layout::GenericLayout::lastConfigViewForChanged, this, &View::configViewShownFor); Latte::Corona *latteCorona = qobject_cast(this->corona()); connectionsLayout << connect(latteCorona->activitiesConsumer(), &KActivities::Consumer::currentActivityChanged, this, [&]() { if (m_layout && m_visibility) { setActivities(m_layout->appliedActivities()); applyActivitiesToWindows(); showHiddenViewFromActivityStopping(); qDebug() << "DOCK VIEW FROM LAYOUT (currentActivityChanged) ::: " << m_layout->name() << " - activities: " << m_activities; } }); if (latteCorona->layoutsManager()->memoryUsage() == MemoryUsage::MultipleLayouts) { connectionsLayout << connect(latteCorona->activitiesConsumer(), &KActivities::Consumer::runningActivitiesChanged, this, [&]() { if (m_layout && m_visibility) { setActivities(m_layout->appliedActivities()); qDebug() << "DOCK VIEW FROM LAYOUT (runningActivitiesChanged) ::: " << m_layout->name() << " - activities: " << m_activities; } }); connectionsLayout << connect(m_layout, &Layout::GenericLayout::activitiesChanged, this, [&]() { if (m_layout) { setActivities(m_layout->appliedActivities()); } }); connectionsLayout << connect(latteCorona->layoutsManager(), &Layouts::Manager::layoutsChanged, this, [&]() { if (m_layout) { setActivities(m_layout->appliedActivities()); } }); //! 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, [&]() { applyActivitiesToWindows(); showHiddenViewFromActivityStopping(); emit activitiesChanged(); }); connectionsLayout << connect(&m_visibleHackTimer2, &QTimer::timeout, this, [&]() { applyActivitiesToWindows(); showHiddenViewFromActivityStopping(); emit activitiesChanged(); }); //! 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::configViewShownFor(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) { if (hiddenConfigurationWindowsAreDeleted()) { configDialog->deleteLater(); } else if (configDialog->isVisible()) { configDialog->hideConfigWindow(); } } } } void View::hideWindowsForSlidingOut() { 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::ContainmentInterface *View::extendedInterface() const { return m_interface; } ViewPart::Positioner *View::positioner() const { return m_positioner; } ViewPart::VisibilityManager *View::visibility() const { return m_visibility; } ViewPart::WindowsTracker *View::windowsTracker() const { return m_windowsTracker; } Latte::Interfaces *View::interfacesGraphicObj() const { return m_interfacesGraphicObj; } void View::setInterfacesGraphicObj(Latte::Interfaces *ifaces) { if (m_interfacesGraphicObj == ifaces) { return; } m_interfacesGraphicObj = ifaces; if (containment()) { QQuickItem *containmentGraphicItem = qobject_cast(containment()->property("_plasma_graphicObject").value()); if (containmentGraphicItem) { containmentGraphicItem->setProperty("_latte_view_interfacesobject", QVariant::fromValue(m_interfacesGraphicObj)); } } emit interfacesGraphicObjChanged(); } 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: if (m_visibility) { m_visibility->initViewFlags(); } break; case QEvent::Wheel: if (auto wheelEvent = dynamic_cast(e)) { #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) QPoint position = QPoint(wheelEvent->x(), wheelEvent->y()); #else QPoint position = wheelEvent->position().toPoint(); #endif emit wheelScrolled(position, wheelEvent->angleDelta(), wheelEvent->buttons()); } break; default: break; } } return ContainmentView::event(e); } //! release grab and restore mouse state void View::unblockMouse(int x, int y) { setMouseGrabEnabled(false); m_releaseGrab_x = x; m_releaseGrab_y = y; m_releaseGrabTimer.start(); } void View::releaseGrab() { //! ungrab mouse if (mouseGrabberItem()) { mouseGrabberItem()->ungrabMouse(); } //! properly release grabbed mouse in order to inform all views setMouseGrabEnabled(true); setMouseGrabEnabled(false); //! Send a fake QEvent::Leave to inform applets for mouse leaving the view QHoverEvent e(QEvent::Leave, QPoint(-5,-5), QPoint(m_releaseGrab_x, m_releaseGrab_y)); QCoreApplication::instance()->sendEvent(this, &e); } 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; } bool View::isHighestPriorityView() { if (m_layout) { return this == m_layout->highestPriorityView(); } return false; } //! BEGIN: WORKAROUND order to force top panels always on top and above left/right panels void View::topViewAlwaysOnTop() { if (!m_visibility) { return; } if (location() == Plasma::Types::TopEdge && m_visibility->mode() != Latte::Types::WindowsCanCover && m_visibility->mode() != Latte::Types::WindowsAlwaysCover) { //! this is needed in order to preserve that the top dock will be above others. //! Unity layout paradigm is a good example for this. The top panel shadow //! should be always on top compared to left panel m_visibility->setViewOnFrontLayer(); } } void View::verticalUnityViewHasFocus() { if (formFactor() == Plasma::Types::Vertical && (y() != screenGeometry().y()) && ( (m_alignment == Latte::Types::Justify && m_maxLength == 1.0) ||(m_alignment == Latte::Types::Top && m_offset == 0.0) )) { emit m_corona->verticalUnityViewHasFocus(); } } //! END: WORKAROUND //!BEGIN overriding context menus behavior void View::mousePressEvent(QMouseEvent *event) { bool result = m_contextMenu->mousePressEvent(event); if (result) { PlasmaQuick::ContainmentView::mousePressEvent(event); updateTransientWindowsTracking(); } verticalUnityViewHasFocus(); } //!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); } 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 db88540e..7dac01c2 100644 --- a/app/view/view.h +++ b/app/view/view.h @@ -1,446 +1,438 @@ /* * 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 #include "containmentinterface.h" #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" // C++ #include // Qt #include #include #include #include #include #include #include namespace Plasma { class Types; class Corona; class Containment; } namespace PlasmaQuick { class AppletQuickItem; } namespace KWayland { namespace Client { class PlasmaShellSurface; } } namespace Latte { class Corona; class Interfaces; 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) Q_PROPERTY(bool inSettingsAdvancedMode READ inSettingsAdvancedMode NOTIFY inSettingsAdvancedModeChanged) //! 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) Q_PROPERTY(bool screenEdgeMarginEnabled READ screenEdgeMarginEnabled WRITE setScreenEdgeMarginEnabled NOTIFY screenEdgeMarginEnabledChanged) //! values to be used from Smart surrounding Views Q_PROPERTY(bool isTouchingBottomViewAndIsBusy READ isTouchingBottomViewAndIsBusy WRITE setIsTouchingBottomViewAndIsBusy NOTIFY isTouchingBottomViewAndIsBusyChanged) Q_PROPERTY(bool isTouchingTopViewAndIsBusy READ isTouchingTopViewAndIsBusy WRITE setIsTouchingTopViewAndIsBusy NOTIFY isTouchingTopViewAndIsBusyChanged) 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 headThicknessGap READ headThicknessGap WRITE setHeadThicknessGap NOTIFY headThicknessGapChanged) Q_PROPERTY(int screenEdgeMargin READ screenEdgeMargin WRITE setScreenEdgeMargin NOTIFY screenEdgeMarginChanged) Q_PROPERTY(float maxLength READ maxLength WRITE setMaxLength NOTIFY maxLengthChanged) Q_PROPERTY(float offset READ offset WRITE setOffset NOTIFY offsetChanged) 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::ContainmentInterface *extendedInterface READ extendedInterface NOTIFY extendedInterfaceChanged) 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(Latte::Interfaces *interfacesGraphicObj READ interfacesGraphicObj WRITE setInterfacesGraphicObj NOTIFY interfacesGraphicObjChanged) 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(Plasma::Containment *plasma_containment = nullptr); Types::ViewType type() const; void setType(Types::ViewType type); bool alternativesIsShown() const; void setAlternativesIsShown(bool show); bool inDelete() const; bool inReadyState() const; bool hiddenConfigurationWindowsAreDeleted() 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 isFloatingWindow() const; bool isPreferredForShortcuts() const; void setIsPreferredForShortcuts(bool preferred); - bool latteTasksArePresent() const; - void setLatteTasksArePresent(bool present); - bool inSettingsAdvancedMode() const; bool isTouchingBottomViewAndIsBusy() const; void setIsTouchingBottomViewAndIsBusy(bool touchAndBusy); bool isTouchingTopViewAndIsBusy() const; void setIsTouchingTopViewAndIsBusy(bool touchAndBusy); bool screenEdgeMarginEnabled() const; void setScreenEdgeMarginEnabled(bool enabled); 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 headThicknessGap() const; void setHeadThicknessGap(int thickness); int screenEdgeMargin() const; void setScreenEdgeMargin(int margin); int alignment() const; void setAlignment(int alignment); float maxLength() const; void setMaxLength(float length); float offset() const; void setOffset(float offset); QRect absoluteGeometry() const; QRect screenGeometry() const; QRect localGeometry() const; void setLocalGeometry(const QRect &geometry); QString validTitle() const; bool isOnActivity(const QString &activity) const; bool isOnAllActivities() const; QStringList activities() const; void setActivities(const QStringList &ids); bool settingsWindowIsShown(); void showSettingsWindow(); PlasmaQuick::ConfigView *configView(); ViewPart::Effects *effects() const; ViewPart::ContainmentInterface *extendedInterface() const; ViewPart::Indicator *indicator() const; ViewPart::Positioner *positioner() const; ViewPart::VisibilityManager *visibility() const; ViewPart::WindowsTracker *windowsTracker() const; Latte::Interfaces *interfacesGraphicObj() const; void setInterfacesGraphicObj(Latte::Interfaces *ifaces); Layout::GenericLayout *layout() const; void setLayout(Layout::GenericLayout *layout); KWayland::Client::PlasmaShellSurface *surface(); //! release grab and restore mouse state void unblockMouse(int x, int y); 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 moveToLayout(QString layoutName); - Q_INVOKABLE void removeTasksPlasmoid(); Q_INVOKABLE bool mimeContainsPlasmoid(QMimeData *mimeData, QString name); - Q_INVOKABLE bool tasksPresent(); void updateAbsoluteGeometry(bool bypassChecks = false); 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 wheelScrolled(const QPoint pos, const QPoint angleDelta, const int buttons); 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 extendedInterfaceChanged(); void fontPixelSizeChanged(); void forcedShown(); //[workaround] forced shown to avoid a KWin issue that hides windows when closing activities void widthChanged(); void headThicknessGapChanged(); void heightChanged(); void hiddenConfigurationWindowsAreDeletedChanged(); void inEditModeChanged(); void indicatorChanged(); void inSettingsAdvancedModeChanged(); void interfacesGraphicObjChanged(); void isPreferredForShortcutsChanged(); void isTouchingBottomViewAndIsBusyChanged(); void isTouchingTopViewAndIsBusyChanged(); - void latteTasksArePresentChanged(); void layoutChanged(); void localGeometryChanged(); void maxLengthChanged(); void maxThicknessChanged(); void normalThicknessChanged(); void offsetChanged(); void onPrimaryChanged(); void positionerChanged(); void screenEdgeMarginChanged(); void screenEdgeMarginEnabledChanged(); void screenGeometryChanged(); void typeChanged(); void visibilityChanged(); void windowsTrackerChanged(); void xChanged(); void yChanged(); void absoluteGeometryChanged(const QRect &geometry); void indicatorPluginChanged(const QString &indicatorId); void indicatorPluginRemoved(const QString &indicatorId); //! are used to trigger the Corona relevant signals and in that //! way we can disable any such signaling all together, e.g. through disconnectSensitiveSignals() void availableScreenRectChangedFrom(Latte::View *origin); void availableScreenRegionChangedFrom(Latte::View *origin); private slots: void applyActivitiesToWindows(); void availableScreenRectChangedFromSlot(View *origin); void configViewShownFor(Latte::View *view); void hideWindowsForSlidingOut(); void preferredViewForShortcutsChangedSlot(Latte::View *view); void releaseGrab(); void reloadSource(); void updateTransientWindowsTracking(); void statusChanged(Plasma::Types::ItemStatus); void addTransientWindow(QWindow *window); void removeTransientWindow(const bool &visible); //! workaround in order for top panels to be always on top void topViewAlwaysOnTop(); void verticalUnityViewHasFocus(); //!workround for when kwin hides view when an activity is closing void showHiddenViewFromActivityStopping(); void restoreConfig(); void saveConfig(); private: 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_screenEdgeMarginEnabled{false}; bool m_isTouchingBottomViewAndIsBusy{false}; bool m_isTouchingTopViewAndIsBusy{false}; int m_fontPixelSize{ -1}; int m_editThickness{24}; int m_maxThickness{24}; int m_normalThickness{24}; int m_headThicknessGap{0}; int m_screenEdgeMargin{-1}; float m_maxLength{1}; float m_offset{0}; Types::Alignment m_alignment{Types::Center}; Types::ViewType m_type{Types::DockView}; QRect m_localGeometry; QRect m_absoluteGeometry; QStringList m_activities; //! HACK: In order to avoid crashes when the View is added and removed //! immediately during startup QTimer m_initLayoutTimer; //! 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; QTimer m_releaseGrabTimer; int m_releaseGrab_x; int m_releaseGrab_y; Layout::GenericLayout *m_layout{nullptr}; QPointer m_configView; QPointer m_contextMenu; QPointer m_effects; QPointer m_indicator; QPointer m_interface; QPointer m_positioner; QPointer m_visibility; QPointer m_windowsTracker; QPointer m_interfacesGraphicObj; //! Connections to release and bound for the assigned layout QList connectionsLayout; //! track transientWindows QList m_transientWindows; 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 aca8e955..46ed03ca 100644 --- a/containment/package/contents/ui/VisibilityManager.qml +++ b/containment/package/contents/ui/VisibilityManager.qml @@ -1,1033 +1,1026 @@ /* * 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.core 0.2 as LatteCore import org.kde.latte.private.containment 0.1 as LatteContainment Item{ id: manager anchors.fill: parent property QtObject window property bool debugManager: 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 && (background.totals.visualThickness >= (metrics.iconSize + metrics.margin.thickness)) property bool maskIsFloating: !root.behaveAsPlasmaPanel && !root.editMode && screenEdgeMarginEnabled && !plasmoid.configuration.fittsLawIsRequested && !inSlidingIn && !inSlidingOut property int maskFloatedGap: maskIsFloating ? Math.max(0, metrics.margin.screenEdge - background.shadows.headThickness) : 0 property int animationSpeed: LatteCore.WindowSystem.compositingActive ? (editModeVisual.inEditMode ? editModeVisual.speed * 0.8 : animations.speedFactor.normal * 1.62 * animations.duration.large) : 0 property bool inLocationAnimation: latteView && latteView.positioner && latteView.positioner.inLocationAnimation property bool inSlidingIn: false //necessary because of its init structure property alias inSlidingOut: slidingAnimationAutoHiddenOut.running property bool inTempHiding: false property bool inScreenEdgeInternalWindowSliding: root.behaveAsDockWithMask && hideThickScreenGap property int length: root.isVertical ? Screen.height : Screen.width //screenGeometry.height : screenGeometry.width property int slidingOutToPos: { if (root.behaveAsPlasmaPanel) { var edgeMargin = screenEdgeMarginEnabled ? plasmoid.configuration.screenEdgeMargin : 0 root.isHorizontal ? root.height + edgeMargin - 1 : root.width + edgeMargin - 1; } else { var topOrLeftEdge = ((plasmoid.location===PlasmaCore.Types.LeftEdge)||(plasmoid.location===PlasmaCore.Types.TopEdge)); return (topOrLeftEdge ? -thicknessNormal : thicknessNormal); } } property int finalScreenEdgeMargin: { //! is used for window geometry calculations if (!screenEdgeMarginEnabled || (hideThickScreenGap && metrics.margin.screenEdge === 0)) { /*window geometry is updated after the local screen margin animation was zeroed*/ return 0; } return plasmoid.configuration.screenEdgeMargin; } property int thicknessAutoHidden: LatteCore.WindowSystem.compositingActive ? 2 : 1 property int thicknessMid: finalScreenEdgeMargin + (1 + (0.65 * (parabolic.factor.maxZoom-1)))*(metrics.totals.thickness+extraZoomThickMask) //needed in some animations property int thicknessNormal: finalScreenEdgeMargin + Math.max(metrics.totals.thickness + extraNormalThickMask, background.thickness + background.shadows.headThickness) property int thicknessZoom: finalScreenEdgeMargin + ((metrics.totals.thickness+extraZoomThickMask) * parabolic.factor.maxZoom) + 2 //it is used to keep thickness solid e.g. when iconSize changes from auto functions property int thicknessMidOriginal: finalScreenEdgeMargin + Math.max(thicknessNormalOriginal,extraNormalThickMask + (1 + (0.65 * (parabolic.factor.maxZoom-1)))*(metrics.maxIconSize+metrics.margin.maxThickness)) //needed in some animations property int thicknessNormalOriginal: finalScreenEdgeMargin + metrics.maxIconSize + (metrics.margin.maxThickness * 2) //this way we always have the same thickness published at all states /*property int thicknessNormalOriginal: !root.behaveAsPlasmaPanel || root.editMode ? thicknessNormalOriginalValue : background.thickness + background.shadows.headThickness*/ property int thicknessNormalOriginalValue: finalScreenEdgeMargin + metrics.maxIconSize + (metrics.margin.maxThickness * 2) + extraNormalThickMask property int thicknessZoomOriginal: finalScreenEdgeMargin + Math.max( ((metrics.maxIconSize+(metrics.margin.maxThickness * 2)) * parabolic.factor.maxZoom) + extraZoomThickMask, background.thickness + background.shadows.headThickness, (LatteCore.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 //! when Latte behaves as Plasma panel property int thicknessAsPanel: metrics.totals.thickness //! is used to increase the mask thickness readonly property int marginBetweenContentsAndRuler: 10 property int extraNormalThickMask: Math.max(indicatorsExtraThickMask, shadowsExtraThickMask) property int extraZoomThickMask: 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 (LatteCore.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 && metrics.margin.maxThickness < shadowMaxNeededMargin) { return shadowMaxNeededMargin - metrics.margin.maxThickness; } return 0; } property Item applets: null Binding{ target: latteView property:"maxThickness" //! prevents updating window geometry during closing window in wayland and such fixes a crash when: latteView && !inTempHiding && !inForceHiding && !inScreenEdgeInternalWindowSliding value: root.behaveAsPlasmaPanel && !root.editMode ? thicknessAsPanel : thicknessZoomOriginal } property bool validIconSize: (metrics.iconSize===metrics.maxIconSize || metrics.iconSize === autosize.iconSize) property bool inPublishingState: validIconSize && !inSlidingIn && !inSlidingOut && !inTempHiding && !inForceHiding Binding{ target: latteView property:"normalThickness" when: latteView && inPublishingState value: root.behaveAsPlasmaPanel && !root.editMode ? thicknessAsPanel : thicknessNormalOriginal } Binding{ target: latteView property:"editThickness" when: latteView value: thicknessEditMode } Binding { target: latteView property: "headThicknessGap" when: latteView && !root.editMode && !editModeVisual.inEditMode && !inTempHiding && !inForceHiding && !inScreenEdgeInternalWindowSliding && inPublishingState value: { if (root.behaveAsPlasmaPanel || root.viewType === LatteCore.Types.PanelView) { return 0; } return thicknessZoomOriginal - thicknessNormalOriginalValue + extraNormalThickMask; } } 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: maxLengthPerCentage/100 } Binding{ target: latteView property: "offset" when: latteView value: plasmoid.configuration.offset/100 } Binding{ target: latteView property: "screenEdgeMargin" when: latteView value: Math.max(0, plasmoid.configuration.screenEdgeMargin) } Binding{ target: latteView property: "screenEdgeMarginEnabled" when: latteView value: root.screenEdgeMarginEnabled && !root.hideThickScreenGap } Binding{ target: latteView property: "alignment" when: latteView value: root.panelAlignment } Binding{ target: latteView property: "isTouchingTopViewAndIsBusy" when: root.viewIsAvailable value: { if (!root.viewIsAvailable) { return false; } var isTouchingTopScreenEdge = (latteView.y === latteView.screenGeometry.y); var hasTopBorder = ((latteView.effects && (latteView.effects.enabledBorders & PlasmaCore.FrameSvg.TopBorder)) > 0); return root.isVertical && !latteView.visibility.isHidden && !isTouchingTopScreenEdge && !hasTopBorder && background.isShown; } } Binding{ target: latteView property: "isTouchingBottomViewAndIsBusy" when: latteView value: { if (!root.viewIsAvailable) { return false; } 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 && !latteView.visibility.isHidden && !isTouchingBottomScreenEdge && !hasBottomBorder && background.isShown; } } //! View::Effects bindings Binding{ target: latteView && latteView.effects ? latteView.effects : null property: "backgroundOpacity" when: latteView && latteView.effects value: background.currentOpacity } Binding{ target: latteView && latteView.effects ? latteView.effects : null property: "drawEffects" when: latteView && latteView.effects value: LatteCore.WindowSystem.compositingActive && !root.inConfigureAppletsMode && (((root.blurEnabled && root.useThemePanel) || (root.blurEnabled && root.forceSolidPanel && LatteCore.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) && !(latteView && latteView.visibility.isHidden) } 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 background.shadows.headThickness; } } } //! View::Positioner bindings Binding{ target: latteView && latteView.positioner ? latteView.positioner : null property: "isStickedOnTopEdge" when: latteView && latteView.positioner value: plasmoid.configuration.isStickedOnTopEdge } Binding{ target: latteView && latteView.positioner ? latteView.positioner : null property: "isStickedOnBottomEdge" when: latteView && latteView.positioner value: plasmoid.configuration.isStickedOnBottomEdge } //! 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 === LatteCore.Types.AlwaysVisible /* Visibility */ || latteView.visibility.mode === LatteCore.Types.WindowsGoBelow || latteView.visibility.mode === LatteCore.Types.AutoHide)) || applets.require.windowsTrackingCount > 0 /*Applets Need Windows Tracking */ || root.dragActiveWindowEnabled /*Dragging Active Window(Empty Areas)*/ || ((root.backgroundOnlyOnMaximized /*Dynamic Background */ || plasmoid.configuration.solidBackgroundForMaximized || root.disablePanelShadowMaximized || root.windowColors !== LatteContainment.Types.NoneWindowColors)) || (root.screenEdgeMarginsEnabled /*Dynamic Screen Edge Margin*/ && plasmoid.configuration.hideScreenGapForMaximized) } Connections{ target:root onEditModeChanged: { if (root.editMode) { visibilityManager.updateMaskArea(); } } } Connections{ target: background.totals onVisualLengthChanged: updateMaskArea(); onVisualThicknessChanged: updateMaskArea(); } Connections{ target: background.shadows onHeadThicknessChanged: updateMaskArea(); } Connections{ target: latteView ? latteView : null onXChanged: updateMaskArea(); onYChanged: updateMaskArea() onWidthChanged: updateMaskArea(); onHeightChanged: updateMaskArea(); } Connections{ target: animations.needBothAxis onCountChanged: updateMaskArea(); } Connections{ target: animations.needLength onCountChanged: updateMaskArea(); } Connections{ target: animations.needThickness onCountChanged: updateMaskArea(); } Connections{ target: layoutsManager onCurrentLayoutIsSwitching: { if (LatteCore.WindowSystem.compositingActive && latteView && latteView.layout && latteView.layout.name === layoutName) { manager.inTempHiding = true; manager.inForceHiding = true; parabolic.sglClearZoom(); manager.slotMustBeHide(); } } } Connections{ target: themeExtended ? themeExtended : null onRoundnessChanged: latteView.effects.forceMaskRedraw(); onThemeChanged: latteView.effects.forceMaskRedraw(); } onMaskIsFloatingChanged: updateMaskArea(); onNormalStateChanged: { if (normalState) { autosize.updateIconSize(); layouter.updateSizeForAppletsInFill(); } } onThicknessZoomOriginalChanged: updateMaskArea(); function slotContainsMouseChanged() { if(latteView.visibility.containsMouse && latteView.visibility.mode !== LatteCore.Types.SideBar) { updateMaskArea(); if (slidingAnimationAutoHiddenOut.running && !inTempHiding && !inForceHiding) { slotMustBeShown(); } } } function slotMustBeShown() { //! WindowsCanCover case if (latteView && latteView.visibility.mode === LatteCore.Types.WindowsCanCover) { latteView.visibility.setViewOnFrontLayer(); return; } //! Normal Dodge/AutoHide case if (!slidingAnimationAutoHiddenIn.running && !inTempHiding && !inForceHiding){ slidingAnimationAutoHiddenIn.init(); } } function slotMustBeHide() { if (latteView && latteView.visibility.mode === LatteCore.Types.WindowsCanCover) { latteView.visibility.setViewOnBackLayer(); return; } //! prevent sliding-in on startup if the dodge modes have sent a hide signal if (inStartupTimer.running && root.inStartup) { root.inStartup = false; } //! Normal Dodge/AutoHide case if((!slidingAnimationAutoHiddenOut.running && !latteView.visibility.blockHiding && (!latteView.visibility.containsMouse || latteView.visibility.mode === LatteCore.Types.SideBar)) || inForceHiding) { slidingAnimationAutoHiddenOut.init(); } } //! functions used for sliding out/in during location/screen changes function slotHideDockDuringLocationChange() { inTempHiding = true; blockUpdateMask = true; if(!slidingAnimationAutoHiddenOut.running) { slidingAnimationAutoHiddenOut.init(); } } function slotShowDockAfterLocationChange() { slidingAnimationAutoHiddenIn.init(); } function sendHideDockDuringLocationChangeFinished(){ blockUpdateMask = false; latteView.positioner.hideDockDuringLocationChangeFinished(); } function sendSlidingOutAnimationEnded() { latteView.visibility.hide(); latteView.visibility.isHidden = true; if (visibilityManager.debugManager) { console.log("hiding animation ended..."); } sendHideDockDuringLocationChangeFinished(); } ///test maskArea function updateMaskArea() { if (!latteView || !root.viewIsAvailable || blockUpdateMask) { return; } var localX = 0; var localY = 0; normalState = ((animations.needBothAxis.count === 0) && (animations.needLength.count === 0)) || (latteView && latteView.visibility.isHidden && !latteView.visibility.containsMouse && animations.needThickness.count === 0); // debug maskArea criteria if (debugManager) { console.log(animations.needBothAxis.count + ", " + animations.needLength.count + ", " + animations.needThickness.count + ", " + 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 noCompositingEdit = !LatteCore.WindowSystem.compositingActive && root.editMode; if (LatteCore.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 { tempLength = background.totals.visualLength; } tempThickness = thicknessNormal; if (animations.needThickness.count > 0) { tempThickness = LatteCore.WindowSystem.compositingActive ? thicknessZoom : thicknessNormal; } if (maskIsFloating) { tempThickness = tempThickness - maskFloatedGap; } 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) { if (latteView.visibility.isHidden && latteView.visibility.supportsKWinEdges) { localY = latteView.height + tempThickness; } else if (maskIsFloating && !latteView.visibility.isHidden) { localY = latteView.height - tempThickness - maskFloatedGap; } else { localY = latteView.height - tempThickness; } } else if (plasmoid.location === PlasmaCore.Types.TopEdge) { if (latteView.visibility.isHidden && latteView.visibility.supportsKWinEdges) { localY = -tempThickness; } else if (maskIsFloating && !latteView.visibility.isHidden) { localY = maskFloatedGap; } else { localY = 0; } } if (noCompositingEdit) { localX = 0; } else if (plasmoid.configuration.alignment === LatteCore.Types.Justify) { localX = (latteView.width/2) - tempLength/2 + background.offset; } else if (root.panelAlignment === LatteCore.Types.Left) { localX = background.offset; } else if (root.panelAlignment === LatteCore.Types.Center) { localX = (latteView.width/2) - tempLength/2 + background.offset; } else if (root.panelAlignment === LatteCore.Types.Right) { localX = latteView.width - tempLength - background.offset; } } else if ((plasmoid.location === PlasmaCore.Types.LeftEdge) || (plasmoid.location === PlasmaCore.Types.RightEdge)){ if (plasmoid.location === PlasmaCore.Types.LeftEdge) { if (latteView.visibility.isHidden && latteView.visibility.supportsKWinEdges) { localX = -tempThickness; } else if (maskIsFloating && !latteView.visibility.isHidden) { localX = maskFloatedGap; } else { localX = 0; } } else if (plasmoid.location === PlasmaCore.Types.RightEdge) { if (latteView.visibility.isHidden && latteView.visibility.supportsKWinEdges) { localX = latteView.width + tempThickness; } else if (maskIsFloating && !latteView.visibility.isHidden) { localX = latteView.width - tempThickness - maskFloatedGap; } else { localX = latteView.width - tempThickness; } } if (noCompositingEdit) { localY = 0; } else if (plasmoid.configuration.alignment === LatteCore.Types.Justify) { localY = (latteView.height/2) - tempLength/2 + background.offset; } else if (root.panelAlignment === LatteCore.Types.Top) { localY = background.offset; } else if (root.panelAlignment === LatteCore.Types.Center) { localY = (latteView.height/2) - tempLength/2 + background.offset; } else if (root.panelAlignment === LatteCore.Types.Bottom) { localY = latteView.height - tempLength - background.offset; } } if (latteView.visibility.isHidden && latteView && latteView.visibility.mode === LatteCore.Types.SideBar) { //!hide completely localX = -1; localY = -1; tempThickness = 1; tempLength = 1; } } else { // !inNormalState if(root.isHorizontal) tempLength = Screen.width; //screenGeometry.width; else tempLength = Screen.height; //screenGeometry.height; //grow only on length and not thickness var onlyLengthAnimation = (animations.needLength.count>0 && animations.needBothAxis.count === 0); if(onlyLengthAnimation) { //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 (animations.needThickness.count > 0) { tempThickness = thicknessZoomOriginal; } } else{ //use all thickness space if (latteView.visibility.isHidden && !slidingAnimationAutoHiddenOut.running ) { tempThickness = LatteCore.WindowSystem.compositingActive ? thicknessAutoHidden : thicknessNormalOriginal; } else { tempThickness = !maskIsFloating ? thicknessZoomOriginal : thicknessZoomOriginal - maskFloatedGap; } } //configure the x,y position based on thickness if(plasmoid.location === PlasmaCore.Types.RightEdge) { localX = !maskIsFloating ? latteView.width - tempThickness : latteView.width - tempThickness - maskFloatedGap; if (localX < 0) { tempThickness = tempThickness + localX; localX = 0; } } else if (plasmoid.location === PlasmaCore.Types.BottomEdge) { localY = !maskIsFloating ? latteView.height - tempThickness : latteView.height - tempThickness - maskFloatedGap; if (localY < 0) { tempThickness = tempThickness + localY; localY = 0; } } else if (plasmoid.location === PlasmaCore.Types.TopEdge) { localY = !maskIsFloating ? 0 : maskFloatedGap; } else if (plasmoid.location === PlasmaCore.Types.LeftEdge) { localX = !maskIsFloating ? 0 : maskFloatedGap; } } } // end of compositing calculations var maskArea = latteView.effects.mask; if (LatteCore.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 (!LatteCore.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 = (metrics.iconSize===metrics.maxIconSize || metrics.iconSize === autosize.iconSize); //console.log("reached updating geometry ::: "+dock.maskArea); if(inPublishingState && !latteView.visibility.isHidden && (normalState || root.editMode)) { //! Important: Local Geometry must not be updated when view ISHIDDEN //! because it breaks Dodge(s) modes in such case var localGeometry = Qt.rect(0, 0, root.width, root.height); //the shadows size must be removed from the maskArea //before updating the localDockGeometry if (!latteView.behaveAsPlasmaPanel || root.editMode) { var cleanThickness = metrics.totals.thickness; var edgeMargin = finalScreenEdgeMargin; if (plasmoid.location === PlasmaCore.Types.TopEdge) { localGeometry.x = latteView.effects.rect.x; // from effects area localGeometry.width = latteView.effects.rect.width; // from effects area localGeometry.y = edgeMargin; localGeometry.height = cleanThickness ; } else if (plasmoid.location === PlasmaCore.Types.BottomEdge) { localGeometry.x = latteView.effects.rect.x; // from effects area localGeometry.width = latteView.effects.rect.width; // from effects area localGeometry.y = root.height - cleanThickness - edgeMargin; localGeometry.height = cleanThickness; } else if (plasmoid.location === PlasmaCore.Types.LeftEdge) { localGeometry.y = latteView.effects.rect.y; // from effects area localGeometry.height = latteView.effects.rect.height; // from effects area localGeometry.x = edgeMargin; localGeometry.width = cleanThickness; } else if (plasmoid.location === PlasmaCore.Types.RightEdge) { localGeometry.y = latteView.effects.rect.y; // from effects area localGeometry.height = latteView.effects.rect.height; // from effects area localGeometry.x = root.width - cleanThickness - edgeMargin; localGeometry.width = cleanThickness; } //set the boundaries for latteView local geometry //qBound = qMax(min, qMin(value, max)). localGeometry.x = Math.max(0, Math.min(localGeometry.x, latteView.width)); localGeometry.y = Math.max(0, Math.min(localGeometry.y, latteView.height)); localGeometry.width = Math.min(localGeometry.width, latteView.width); localGeometry.height = Math.min(localGeometry.height, latteView.height); } //console.log("update geometry ::: "+localGeometry); latteView.localGeometry = localGeometry; } } 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: !root.behaveAsPlasmaPanel ? layoutsContainer : latteView.positioner property: !root.behaveAsPlasmaPanel ? (root.isVertical ? "x" : "y") : "slideOffset" to: { if (root.behaveAsPlasmaPanel) { return slidingOutToPos; } if (LatteCore.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; if (root.behaveAsPlasmaPanel && latteView.positioner.slideOffset !== 0) { //! hide real panels when they slide-out latteView.visibility.hide(); } } } onStarted: { if (manager.debugManager) { 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 (manager.inLocationAnimation || !latteView.visibility.blockHiding) { start(); } } } SequentialAnimation{ id: slidingAnimationAutoHiddenIn PauseAnimation{ duration: manager.inTempHiding && animations.active ? 500 : 0 } PropertyAnimation { target: !root.behaveAsPlasmaPanel ? layoutsContainer : latteView.positioner property: !root.behaveAsPlasmaPanel ? (root.isVertical ? "x" : "y") : "slideOffset" to: 0 duration: manager.animationSpeed easing.type: Easing.OutQuad } ScriptAction{ script: { root.isHalfShown = false; root.inStartup = false; } } onStarted: { latteView.visibility.show(); if (manager.debugManager) { console.log("showing animation started..."); } } onStopped: { inSlidingIn = false; if (manager.inTempHiding) { manager.inTempHiding = false; autosize.updateIconSize(); } manager.inTempHiding = false; autosize.updateIconSize(); if (manager.debugManager) { console.log("showing animation ended..."); } latteView.visibility.slideInFinished(); //! this is needed in order to update dock absolute geometry correctly in the end AND //! when a floating dock is sliding-in through masking techniques updateMaskArea(); } function init() { if (!root.viewIsAvailable) { return; } inSlidingIn = true; if (slidingAnimationAutoHiddenOut.running) { slidingAnimationAutoHiddenOut.stop(); } latteView.visibility.isHidden = false; updateMaskArea(); start(); } } //! Slides Animations for FLOATING+BEHAVEASPLASMAPANEL when //! HIDETHICKSCREENCAP dynamically is enabled/disabled SequentialAnimation{ id: slidingInRealFloating PropertyAnimation { target: latteView ? latteView.positioner : null property: "slideOffset" to: 0 duration: manager.animationSpeed easing.type: Easing.OutQuad } } SequentialAnimation{ id: slidingOutRealFloating PropertyAnimation { target: latteView ? latteView.positioner : null property: "slideOffset" to: plasmoid.configuration.screenEdgeMargin duration: manager.animationSpeed easing.type: Easing.InQuad } } Connections { target: root onHideThickScreenGapChanged: { if (!latteView || !root.viewIsAvailable) { return; } if (root.behaveAsPlasmaPanel && !latteView.visibility.isHidden && !inSlidingIn && !inSlidingOut && !inStartup) { if (hideThickScreenGap) { latteView.positioner.inSlideAnimation = true; slidingInRealFloating.stop(); slidingOutRealFloating.start(); } else { slidingOutRealFloating.stop(); slidingInRealFloating.start(); latteView.positioner.inSlideAnimation = false; } } } } } diff --git a/containment/package/contents/ui/main.qml b/containment/package/contents/ui/main.qml index 74a746f1..8856ed81 100644 --- a/containment/package/contents/ui/main.qml +++ b/containment/package/contents/ui/main.qml @@ -1,1697 +1,1684 @@ /* * 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.8 import QtQuick.Layouts 1.1 import QtQuick.Window 2.2 import QtGraphicalEffects 1.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.kquickcontrolsaddons 2.0 import org.kde.plasma.plasmoid 2.0 import org.kde.latte.core 0.2 as LatteCore import org.kde.latte.components 1.0 as LatteComponents import org.kde.latte.private.app 0.1 as LatteApp import org.kde.latte.private.containment 0.1 as LatteContainment import "abilities" as Ability import "applet" as Applet import "colorizer" as Colorizer import "editmode" as EditMode import "indicators" as Indicators import "layouts" as Layouts import "./background" as Background import "./debug" as Debug import "../code/LayoutManager.js" as LayoutManager Item { id: root objectName: "containmentViewLayout" LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft && !root.isVertical LayoutMirroring.childrenInherit: true //// BEGIN SIGNALS signal destroyInternalViewSplitters(); signal emptyAreasWheel(QtObject wheel); signal separatorsUpdated(); signal signalActivateEntryAtIndex(int entryIndex); signal signalNewInstanceForEntryAtIndex(int entryIndex); signal updateEffectsArea(); signal updateIndexes(); signal broadcastedToApplet(string pluginName, string action, variant value); //// END SIGNALS ////BEGIN properties property bool debugMode: Qt.application.arguments.indexOf("--graphics")>=0 property bool debugModeLayouter: Qt.application.arguments.indexOf("--layouter")>=0 property bool debugModeLocalGeometry: Qt.application.arguments.indexOf("--localgeometry")>=0 property bool debugModeSpacers: Qt.application.arguments.indexOf("--spacers")>=0 property bool debugModeTimers: Qt.application.arguments.indexOf("--timers")>=0 property bool debugModeWindow: Qt.application.arguments.indexOf("--with-window")>=0 property bool debugModeOverloadedIcons: Qt.application.arguments.indexOf("--overloaded-icons")>=0 readonly property int version: LatteCore.Environment.makeVersion(0,9,4) property bool addLaunchersMessage: false property bool addLaunchersInTaskManager: plasmoid.configuration.addLaunchersInTaskManager property bool backgroundOnlyOnMaximized: plasmoid.configuration.backgroundOnlyOnMaximized property bool behaveAsPlasmaPanel: { if (!LatteCore.WindowSystem.compositingActive) { //! In NOCOMPOSITING mode VIEWS should behave as real windows and that way //! we gain also the snapping features from KWin return true; } if (!latteView || !latteView.visibility) { return false; } if (screenEdgeMarginEnabled && plasmoid.configuration.fittsLawIsRequested) { //! dont use when floating views are requesting Fitt's Law return false; } var staticLayout = (plasmoid.configuration.minLength === plasmoid.configuration.maxLength); return (visibilityManager.panelIsBiggerFromIconSize && (parabolic.factor.maxZoom === 1.0) && (plasmoid.configuration.alignment === LatteCore.Types.Justify || staticLayout) && !root.editMode && !visibilityManager.inLocationAnimation); } readonly property bool behaveAsDockWithMask: !behaveAsPlasmaPanel readonly property bool viewIsAvailable: latteView && latteView.visibility && latteView.effects property int viewType: { var staticLayout = (plasmoid.configuration.minLength === plasmoid.configuration.maxLength); if ((plasmoid.configuration.alignment === LatteCore.Types.Justify || staticLayout) && (plasmoid.configuration.useThemePanel) && (plasmoid.configuration.panelSize === 100) && (parabolic.factor.maxZoom === 1.0)) { return LatteCore.Types.PanelView; } return LatteCore.Types.DockView; } property bool blurEnabled: plasmoid.configuration.blurEnabled && (!forceTransparentPanel || forcePanelForBusyBackground) readonly property bool ignoreRegularFilesDragging: !root.editMode && (dragInfo.computationsAreValid || foreDropArea.dragInfo.computationsAreValid) && !root.dragInfo.isPlasmoid && !root.dragInfo.onlyLaunchers readonly property Item dragInfo: Item { property bool entered: backDropArea.dragInfo.entered || foreDropArea.dragInfo.entered property bool isTask: backDropArea.dragInfo.isTask || foreDropArea.dragInfo.isTask property bool isPlasmoid: backDropArea.dragInfo.isPlasmoid || foreDropArea.dragInfo.isPlasmoid property bool isSeparator: backDropArea.dragInfo.isSeparator || foreDropArea.dragInfo.isSeparator property bool isLatteTasks: backDropArea.dragInfo.isLatteTasks || foreDropArea.dragInfo.isLatteTasks property bool onlyLaunchers: backDropArea.dragInfo.onlyLaunchers || foreDropArea.dragInfo.onlyLaunchers // onIsPlasmoidChanged: console.log("isPlasmoid :: " + backDropArea.dragInfo.isPlasmoid + " _ " + foreDropArea.dragInfo.isPlasmoid ); // onEnteredChanged: console.log("entered :: " + backDropArea.dragInfo.entered + " _ " + foreDropArea.dragInfo.entered ); } - property bool containsOnlyPlasmaTasks: false //this is flag to indicate when from tasks only a plasma based one is found + property bool containsOnlyPlasmaTasks: latteView ? latteView.extendedInterface.hasPlasmaTasks && !latteView.extendedInterface.hasLatteTasks : false property bool dockContainsMouse: latteView && latteView.visibility ? latteView.visibility.containsMouse : false property bool disablePanelShadowMaximized: plasmoid.configuration.disablePanelShadowForMaximized && LatteCore.WindowSystem.compositingActive property bool drawShadowsExternal: panelShadowsActive && behaveAsPlasmaPanel && !visibilityManager.inTempHiding property bool editMode: editModeVisual.inEditMode property bool windowIsTouching: latteView && latteView.windowsTracker && (latteView.windowsTracker.currentScreen.activeWindowTouching || latteView.windowsTracker.currentScreen.activeWindowTouchingEdge || hasExpandedApplet) property bool forceSolidPanel: (latteView && latteView.visibility && LatteCore.WindowSystem.compositingActive && !inConfigureAppletsMode && userShowPanelBackground && ( (plasmoid.configuration.solidBackgroundForMaximized && !(hasExpandedApplet && !plasmaBackgroundForPopups) && (latteView.windowsTracker.currentScreen.existsWindowTouching || latteView.windowsTracker.currentScreen.existsWindowTouchingEdge)) || (hasExpandedApplet && plasmaBackgroundForPopups) )) || solidBusyForTouchingBusyVerticalView || plasmaStyleBusyForTouchingBusyVerticalView || !LatteCore.WindowSystem.compositingActive property bool forceTransparentPanel: root.backgroundOnlyOnMaximized && latteView && latteView.visibility && LatteCore.WindowSystem.compositingActive && !inConfigureAppletsMode && !forceSolidPanel && !(latteView.windowsTracker.currentScreen.existsWindowTouching || latteView.windowsTracker.currentScreen.existsWindowTouchingEdge) && !(windowColors === LatteContainment.Types.ActiveWindowColors && selectedWindowsTracker.existsWindowActive) property bool forcePanelForBusyBackground: userShowPanelBackground && (root.themeColors === LatteContainment.Types.SmartThemeColors) && ( (root.forceTransparentPanel && colorizerManager.backgroundIsBusy) || normalBusyForTouchingBusyVerticalView ) property bool normalBusyForTouchingBusyVerticalView: (latteView && latteView.windowsTracker /*is touching a vertical view that is in busy state and the user prefers isBusy transparency*/ && latteView.windowsTracker.currentScreen.isTouchingBusyVerticalView && root.themeColors === LatteContainment.Types.SmartThemeColors && plasmoid.configuration.backgroundOnlyOnMaximized /*&& !plasmoid.configuration.solidBackgroundForMaximized && !plasmaBackgroundForPopups*/) property bool solidBusyForTouchingBusyVerticalView: false //DISABLED, until to check if the normalBusyForTouchingBusyVerticalView is enough to catch and handle the case /*(latteView && latteView.windowsTracker /*is touching a vertical view that is in busy state and the user prefers solidness*/ /* && latteView.windowsTracker.currentScreen.isTouchingBusyVerticalView && root.themeColors === LatteContainment.Types.SmartThemeColors && plasmoid.configuration.backgroundOnlyOnMaximized && plasmoid.configuration.solidBackgroundForMaximized && !plasmaBackgroundForPopups)*/ property bool plasmaStyleBusyForTouchingBusyVerticalView: false //DISABLED, until to check if the normalBusyForTouchingBusyVerticalView is enough to catch and handle the case //(latteView && latteView.windowsTracker /*is touching a vertical view that is in busy state and the user prefers solidness*/ /* && latteView.windowsTracker.currentScreen.isTouchingBusyVerticalView && root.themeColors === LatteContainment.Types.SmartThemeColors && plasmoid.configuration.backgroundOnlyOnMaximized && plasmaBackgroundForPopups)*/ property bool hideThickScreenGap: screenEdgeMarginEnabled && plasmoid.configuration.hideScreenGapForMaximized && latteView && latteView.windowsTracker && latteView.windowsTracker.currentScreen.existsWindowMaximized property bool hideLengthScreenGaps: hideThickScreenGap && (latteView.visibility.mode === LatteCore.Types.AlwaysVisible || latteView.visibility.mode === LatteCore.Types.WindowsGoBelow) && (plasmoid.configuration.alignment === LatteCore.Types.Justify) && plasmoid.configuration.maxLength>85 && !root.editMode property int themeColors: plasmoid.configuration.themeColors property int windowColors: plasmoid.configuration.windowColors property bool colorizerEnabled: themeColors !== LatteContainment.Types.PlasmaThemeColors || windowColors !== LatteContainment.Types.NoneWindowColors property bool plasmaBackgroundForPopups: plasmoid.configuration.plasmaBackgroundForPopups readonly property bool hasExpandedApplet: latteView && latteView.extendedInterface.hasExpandedApplet; readonly property bool hasUserSpecifiedBackground: (latteView && latteView.layout && latteView.layout.background.startsWith("/")) ? true : false readonly property bool inConfigureAppletsMode: root.editMode && (plasmoid.configuration.inConfigureAppletsMode || !LatteCore.WindowSystem.compositingActive) readonly property bool parabolicEffectEnabled: parabolic.factor.zoom>1 && !inConfigureAppletsMode property bool dockIsShownCompletely: !(dockIsHidden || inSlidingIn || inSlidingOut) && !root.editMode property bool closeActiveWindowEnabled: plasmoid.configuration.closeActiveWindowEnabled property bool dragActiveWindowEnabled: plasmoid.configuration.dragActiveWindowEnabled property bool immutable: plasmoid.immutable property bool inFullJustify: (plasmoid.configuration.alignment === LatteCore.Types.Justify) && (maxLengthPerCentage===100) property bool inSlidingIn: visibilityManager ? visibilityManager.inSlidingIn : false property bool inSlidingOut: visibilityManager ? visibilityManager.inSlidingOut : false property bool inStartup: true property bool isHalfShown: false //is used to disable the zoom hovering effect at sliding in-out the dock property bool isHorizontal: plasmoid.formFactor === PlasmaCore.Types.Horizontal property bool isReady: !(dockIsHidden || inSlidingIn || inSlidingOut) property bool isVertical: !isHorizontal property bool mouseWheelActions: plasmoid.configuration.mouseWheelActions property bool onlyAddingStarup: true //is used for the initialization phase in startup where there aren't removals, this variable provides a way to grow icon size property bool shrinkThickMargins: plasmoid.configuration.shrinkThickMargins property bool showLatteShortcutBadges: false property bool showAppletShortcutBadges: false property bool showMetaBadge: false property int applicationLauncherId: -1 //FIXME: possibly this is going to be the default behavior, this user choice //has been dropped from the Dock Configuration Window //property bool smallAutomaticIconJumps: plasmoid.configuration.smallAutomaticIconJumps property bool smallAutomaticIconJumps: true property bool userShowPanelBackground: LatteCore.WindowSystem.compositingActive ? plasmoid.configuration.useThemePanel : true property bool useThemePanel: noApplets === 0 || !LatteCore.WindowSystem.compositingActive ? true : (plasmoid.configuration.useThemePanel || plasmoid.configuration.solidBackgroundForMaximized) property bool plasma515: LatteCore.Environment.plasmaDesktopVersion >= LatteCore.Environment.makeVersion(5,15,0) property bool plasma518: LatteCore.Environment.plasmaDesktopVersion >= LatteCore.Environment.makeVersion(5,18,0) readonly property int minAppletLengthInConfigure: 16 readonly property int maxJustifySplitterSize: 64 property int latteAppletPos: -1 property real minLengthPerCentage: plasmoid.configuration.minLength property real maxLengthPerCentage: hideLengthScreenGaps ? 100 : plasmoid.configuration.maxLength property int minLength: { if (root.panelAlignment === LatteCore.Types.Justify) { return maxLength; } if (root.isHorizontal) { return behaveAsPlasmaPanel && LatteCore.WindowSystem.compositingActive ? width : width * (minLengthPerCentage/100) } else { return behaveAsPlasmaPanel && LatteCore.WindowSystem.compositingActive ? height : height * (minLengthPerCentage/100) } } property int maxLength: { if (root.isHorizontal) { return behaveAsPlasmaPanel ? width : width * (maxLengthPerCentage/100) } else { return behaveAsPlasmaPanel ? height : height * (maxLengthPerCentage/100) } } property int scrollAction: plasmoid.configuration.scrollAction property bool panelOutline: plasmoid.configuration.panelOutline property int panelEdgeSpacing: Math.max(background.lengthMargins, 1.5*appShadowSize) property int panelTransparency: plasmoid.configuration.panelTransparency //user set property bool panelShadowsActive: { if (!userShowPanelBackground) { return false; } if (inConfigureAppletsMode) { return plasmoid.configuration.panelShadows; } var forcedNoShadows = (plasmoid.configuration.panelShadows && disablePanelShadowMaximized && latteView && latteView.windowsTracker && latteView.windowsTracker.currentScreen.activeWindowMaximized); if (forcedNoShadows) { return false; } var transparencyCheck = (blurEnabled || (!blurEnabled && background.currentOpacity>20)); //! Draw shadows for isBusy state only when current panelTransparency is greater than 10% if (plasmoid.configuration.panelShadows && root.forcePanelForBusyBackground && transparencyCheck) { return true; } if (( (plasmoid.configuration.panelShadows && !root.backgroundOnlyOnMaximized) || (plasmoid.configuration.panelShadows && root.backgroundOnlyOnMaximized && !root.forceTransparentPanel)) && !forcedNoShadows) { return true; } if (hasExpandedApplet && plasmaBackgroundForPopups) { return true; } return false; } property int appShadowOpacity: (plasmoid.configuration.shadowOpacity/100) * 255 property int appShadowSize: enableShadows ? (0.5*metrics.iconSize) * (plasmoid.configuration.shadowSize/100) : 0 property int appShadowSizeOriginal: enableShadows ? (0.5*metrics.maxIconSize) * (plasmoid.configuration.shadowSize/100) : 0 property string appChosenShadowColor: { if (plasmoid.configuration.shadowColorType === LatteContainment.Types.ThemeColorShadow) { var strC = String(theme.textColor); return strC.indexOf("#") === 0 ? strC.substr(1) : strC; } else if (plasmoid.configuration.shadowColorType === LatteContainment.Types.UserColorShadow) { return plasmoid.configuration.shadowColor; } // default shadow color return "080808"; } property string appShadowColor: "#" + decimalToHex(appShadowOpacity) + appChosenShadowColor property string appShadowColorSolid: "#" + appChosenShadowColor property int offset: { if (behaveAsPlasmaPanel) { return 0; } if (root.isHorizontal) { return width * (plasmoid.configuration.offset/100); } else { height * (plasmoid.configuration.offset/100) } } property int editShadow: { if (!LatteCore.WindowSystem.compositingActive) { return 0; } else if (latteView && latteView.screenGeometry) { return latteView.screenGeometry.height/90; } else { return 7; } } property bool screenEdgeMarginEnabled: plasmoid.configuration.screenEdgeMargin >= 0 && !plasmoid.configuration.shrinkThickMargins property int widthMargins: root.isVertical ? metrics.totals.thicknessEdges : metrics.totals.lengthEdges property int heightMargins: root.isHorizontal ? metrics.totals.thicknessEdges : metrics.totals.lengthEdges property int panelAlignment: plasmoid.configuration.alignment readonly property string plasmoidName: "org.kde.latte.plasmoid" property var badgesForActivate: { if (!shortcutsEngine) { return ['1','2','3','4','5','6','7','8','9','0', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.']; } return shortcutsEngine.badgesForActivate; } property var iconsArray: [16, 22, 32, 48, 64, 96, 128, 256] property Item dragOverlay property Item toolBox property Item latteAppletContainer property Item latteApplet readonly property alias animations: _animations readonly property alias background: _background readonly property alias autosize: _autosize readonly property alias indexer: _indexer readonly property alias indicatorsManager: indicators readonly property alias layouter: _layouter readonly property alias metrics: _metrics readonly property alias parabolic: _parabolic readonly property alias parabolicManager: _parabolicManager readonly property alias maskManager: visibilityManager readonly property alias layoutsContainerItem: layoutsContainer readonly property alias latteView: _interfaces.view readonly property alias layoutsManager: _interfaces.layoutsManager readonly property alias shortcutsEngine: _interfaces.globalShortcuts readonly property alias themeExtended: _interfaces.themeExtended readonly property alias universalSettings: _interfaces.universalSettings readonly property QtObject viewLayout: latteView && latteView.layout ? latteView.layout : null readonly property QtObject selectedWindowsTracker: { if (latteView && latteView.windowsTracker) { switch(plasmoid.configuration.activeWindowFilter) { case LatteContainment.Types.ActiveInCurrentScreen: return latteView.windowsTracker.currentScreen; case LatteContainment.Types.ActiveFromAllScreens: return latteView.windowsTracker.allScreens; } } return null; } // TO BE DELETED, if not needed: property int counter:0; ///BEGIN properties provided to Latte Plasmoid //shadows for applets, it should be removed as the appleitems don't need it any more property bool badges3DStyle: universalSettings ? universalSettings.badges3DStyle : true property bool enableShadows: plasmoid.configuration.appletShadowsEnabled property bool dockIsHidden: latteView && latteView.visibility ? latteView.visibility.isHidden : true property bool titleTooltips: plasmoid.configuration.titleTooltips property bool unifiedGlobalShortcuts: true property int tasksCount: latteApplet ? latteApplet.tasksCount : 0 property rect screenGeometry: latteView ? latteView.screenGeometry : plasmoid.screenGeometry readonly property color minimizedDotColor: colorizerManager.minimizedDotColor ///END properties from latteApplet Plasmoid.backgroundHints: PlasmaCore.Types.NoBackground //// BEGIN properties in functions property int noApplets: { var count1 = 0; var count2 = 0; count1 = layoutsContainer.mainLayout.children.length; var tempLength = layoutsContainer.mainLayout.children.length; for (var i=tempLength-1; i>=0; --i) { var applet = layoutsContainer.mainLayout.children[i]; if (applet && (applet === dndSpacer || applet === lastSpacer || applet.isInternalViewSplitter)) count1--; } count2 = layoutsContainer.endLayout.children.length; tempLength = layoutsContainer.endLayout.children.length; for (var i=tempLength-1; i>=0; --i) { var applet = layoutsContainer.endLayout.children[i]; if (applet && (applet === dndSpacer || applet === lastSpacer || applet.isInternalViewSplitter)) count2--; } return (count1 + count2); } ///The index of user's current icon size property int currentIconIndex:{ for(var i=iconsArray.length-1; i>=0; --i){ if(iconsArray[i] === metrics.iconSize){ return i; } } return 3; } //// END properties in functions ////////////////END properties //// BEGIN OF Behaviors Behavior on offset { enabled: editModeVisual.editAnimationInFullThickness NumberAnimation { id: offsetAnimation duration: 0.8 * animations.duration.proposed easing.type: Easing.OutCubic } } //// END OF Behaviors //////////////START OF CONNECTIONS onEditModeChanged: { if (!editMode) { layouter.updateSizeForAppletsInFill(); } //! This is used in case the dndspacer has been left behind //! e.g. the user drops a folder and a context menu is appearing //! but the user decides to not make a choice for the applet type if (dndSpacer.parent !== root) { dndSpacer.parent = root; } } onInConfigureAppletsModeChanged: { updateIndexes(); } //! It is used only when the user chooses different alignment types //! and not during startup onPanelAlignmentChanged: { if (!root.editMode) { return; } if (root.editMode/*!inConfigureAppletsMode*/){ if (panelAlignment===LatteCore.Types.Justify) { addInternalViewSplitters(); splitMainLayoutToLayouts(); } else { joinLayoutsToMainLayout(); root.destroyInternalViewSplitters(); } } LayoutManager.save(); updateIndexes(); } onLatteViewChanged: { if (latteView) { if (latteView.positioner) { latteView.positioner.hideDockDuringLocationChangeStarted.connect(visibilityManager.slotHideDockDuringLocationChange); latteView.positioner.showDockAfterLocationChangeFinished.connect(visibilityManager.slotShowDockAfterLocationChange); latteView.positioner.hideDockDuringScreenChangeStarted.connect(visibilityManager.slotHideDockDuringLocationChange); latteView.positioner.showDockAfterScreenChangeFinished.connect(visibilityManager.slotShowDockAfterLocationChange); latteView.positioner.hideDockDuringMovingToLayoutStarted.connect(visibilityManager.slotHideDockDuringLocationChange); latteView.positioner.showDockAfterMovingToLayoutFinished.connect(visibilityManager.slotShowDockAfterLocationChange); } if (latteView.visibility) { latteView.visibility.onContainsMouseChanged.connect(visibilityManager.slotContainsMouseChanged); latteView.visibility.onMustBeHide.connect(visibilityManager.slotMustBeHide); latteView.visibility.onMustBeShown.connect(visibilityManager.slotMustBeShown); } - - updateContainsOnlyPlasmaTasks(); } } Connections { target: latteView onPositionerChanged: { if (latteView.positioner) { latteView.positioner.hideDockDuringLocationChangeStarted.connect(visibilityManager.slotHideDockDuringLocationChange); latteView.positioner.showDockAfterLocationChangeFinished.connect(visibilityManager.slotShowDockAfterLocationChange); latteView.positioner.hideDockDuringScreenChangeStarted.connect(visibilityManager.slotHideDockDuringLocationChange); latteView.positioner.showDockAfterScreenChangeFinished.connect(visibilityManager.slotShowDockAfterLocationChange); latteView.positioner.hideDockDuringMovingToLayoutStarted.connect(visibilityManager.slotHideDockDuringLocationChange); latteView.positioner.showDockAfterMovingToLayoutFinished.connect(visibilityManager.slotShowDockAfterLocationChange); } } onVisibilityChanged: { if (latteView.visibility) { latteView.visibility.onContainsMouseChanged.connect(visibilityManager.slotContainsMouseChanged); latteView.visibility.onMustBeHide.connect(visibilityManager.slotMustBeHide); latteView.visibility.onMustBeShown.connect(visibilityManager.slotMustBeShown); } } } onMaxLengthChanged: { layouter.updateSizeForAppletsInFill(); } onToolBoxChanged: { if (toolBox) { toolBox.visible = false; } } onIsReadyChanged: { if (isReady && !titleTooltipDialog.visible && titleTooltipDialog.activeItemHovered){ titleTooltipDialog.show(titleTooltipDialog.activeItem, titleTooltipDialog.activeItemText); } } onIsVerticalChanged: { if (isVertical) { if (plasmoid.configuration.alignment === LatteCore.Types.Left) plasmoid.configuration.alignment = LatteCore.Types.Top; else if (plasmoid.configuration.alignment === LatteCore.Types.Right) plasmoid.configuration.alignment = LatteCore.Types.Bottom; } else { if (plasmoid.configuration.alignment === LatteCore.Types.Top) plasmoid.configuration.alignment = LatteCore.Types.Left; else if (plasmoid.configuration.alignment === LatteCore.Types.Bottom) plasmoid.configuration.alignment = LatteCore.Types.Right; } } Component.onCompleted: { // currentLayout.isLayoutHorizontal = isHorizontal LayoutManager.plasmoid = plasmoid; LayoutManager.root = root; LayoutManager.layout = layoutsContainer.mainLayout; LayoutManager.layoutS = layoutsContainer.startLayout; LayoutManager.layoutE = layoutsContainer.endLayout; LayoutManager.lastSpacer = lastSpacer; LayoutManager.metrics = metrics; upgrader_v010_alignment(); LayoutManager.restore(); plasmoid.action("configure").visible = !plasmoid.immutable; plasmoid.action("configure").enabled = !plasmoid.immutable; inStartupTimer.start(); } Component.onDestruction: { console.debug("Destroying Latte Dock Containment ui..."); layouter.appletsInParentChange = true; if (latteView) { if (latteView.positioner) { latteView.positioner.hideDockDuringLocationChangeStarted.disconnect(visibilityManager.slotHideDockDuringLocationChange); latteView.positioner.showDockAfterLocationChangeFinished.disconnect(visibilityManager.slotShowDockAfterLocationChange); latteView.positioner.hideDockDuringScreenChangeStarted.disconnect(visibilityManager.slotHideDockDuringLocationChange); latteView.positioner.showDockAfterScreenChangeFinished.disconnect(visibilityManager.slotShowDockAfterLocationChange); latteView.positioner.hideDockDuringMovingToLayoutStarted.disconnect(visibilityManager.slotHideDockDuringLocationChange); latteView.positioner.showDockAfterMovingToLayoutFinished.disconnect(visibilityManager.slotShowDockAfterLocationChange); } if (latteView.visibility) { latteView.visibility.onContainsMouseChanged.disconnect(visibilityManager.slotContainsMouseChanged); latteView.visibility.onMustBeHide.disconnect(visibilityManager.slotMustBeHide); latteView.visibility.onMustBeShown.disconnect(visibilityManager.slotMustBeShown); } } } Containment.onAppletAdded: { addApplet(applet, x, y); console.log(applet.pluginName); LayoutManager.save(); updateIndexes(); } Containment.onAppletRemoved: { LayoutManager.removeApplet(applet); var flexibleFound = false; for (var i = 0; i < layoutsContainer.mainLayout.children.length; ++i) { var applet = layoutsContainer.mainLayout.children[i].applet; if (applet && ((root.isHorizontal && applet.Layout.fillWidth) || (!root.isHorizontal && applet.Layout.fillHeight)) && applet.visible) { flexibleFound = true; break } } if (!flexibleFound) { lastSpacer.parent = layoutsContainer.mainLayout; } LayoutManager.save(); updateIndexes(); - updateContainsOnlyPlasmaTasks(); } Plasmoid.onUserConfiguringChanged: { if (plasmoid.immutable) { if (dragOverlay) { dragOverlay.destroy(); } return; } // console.debug("user configuring", plasmoid.userConfiguring) if (plasmoid.userConfiguring) { // console.log("applets------"); for (var i = 0; i < plasmoid.applets.length; ++i) { // console.log("applet:"+i); plasmoid.applets[i].expanded = false; } if (!dragOverlay) { var component = Qt.createComponent("editmode/ConfigOverlay.qml"); if (component.status == Component.Ready) { dragOverlay = component.createObject(root); } else { console.log("Could not create ConfigOverlay"); console.log(component.errorString()); } component.destroy(); } else { dragOverlay.visible = true; } } else { if (dragOverlay) { dragOverlay.visible = false; dragOverlay.destroy(); } } } Plasmoid.onImmutableChanged: { plasmoid.action("configure").visible = !plasmoid.immutable; plasmoid.action("configure").enabled = !plasmoid.immutable; ///Set Preferred Sizes/// ///Notice: they are set here because if they are set with a binding ///they break the !immutable experience, the latteView becomes too small ///to add applets /* if (plasmoid.immutable) { if(root.isHorizontal) { root.Layout.preferredWidth = (plasmoid.configuration.alignment === LatteCore.Types.Justify ? layoutsContainer.width + 0.5*iconMargin : layoutsContainer.mainLayout.width + iconMargin); } else { root.Layout.preferredHeight = (plasmoid.configuration.alignment === LatteCore.Types.Justify ? layoutsContainer.height + 0.5*iconMargin : layoutsContainer.mainLayout.height + iconMargin); } } else { if (root.isHorizontal) { root.Layout.preferredWidth = Screen.width; } else { root.Layout.preferredHeight = Screen.height; } }*/ visibilityManager.updateMaskArea(); } //////////////END OF CONNECTIONS //////////////START OF FUNCTIONS function addApplet(applet, x, y) { var container = appletContainerComponent.createObject(dndSpacer.parent) container.applet = applet; applet.parent = container.appletWrapper; applet.anchors.fill = container.appletWrapper; applet.visible = true; // don't show applet if it chooses to be hidden but still make it // accessible in the panelcontroller container.visible = Qt.binding(function() { return applet.status !== PlasmaCore.Types.HiddenStatus || (!plasmoid.immutable && root.inConfigureAppletsMode) }) addContainerInLayout(container, applet, x, y); - - updateContainsOnlyPlasmaTasks(); } function addContainerInLayout(container, applet, x, y){ // Is there a DND placeholder? Replace it! if ( (dndSpacer.parent === layoutsContainer.mainLayout) || (dndSpacer.parent === layoutsContainer.startLayout) || (dndSpacer.parent===layoutsContainer.endLayout)) { LayoutManager.insertBeforeForLayout(dndSpacer.parent, dndSpacer, container); dndSpacer.parent = root; return; // If the provided position is valid, use it. } else if (x >= 0 && y >= 0) { var index = LayoutManager.insertAtCoordinates2(container, x , y); // Fall through to determining an appropriate insert position. } else { var before = null; container.animationsEnabled = false; if (lastSpacer.parent === layoutsContainer.mainLayout) { before = lastSpacer; } // Insert icons to the left of whatever is at the center (usually a Task Manager), // if it exists. // FIXME TODO: This is a real-world fix to produce a sensible initial position for // launcher icons added by launcher menu applets. The basic approach has been used // since Plasma 1. However, "add launcher to X" is a generic-enough concept and // frequent-enough occurrence that we'd like to abstract it further in the future // and get rid of the ugliness of parties external to the containment adding applets // of a specific type, and the containment caring about the applet type. In a better // system the containment would be informed of requested launchers, and determine by // itself what it wants to do with that information. if (applet.pluginName == "org.kde.plasma.icon") { var middle = layoutsContainer.mainLayout.childAt(root.width / 2, root.height / 2); if (middle) { before = middle; } // Otherwise if lastSpacer is here, enqueue before it. } if (before) { LayoutManager.insertBefore(before, container); // Fall through to adding at the end. } else { container.parent = layoutsContainer.mainLayout; } } //Important, removes the first children of the layoutsContainer.mainLayout after the first //applet has been added lastSpacer.parent = root; updateIndexes(); } function addInternalViewSplitters(){ if (internalViewSplittersCount() === 0) { addInternalViewSplitter(plasmoid.configuration.splitterPosition); addInternalViewSplitter(plasmoid.configuration.splitterPosition2); } } function addInternalViewSplitter(pos){ var splittersCount = internalViewSplittersCount(); if(splittersCount<2){ var container = appletContainerComponent.createObject(root); container.internalSplitterId = splittersCount+1; container.visible = true; if(pos>=0 ){ LayoutManager.insertAtIndex(layoutsContainer.mainLayout, container, pos); } else { LayoutManager.insertAtIndex(layoutsContainer.mainLayout, container, Math.floor(layouter.mainLayout.count / 2)); } } } //! it is used in order to check the right click position //! the only way to take into account the visual appearance //! of the applet (including its spacers) function appletContainsPos(appletId, pos){ for (var i = 0; i < layoutsContainer.startLayout.children.length; ++i) { var child = layoutsContainer.startLayout.children[i]; if (child && child.applet && child.applet.id === appletId && child.containsPos(pos)) return true; } for (var i = 0; i < layoutsContainer.mainLayout.children.length; ++i) { var child = layoutsContainer.mainLayout.children[i]; if (child && child.applet && child.applet.id === appletId && child.containsPos(pos)) return true; } for (var i = 0; i < layoutsContainer.endLayout.children.length; ++i) { var child = layoutsContainer.endLayout.children[i]; if (child && child.applet && child.applet.id === appletId && child.containsPos(pos)) return true; } return false; } function checkLastSpacer() { lastSpacer.parent = root var expands = false; if (isHorizontal) { for (var container in layoutsContainer.mainLayout.children) { var item = layoutsContainer.mainLayout.children[container]; if (item.Layout && item.Layout.fillWidth) { expands = true; } } } else { for (var container in layoutsContainer.mainLayout.children) { var item = layoutsContainer.mainLayout.children[container]; if (item.Layout && item.Layout.fillHeight) { expands = true; } } } if (!expands) { lastSpacer.parent = layoutsContainer.mainLayout } } function containmentActions(){ return latteView.containmentActions(); } function decimalToHex(d, padding) { var hex = Number(d).toString(16); padding = typeof (padding) === "undefined" || padding === null ? padding = 2 : padding; while (hex.length < padding) { hex = "0" + hex; } return hex; } function internalViewSplittersCount(){ var splitters = 0; for (var container in layoutsContainer.startLayout.children) { var item = layoutsContainer.startLayout.children[container]; if(item && item.isInternalViewSplitter) { splitters = splitters + 1; } } for (var container in layoutsContainer.mainLayout.children) { var item = layoutsContainer.mainLayout.children[container]; if(item && item.isInternalViewSplitter) { splitters = splitters + 1; } } for (var container in layoutsContainer.endLayout.children) { var item = layoutsContainer.endLayout.children[container]; if(item && item.isInternalViewSplitter) { splitters = splitters + 1; } } return splitters; } function layoutManager() { return LayoutManager; } function layoutManagerInsertBefore(place, item) { LayoutManager.insertBefore(place, item); } function layoutManagerInsertAfter(place, item) { LayoutManager.insertAfter(place, item); } function layoutManagerSave() { LayoutManager.save(); } function layoutManagerSaveOptions() { LayoutManager.saveOptions(); } function mouseInCanBeHoveredApplet(){ var applets = layoutsContainer.startLayout.children; for(var i=0; i=0; --i){ if(iconsArray[i] === size){ return true; } } return false; } function slotPreviewsShown(){ if (latteView) { latteView.extendedInterface.deactivateApplets(); } } - function updateContainsOnlyPlasmaTasks() { - if (latteView) { - root.containsOnlyPlasmaTasks = (latteView.tasksPresent() && !latteApplet); - } else { - root.containsOnlyPlasmaTasks = false; - } - } - function layoutManagerMoveAppletsBasedOnJustifyAlignment() { if (plasmoid.configuration.alignment !== 10) { return; } layouter.appletsInParentChange = true; var splitter = -1; var startChildrenLength = layoutsContainer.startLayout.children.length; //! Check if there is a splitter inside start layout after the user was dragging its applets for (var i=0; i=0) { for (var i=startChildrenLength-1; i>=splitter; --i){ var item = layoutsContainer.startLayout.children[i]; LayoutManager.insertAtIndex(layoutsContainer.mainLayout, item, 0); } } var splitter2 = -1; var endChildrenLength = layoutsContainer.endLayout.children.length; //! Check if there is a splitter inside endlayout after the user was dragging its applets for (var i=0; i=0) { for (var i=0; i<=splitter2; ++i){ var item = layoutsContainer.endLayout.children[0]; item.parent = layoutsContainer.mainLayout; } } //! Validate applets positioning and move applets out of splitters to start/endlayouts accordingly splitMainLayoutToLayouts(); layouter.appletsInParentChange = false; } function splitMainLayoutToLayouts() { if (internalViewSplittersCount() === 2) { layouter.appletsInParentChange = true; console.log("LAYOUTS: Moving applets from MAIN to THREE Layouts mode..."); var splitter = -1; var splitter2 = -1; var totalChildren = layoutsContainer.mainLayout.children.length; for (var i=0; i=0 && splitter2 === -1) { splitter2 = i; } } // console.log("update layouts 1:"+splitter + " - "+splitter2); if (splitter > 0) { for (var i=0; i 0) { splitter2 = splitter2 - splitter; // console.log("update layouts 2:"+splitter + " - "+splitter2); totalChildren = layoutsContainer.mainLayout.children.length; for (var i=totalChildren-1; i>=splitter2+1; --i){ var item = layoutsContainer.mainLayout.children[i]; LayoutManager.insertAtIndex(layoutsContainer.endLayout, item, 0); } } layouter.appletsInParentChange = false; } } function joinLayoutsToMainLayout() { layouter.appletsInParentChange = true; console.log("LAYOUTS: Moving applets from THREE to MAIN Layout mode..."); var totalChildren1 = layoutsContainer.mainLayout.children.length; for (var i=totalChildren1-1; i>=0; --i) { var item1 = layoutsContainer.mainLayout.children[0]; item1.parent = layoutsContainer.startLayout; } var totalChildren2 = layoutsContainer.endLayout.children.length; for (var i=totalChildren2-1; i>=0; --i) { var item2 = layoutsContainer.endLayout.children[0]; item2.parent = layoutsContainer.startLayout; } var totalChildrenL = layoutsContainer.startLayout.children.length; for (var i=totalChildrenL-1; i>=0; --i) { var itemL = layoutsContainer.startLayout.children[0]; itemL.parent = layoutsContainer.mainLayout; } layouter.appletsInParentChange = false; } function upgrader_v010_alignment() { //! IMPORTANT, special case because it needs to be loaded on Component constructor if (!plasmoid.configuration.alignmentUpgraded) { plasmoid.configuration.alignment = plasmoid.configuration.panelPosition; plasmoid.configuration.alignmentUpgraded = true; } } //END functions ////BEGIN interfaces Connections { target: LatteCore.WindowSystem onCompositingActiveChanged: { visibilityManager.updateMaskArea(); } } ////END interfaces /////BEGIN: Title Tooltip/////////// PlasmaCore.Dialog{ id: titleTooltipDialog type: PlasmaCore.Dialog.Tooltip flags: Qt.WindowStaysOnTopHint | Qt.WindowDoesNotAcceptFocus | Qt.ToolTip location: plasmoid.location mainItem: RowLayout{ Layout.fillWidth: true Layout.fillHeight: true PlasmaComponents.Label{ id:titleLbl Layout.leftMargin: 4 Layout.rightMargin: 4 Layout.topMargin: 2 Layout.bottomMargin: 2 text: titleTooltipDialog.title } } visible: false property string title: "" property bool activeItemHovered: false property Item activeItem: null property Item activeItemTooltipParent: null property string activeItemText: "" Component.onCompleted: { parabolic.sglClearZoom.connect(titleTooltipDialog.hide); } Component.onDestruction: { parabolic.sglClearZoom.disconnect(titleTooltipDialog.hide); } function hide(debug){ if (!root.titleTooltips) return; activeItemHovered = false; hideTitleTooltipTimer.start(); } function show(taskItem, text){ if (!root.titleTooltips || (latteApplet && latteApplet.contextMenu)){ return; } activeItemHovered = true; if (activeItem !== taskItem) { activeItem = taskItem; activeItemTooltipParent = taskItem.tooltipVisualParent; activeItemText = text; } if (isReady) { showTitleTooltipTimer.start(); } } function update() { activeItemHovered = true title = activeItemText; visualParent = activeItemTooltipParent; if (latteApplet && latteApplet.windowPreviewIsShown) { latteApplet.hidePreview(); } visible = true; } } Timer { id: showTitleTooltipTimer interval: 100 onTriggered: { if (latteView && latteView.visibility && latteView.visibility.containsMouse) { titleTooltipDialog.update(); } if (titleTooltipDialog.visible) { titleTooltipCheckerToNotShowTimer.start(); } if (root.debugModeTimers) { console.log("containment timer: showTitleTooltipTimer called..."); } } } Timer { id: hideTitleTooltipTimer interval: 200 onTriggered: { if (!titleTooltipDialog.activeItemHovered) { titleTooltipDialog.visible = false; } if (root.debugModeTimers) { console.log("containment timer: hideTitleTooltipTimer called..."); } } } //! Timer to fix #811, rare cases that both a window preview and context menu are //! shown Timer { id: titleTooltipCheckerToNotShowTimer interval: 250 onTriggered: { if (titleTooltipDialog.visible && latteApplet && (latteApplet.contextMenu || latteApplet.windowPreviewIsShown)) { titleTooltipDialog.visible = false; } } } /////END: Title Tooltip/////////// ///////////////BEGIN components Component { id: appletContainerComponent Applet.AppletItem{ animations: _animations indexer: _indexer layouter: _layouter metrics: _metrics parabolic: _parabolic } } ParabolicManager{ id: _parabolicManager } Indicators.Manager{ id: indicators } Item { id: graphicsSystem readonly property bool isAccelerated: (GraphicsInfo.api !== GraphicsInfo.Software) && (GraphicsInfo.api !== GraphicsInfo.Unknown) } Upgrader { id: upgrader } ///////////////END components PlasmaCore.ColorScope{ id: colorScopePalette } ///////////////BEGIN UI elements //it is used to check if the mouse is outside the layoutsContainer borders, //so in that case the onLeave event behavior should be trigerred RootMouseArea{ id: rootMouseArea } Loader{ active: root.debugModeWindow sourceComponent: Debug.DebugWindow{} } EditMode.Visual{ id:editModeVisual // z: root.behaveAsPlasmaPanel ? 1 : 0 } Item { id: lastSpacer parent: layoutsContainer.mainLayout Layout.fillWidth: true Layout.fillHeight: true z:10 Rectangle{ anchors.fill: parent color: "transparent" border.color: "yellow" border.width: 1 } } Loader{ anchors.fill: parent active: root.debugMode z:10 sourceComponent: Item{ Rectangle{ anchors.fill: parent color: "yellow" opacity: 0.06 } } } VisibilityManager{ id: visibilityManager applets: layoutsContainer.applets } DragDropArea { id: backDropArea anchors.fill: parent readonly property bool higherPriority: latteView && latteView.containsDrag && ((root.dragInfo.isPlasmoid && root.dragInfo.isSeparator) || (foreDropArea.dragInfo.computationsAreValid && !root.dragInfo.isPlasmoid && !root.dragInfo.onlyLaunchers)) Item{ anchors.fill: layoutsContainer Background.MultiLayered{ id: _background } } Layouts.LayoutsContainer { id: layoutsContainer } DragDropArea { id: foreDropArea anchors.fill: parent visible: !backDropArea.higherPriority isForeground: true /* Rectangle { anchors.fill: parent color: "blue" opacity: 0.5 }*/ } } Colorizer.Manager { id: colorizerManager } Item { id: dndSpacer width: root.isHorizontal ? length : thickness height: root.isHorizontal ? thickness : length readonly property bool isDndSpacer: true readonly property int length: metrics.totals.length readonly property int thickness: metrics.totals.thickness + metrics.margin.screenEdge Layout.preferredWidth: width Layout.preferredHeight: height opacity: 0 z:1500 LatteComponents.AddItem{ id: dndSpacerAddItem width: root.isHorizontal ? parent.width : parent.width - metrics.margin.screenEdge height: root.isHorizontal ? parent.height - metrics.margin.screenEdge: parent.height states:[ State{ name: "bottom" when: plasmoid.location === PlasmaCore.Types.BottomEdge AnchorChanges{ target: dndSpacerAddItem; anchors.horizontalCenter: parent.horizontalCenter; anchors.verticalCenter: undefined; anchors.right: undefined; anchors.left: undefined; anchors.top: undefined; anchors.bottom: parent.bottom; } PropertyChanges{ target: dndSpacerAddItem; anchors.leftMargin: 0; anchors.rightMargin: 0; anchors.topMargin:0; anchors.bottomMargin: metrics.margin.screenEdge; anchors.horizontalCenterOffset: 0; anchors.verticalCenterOffset: 0; } }, State{ name: "top" when: plasmoid.location === PlasmaCore.Types.TopEdge AnchorChanges{ target: dndSpacerAddItem; anchors.horizontalCenter: parent.horizontalCenter; anchors.verticalCenter: undefined; anchors.right: undefined; anchors.left: undefined; anchors.top: parent.top; anchors.bottom: undefined; } PropertyChanges{ target: dndSpacerAddItem; anchors.leftMargin: 0; anchors.rightMargin: 0; anchors.topMargin: metrics.margin.screenEdge; anchors.bottomMargin: 0; anchors.horizontalCenterOffset: 0; anchors.verticalCenterOffset: 0; } }, State{ name: "left" when: plasmoid.location === PlasmaCore.Types.LeftEdge AnchorChanges{ target: dndSpacerAddItem; anchors.horizontalCenter: undefined; anchors.verticalCenter: parent.verticalCenter; anchors.right: undefined; anchors.left: parent.left; anchors.top: undefined; anchors.bottom: undefined; } PropertyChanges{ target: dndSpacerAddItem; anchors.leftMargin: metrics.margin.screenEdge; anchors.rightMargin: 0; anchors.topMargin:0; anchors.bottomMargin: 0; anchors.horizontalCenterOffset: 0; anchors.verticalCenterOffset: 0; } }, State{ name: "right" when: plasmoid.location === PlasmaCore.Types.RightEdge AnchorChanges{ target: dndSpacerAddItem; anchors.horizontalCenter: undefined; anchors.verticalCenter: parent.verticalCenter; anchors.right: parent.right; anchors.left: undefined; anchors.top: undefined; anchors.bottom: undefined; } PropertyChanges{ target: dndSpacerAddItem; anchors.leftMargin: 0; anchors.rightMargin: metrics.margin.screenEdge; anchors.topMargin:0; anchors.bottomMargin: 0; anchors.horizontalCenterOffset: 0; anchors.verticalCenterOffset: 0; } } ] } } ///////////////END UI elements ///////////////BEGIN ABILITIES Ability.Animations { id: _animations metrics: _metrics settings: universalSettings } Ability.AutoSize { id: _autosize layouts: layoutsContainer layouter: _layouter metrics: _metrics visibility: visibilityManager } Ability.Indexer { id: _indexer layouts: layoutsContainer } Ability.Layouter { id: _layouter animations: _animations indexer: _indexer layouts: layoutsContainer } Ability.Metrics { id: _metrics animations: _animations autosize: _autosize background: _background indicators: indicatorsManager } Ability.ParabolicEffect { id: _parabolic animations: _animations applets: layoutsContainer.applets view: latteView } LatteApp.Interfaces { id: _interfaces plasmoidInterface: plasmoid Component.onCompleted: { view.interfacesGraphicObj = _interfaces; } onViewChanged: { if (view) { view.interfacesGraphicObj = _interfaces; } } } ///////////////END ABILITIES ///////////////BEGIN TIMER elements //! It is used in order to slide-in the latteView on startup Timer{ id: inStartupTimer interval: 1500 repeat: false onTriggered: { if (inStartup) { visibilityManager.slotMustBeShown(); } } } ///////////////END TIMER elements Loader{ anchors.fill: parent active: root.debugModeLocalGeometry sourceComponent: Rectangle{ x: latteView.localGeometry.x y: latteView.localGeometry.y width: latteView.localGeometry.width height: latteView.localGeometry.height color: "blue" border.width: 2 border.color: "red" opacity: 0.35 } } } diff --git a/shell/package/contents/configuration/LatteDockConfiguration.qml b/shell/package/contents/configuration/LatteDockConfiguration.qml index b315812d..5a884485 100644 --- a/shell/package/contents/configuration/LatteDockConfiguration.qml +++ b/shell/package/contents/configuration/LatteDockConfiguration.qml @@ -1,705 +1,705 @@ /* * 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.7 import QtQuick.Controls 1.4 import QtQuick.Layouts 1.3 import QtGraphicalEffects 1.0 import QtQuick.Window 2.2 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.components 3.0 as PlasmaComponents3 import org.kde.plasma.extras 2.0 as PlasmaExtras import QtQuick.Controls.Styles.Plasma 2.0 as Styles import org.kde.plasma.plasmoid 2.0 import org.kde.kquickcontrolsaddons 2.0 as KQuickControlAddons import org.kde.latte.core 0.2 as LatteCore import org.kde.latte.components 1.0 as LatteComponents import "pages" as Pages import "../controls" as LatteExtraControls FocusScope { id: dialog readonly property bool basicLevel: !advancedLevel readonly property bool advancedLevel: viewConfig.inAdvancedMode readonly property bool inConfigureAppletsMode: plasmoid.configuration.inConfigureAppletsMode || !LatteCore.WindowSystem.compositingActive //! max size based on screen resolution //! TODO: if we can access availableScreenGeometry.height this can be improved, currently //! we use 100px. or 50px. in order to give space for othe views to be shown and to have also //! some space around the settings window property int maxHeight: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? viewConfig.availableScreenGeometry.height - (latteView.editThickness - latteView.normalThickness) - 16 : viewConfig.availableScreenGeometry.height - 2 * units.largeSpacing property int maxWidth: 0.6 * latteView.screenGeometry.width //! propose size based on font size property int proposedWidth: 0.82 * proposedHeight + units.smallSpacing * 2 property int proposedHeight: 36 * theme.mSize(theme.defaultFont).height //! chosen size to be applied, if the user has set or not a different scale for the settings window property int chosenWidth: userScaleWidth !== 1 ? userScaleWidth * proposedWidth : proposedWidth property int chosenHeight: userScaleHeight !== 1 ? userScaleHeight * heightLevel * proposedHeight : heightLevel * proposedHeight readonly property int optionsWidth: appliedWidth - units.smallSpacing * 10 //! user set scales based on its preference, e.g. 96% of the proposed size property real userScaleWidth: 1 property real userScaleHeight: 1 readonly property real heightLevel: (dialog.advancedLevel ? 100 : 1) onHeightChanged: viewConfig.syncGeometry(); //! applied size in order to not be out of boundaries //! width can be between 200px - maxWidth //! height can be between 400px - maxHeight property int appliedWidth: Math.min(maxWidth, Math.max(200, chosenWidth)) property int appliedHeight: Math.min(maxHeight, Math.max(400, chosenHeight)) width: appliedWidth height: appliedHeight Layout.minimumWidth: width Layout.minimumHeight: height LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft LayoutMirroring.childrenInherit: true readonly property bool viewIsPanel: latteView.type === LatteCore.Types.PanelView property bool panelIsVertical: plasmoid.formFactor === PlasmaCore.Types.Vertical property int subGroupSpacing: units.largeSpacing + units.smallSpacing * 1.5 property color bC: theme.backgroundColor property color transparentBackgroundColor: Qt.rgba(bC.r, bC.g, bC.b, 0.7) onAdvancedLevelChanged: { //! switch to appearancePage when effectsPage becomes hidden because //! advancedLevel was disabled by the user if (!advancedLevel && tabGroup.currentTab === effectsPage) { tabGroup.currentTab = appearancePage; tabBar.currentTab = appearanceTabBtn; } } Component.onCompleted: { updateScales(); } Connections { target: latteView.positioner onCurrentScreenNameChanged: dialog.updateScales(); } function updateScales() { userScaleWidth = universalSettings.screenWidthScale(latteView.positioner.currentScreenName); userScaleHeight = universalSettings.screenHeightScale(latteView.positioner.currentScreenName); } PlasmaCore.FrameSvgItem{ id: backgroundFrameSvgItem anchors.fill: parent imagePath: "dialogs/background" enabledBorders: viewConfig.enabledBorders onEnabledBordersChanged: viewConfig.updateEffects() Component.onCompleted: viewConfig.updateEffects() } MouseArea{ id: backgroundMouseArea anchors.fill: parent hoverEnabled: true property bool blockWheel: false property bool updatingWidthScale: false property bool updatingHeightScale: false property bool wheelTriggeredOnce: false property real scaleStep: 0.04 onWheel: { var metaModifier = (wheel.modifiers & Qt.MetaModifier); var ctrlModifier = (wheel.modifiers & Qt.ControlModifier); if (blockWheel || !(metaModifier || ctrlModifier)){ return; } updatingWidthScale = metaModifier || (dialog.advancedLevel && ctrlModifier); updatingHeightScale = dialog.basicLevel && ctrlModifier; blockWheel = true; wheelTriggeredOnce = true; scrollDelayer.start(); var angle = wheel.angleDelta.y / 8; //positive direction if (angle > 12) { var scales; if (updatingWidthScale) { userScaleWidth = userScaleWidth + scaleStep; } if (updatingHeightScale) { userScaleHeight = userScaleHeight + scaleStep; } universalSettings.setScreenScales(latteView.positioner.currentScreenName, userScaleWidth, userScaleHeight); viewConfig.syncGeometry(); //negative direction } else if (angle < -12) { if (updatingWidthScale) { userScaleWidth = userScaleWidth - scaleStep; } if (updatingHeightScale) { userScaleHeight = userScaleHeight - scaleStep; } universalSettings.setScreenScales(latteView.positioner.currentScreenName, userScaleWidth, userScaleHeight); viewConfig.syncGeometry(); } } } PlasmaComponents.Button { id: backgroundMouseAreaTooltip anchors.fill: parent opacity: 0 //tooltip: i18n("You can use Ctrl/Meta + Scroll Wheel to alter the window size") onHoveredChanged: { if (!hovered) { backgroundMouseArea.wheelTriggeredOnce = false; } } } PlasmaComponents.Label{ anchors.top: parent.top anchors.horizontalCenter: parent.horizontalCenter text: backgroundMouseArea.updatingWidthScale ? i18nc("view settings width scale","Width scale at %0%").arg(userScaleWidth * 100) : i18nc("view settings height scale","Height scale at %0%").arg(userScaleHeight * 100) visible: backgroundMouseAreaTooltip.hovered && backgroundMouseArea.wheelTriggeredOnce } //! A timer is needed in order to handle also touchpads that probably //! send too many signals very fast. This way the signals per sec are limited. //! The user needs to have a steady normal scroll in order to not //! notice a annoying delay Timer{ id: scrollDelayer interval: 75 onTriggered: backgroundMouseArea.blockWheel = false; } ColumnLayout { id: content Layout.minimumWidth: width Layout.minimumHeight: calculatedHeight Layout.preferredWidth: width Layout.preferredHeight: calculatedHeight width: (dialog.appliedWidth - units.smallSpacing * 2) anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top spacing: units.smallSpacing property int calculatedHeight: header.height + headerSpacer.height+ tabBar.height + pagesBackground.height + actionButtons.height + spacing * 3 Keys.onPressed: { if (event.key === Qt.Key_Escape) { viewConfig.hideConfigWindow(); } } Component.onCompleted: forceActiveFocus(); RowLayout { id: header Layout.fillWidth: true spacing: 0 Item { Layout.alignment: Qt.AlignLeft | Qt.AlignTop Layout.fillWidth: false Layout.topMargin: units.smallSpacing Layout.preferredWidth: width Layout.preferredHeight: height width: Qt.application.layoutDirection !== Qt.RightToLeft ? logo.width + latteTxt.width + units.smallSpacing : logo.width + units.smallSpacing height: logo.height LatteCore.IconItem { id: logo width: Math.round(1.4 * latteTxtMetrics.font.pixelSize) height: width smooth: true source: "latte-dock" usesPlasmaTheme: false } PlasmaComponents.Label { id: latteTxtMetrics text: i18n("Latte") width: 0 font.pointSize: 2 * theme.defaultFont.pointSize visible: false } PlasmaCore.SvgItem{ id: latteTxt width: 2.2 * height height: 0.4 * latteTxtMetrics.font.pixelSize visible: Qt.application.layoutDirection !== Qt.RightToLeft anchors.left: logo.right anchors.verticalCenter: logo.verticalCenter svg: PlasmaCore.Svg{ imagePath: universalSettings.trademarkIconPath() } } } Item{ id: headerSpacer Layout.minimumHeight: advancedSettings.height + 2*units.smallSpacing } ColumnLayout { PlasmaComponents3.ToolButton { id: pinButton Layout.fillWidth: false Layout.fillHeight: false Layout.preferredWidth: width Layout.preferredHeight: height Layout.alignment: Qt.AlignRight | Qt.AlignVCenter Layout.bottomMargin: units.smallSpacing * 1.5 //!avoid editMode box shadow Layout.topMargin: units.smallSpacing * 2 Layout.rightMargin: units.smallSpacing icon.name: "window-pin" checkable: true width: 7 * units.smallSpacing height: width property bool inStartup: true onClicked: { plasmoid.configuration.configurationSticker = checked viewConfig.setSticker(checked) } Component.onCompleted: { checked = plasmoid.configuration.configurationSticker viewConfig.setSticker(plasmoid.configuration.configurationSticker) } } RowLayout { id: advancedSettings Layout.fillWidth: true Layout.rightMargin: units.smallSpacing * 2 Layout.alignment: Qt.AlignRight | Qt.AlignTop PlasmaComponents.Label { Layout.fillWidth: true Layout.alignment: Qt.AlignRight } PlasmaComponents.Label { id: advancedLbl Layout.alignment: Qt.AlignRight // opacity: dialog.basicLevel ? basicOpacity : 1 //! TODO: the term here is not accurate because the expert settings mode //! is used currently. In the future this term will be rethought if //! it must remain or be changed text: i18nc("advanced settings", "Advanced") readonly property real textColorBrightness: colorBrightness(theme.textColor) readonly property real basicOpacity: textColorBrightness > 127 ? 0.7 : 0.3 color: { if (dialog.basicLevel) { return textColorBrightness > 127 ? Qt.darker(theme.textColor, 1.4) : Qt.lighter(theme.textColor, 2.8); } return theme.textColor; } function colorBrightness(color) { return colorBrightnessFromRGB(color.r * 255, color.g * 255, color.b * 255); } // formula for brightness according to: // https://www.w3.org/TR/AERT/#color-contrast function colorBrightnessFromRGB(r, g, b) { return (r * 299 + g * 587 + b * 114) / 1000 } MouseArea { id: advancedMouseArea anchors.fill: parent hoverEnabled: true onClicked: { advancedSwitch.checked = !advancedSwitch.checked; } } } LatteComponents.Switch { id: advancedSwitch checked: viewConfig.inAdvancedMode onCheckedChanged: viewConfig.inAdvancedMode = checked; } } } } PlasmaComponents.TabBar { id: tabBar Layout.fillWidth: true Layout.maximumWidth: (dialog.appliedWidth - units.smallSpacing * 2) PlasmaComponents.TabButton { id: behaviorTabBtn text: i18n("Behavior") tab: behaviorPage } PlasmaComponents.TabButton { id: appearanceTabBtn text: i18n("Appearance") tab: appearancePage } PlasmaComponents.TabButton { id: effectsTabBtn text: i18n("Effects") tab: effectsPage visible: dialog.advancedLevel } Repeater { id: tasksTabButtonRepeater model: latteView.extendedInterface.latteTasksModel PlasmaComponents.TabButton { text: index >= 1 ? i18nc("tasks header and index","Tasks <%0>").arg(index+1) : i18n("Tasks") tab: tasksRepeater.itemAt(index) } } } Rectangle { id: pagesBackground Layout.fillWidth: true Layout.fillHeight: false Layout.minimumWidth: dialog.appliedWidth - units.smallSpacing * 4 Layout.minimumHeight: height Layout.maximumHeight: height width: dialog.appliedWidth - units.smallSpacing * 3 height: availableFreeHeight + units.smallSpacing * 4 color: transparentBackgroundColor border.width: 1 border.color: theme.backgroundColor //fix the height binding loop when showing the configuration window property int availableFreeHeight: dialog.appliedHeight - header.height - headerSpacer.height - tabBar.height - actionButtons.height - 2 * units.smallSpacing PlasmaExtras.ScrollArea { id: scrollArea anchors.fill: parent verticalScrollBarPolicy: Qt.ScrollBarAsNeeded horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff flickableItem.flickableDirection: Flickable.VerticalFlick PlasmaComponents.TabGroup { id: tabGroup width: currentTab.Layout.maximumWidth height: currentTab.Layout.maximumHeight Pages.BehaviorConfig { id: behaviorPage } Pages.AppearanceConfig { id: appearancePage } Pages.EffectsConfig { id: effectsPage } } } Repeater { id: tasksRepeater //! needs to be out of TabGroup otherwise the Repeater is consider as TabGroup direct children //! and thus only the first Tasks tab is shown model: latteView.extendedInterface.latteTasksModel //! Reparent TasksPages when all of them are loaded //! this way we avoid warnings from ::stackAfter //! After startup any new TasksPages can be added directly property bool isReady: false property int pages: 0 Pages.TasksConfig { id: tasksPage Component.onCompleted: { if (tasksRepeater.isReady) { parent = tabGroup; } else { tasksRepeater.pages = tasksRepeater.pages + 1; } } } onPagesChanged: { if (pages === latteView.extendedInterface.latteTasksModel.count) { //! Reparent TasksPages when all of them are loaded //! this way we avoid warnings from ::stackAfter for(var i=0; i 0; if (actionsModel.count > 0) { actionsModel.get(0).enabled = actionsComboBtn.buttonEnabled; } - removeView.enabled = latteView.layout.viewsCount>1 /*&& !(latteView.layout.viewsWithTasks()===1 && latteView.tasksPresent())*/ + removeView.enabled = latteView.layout.viewsCount>1 } LatteComponents.ComboBoxButton { id: actionsComboBtn Layout.fillWidth: true implicitWidth: removeView.implicitWidth implicitHeight: removeView.implicitHeight buttonEnabled: true buttonText: i18n("New Dock") buttonIconSource: "list-add" buttonToolTip: i18n("Add a new dock") comboBoxEnabled: true comboBoxBlankSpaceForEmptyIcons: true comboBoxPopUpAlignRight: Qt.application.layoutDirection === Qt.RightToLeft comboBoxEnabledRole: "enabled" comboBoxTextRole: "name" comboBoxIconRole: "icon" comboBoxMinimumPopUpWidth: actionsModel.count > 1 ? dialog.width / 2 : 150 property var centralLayoutsNames: [] property var sharedLayoutsNames: [] Component.onCompleted: { comboBox.model = actionsModel; actionButtons.updateEnabled(); } ListModel { id: actionsModel } Connections{ target: actionsComboBtn.comboBox Component.onCompleted:{ actionsComboBtn.addModel(); actionButtons.updateEnabled(); } onActivated: { if (index==0) { latteView.copyView(); } else if (index>=1) { var layouts = actionsComboBtn.sharedLayoutsNames.concat(actionsComboBtn.centralLayoutsNames); latteView.positioner.hideDockDuringMovingToLayout(layouts[index-1]); } actionsComboBtn.comboBox.currentIndex = -1; } onEnabledChanged: { if (enabled) { actionsComboBtn.addModel(); } else { actionsComboBtn.emptyModel(); } } } Connections{ target: actionsComboBtn.button onClicked: latteView.layout.addNewView(); } Connections{ target: latteView onTypeChanged: actionsComboBtn.updateCopyText() } function addModel() { actionsModel.clear(); var copy = {actionId: 'copy:', enabled: true, name: '', icon: 'edit-copy'}; actionsModel.append(copy); updateCopyText(); var tempCentralLayouts = layoutsManager.centralLayoutsNames(); var tempSharedLayouts = layoutsManager.sharedLayoutsNames(); if (tempSharedLayouts.length > 0) { var curIndex = tempSharedLayouts.indexOf(latteView.layout.name); if (curIndex >=0) { tempSharedLayouts.splice(curIndex,1); } sharedLayoutsNames = tempSharedLayouts; var icon = "document-share"; for(var i=0; i 0) { var curIndex = tempCentralLayouts.indexOf(latteView.layout.name); if (curIndex >=0) { tempCentralLayouts.splice(curIndex,1); } centralLayoutsNames = tempCentralLayouts; var iconArrow = Qt.application.layoutDirection === Qt.RightToLeft ? 'arrow-left' : 'arrow-right'; for(var i=0; i 1 ? 1 : 0 tooltip: i18n("Remove current dock") onClicked: latteView.removeView() } PlasmaComponents.Button { id: closeButton Layout.fillWidth: true text: i18n("Close") iconSource: "dialog-close" tooltip: i18n("Close settings window") onClicked: viewConfig.hideConfigWindow(); } } } //! HACK FOR X11 environments //! show an inner shadow similar to Latte::View editShadow in order to //! not break the visual user experience LatteExtraControls.InnerShadow{ width: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? dialog.width + 2*shadowSize : shadowSize height: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? shadowSize : dialog.height + 2*shadowSize shadowSize: latteView.effects.editShadow shadowOpacity: Math.max(0.35, maxOpacity) shadowDirection: plasmoid.location visible: !LatteCore.WindowSystem.isPlatformWayland && LatteCore.WindowSystem.compositingActive readonly property real maxOpacity: LatteCore.WindowSystem.compositingActive && !plasmoid.configuration.inConfigureAppletsMode ? plasmoid.configuration.editBackgroundOpacity : 1 } }