diff --git a/app/dock/dockmenumanager.cpp b/app/dock/dockmenumanager.cpp index e68f20f4..2a43a13a 100644 --- a/app/dock/dockmenumanager.cpp +++ b/app/dock/dockmenumanager.cpp @@ -1,437 +1,462 @@ /* * Copyright 2018 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 "dockmenumanager.h" #include "dockview.h" #include "visibilitymanager.h" #include "../dockcorona.h" #include "../layoutmanager.h" #include #include #include #include #include #include #include #include #include #include namespace Latte { DockMenuManager::DockMenuManager(DockView *view) : QObject(view), m_dockView(view) { } DockMenuManager::~DockMenuManager() { } QMenu *DockMenuManager::contextMenu() { return m_contextMenu; } void DockMenuManager::menuAboutToHide() { if (!m_dockView) { return; } m_contextMenu = 0; m_dockView->visibility()->setBlockHiding(false); emit contextMenuChanged(); } bool DockMenuManager::mousePressEvent(QMouseEvent *event) { //qDebug() << "Step -1 ..."; if (!event || !m_dockView->containment()) { return false; } //qDebug() << "Step 0..."; //even if the menu is executed synchronously, other events may be processed //by the qml incubator when plasma is loading, so we need to guard there if (m_contextMenu) { //qDebug() << "Step 0.5 ..."; m_contextMenu->close(); m_contextMenu = 0; emit contextMenuChanged(); // PlasmaQuick::ContainmentView::mousePressEvent(event); - return true; + return false; } //qDebug() << "1 ..."; QString trigger = Plasma::ContainmentActions::eventToString(event); if (trigger == "RightButton;NoModifier") { Plasma::ContainmentActions *plugin = m_dockView->containment()->containmentActions().value(trigger); if (!plugin || plugin->contextualActions().isEmpty()) { event->setAccepted(false); return false; } //qDebug() << "2 ..."; //the plugin can be a single action or a context menu //Don't have an action list? execute as single action //and set the event position as action data /*if (plugin->contextualActions().length() == 1) { QAction *action = plugin->contextualActions().at(0); action->setData(event->pos()); action->trigger(); event->accept(); return; }*/ //FIXME: very inefficient appletAt() implementation Plasma::Applet *applet = 0; bool inSystray = false; //! initialize the appletContainsMethod on the first right click if (!m_appletContainsMethod.isValid()) { updateAppletContainsMethod(); } foreach (Plasma::Applet *appletTemp, m_dockView->containment()->applets()) { PlasmaQuick::AppletQuickItem *ai = appletTemp->property("_plasma_graphicObject").value(); bool appletContainsMouse = false; if (m_appletContainsMethod.isValid()) { QVariant retVal; m_appletContainsMethod.invoke(m_appletContainsMethodItem, Qt::DirectConnection, Q_RETURN_ARG(QVariant, retVal) , Q_ARG(QVariant, appletTemp->id()), Q_ARG(QVariant, event->pos())); appletContainsMouse = retVal.toBool(); } else { appletContainsMouse = ai->contains(ai->mapFromItem(m_dockView->contentItem(), event->pos())); } if (ai && ai->isVisible() && appletContainsMouse) { applet = ai->applet(); KPluginMetaData meta = applet->kPackage().metadata(); //Try to find applets inside a systray if (meta.pluginId() == "org.kde.plasma.systemtray" || meta.pluginId() == "org.nomad.systemtray") { auto systrayId = applet->config().readEntry("SystrayContainmentId"); applet = 0; inSystray = true; Plasma::Containment *cont = containmentById(systrayId.toInt()); if (cont) { foreach (Plasma::Applet *appletCont, cont->applets()) { PlasmaQuick::AppletQuickItem *ai2 = appletCont->property("_plasma_graphicObject").value(); if (ai2 && ai2->isVisible() && ai2->contains(ai2->mapFromItem(m_dockView->contentItem(), event->pos()))) { applet = ai2->applet(); break; } } } break; } else { ai = 0; } } } if (!applet && !inSystray) { applet = m_dockView->containment(); } //qDebug() << "3 ..."; if (applet) { const auto &provides = KPluginMetaData::readStringList(applet->pluginMetaData().rawData(), QStringLiteral("X-Plasma-Provides")); //qDebug() << "3.5 ..."; if (!provides.contains(QLatin1String("org.kde.plasma.multitasking"))) { //qDebug() << "4..."; QMenu *desktopMenu = new QMenu; + + //this is a workaround where Qt now creates the menu widget + //in .exec before oxygen can polish it and set the following attribute + desktopMenu->setAttribute(Qt::WA_TranslucentBackground); + //end workaround + + if (desktopMenu->winId()) { + desktopMenu->windowHandle()->setTransientParent(m_dockView); + } + desktopMenu->setAttribute(Qt::WA_DeleteOnClose); m_contextMenu = desktopMenu; //! deprecated old code that can be removed if the following plasma approach doesnt //! create any issues with context menu creation in Latte /*if (m_dockView->mouseGrabberItem()) { //workaround, this fixes for me most of the right click menu behavior m_dockView->mouseGrabberItem()->ungrabMouse(); return; }*/ //!plasma official code //this is a workaround where Qt will fail to realise a mouse has been released // this happens if a window which does not accept focus spawns a new window that takes focus and X grab // whilst the mouse is depressed // https://bugreports.qt.io/browse/QTBUG-59044 // this causes the next click to go missing //by releasing manually we avoid that situation auto ungrabMouseHack = [this]() { if (m_dockView->mouseGrabberItem()) { m_dockView->mouseGrabberItem()->ungrabMouse(); } }; //pre 5.8.0 QQuickWindow code is "item->grabMouse(); sendEvent(item, mouseEvent)" //post 5.8.0 QQuickWindow code is sendEvent(item, mouseEvent); item->grabMouse() if (QVersionNumber::fromString(qVersion()) > QVersionNumber(5, 8, 0)) { QTimer::singleShot(0, this, ungrabMouseHack); } else { ungrabMouseHack(); } //end workaround //!end of plasma official code(workaround) //qDebug() << "5 ..."; if (applet && applet != m_dockView->containment()) { //qDebug() << "5.3 ..."; emit applet->contextualActionsAboutToShow(); addAppletActions(desktopMenu, applet, event); } else { //qDebug() << "5.6 ..."; emit m_dockView->containment()->contextualActionsAboutToShow(); addContainmentActions(desktopMenu, event); } //this is a workaround where Qt now creates the menu widget //in .exec before oxygen can polish it and set the following attribute desktopMenu->setAttribute(Qt::WA_TranslucentBackground); //end workaround QPoint pos = event->globalPos(); if (applet) { //qDebug() << "6 ..."; desktopMenu->adjustSize(); if (m_dockView->screen()) { const QRect scr = m_dockView->screen()->geometry(); int smallStep = 3; int x = event->globalPos().x() + smallStep; int y = event->globalPos().y() + smallStep; //qDebug()<globalPos().x() > scr.center().x()) { x = event->globalPos().x() - desktopMenu->width() - smallStep; } if (event->globalPos().y() > scr.center().y()) { y = event->globalPos().y() - desktopMenu->height() - smallStep; } pos = QPoint(x, y); } } //qDebug() << "7..."; if (desktopMenu->isEmpty()) { //qDebug() << "7.5 ..."; delete desktopMenu; event->accept(); return false; } connect(desktopMenu, SIGNAL(aboutToHide()), this, SLOT(menuAboutToHide())); m_dockView->visibility()->setBlockHiding(true); desktopMenu->popup(pos); event->setAccepted(true); emit contextMenuChanged(); return false; } //qDebug() << "8 ..."; } //qDebug() << "9 ..."; } //qDebug() << "10 ..."; emit contextMenuChanged(); return true; // PlasmaQuick::ContainmentView::mousePressEvent(event); } //! update the appletContainsPos method from Panel view void DockMenuManager::updateAppletContainsMethod() { for (QQuickItem *item : m_dockView->contentItem()->childItems()) { if (auto *metaObject = item->metaObject()) { // not using QMetaObject::invokeMethod to avoid warnings when calling // this on applets that don't have it or other child items since this // is pretty much trial and error. // Also, "var" arguments are treated as QVariant in QMetaObject int methodIndex = metaObject->indexOfMethod("appletContainsPos(QVariant,QVariant)"); if (methodIndex == -1) { continue; } m_appletContainsMethod = metaObject->method(methodIndex); m_appletContainsMethodItem = item; } } } void DockMenuManager::addAppletActions(QMenu *desktopMenu, Plasma::Applet *applet, QEvent *event) { if (!m_dockView->containment()) { return; } foreach (QAction *action, applet->contextualActions()) { if (action) { desktopMenu->addAction(action); } } if (!applet->failedToLaunch()) { QAction *runAssociatedApplication = applet->actions()->action(QStringLiteral("run associated application")); if (runAssociatedApplication && runAssociatedApplication->isEnabled()) { desktopMenu->addAction(runAssociatedApplication); } QAction *configureApplet = applet->actions()->action(QStringLiteral("configure")); if (configureApplet && configureApplet->isEnabled()) { desktopMenu->addAction(configureApplet); } QAction *appletAlternatives = applet->actions()->action(QStringLiteral("alternatives")); if (appletAlternatives && appletAlternatives->isEnabled() && m_dockView->containment()->isUserConfiguring()) { desktopMenu->addAction(appletAlternatives); } } - QMenu *containmentMenu = new QMenu(i18nc("%1 is the name of the containment", "%1 Options", m_dockView->containment()->title()), desktopMenu); - addContainmentActions(containmentMenu, event); + QAction *containmentAction = desktopMenu->menuAction(); + containmentAction->setText(i18nc("%1 is the name of the containment", "%1 Options", m_dockView->containment()->title())); + + addContainmentActions(containmentAction->menu(), event); - if (!containmentMenu->isEmpty()) { + if (!containmentAction->menu()->isEmpty()) { int enabled = 0; //count number of real actions - QListIterator actionsIt(containmentMenu->actions()); + QListIterator actionsIt(containmentAction->menu()->actions()); while (enabled < 3 && actionsIt.hasNext()) { QAction *action = actionsIt.next(); if (action->isVisible() && !action->isSeparator()) { ++enabled; } } desktopMenu->addSeparator(); if (enabled) { //if there is only one, don't create a submenu // if (enabled < 2) { - foreach (QAction *action, containmentMenu->actions()) { + foreach (QAction *action, containmentAction->menu()->actions()) { if (action->isVisible()) { desktopMenu->addAction(action); } } // } else { // desktopMenu->addMenu(containmentMenu); // } } } if (m_dockView->containment()->immutability() == Plasma::Types::Mutable && (m_dockView->containment()->containmentType() != Plasma::Types::PanelContainment || m_dockView->containment()->isUserConfiguring())) { QAction *closeApplet = applet->actions()->action(QStringLiteral("remove")); //qDebug() << "checking for removal" << closeApplet; if (closeApplet) { if (!desktopMenu->isEmpty()) { desktopMenu->addSeparator(); } //qDebug() << "adding close action" << closeApplet->isEnabled() << closeApplet->isVisible(); desktopMenu->addAction(closeApplet); } } } void DockMenuManager::addContainmentActions(QMenu *desktopMenu, QEvent *event) { if (!m_dockView->containment()) { return; } if (m_dockView->containment()->corona()->immutability() != Plasma::Types::Mutable && !KAuthorized::authorizeAction(QStringLiteral("plasma/containment_actions"))) { //qDebug() << "immutability"; return; } //this is what ContainmentPrivate::prepareContainmentActions was const QString trigger = Plasma::ContainmentActions::eventToString(event); //"RightButton;NoModifier" Plasma::ContainmentActions *plugin = m_dockView->containment()->containmentActions().value(trigger); if (!plugin) { return; } if (plugin->containment() != m_dockView->containment()) { plugin->setContainment(m_dockView->containment()); // now configure it KConfigGroup cfg(m_dockView->containment()->corona()->config(), "ActionPlugins"); cfg = KConfigGroup(&cfg, QString::number(m_dockView->containment()->containmentType())); KConfigGroup pluginConfig = KConfigGroup(&cfg, trigger); plugin->restore(pluginConfig); } QList actions = plugin->contextualActions(); + foreach (auto act, actions) { + if (act->menu()) { + //this is a workaround where Qt now creates the menu widget + //in .exec before oxygen can polish it and set the following attribute + act->menu()->setAttribute(Qt::WA_TranslucentBackground); + //end workaround + + if (act->menu()->winId()) { + act->menu()->windowHandle()->setTransientParent(m_dockView); + } + } + } + desktopMenu->addActions(actions); return; } Plasma::Containment *DockMenuManager::containmentById(uint id) { foreach (auto containment, m_dockView->corona()->containments()) { if (id == containment->id()) { return containment; } } return 0; } } diff --git a/containment/package/contents/ui/applet/AppletItem.qml b/containment/package/contents/ui/applet/AppletItem.qml index 68d97000..da3d6d57 100644 --- a/containment/package/contents/ui/applet/AppletItem.qml +++ b/containment/package/contents/ui/applet/AppletItem.qml @@ -1,814 +1,816 @@ /* * 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.Layouts 1.1 import QtGraphicalEffects 1.0 import org.kde.plasma.plasmoid 2.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.extras 2.0 as PlasmaExtras import org.kde.kquickcontrolsaddons 2.0 import org.kde.latte 0.1 as Latte import "../../code/AppletIdentifier.js" as AppletIndetifier Item { id: container visible: false width: isInternalViewSplitter && !root.editMode ? 0 : (root.isHorizontal ? computeWidth : computeWidth + shownAppletMargin) height: isInternalViewSplitter && !root.editMode ? 0 : (root.isVertical ? computeHeight : computeHeight + shownAppletMargin) property bool animationsEnabled: true property bool animationWasSent: false //protection flag for animation broadcasting property bool canBeHovered: true property bool canShowAppletNumberBadge: !isSeparator && !isHidden && !isLattePlasmoid && !isSpacer && !isInternalViewSplitter property bool inFillCalculations: false //temp record, is used in calculations for fillWidth,fillHeight applets property bool needsFillSpace: { //fill flag, it is used in calculations for fillWidth,fillHeight applets if (!applet || !applet.Layout || (applet && applet.pluginName === "org.kde.plasma.panelspacer")) return false; if (((root.isHorizontal && applet.Layout.fillWidth===true) || (root.isVertical && applet.Layout.fillHeight===true)) && (applet.status !== PlasmaCore.Types.HiddenStatus)) return true; else return false; } property bool showZoomed: false property bool lockZoom: false property bool isExpanded: applet && applet.status >= PlasmaCore.Types.NeedsAttentionStatus && applet.status !== PlasmaCore.Types.HiddenStatus && applet.pluginName !== "org.kde.activeWindowControl" property bool isHidden: applet && applet.status === PlasmaCore.Types.HiddenStatus ? true : false property bool isInternalViewSplitter: (internalSplitterId > 0) property bool isLattePlasmoid: latteApplet !== null property bool isZoomed: false property bool isSeparator: applet && applet.pluginName === "audoban.applet.separator" property bool isSpacer: latteSpacer !== null property bool isSystray: applet && (applet.pluginName === "org.kde.plasma.systemtray" || applet.pluginName === "org.nomad.systemtray" ) property bool firstChildOfStartLayout: (index === layoutsContainer.startLayout.beginIndex) property bool lastChildOfEndLayout: ((index === layoutsContainer.endLayout.beginIndex+layoutsContainer.endLayout.count-1)&&(layoutsContainer.endLayout.count>1)) //applet is in starting edge /*property bool startEdge: index < layoutsContainer.endLayout.beginIndex ? (index === 0)&&(layoutsContainer.mainLayout.count > 1) : (index === layoutsContainer.endLayout.beginIndex)&&(layoutsContainer.endLayout.count > 1)*/ property bool startEdge: (index === layoutsContainer.startLayout.beginIndex) || (index === layoutsContainer.mainLayout.beginIndex) || (index === layoutsContainer.endLayout.beginIndex) //applet is in ending edge property bool endEdge: plasmoid.configuration.panelPosition !== Latte.Dock.Justify ? (index === layoutsContainer.mainLayout.beginIndex + layoutsContainer.mainLayout.count - 1)&&(layoutsContainer.mainLayout.count>1) : (((index === layoutsContainer.startLayout.beginIndex+layoutsContainer.startLayout.count-2)&&(layoutsContainer.startLayout.count>2)) ||((index === layoutsContainer.mainLayout.beginIndex+layoutsContainer.mainLayout.count-2)&&(layoutsContainer.mainLayout.count>2)) ||((index === layoutsContainer.endLayout.beginIndex+layoutsContainer.endLayout.count-1)&&(layoutsContainer.endLayout.count>1))) property int animationTime: root.durationTime* (1.2 *units.shortDuration) // 70 property int hoveredIndex: layoutsContainer.hoveredIndex property int index: -1 property int appletMargin: (applet && (applet.pluginName === root.plasmoidName)) || isInternalViewSplitter || root.reverseLinesPosition ? 0 : root.statesLineSize property int maxWidth: root.isHorizontal ? root.height : root.width property int maxHeight: root.isHorizontal ? root.height : root.width property int shownAppletMargin: isSystray ? 0 : appletMargin property int internalSplitterId: 0 property int previousIndex: -1 property int sizeForFill: -1 //it is used in calculations for fillWidth,fillHeight applets property int spacersMaxSize: Math.max(0,Math.ceil(0.5*root.iconSize) - root.iconMargin) property int status: applet ? applet.status : -1 property real computeWidth: root.isVertical ? wrapper.width : hiddenSpacerLeft.width+wrapper.width+hiddenSpacerRight.width property real computeHeight: root.isVertical ? hiddenSpacerLeft.height + wrapper.height + hiddenSpacerRight.height : wrapper.height property string title: isInternalViewSplitter ? "Now Dock Splitter" : "" property Item applet: null property Item latteApplet: applet && (applet.pluginName === root.plasmoidName) ? (applet.children[0] ? applet.children[0] : null) : null property Item latteSpacer: applet && (applet.pluginName === "org.kde.latte.spacer") ? (applet.children[0] ? applet.children[0] : null) : null property Item appletWrapper: applet && ((applet.pluginName === root.plasmoidName) || isSystray) ? wrapper : wrapper.wrapperContainer property Item appletIconItem; //first applet's IconItem, to be activated onExit signal property Item appletImageItem; property Item tooltipVisualParent: titleTooltipParent //this is used for folderView and icon widgets to fake their visual property bool fakeIconItem: applet && appletIconItem //(applet.pluginName === "org.kde.plasma.folder" || applet.pluginName === "org.kde.plasma.icon") property alias containsMouse: appletMouseArea.containsMouse property alias pressed: appletMouseArea.pressed /*onComputeHeightChanged: { if(index==0) console.log(computeHeight); }*/ //a timer that is used in order to init the fake applets on startup Timer { id: fakeInitTimer interval: 4000 onTriggered: { AppletIndetifier.reconsiderAppletIconItem(); if (root.debugModeTimers) { console.log("containment timer: appletItem fakeInitTimer called..."); } } } //set up the fake containers and properties for when a fakeIconItem must be presented to the user //because the plasma widgets specific implementation breaks the Latte experience onFakeIconItemChanged: { if (fakeIconItem) { applet.opacity = 0; if (applet.pluginName === "org.kde.plasma.folder") { applet.parent = wrapper.fakeIconItemContainer; applet.anchors.fill = wrapper.fakeIconItemContainer; } wrapper.disableScaleWidth = false; wrapper.disableScaleHeight = false; wrapper.updateLayoutWidth(); wrapper.updateLayoutHeight(); } } /// BEGIN functions function activateAppletForNeutralAreas(mouse){ //if the event is at the active indicator or spacers area then try to expand the applet, //unfortunately for other applets there is no other way to activate them yet //for example the icon-only applets var choords = mapToItem(container.appletWrapper, mouse.x, mouse.y); if (choords.x<0 || choords.y<0 || choords.x>=container.appletWrapper.width || choords.y>=container.appletWrapper.height) { dock.toggleAppletExpanded(applet.id); } } function checkIndex(){ index = -1; for(var i=0; i root.iconSize) && root.isHorizontal) || (applet && (applet.Layout.minimumHeight > root.iconSize) && root.isVertical)) && (applet && applet.pluginName !== "org.kde.plasma.panelspacer") && !container.fakeIconItem) || (isSystray) || (container.needsFillSpace) ) { canBeHovered = false; } else{ canBeHovered = true; } } //! pos in global root positioning function containsPos(pos) { var relPos = root.mapToItem(container,pos.x, pos.y); if (relPos.x>=0 && relPos.x<=width && relPos.y>=0 && relPos.y<=height) return true; return false; } function reconsiderAppletIconItem() { AppletIndetifier.reconsiderAppletIconItem(); } ///END functions //BEGIN connections onAppletChanged: { if (!applet) { destroy(); } else { AppletIndetifier.reconsiderAppletIconItem(); fakeInitTimer.start(); } } onHoveredIndexChanged:{ if ( (Math.abs(hoveredIndex-index) > 1) && (hoveredIndex !== -1) ) { wrapper.zoomScale = 1; } if (Math.abs(hoveredIndex-index) >= 1) { hiddenSpacerLeft.nScale = 0; hiddenSpacerRight.nScale = 0; } } onIndexChanged: { if (container.latteApplet) { root.latteAppletPos = index; } if (isHidden) { parabolicManager.setHidden(previousIndex, index); } if (isSeparator) { parabolicManager.setSeparator(previousIndex, index); } if (index>-1) { previousIndex = index; } } onIsExpandedChanged: { if (isExpanded) { root.hideTooltipLabel(); } } onIsHiddenChanged: { if (isHidden) { parabolicManager.setHidden(-1, index); } else { parabolicManager.setHidden(index, -1); } } onIsSeparatorChanged: { if (isSeparator) { parabolicManager.setSeparator(-1, index); } else { parabolicManager.setSeparator(index, -1); } } onLatteAppletChanged: { if(container.latteApplet){ root.latteApplet = container.latteApplet; root.latteAppletContainer = container; root.latteAppletPos = index; container.latteApplet.latteDock = root; container.latteApplet.forceHidePanel = true; container.latteApplet.signalAnimationsNeedBothAxis.connect(slotAnimationsNeedBothAxis); container.latteApplet.signalAnimationsNeedLength.connect(slotAnimationsNeedLength); container.latteApplet.signalAnimationsNeedThickness.connect(slotAnimationsNeedThickness); container.latteApplet.signalActionsBlockHiding.connect(slotActionsBlockHiding); container.latteApplet.signalPreviewsShown.connect(slotPreviewsShown); container.latteApplet.clearZoomSignal.connect(titleTooltipDialog.hide); } } onLatteSpacerChanged: { if(container.latteSpacer){ latteSpacer.latteDock = root; container.lockZoom = true; } } onNeedsFillSpaceChanged: checkCanBeHovered(); onShowZoomedChanged: { if(showZoomed){ //var newZ = container.maxHeight / root.iconSize; //wrapper.zoomScale = newZ; wrapper.zoomScale = 1; } else{ wrapper.zoomScale = 1; } } Component.onCompleted: { checkIndex(); root.updateIndexes.connect(checkIndex); root.clearZoomSignal.connect(clearZoom); } Component.onDestruction: { if (isSeparator){ parabolicManager.setSeparator(previousIndex, -1); } if (isHidden) parabolicManager.setHidden(previousIndex, -1); root.updateIndexes.disconnect(checkIndex); root.clearZoomSignal.disconnect(clearZoom); if (container.latteApplet) { container.latteApplet.signalAnimationsNeedBothAxis.disconnect(slotAnimationsNeedBothAxis); container.latteApplet.signalAnimationsNeedLength.disconnect(slotAnimationsNeedLength); container.latteApplet.signalAnimationsNeedThickness.disconnect(slotAnimationsNeedThickness); container.latteApplet.signalActionsBlockHiding.disconnect(slotActionsBlockHiding); container.latteApplet.signalPreviewsShown.disconnect(slotPreviewsShown); container.latteApplet.clearZoomSignal.disconnect(titleTooltipDialog.hide); } } Connections{ target: root /* onGlobalDirectRenderChanged:{ if (root.globalDirectRender && restoreAnimation.running) { // console.log("CLEAR APPLET SCALE !!!!"); //restoreAnimation.stop(); //wrapper.zoomScale = 1; } }*/ onLatteAppletHoveredIndexChanged: { if ( (root.zoomFactor>1) && (root.latteAppletHoveredIndex >= 0) ){ var distance = 2; //for Tasks plasmoid distance of 2 is not always safe there are //cases that needs to be 3, when an internal separator there is //between the hovered task and the current applet if (root.hasInternalSeparator) { if (index < root.latteAppletPos) { var firstTaskIndex = root.latteApplet.parabolicManager.availableHigherIndex(0); distance = firstTaskIndex+2; } else if (index > root.latteAppletPos) { var lastTaskIndex = root.latteApplet.parabolicManager.availableLowerIndex(root.tasksCount-1); distance = root.tasksCount-1-lastTaskIndex+2; } } if(Math.abs(index-root.latteAppletPos+root.latteAppletHoveredIndex)>=Math.max(2,distance)) { container.clearZoom(); } } } onSignalActivateEntryAtIndex: { if (parabolicManager.pseudoIndexBelongsToLatteApplet(entryIndex) && container.isLattePlasmoid) { latteApplet.activateTaskAtIndex(entryIndex - latteApplet.tasksNumbersBase); } else if (entryIndex === parabolicManager.pseudoAppletIndex(container.index)) { dock.toggleAppletExpanded(applet.id); } } onSignalNewInstanceForEntryAtIndex: { if (parabolicManager.pseudoIndexBelongsToLatteApplet(entryIndex) && container.isLattePlasmoid) { latteApplet.newInstanceForTaskAtIndex(entryIndex - latteApplet.tasksNumbersBase); } else if (entryIndex === parabolicManager.pseudoAppletIndex(container.index)) { dock.toggleAppletExpanded(applet.id); } } } Connections{ target: layoutsContainer onHoveredIndexChanged:{ //for applets it is safe to consider that a distance of 2 //is enough to clearZoom if ( (root.zoomFactor>1) && (layoutsContainer.hoveredIndex>=0) && (Math.abs(index-layoutsContainer.hoveredIndex)>=2)) container.clearZoom(); if ((restoreAnimation.running) && (layoutsContainer.hoveredIndex !== -1)) { restoreAnimation.stop(); } } } Connections{ target: root onLatteAppletHoveredIndexChanged: { if ((restoreAnimation.running) && (root.latteAppletHoveredIndex !== -1)) { restoreAnimation.stop(); } } } ///END connections PlasmaComponents.BusyIndicator { z: 1000 visible: applet && applet.busy running: visible anchors.centerIn: parent width: Math.min(parent.width, parent.height) height: width } /* Rectangle{ anchors.fill: parent color: "transparent" border.color: "green" border.width: 1 }*/ MouseArea{ id: appletMouseAreaBottom anchors.fill: parent propagateComposedEvents: true visible: !appletMouseArea.visible && !root.editMode onPressed: { container.activateAppletForNeutralAreas(mouse); mouse.accepted = false; } } Flow{ id: appletFlow width: container.computeWidth height: container.computeHeight anchors.rightMargin: (latteApplet || (showZoomed && root.editMode)) || (plasmoid.location !== PlasmaCore.Types.RightEdge) ? 0 : shownAppletMargin anchors.leftMargin: (latteApplet || (showZoomed && root.editMode)) || (plasmoid.location !== PlasmaCore.Types.LeftEdge) ? 0 : shownAppletMargin anchors.topMargin: (latteApplet || (showZoomed && root.editMode)) || (plasmoid.location !== PlasmaCore.Types.TopEdge)? 0 : shownAppletMargin anchors.bottomMargin: (latteApplet || (showZoomed && root.editMode)) || (plasmoid.location !== PlasmaCore.Types.BottomEdge) ? 0 : shownAppletMargin // a hidden spacer for the first element to add stability // IMPORTANT: hidden spacers must be tested on vertical !!! AppletHiddenSpacer{id: hiddenSpacerLeft} AppletItemWrapper{ id: wrapper TitleTooltipParent{ id: titleTooltipParent } } // a hidden spacer on the right for the last item to add stability AppletHiddenSpacer{id: hiddenSpacerRight; rightSpacer: true} }// Flow with hidden spacers inside //! The Launchers Area Indicator Rectangle{ anchors.fill: parent radius: root.iconSize/10 property color tempColor: "#aa222222" color: tempColor border.width: 1 border.color: "#ff656565" opacity: latteApplet && root.addLaunchersMessage ? 1 : 0 Behavior on opacity{ NumberAnimation { duration: 2*root.durationTime*container.animationTime } } PlasmaExtras.Heading { width: parent.width height: parent.height text: i18n("Launchers Area") level: 3 font.bold: true horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter wrapMode: Text.WordWrap elide: Text.ElideRight rotation: { if (root.isHorizontal) return 0; else if (plasmoid.location === PlasmaCore.Types.LeftEdge) return -90; else if (plasmoid.location === PlasmaCore.Types.RightEdge) return 90; } } } MouseArea{ id: appletMouseArea anchors.fill: parent enabled: (!latteApplet)&&(canBeHovered)&&(!root.editMode)//&&(!lockZoom) hoverEnabled: !root.editMode && (!latteApplet) ? true : false propagateComposedEvents: true //! a way must be found in order for this be enabled //! only to support springloading for plasma 5.10 //! also on this is based the tooltips behavior by enabling it //! plasma tooltips are disabled visible: !container.latteApplet && !lockZoom && canBeHovered && !(container.isSeparator && !root.editMode) //&& (root.zoomFactor>1) property bool fastEnteringFlag: false property bool pressed: false onClicked: { pressed = false; mouse.accepted = false; } onEntered: { //AppletIndetifier.reconsiderAppletIconItem(); if (containsMouse && !container.lockZoom && container.canBeHovered){ root.stopCheckRestoreZoomTimer(); } if (restoreAnimation.running) { restoreAnimation.stop(); } root.showTooltipLabel(container, applet.title); //console.log("entered applet:" + layoutsContainer.hoveredIndex); if (fastEnteringFlag) { fastEnteringFlag = false; } if ((layoutsContainer.hoveredIndex !== -1) && !root.globalDirectRender) { fastEnteringFlag = true; } if (!(root.dockIsHidden || root.inSlidingIn || root.inSlidingOut)){ layoutsContainer.hoveredIndex = index; } if (lockZoom || !canBeHovered) { return; } - if (root.isHalfShown || (root.latteApplet && root.latteApplet.noTasksInAnimation>0)) { + if (root.isHalfShown || (root.latteApplet + && (root.latteApplet.noTasksInAnimation>0 || root.latteApplet.contextMenu))) { return; } if (root.isHorizontal){ layoutsContainer.currentSpot = mouseX; wrapper.calculateScales(mouseX); } else{ layoutsContainer.currentSpot = mouseY; wrapper.calculateScales(mouseY); } } onExited:{ if (fastEnteringFlag) { fastEnteringFlag = false; } if (appletIconItem && appletIconItem.visible) appletIconItem.active = false; root.hideTooltipLabel(); if (root.zoomFactor>1){ root.startCheckRestoreZoomTimer(); } } onPositionChanged: { // if(!pressed){ if (lockZoom || !canBeHovered) { mouse.accepted = false; return; } - if (root.isHalfShown || (root.latteApplet && root.latteApplet.noTasksInAnimation>0)) { + if (root.isHalfShown || (root.latteApplet + && (root.latteApplet.noTasksInAnimation>0 || root.latteApplet.contextMenu))) { return; } if (fastEnteringFlag) { var lengthPos; if (root.vertical) { lengthPos = mouse.x; } else { lengthPos = mouse.y; } //! check if the mouse enters a second applet and it is near the center //! this way the directRendering isnt activated too fast or when the //! mouse enters between two tasks at start if (lengthPos >= (wrapper.center - root.iconSize/2) && lengthPos <= (wrapper.center + root.iconSize/2)) { if (!root.globalDirectRender) { root.setGlobalDirectRender(true); } fastEnteringFlag = false; } } if( ((wrapper.zoomScale == 1 || wrapper.zoomScale === root.zoomFactor) && !root.globalDirectRender) || root.globalDirectRender) { if (!(root.dockIsHidden || root.inSlidingIn || root.inSlidingOut)){ layoutsContainer.hoveredIndex = index; } if (root.isHorizontal){ var step = Math.abs(layoutsContainer.currentSpot-mouse.x); if (step >= root.animationStep){ layoutsContainer.currentSpot = mouse.x; wrapper.calculateScales(mouse.x); } } else{ var step = Math.abs(layoutsContainer.currentSpot-mouse.y); if (step >= root.animationStep){ layoutsContainer.currentSpot = mouse.y; wrapper.calculateScales(mouse.y); } } } mouse.accepted = false; } onPressed: { container.activateAppletForNeutralAreas(mouse); pressed = true; // mouse.accepted = false; } onReleased: { pressed = false; } } //BEGIN states states: [ State { name: "left" when: (plasmoid.location === PlasmaCore.Types.LeftEdge) AnchorChanges { target: appletFlow anchors{ top:undefined; bottom:undefined; left:parent.left; right:undefined;} } }, State { name: "right" when: (plasmoid.location === PlasmaCore.Types.RightEdge) AnchorChanges { target: appletFlow anchors{ top:undefined; bottom:undefined; left:undefined; right:parent.right;} } }, State { name: "bottom" when: (plasmoid.location === PlasmaCore.Types.BottomEdge) AnchorChanges { target: appletFlow anchors{ top:undefined; bottom:parent.bottom; left:undefined; right:undefined;} } }, State { name: "top" when: (plasmoid.location === PlasmaCore.Types.TopEdge) AnchorChanges { target: appletFlow anchors{ top:parent.top; bottom:undefined; left:undefined; right:undefined;} } } ] //END states //BEGIN animations ///////Restore Zoom Animation///// ParallelAnimation{ id: restoreAnimation PropertyAnimation { target: wrapper property: "zoomScale" to: 1 duration: 4 * container.animationTime easing.type: Easing.InCubic } PropertyAnimation { target: hiddenSpacerLeft property: "nScale" to: 0 duration: 4 * container.animationTime easing.type: Easing.InCubic } PropertyAnimation { target: hiddenSpacerRight property: "nScale" to: 0 duration: 4 * container.animationTime easing.type: Easing.InCubic } } /////Clicked Animation///// SequentialAnimation{ id: clickedAnimation alwaysRunToEnd: true running: appletMouseArea.pressed && (root.durationTime > 0) onStopped: appletMouseArea.pressed = false; ParallelAnimation{ PropertyAnimation { target: wrapper.clickedEffect property: "brightness" to: -0.35 duration: units.longDuration easing.type: Easing.OutQuad } } ParallelAnimation{ PropertyAnimation { target: wrapper.clickedEffect property: "brightness" to: 0 duration: units.longDuration easing.type: Easing.OutQuad } } } //END animations } diff --git a/plasmoid/package/contents/ui/task/TaskDelegate.qml b/plasmoid/package/contents/ui/task/TaskDelegate.qml index b33c6e88..dc723917 100644 --- a/plasmoid/package/contents/ui/task/TaskDelegate.qml +++ b/plasmoid/package/contents/ui/task/TaskDelegate.qml @@ -1,1432 +1,1436 @@ /* * 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.0 import QtQuick.Layouts 1.1 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.plasma.plasmoid 2.0 import org.kde.plasma.private.taskmanager 0.1 as TaskManagerApplet import org.kde.latte 0.1 as Latte import "animations" as TaskAnimations MouseArea{ id: mainItemContainer visible: false //true//(isStartup && root.durationTime !== 0) ? false : true anchors.bottom: (root.position === PlasmaCore.Types.BottomPositioned) ? parent.bottom : undefined anchors.top: (root.position === PlasmaCore.Types.TopPositioned) ? parent.top : undefined anchors.left: (root.position === PlasmaCore.Types.LeftPositioned) ? parent.left : undefined anchors.right: (root.position === PlasmaCore.Types.RightPositioned) ? parent.right : undefined objectName: "TaskDelegate" width: { if (!visible) return 0; if (isSeparator) return root.vertical ? separatorItem.width : (root.dragSource || root.editMode ? 5+root.iconMargin : 0); if (root.vertical) { if (!inAttentionAnimation) return wrapper.width; else return wrapper.maxThickness; } else { return hiddenSpacerLeft.width+wrapper.width+hiddenSpacerRight.width; } } height: { if (!visible) return 0; if (isSeparator) return !root.vertical ? separatorItem.height : (root.dragSource || root.editMode ? 5+root.iconMargin: 0); if (root.vertical) { return hiddenSpacerLeft.height + wrapper.height + hiddenSpacerRight.height; } else { if (!inAttentionAnimation) return wrapper.height; else return wrapper.maxThickness; } } acceptedButtons: Qt.LeftButton | Qt.MidButton | Qt.RightButton hoverEnabled: visible && (!inAnimation) && (!IsStartup) && (!root.taskInAnimation) && (!root.editMode || root.debugLocation)&&(!inBouncingAnimation) && !isSeparator // hoverEnabled: false //opacity : isSeparator && (hiddenSpacerLeft.neighbourSeparator || hiddenSpacerRight.neighbourSeparator) ? 0 : 1 property bool buffersAreReady: false property bool fastEnteringFlag: false //!flag to check if the mouse entered the dock very fast property bool delayingRemove: ListView.delayRemove property bool scalesUpdatedOnce: false //states that exist in windows in a Group of windows property bool hasActive: isActive property bool hasMinimized: (IsGroupParent === true) ? tasksWindows.hasMinimized : isMinimized property bool hasShown: (IsGroupParent === true) ? tasksWindows.hasShown : !isMinimized property bool inAddRemoveAnimation: true property bool inAnimation: true property bool inAttentionAnimation: false property bool inBlockingAnimation: false property bool inBouncingAnimation: false property bool inFastRestoreAnimation: false property bool inMimicParabolicAnimation: false property real mimicParabolicScale: -1 property bool inPopup: false property bool inRemoveStage: false property bool inWheelAction: false property bool isActive: (IsActive === true) ? true : false property bool isDemandingAttention: (IsDemandingAttention === true) ? true : false property bool isDragged: false property bool isGroupParent: (IsGroupParent === true) ? true : false property bool isLauncher: (IsLauncher === true) ? true : false property bool isMinimized: (IsMinimized === true) ? true : false property bool isSeparator: false property bool isStartup: (IsStartup === true) ? true : false property bool isWindow: (IsWindow === true) ? true : false property bool isZoomed: false property bool pressed: false readonly property bool showAttention: isDemandingAttention && plasmoid.status === PlasmaCore.Types.RequiresAttentionStatus ? true : false property int animationTime: root.durationTime * 1.2 * units.shortDuration property int badgeIndicator: 0 //it is used from external apps property int hoveredIndex: icList.hoveredIndex property int itemIndex: index property int lastValidIndex: -1 //used for the removal animation property int lastButtonClicked: -1; property int pressX: -1 property int pressY: -1 property int resistanceDelay: 450 property int spacersMaxSize: Math.max(0,Math.ceil(0.55*root.iconSize) - root.iconMargin) property string activity: tasksModel.activity readonly property var m: model readonly property int pid: model && model.AppPid ? model.AppPid : -1 readonly property string appName: model && model.AppName ? model.AppName : "" property string modelLauncherUrl: (LauncherUrlWithoutIcon && LauncherUrlWithoutIcon !== null) ? LauncherUrlWithoutIcon : "" property string modelLauncherUrlWithIcon: (LauncherUrl && LauncherUrl !== null) ? LauncherUrl : "" property string launcherUrl: "" property string launcherUrlWithIcon: "" property string launcherName: "" property Item tooltipVisualParent: wrapper.titleTooltipVisualParent property Item previewsVisualParent: wrapper.previewsTooltipVisualParent onModelLauncherUrlChanged: { if (modelLauncherUrl !== ""){ launcherUrl = modelLauncherUrl; //!extract the launcherName if possible var nameStarts = launcherUrl.lastIndexOf("/"); if (nameStarts === -1){ nameStarts = launcherUrl.lastIndexOf(":"); } var nameEnds = launcherUrl.lastIndexOf(".desktop"); if (nameStarts!==-1 && nameEnds!==-1 && nameStarts 0 && !isLauncher readonly property bool playingAudio: hasAudioStream && audioStreams.some(function (item) { return !item.corked }) readonly property bool muted: hasAudioStream && audioStreams.every(function (item) { return item.muted }) readonly property int volume: { if (!hasAudioStream){ return 0; } var maxVolume = 0; for (var i=0; i maxVolume) maxVolume = audioStreams[i].volume; } return maxVolume; } ////// property QtObject contextMenu: null property QtObject draggingResistaner: null property QtObject hoveredTimerObj: null signal groupWindowAdded(); signal groupWindowRemoved(); signal checkWindowsStates(); Behavior on opacity { // NumberAnimation { duration: (IsStartup || (IsLauncher) ) ? 0 : 400 } NumberAnimation { duration: root.durationTime*units.longDuration } } TaskWindows{ id: tasksWindows property int previousCount: 0 onWindowsCountChanged: { if ((windowsCount >= 2) && (windowsCount > previousCount) && !(mainItemContainer.containsMouse || parabolicManager.neighbourIsHovered(itemIndex)) ){ if(root.dragSource == null) mainItemContainer.groupWindowAdded(); } else if ((windowsCount >=1) &&(windowsCount < previousCount)){ //sometimes this is triggered in dragging with no reason if(root.dragSource == null && !mainItemContainer.delayingRemove) mainItemContainer.groupWindowRemoved(); } if (windowsCount>=1) { mainItemContainer.slotPublishGeometries(); } previousCount = windowsCount; } } Loader { id: isSeparatorRectangle active: (opacityN>0) width: mainItemContainer.width height: mainItemContainer.height anchors.centerIn: separatorItem property real opacityN: isSeparator && root.contextMenu && root.contextMenu.visualParent === mainItemContainer ? 1 : 0 Behavior on opacityN { NumberAnimation { duration: root.durationTime*units.longDuration } } sourceComponent: Rectangle{ anchors.fill: parent opacity: isSeparatorRectangle.opacityN radius: 3 property color tempColor: theme.highlightColor color: tempColor border.width: 1 border.color: theme.highlightColor onTempColorChanged: tempColor.a = 0.35; } } Item{ id:separatorItem anchors.rightMargin: root.position === PlasmaCore.Types.RightPositioned ? localThickMargin : 0 anchors.leftMargin: root.position === PlasmaCore.Types.LeftPositioned ? localThickMargin : 0 anchors.bottomMargin: root.position === PlasmaCore.Types.BottomPositioned ? localThickMargin : 0 anchors.topMargin: root.position === PlasmaCore.Types.TopPositioned ? localThickMargin : 0 anchors.horizontalCenter: !root.vertical ? parent.horizontalCenter : undefined anchors.verticalCenter: root.vertical ? parent.verticalCenter : undefined anchors.right: root.position === PlasmaCore.Types.RightPositioned ? parent.right : undefined; anchors.left: root.position === PlasmaCore.Types.LeftPositioned ? parent.left : undefined; anchors.top: root.position === PlasmaCore.Types.TopPositioned ? parent.top : undefined; anchors.bottom: root.position === PlasmaCore.Types.BottomPositioned ? parent.bottom : undefined; //opacity: separatorShadow.active || root.internalSeparatorHidden ? 0 : 0.4 opacity: separatorShadow.active || forceHiddenState ? 0 : 0.4 visible: mainItemContainer.isSeparator width: root.vertical ? root.iconSize : (root.dragSource || root.editMode) ? 5+root.iconMargin: 1 height: !root.vertical ? root.iconSize : (root.dragSource || root.editMode) ? 5+root.iconMargin: 1 property int localThickMargin: root.statesLineSize + root.thickMarginBase + 4 property bool forceHiddenState: false Behavior on opacity { NumberAnimation { duration: root.durationTime*units.longDuration } } function updateForceHiddenState() { if (!isSeparator || root.editMode || root.dragSource) { forceHiddenState = false; } else { var firstPosition = (index>=0) && (index < parabolicManager.firstRealTaskIndex); var sepNeighbour = parabolicManager.taskIsSeparator(index-1); var firstSepFromLastSeparatorsGroup = (index>=0) && (index > parabolicManager.lastRealTaskIndex); forceHiddenState = (firstPosition || sepNeighbour || firstSepFromLastSeparatorsGroup); } } Component.onCompleted: updateForceHiddenState(); onForceHiddenStateChanged: root.separatorsUpdated(); Connections{ target: root onEditModeChanged: separatorItem.updateForceHiddenState(); onDragSourceChanged: { separatorItem.updateForceHiddenState(); if (isSeparator && !root.dragSource) { parabolicManager.setSeparator(launcherUrl, itemIndex); } } onSeparatorsUpdated: separatorItem.updateForceHiddenState(); onGlobalDirectRenderChanged:{ if (root.globalDirectRender && restoreAnimation.running) { // console.log("Cleat Task Scale !!!!"); restoreAnimation.stop(); } } } Rectangle { anchors.horizontalCenter: !root.vertical ? parent.horizontalCenter : undefined anchors.verticalCenter: root.vertical ? parent.verticalCenter : undefined anchors.right: root.position === PlasmaCore.Types.RightPositioned ? parent.right : undefined; anchors.left: root.position === PlasmaCore.Types.LeftPositioned ? parent.left : undefined; anchors.top: root.position === PlasmaCore.Types.TopPositioned ? parent.top : undefined; anchors.bottom: root.position === PlasmaCore.Types.BottomPositioned ? parent.bottom : undefined; radius: 2 width: root.vertical ? root.iconSize - 8 : 1 height: !root.vertical ? root.iconSize - 8 : 1 color: theme.textColor } } ///Shadow in tasks Loader{ id: separatorShadow anchors.fill: separatorItem active: root.enableShadows && isSeparator opacity: separatorItem.forceHiddenState ? 0 : 0.4 Behavior on opacity { NumberAnimation { duration: root.durationTime*units.longDuration } } sourceComponent: DropShadow{ anchors.fill: parent color: root.appShadowColor fast: true samples: 2 * radius source: separatorItem radius: root.appShadowSize verticalOffset: 2 } } /* Rectangle{ anchors.fill: parent color: "transparent" border.width: 1 border.color: "blue" } */ Flow{ id: taskFlow width: parent.width height: parent.height // a hidden spacer for the first element to add stability // IMPORTANT: hidden spacers must be tested on vertical !!! TaskHiddenSpacer{ id:hiddenSpacerLeft;} TaskWrapper{ id: wrapper } // a hidden spacer on the right for the last item to add stability TaskHiddenSpacer{ id:hiddenSpacerRight; rightSpacer: true } }// Flow with hidden spacers inside /*Rectangle{ anchors.fill: taskFlow color: "transparent" border.width: 1 border.color: "blue" }*/ Component { id: taskInitComponent Timer { id: timer interval: 800 repeat: false onTriggered: { // mainItemContainer.hoverEnabled = true; slotPublishGeometries(); if (latteDock && latteDock.debugModeTimers) { console.log("plasmoid timer: taskInitComponentTimer called..."); } timer.destroy(); } Component.onCompleted: timer.start() } } ////// Values Changes ///// //restore scales when there is no zoom factor for that item or //the mouse is out of the ListView // onItemIndexChanged: { // } onAppNameChanged: updateAudioStreams() onPidChanged: updateAudioStreams() onHasAudioStreamChanged: updateAudioStreams() onHoveredIndexChanged: { var distanceFromHovered = Math.abs(index - icList.hoveredIndex); /*if( (distanceFromHovered > 1) && (hoveredIndex !== -1)){ if(!isDragged) wrapper.mScale = 1; }*/ if (distanceFromHovered >= 1 && !inAttentionAnimation && !inFastRestoreAnimation && !inMimicParabolicAnimation) { hiddenSpacerLeft.nScale = 0; hiddenSpacerRight.nScale = 0; } } onItemIndexChanged: { if (itemIndex>=0) lastValidTimer.start(); if (isSeparator){ parabolicManager.setSeparator(launcherUrl, itemIndex); } } onIsDraggedChanged: { if(isDragged && (!root.editMode)){ root.dragSource = mainItemContainer; dragHelper.startDrag(mainItemContainer, model.MimeType, model.MimeData, model.LauncherUrlWithoutIcon, model.decoration); pressX = -1; pressY = -1; } } onIsWindowChanged: { if (isWindow) { taskInitComponent.createObject(mainItemContainer); } } onIsMinimizedChanged: { checkWindowsStates(); } onIsActiveChanged: { checkWindowsStates(); } onIsSeparatorChanged: { if (isSeparator) { parabolicManager.setSeparator(launcherUrl, itemIndex); if (parabolicManager.isLauncherToBeMoved(launcherUrl) && itemIndex>=0) { parabolicManager.moveLauncherToCorrectPos(launcherUrl, itemIndex); } } else { parabolicManager.setSeparator(launcherUrl, -1); } } onLauncherUrlChanged: updateBadge(); ////// End of Values Changes ///// ///////////////// Mouse Area Events /////////////////// onEntered: { if (root.editMode) return; root.stopCheckRestoreZoomTimer(); if (restoreAnimation.running) { restoreAnimation.stop(); } // console.log("entered task:" + icList.hoveredIndex); if (fastEnteringFlag) { fastEnteringFlag = false; } if (icList.hoveredIndex!==-1 && !root.globalDirectRender) { fastEnteringFlag = true; } if ((icList.hoveredIndex !== itemIndex) && isLauncher && windowsPreviewDlg.visible) { windowsPreviewDlg.hide(1); } if (!latteDock || (latteDock && !(latteDock.dockIsHidden || latteDock.inSlidingIn || latteDock.inSlidingOut))){ icList.hoveredIndex = index; } if (root.latteDock && (!root.showPreviews || (root.showPreviews && isLauncher))){ root.latteDock.showTooltipLabel(mainItemContainer, model.AppName); } if (root.latteDock && root.latteDock.isHalfShown) { return; } /* if((!inAnimation)&&(root.dragSource == null)&&(!root.taskInAnimation) && hoverEnabled){ if (inAttentionAnimation) { var subSpacerScale = (root.zoomFactor-1)/2; hiddenSpacerLeft.nScale = subSpacerScale; hiddenSpacerRight.nScale = subSpacerScale; } if (!inBlockingAnimation || inAttentionAnimation) { if (icList.orientation == Qt.Horizontal){ icList.currentSpot = mouseX; wrapper.calculateScales(mouseX); } else{ icList.currentSpot = mouseY; wrapper.calculateScales(mouseY); } } }*/ } // IMPORTANT: This must be improved ! even for small miliseconds it reduces performance onExited: { mainItemContainer.scalesUpdatedOnce = false; if (fastEnteringFlag) { fastEnteringFlag = false; } if (root.latteDock && (!root.showPreviews || (root.showPreviews && isLauncher))){ root.latteDock.hideTooltipLabel(); } if(mainItemContainer.contextMenu && mainItemContainer.contextMenu.status == PlasmaComponents.DialogStatus.Open){ ///dont check to restore zooms } else{ if(!inAnimation){ root.startCheckRestoreZoomTimer(); } } /* if(draggingResistaner != null){ draggingResistaner.destroy(); draggingResistaner = null; isDragged = false; }*/ } //! mouseX-Y values are delayed to be updated onEntered events and at the same time //! onPositionChanged signal may be delayed. we can fix this by dont delay at all //! when mouseX-Y is updated based on the plasmoid formFactor function mousePosChanged(mousePos) { if (root.editMode || mousePos<0 || (inBlockingAnimation && !(inAttentionAnimation||inFastRestoreAnimation||inMimicParabolicAnimation))) return; root.stopCheckRestoreZoomTimer(); if (root.latteDock && root.latteDock.isHalfShown) { return; } if((inAnimation == false)&&(!root.taskInAnimation)&&(!root.disableRestoreZoom) && hoverEnabled){ if (!latteDock || (latteDock && !(latteDock.dockIsHidden || latteDock.inSlidingIn || latteDock.inSlidingOut))){ icList.hoveredIndex = index; } if (fastEnteringFlag) { var lengthPos = mousePos; //! check if the mouse enters a second task and it is near the center //! this way the directRendering isnt activated too fast or when the //! mouse enters between two tasks at start if (lengthPos >= (wrapper.center - root.iconSize/2) && lengthPos <= (wrapper.center + root.iconSize/2)) { if (!root.globalDirectRender) { root.setGlobalDirectRender(true); } fastEnteringFlag = false; } } if( ((wrapper.mScale == 1 || wrapper.mScale === root.zoomFactor) && !root.globalDirectRender) || root.globalDirectRender || !scalesUpdatedOnce) { if(root.dragSource == null){ var step = Math.abs(icList.currentSpot-mousePos); if (step >= root.animationStep){ icList.currentSpot = mousePos; wrapper.calculateScales(mousePos); } } } } } onMouseXChanged: { if (!root.vertical) { mousePosChanged(mouseX); } } onMouseYChanged: { if (root.vertical) { mousePosChanged(mouseY); } } onPositionChanged: { if (root.editMode || (inBlockingAnimation && !(inAttentionAnimation||inFastRestoreAnimation||inMimicParabolicAnimation))) return; if (root.latteDock && root.latteDock.isHalfShown) { return; } if((inAnimation == false)&&(!root.taskInAnimation)&&(!root.disableRestoreZoom) && hoverEnabled){ // mouse.button is always 0 here, hence checking with mouse.buttons if (pressX != -1 && mouse.buttons == Qt.LeftButton && isDragged && !root.editMode && dragHelper.isDrag(pressX, pressY, mouse.x, mouse.y) ) { root.dragSource = mainItemContainer; dragHelper.startDrag(mainItemContainer, model.MimeType, model.MimeData, model.LauncherUrlWithoutIcon, model.decoration); pressX = -1; pressY = -1; } } } onContainsMouseChanged:{ if(!containsMouse){ // hiddenSpacerLeft.nScale = 0; // hiddenSpacerRight.nScale = 0; if(!inAnimation) pressed=false; } ////window previews///////// if (isWindow) { if(containsMouse && (root.showPreviews || (!root.showPreviews && root.highlightWindows)) && Latte.WindowSystem.compositingActive){ hoveredTimerObj = hoveredTimerComponent.createObject(mainItemContainer); } else{ if (hoveredTimerObj){ hoveredTimerObj.stop(); hoveredTimerObj.destroy(); } } } ////disable hover effect/// if (isWindow && root.highlightWindows && !containsMouse) { root.windowsHovered(model.LegacyWinIdList, false); } } onPressed: { //console.log("Pressed Task Delegate.."); if (Latte.WindowSystem.compositingActive) { windowsPreviewDlg.hide(2); } var modAccepted = modifierAccepted(mouse); if ((mouse.button == Qt.LeftButton)||(mouse.button == Qt.MidButton) || modAccepted) { lastButtonClicked = mouse.button; pressed = true; pressX = mouse.x; pressY = mouse.y; if(draggingResistaner == null && !modAccepted) draggingResistaner = resistanerTimerComponent.createObject(mainItemContainer); } else if (mouse.button == Qt.RightButton && !modAccepted){ // When we're a launcher, there's no window controls, so we can show all // places without the menu getting super huge. if (model.IsLauncher === true && !isSeparator) { showContextMenu({showAllPlaces: true}) } else { showContextMenu(); } //root.createContextMenu(mainItemContainer).show(); } if (hoveredTimerObj){ hoveredTimerObj.restart(); /*hoveredTimerObj.stop(); hoveredTimerObj.destroy();*/ } } onReleased: { //console.log("Released Task Delegate..."); if (draggingResistaner != null){ draggingResistaner.destroy(); draggingResistaner = null; } if(pressed && (!inBlockingAnimation || inAttentionAnimation) && !isSeparator){ if (modifierAccepted(mouse)){ if( !mainItemContainer.isLauncher){ if (root.modifierClickAction == Latte.Dock.NewInstance) { tasksModel.requestNewInstance(modelIndex()); } else if (root.modifierClickAction == Latte.Dock.Close) { tasksModel.requestClose(modelIndex()); } else if (root.modifierClickAction == Latte.Dock.ToggleMinimized) { tasksModel.requestToggleMinimized(modelIndex()); } else if ( root.modifierClickAction == Latte.Dock.CycleThroughTasks) { if (isGroupParent) tasksWindows.activateNextTask(); else activateTask(); } else if (root.modifierClickAction == Latte.Dock.ToggleGrouping) { tasksModel.requestToggleGrouping(modelIndex()); } } else { activateTask(); } } else if (mouse.button == Qt.MidButton){ if( !mainItemContainer.isLauncher){ if (root.middleClickAction == Latte.Dock.NewInstance) { tasksModel.requestNewInstance(modelIndex()); } else if (root.middleClickAction == Latte.Dock.Close) { tasksModel.requestClose(modelIndex()); } else if (root.middleClickAction == Latte.Dock.ToggleMinimized) { tasksModel.requestToggleMinimized(modelIndex()); } else if ( root.middleClickAction == Latte.Dock.CycleThroughTasks) { if (isGroupParent) tasksWindows.activateNextTask(); else activateTask(); } else if (root.middleClickAction == Latte.Dock.ToggleGrouping) { tasksModel.requestToggleGrouping(modelIndex()); } } else { activateTask(); } } else if (mouse.button == Qt.LeftButton){ activateTask(); } backend.cancelHighlightWindows(); } pressed = false; if(!inAnimation) { startCheckRestoreZoomTimer(3*units.longDuration); } } onWheel: { if (isSeparator || !root.mouseWheelActions || inWheelAction || inBouncingAnimation || (latteDock && (latteDock.dockIsHidden || latteDock.inSlidingIn || latteDock.inSlidingOut))){ return; } var angle = wheel.angleDelta.y / 8; //positive direction if (angle > 12) { if (isLauncher) { inWheelAction = true; wrapper.runLauncherAnimation(); } else if (isGroupParent) { tasksWindows.activateNextTask(); } else { var taskIndex = modelIndex(); if (isMinimized) { inWheelAction = true; tasksModel.requestToggleMinimized(taskIndex); wheelActionDelayer.start(); } tasksModel.requestActivate(taskIndex); } //negative direction } else if (angle < -12) { if (isLauncher) { // do nothing } else if (isGroupParent) { tasksWindows.activatePreviousTask(); } else { var taskIndex = modelIndex(); if (isMinimized) { inWheelAction = true; tasksModel.requestToggleMinimized(taskIndex); wheelActionDelayer.start(); } tasksModel.requestActivate(taskIndex); } } } ///////////////// End Of Mouse Area Events /////////////////// ///// Handlers for Signals ///// function animationStarted(){ // console.log("Animation started: " + index); inAnimation = true; } function animationEnded(){ // console.log("Animation ended: " + index); inAnimation = false; } function clearZoom(){ if(!root) return; if (root.hoveredIndex === -1 && root.dockHoveredIndex === -1) { restoreAnimation.start(); } } function handlerDraggingFinished(){ isDragged = false; } ///// End of Handlers ////// ///// Helper functions ///// function activateNextTask() { tasksWindows.activateNextTask(); } function activateTask() { if( mainItemContainer.isLauncher){ if (Latte.WindowSystem.compositingActive) { wrapper.runLauncherAnimation(); } else { launcherAction(); } } else{ if (model.IsGroupParent) { if (Latte.WindowSystem.compositingActive && backend.canPresentWindows()) { root.presentWindows(model.LegacyWinIdList); } else { if ((windowsPreviewDlg.visualParent === mainItemContainer)&&(windowsPreviewDlg.visible)) { windowsPreviewDlg.hide(3); } else { preparePreviewWindow(false); windowsPreviewDlg.show(mainItemContainer); } } } else { if (IsMinimized === true) { var i = modelIndex(); tasksModel.requestToggleMinimized(i); tasksModel.requestActivate(i); } else if (IsActive === true) { tasksModel.requestToggleMinimized(modelIndex()); } else { tasksModel.requestActivate(modelIndex()); } } } } function preparePreviewWindow(hideClose){ windowsPreviewDlg.visualParent = previewsVisualParent; toolTipDelegate.parentTask = mainItemContainer; toolTipDelegate.parentIndex = itemIndex; toolTipDelegate.hideCloseButtons = hideClose; toolTipDelegate.appName = Qt.binding(function() { return model.AppName; }); toolTipDelegate.pidParent = Qt.binding(function() { return model.AppPid; }); toolTipDelegate.windows = Qt.binding(function() { return model.LegacyWinIdList; }); toolTipDelegate.isGroup = Qt.binding(function() { return model.IsGroupParent == true; }); toolTipDelegate.icon = Qt.binding(function() { return model.decoration; }); toolTipDelegate.launcherUrl = Qt.binding(function() { return model.LauncherUrlWithoutIcon; }); toolTipDelegate.isLauncher = Qt.binding(function() { return model.IsLauncher == true; }); toolTipDelegate.isMinimizedParent = Qt.binding(function() { return model.IsMinimized == true; }); toolTipDelegate.displayParent = Qt.binding(function() { return model.display; }); toolTipDelegate.genericName = Qt.binding(function() { return model.GenericName; }); toolTipDelegate.virtualDesktopParent = Qt.binding(function() { return model.VirtualDesktop != undefined ? model.VirtualDesktop : 0; }); toolTipDelegate.isOnAllVirtualDesktopsParent = Qt.binding(function() { return model.IsOnAllVirtualDesktops == true; }); toolTipDelegate.activitiesParent = Qt.binding(function() { return model.Activities; }); /* toolTipDelegate.parentIndex = index; toolTipDelegate.windows = Qt.binding(function() { return model.LegacyWinIdList; }); toolTipDelegate.mainText = Qt.binding(function() { return model.display; }); toolTipDelegate.icon = Qt.binding(function() { return model.decoration; }); toolTipDelegate.subText = Qt.binding(function() { return model.IsLauncher === true ? model.GenericName : generateSubText(model); }); toolTipDelegate.launcherUrl = Qt.binding(function() { return model.LauncherUrlWithoutIcon; }); toolTipDelegate.titles = tasksWindows.windowsTitles();*/ } function launcherAction(){ // if ((lastButtonClicked == Qt.LeftButton)||(lastButtonClicked == Qt.MidButton)){ if (Latte.WindowSystem.compositingActive) { inBouncingAnimation = true; root.addWaitingLauncher(mainItemContainer.launcherUrl); } tasksModel.requestActivate(modelIndex()); // } } ///window previews/// function generateSubText(task) { var subTextEntries = new Array(); if (!plasmoid.configuration.showOnlyCurrentDesktop && virtualDesktopInfo.numberOfDesktops > 1 && model.IsOnAllVirtualDesktops !== true && model.VirtualDesktop != -1 && model.VirtualDesktop != undefined) { subTextEntries.push(i18n("On %1", virtualDesktopInfo.desktopNames[model.VirtualDesktop - 1])); } if (model.Activities == undefined) { return subTextEntries.join("\n"); } if (model.Activities.length == 0 && activityInfo.numberOfRunningActivities > 1) { subTextEntries.push(i18nc("Which virtual desktop a window is currently on", "Available on all activities")); } else if (model.Activities.length > 0) { var activityNames = new Array(); for (var i = 0; i < model.Activities.length; i++) { var activity = model.Activities[i]; if (plasmoid.configuration.showOnlyCurrentActivity) { if (activity != activityInfo.currentActivity) { activityNames.push(activityInfo.activityName(model.Activities[i])); } } else if (activity != activityInfo.currentActivity) { activityNames.push(activityInfo.activityName(model.Activities[i])); } } if (plasmoid.configuration.showOnlyCurrentActivity) { if (activityNames.length > 0) { subTextEntries.push(i18nc("Activities a window is currently on (apart from the current one)", "Also available on %1", activityNames.join(", "))); } } else if (activityNames.length > 0) { subTextEntries.push(i18nc("Which activities a window is currently on", "Available on %1", activityNames.join(", "))); } } return subTextEntries.join("\n"); } ///window previews//// function launcherIsPresent(url) { var activities = tasksModel.launcherActivities(url); var NULL_UUID = "00000000-0000-0000-0000-000000000000"; if (activities.indexOf(NULL_UUID) !== -1 || activities.indexOf(activityInfo.currentActivity) !== -1) return true; return false; } function modelIndex(){ return tasksModel.makeModelIndex(index); } function showContextMenu(args) { if (isSeparator && !root.editMode) return; - contextMenu = root.createContextMenu(mainItemContainer, modelIndex(), args); - contextMenu.show(); + if (!root.contextMenu) { + contextMenu = root.createContextMenu(mainItemContainer, modelIndex(), args); + contextMenu.show(); + } else { + root.contextMenu.close(); + } } function modifierAccepted(mouse){ if (mouse.modifiers & root.modifierQt){ if ((mouse.button === Qt.LeftButton && root.modifierClick === Latte.Dock.LeftClick) || (mouse.button === Qt.MiddleButton && root.modifierClick === Latte.Dock.MiddleClick) || (mouse.button === Qt.RightButton && root.modifierClick === Latte.Dock.RightClick)) return true; } return false; } function setBlockingAnimation(value){ inBlockingAnimation = value; } function slotMimicEnterForParabolic(){ if (containsMouse) { if (inMimicParabolicAnimation) { mimicParabolicScale = root.zoomFactor; } wrapper.calculateScales(icList.currentSpot); } } function slotShowPreviewForTasks(group) { if (group === mainItemContainer) { preparePreviewWindow(true); windowsPreviewDlg.show(mainItemContainer); } } function slotPublishGeometries() { //! this way we make sure that layouts that are in different activities that the current layout //! dont publish their geometries if ((isWindow || isStartup || isGroupParent) && icList && !icList.delayingRemoval && (!latteDock || (latteDock && currentLayout && latteDock.universalLayoutManager && currentLayout.name === latteDock.universalLayoutManager.currentLayoutName))) { var globalChoords = backend.globalRect(mainItemContainer); //! Magic Lamp effect doesnt like coordinates outside the screen and //! width,heights of zero value... So we now normalize the geometries //! sent in order to avoid such circumstances if (root.vertical) { globalChoords.width = 1; globalChoords.height = Math.max(root.iconSize, mainItemContainer.height); } else { globalChoords.height = 1; globalChoords.width = Math.max(root.iconSize, mainItemContainer.width); } if (root.position === PlasmaCore.Types.BottomPositioned) { globalChoords.y = plasmoid.screenGeometry.y+plasmoid.screenGeometry.height-1; } else if (root.position === PlasmaCore.Types.TopPositioned) { globalChoords.y = plasmoid.screenGeometry.y+1; } else if (root.position === PlasmaCore.Types.LeftPositioned) { globalChoords.x = plasmoid.screenGeometry.x+1; } else if (root.position === PlasmaCore.Types.RightPositioned) { globalChoords.x = plasmoid.screenGeometry.x+plasmoid.screenGeometry.width - 1; } tasksModel.requestPublishDelegateGeometry(mainItemContainer.modelIndex(), globalChoords, mainItemContainer); } } function slotWaitingLauncherRemoved(launch) { if ((isWindow || isStartup) && !visible && launch === launcherUrl) { wrapper.mScale = 1; visible = true; } } function updateAudioStreams() { if (root.dragSource !== null) { audioStreams = []; return; } var pa = pulseAudio.item; if (!pa) { audioStreams = []; return; } var streams = pa.streamsForPid(mainItemContainer.pid); if (streams.length) { pa.registerPidMatch(mainItemContainer.appName); } else { // We only want to fall back to appName matching if we never managed to map // a PID to an audio stream window. Otherwise if you have two instances of // an application, one playing and the other not, it will look up appName // for the non-playing instance and erroneously show an indicator on both. if (!pa.hasPidMatch(mainItemContainer.appName)) { var streams_result; streams_result = pa.streamsForAppName(mainItemContainer.appName); if (streams_result.length===0 && launcherName !== "") { streams_result = pa.streamsForAppName(launcherName); } streams = streams_result; } } // fix a binding loop concerning audiostreams, the audiostreams // should be updated only when they have changed var changed = false; if (streams.length !== audioStreams.length) { changed = true; } else { for(var i=0; i= 0) mainItemContainer.lastValidIndex = mainItemContainer.itemIndex; if (latteDock && latteDock.debugModeTimers) { console.log("plasmoid timer: lastValidTimer called..."); } } } // The best solution in order to catch when the wheel action ended is to // track the isMinimized state, but when the user has enabled window previews // at all times that flag doesnt work Timer { id: wheelActionDelayer interval: 200 onTriggered: { mainItemContainer.inWheelAction = false; if (latteDock && latteDock.debugModeTimers) { console.log("plasmoid timer: wheelActionDelayer called..."); } } } ///Item's Removal Animation ListView.onRemove: TaskAnimations.TaskRealRemovalAnimation{ id: taskRealRemovalAnimation } }// main Item