mirror of
https://github.com/KDE/latte-dock.git
synced 2024-12-23 13:33:50 +03:00
provide latte internal widget explorer
This commit is contained in:
parent
3ec62e6ecc
commit
84d4d4cef7
@ -45,6 +45,7 @@ void Package::initPackage(KPackage::Package *package)
|
||||
package->setPath("org.kde.latte.shell");
|
||||
package->addFileDefinition("defaults", QStringLiteral("defaults"), i18n("Latte Dock defaults"));
|
||||
package->addFileDefinition("lattedockui", QStringLiteral("views/Panel.qml"), i18n("Latte Dock panel"));
|
||||
package->addFileDefinition("widgetexplorerui", QStringLiteral("views/WidgetExplorer.qml"), i18n("Widget Explorer"));
|
||||
//Configuration
|
||||
package->addFileDefinition("lattedockconfigurationui", QStringLiteral("configuration/LatteDockConfiguration.qml"), i18n("Dock configuration UI"));
|
||||
package->addFileDefinition("lattedocksecondaryconfigurationui", QStringLiteral("configuration/LatteDockSecondaryConfiguration.qml"), i18n("Dock secondary configuration UI"));
|
||||
|
@ -6,5 +6,6 @@ set(lattedock-app_SRCS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/secondaryconfigview.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/subconfigview.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/viewsettingsfactory.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/widgetexplorerview.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
@ -78,7 +78,7 @@ signals:
|
||||
void enabledBordersChanged();
|
||||
|
||||
protected:
|
||||
void syncSlideEffect();
|
||||
virtual void syncSlideEffect();
|
||||
|
||||
virtual void init();
|
||||
virtual void initParentView(Latte::View *view);
|
||||
@ -87,7 +87,7 @@ protected:
|
||||
void showEvent(QShowEvent *ev) override;
|
||||
bool event(QEvent *e) override;
|
||||
|
||||
Qt::WindowFlags wFlags() const;
|
||||
virtual Qt::WindowFlags wFlags() const;
|
||||
|
||||
protected:
|
||||
bool m_isNormalWindow{true};
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "viewsettingsfactory.h"
|
||||
|
||||
#include "primaryconfigview.h"
|
||||
#include "widgetexplorerview.h"
|
||||
#include "../view.h"
|
||||
|
||||
// Plasma
|
||||
@ -81,5 +82,16 @@ ViewPart::PrimaryConfigView *ViewSettingsFactory::primaryConfigView(Latte::View
|
||||
return m_primaryConfigView;
|
||||
}
|
||||
|
||||
ViewPart::WidgetExplorerView *ViewSettingsFactory::widgetExplorerView(Latte::View *view)
|
||||
{
|
||||
if (!m_widgetExplorerView) {
|
||||
m_widgetExplorerView = new ViewPart::WidgetExplorerView(view);
|
||||
} else {
|
||||
m_widgetExplorerView->setParentView(view);
|
||||
}
|
||||
|
||||
return m_widgetExplorerView;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ class View;
|
||||
|
||||
namespace ViewPart {
|
||||
class PrimaryConfigView;
|
||||
class WidgetExplorerView;
|
||||
}
|
||||
|
||||
}
|
||||
@ -55,9 +56,11 @@ public:
|
||||
|
||||
Plasma::Containment *lastContainment();
|
||||
ViewPart::PrimaryConfigView *primaryConfigView(Latte::View *view);
|
||||
ViewPart::WidgetExplorerView *widgetExplorerView(Latte::View *view);
|
||||
|
||||
private:
|
||||
QPointer<ViewPart::PrimaryConfigView> m_primaryConfigView;
|
||||
QPointer<ViewPart::WidgetExplorerView> m_widgetExplorerView;
|
||||
QPointer<Plasma::Containment> m_lastContainment;
|
||||
|
||||
};
|
||||
|
287
app/view/settings/widgetexplorerview.cpp
Normal file
287
app/view/settings/widgetexplorerview.cpp
Normal file
@ -0,0 +1,287 @@
|
||||
/*
|
||||
* Copyright 2021 Michail Vourlakos <mvourlakos@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "widgetexplorerview.h"
|
||||
|
||||
// local
|
||||
#include "../panelshadows_p.h"
|
||||
#include "../view.h"
|
||||
#include "../../lattecorona.h"
|
||||
#include "../../wm/abstractwindowinterface.h"
|
||||
|
||||
// Qt
|
||||
#include <QQuickItem>
|
||||
#include <QScreen>
|
||||
|
||||
// KDE
|
||||
#include <KWindowEffects>
|
||||
#include <KWindowSystem>
|
||||
#include <KWayland/Client/plasmashell.h>
|
||||
|
||||
// Plasma
|
||||
#include <Plasma/Package>
|
||||
|
||||
namespace Latte {
|
||||
namespace ViewPart {
|
||||
|
||||
WidgetExplorerView::WidgetExplorerView(Latte::View *view)
|
||||
: SubConfigView(view, QString("#widgetexplorerview#"), true)
|
||||
{
|
||||
setResizeMode(QQuickView::SizeRootObjectToView);
|
||||
|
||||
connect(this, &QQuickView::widthChanged, this, &WidgetExplorerView::updateEffects);
|
||||
connect(this, &QQuickView::heightChanged, this, &WidgetExplorerView::updateEffects);
|
||||
|
||||
connect(this, &QQuickView::statusChanged, [&](QQuickView::Status status) {
|
||||
if (status == QQuickView::Ready) {
|
||||
updateEffects();
|
||||
}
|
||||
});
|
||||
|
||||
setParentView(view);
|
||||
init();
|
||||
}
|
||||
|
||||
void WidgetExplorerView::init()
|
||||
{
|
||||
SubConfigView::init();
|
||||
|
||||
QByteArray tempFilePath = "widgetexplorerui";
|
||||
|
||||
updateEnabledBorders();
|
||||
|
||||
auto source = QUrl::fromLocalFile(m_latteView->containment()->corona()->kPackage().filePath(tempFilePath));
|
||||
setSource(source);
|
||||
syncGeometry();
|
||||
}
|
||||
|
||||
bool WidgetExplorerView::hideOnWindowDeactivate() const
|
||||
{
|
||||
return m_hideOnWindowDeactivate;
|
||||
}
|
||||
|
||||
void WidgetExplorerView::setHideOnWindowDeactivate(bool hide)
|
||||
{
|
||||
if (m_hideOnWindowDeactivate == hide) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_hideOnWindowDeactivate = hide;
|
||||
emit hideOnWindowDeactivateChanged();
|
||||
}
|
||||
|
||||
Qt::WindowFlags WidgetExplorerView::wFlags() const
|
||||
{
|
||||
return (flags() | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
|
||||
}
|
||||
|
||||
QRect WidgetExplorerView::geometryWhenVisible() const
|
||||
{
|
||||
return m_geometryWhenVisible;
|
||||
}
|
||||
|
||||
void WidgetExplorerView::initParentView(Latte::View *view)
|
||||
{
|
||||
SubConfigView::initParentView(view);
|
||||
|
||||
rootContext()->setContextProperty(QStringLiteral("containmentFromView"), m_latteView->containment());
|
||||
|
||||
updateEnabledBorders();
|
||||
syncGeometry();
|
||||
}
|
||||
|
||||
QRect WidgetExplorerView::availableScreenGeometry() const
|
||||
{
|
||||
int currentScrId = m_latteView->positioner()->currentScreenId();
|
||||
|
||||
QList<Latte::Types::Visibility> ignoreModes{Latte::Types::SidebarOnDemand,Latte::Types::SidebarAutoHide};
|
||||
|
||||
if (m_latteView->visibility() && m_latteView->visibility()->isSidebar()) {
|
||||
ignoreModes.removeAll(Latte::Types::SidebarOnDemand);
|
||||
ignoreModes.removeAll(Latte::Types::SidebarAutoHide);
|
||||
}
|
||||
|
||||
QString activityid = m_latteView->layout()->lastUsedActivity();
|
||||
|
||||
return m_corona->availableScreenRectWithCriteria(currentScrId, activityid, ignoreModes, {}, false, true);
|
||||
}
|
||||
|
||||
void WidgetExplorerView::syncGeometry()
|
||||
{
|
||||
if (!m_latteView || !m_latteView->layout() || !m_latteView->containment() || !rootObject()) {
|
||||
return;
|
||||
}
|
||||
const QSize size(rootObject()->width(), rootObject()->height());
|
||||
auto availGeometry = availableScreenGeometry();
|
||||
|
||||
int margin = availGeometry.height() == m_latteView->screenGeometry().height() ? 100 : 0;
|
||||
auto geometry = QRect(availGeometry.x(), availGeometry.y(), size.width(), availGeometry.height()-margin);
|
||||
|
||||
updateEnabledBorders();
|
||||
|
||||
if (m_geometryWhenVisible == geometry) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_geometryWhenVisible = geometry;
|
||||
|
||||
setPosition(geometry.topLeft());
|
||||
|
||||
if (m_shellSurface) {
|
||||
m_shellSurface->setPosition(geometry.topLeft());
|
||||
}
|
||||
|
||||
setMaximumSize(geometry.size());
|
||||
setMinimumSize(geometry.size());
|
||||
resize(geometry.size());
|
||||
}
|
||||
|
||||
void WidgetExplorerView::showEvent(QShowEvent *ev)
|
||||
{
|
||||
if (m_shellSurface) {
|
||||
//! under wayland it needs to be set again after its hiding
|
||||
m_shellSurface->setPosition(m_geometryWhenVisible.topLeft());
|
||||
}
|
||||
|
||||
SubConfigView::showEvent(ev);
|
||||
|
||||
if (!m_latteView) {
|
||||
return;
|
||||
}
|
||||
|
||||
syncGeometry();
|
||||
|
||||
requestActivate();
|
||||
|
||||
m_screenSyncTimer.start();
|
||||
QTimer::singleShot(400, this, &WidgetExplorerView::syncGeometry);
|
||||
|
||||
emit showSignal();
|
||||
}
|
||||
|
||||
void WidgetExplorerView::focusOutEvent(QFocusEvent *ev)
|
||||
{
|
||||
Q_UNUSED(ev);
|
||||
|
||||
if (!m_latteView) {
|
||||
return;
|
||||
}
|
||||
|
||||
hideConfigWindow();
|
||||
}
|
||||
|
||||
void WidgetExplorerView::updateEffects()
|
||||
{
|
||||
//! Don't apply any effect before the wayland surface is created under wayland
|
||||
//! https://bugs.kde.org/show_bug.cgi?id=392890
|
||||
if (KWindowSystem::isPlatformWayland() && !m_shellSurface) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_background) {
|
||||
m_background = new Plasma::FrameSvg(this);
|
||||
}
|
||||
|
||||
if (m_background->imagePath() != "dialogs/background") {
|
||||
m_background->setImagePath(QStringLiteral("dialogs/background"));
|
||||
}
|
||||
|
||||
m_background->setEnabledBorders(m_enabledBorders);
|
||||
m_background->resizeFrame(size());
|
||||
|
||||
QRegion mask = m_background->mask();
|
||||
|
||||
QRegion fixedMask = mask.isNull() ? QRegion(QRect(0,0,width(),height())) : mask;
|
||||
|
||||
if (!fixedMask.isEmpty()) {
|
||||
setMask(fixedMask);
|
||||
} else {
|
||||
setMask(QRegion());
|
||||
}
|
||||
|
||||
if (KWindowSystem::compositingActive()) {
|
||||
KWindowEffects::enableBlurBehind(winId(), true, fixedMask);
|
||||
} else {
|
||||
KWindowEffects::enableBlurBehind(winId(), false);
|
||||
}
|
||||
}
|
||||
|
||||
void WidgetExplorerView::hideConfigWindow()
|
||||
{
|
||||
if (!m_hideOnWindowDeactivate) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_shellSurface) {
|
||||
//!NOTE: Avoid crash in wayland environment with qt5.9
|
||||
close();
|
||||
} else {
|
||||
hide();
|
||||
}
|
||||
}
|
||||
|
||||
void WidgetExplorerView::syncSlideEffect()
|
||||
{
|
||||
if (!m_latteView || !m_latteView->containment()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto slideLocation = WindowSystem::AbstractWindowInterface::Slide::Left;
|
||||
|
||||
m_corona->wm()->slideWindow(*this, slideLocation);
|
||||
}
|
||||
|
||||
//!BEGIN borders
|
||||
void WidgetExplorerView::updateEnabledBorders()
|
||||
{
|
||||
if (!this->screen()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Plasma::FrameSvg::EnabledBorders borders = Plasma::FrameSvg::AllBorders;
|
||||
|
||||
if (!m_geometryWhenVisible.isEmpty()) {
|
||||
if (m_geometryWhenVisible.x() == m_latteView->screenGeometry().x()) {
|
||||
borders &= ~Plasma::FrameSvg::LeftBorder;
|
||||
}
|
||||
|
||||
if (m_geometryWhenVisible.y() == m_latteView->screenGeometry().y()) {
|
||||
borders &= ~Plasma::FrameSvg::TopBorder;
|
||||
}
|
||||
|
||||
if (m_geometryWhenVisible.height() == m_latteView->screenGeometry().height()) {
|
||||
borders &= ~Plasma::FrameSvg::BottomBorder;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_enabledBorders != borders) {
|
||||
if (isVisible()) {
|
||||
m_enabledBorders = borders;
|
||||
}
|
||||
m_corona->dialogShadows()->addWindow(this, m_enabledBorders);
|
||||
|
||||
emit enabledBordersChanged();
|
||||
}
|
||||
}
|
||||
|
||||
//!END borders
|
||||
|
||||
}
|
||||
}
|
||||
|
105
app/view/settings/widgetexplorerview.h
Normal file
105
app/view/settings/widgetexplorerview.h
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2021 Michail Vourlakos <mvourlakos@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef WIDGETEXPLORERVIEW_H
|
||||
#define WIDGETEXPLORERVIEW_H
|
||||
|
||||
// local
|
||||
#include "subconfigview.h"
|
||||
|
||||
//Qt
|
||||
#include <QObject>
|
||||
#include <QQuickView>
|
||||
#include <QPointer>
|
||||
#include <QTimer>
|
||||
|
||||
// Plasma
|
||||
#include <plasma/package.h>
|
||||
#include <Plasma/FrameSvg>
|
||||
|
||||
|
||||
namespace Plasma {
|
||||
class Applet;
|
||||
class Containment;
|
||||
class FrameSvg;
|
||||
class Types;
|
||||
}
|
||||
|
||||
namespace KWayland {
|
||||
namespace Client {
|
||||
class PlasmaShellSurface;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Latte {
|
||||
class Corona;
|
||||
class View;
|
||||
}
|
||||
|
||||
namespace Latte {
|
||||
namespace ViewPart {
|
||||
|
||||
class WidgetExplorerView : public SubConfigView
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool hideOnWindowDeactivate READ hideOnWindowDeactivate WRITE setHideOnWindowDeactivate NOTIFY hideOnWindowDeactivateChanged)
|
||||
|
||||
public:
|
||||
WidgetExplorerView(Latte::View *view);
|
||||
|
||||
bool hideOnWindowDeactivate() const;
|
||||
void setHideOnWindowDeactivate(bool hide);
|
||||
|
||||
QRect geometryWhenVisible() const;
|
||||
|
||||
public slots:
|
||||
Q_INVOKABLE void hideConfigWindow();
|
||||
Q_INVOKABLE void syncGeometry() override;
|
||||
Q_INVOKABLE void updateEffects();
|
||||
|
||||
signals:
|
||||
void hideOnWindowDeactivateChanged();
|
||||
void showSignal();
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent *ev) override;
|
||||
void syncSlideEffect() override;
|
||||
void focusOutEvent(QFocusEvent *ev) override;
|
||||
|
||||
void init() override;
|
||||
void initParentView(Latte::View *view) override;
|
||||
void updateEnabledBorders() override;
|
||||
|
||||
Qt::WindowFlags wFlags() const override;
|
||||
|
||||
private:
|
||||
QRect availableScreenGeometry() const;
|
||||
|
||||
private:
|
||||
bool m_hideOnWindowDeactivate{true};
|
||||
QRect m_geometryWhenVisible;
|
||||
|
||||
//only for the mask on disabled compositing, not to actually paint
|
||||
Plasma::FrameSvg *m_background{nullptr};
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
#endif //WIDGETEXPLORERVIEW_H
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "settings/primaryconfigview.h"
|
||||
#include "settings/secondaryconfigview.h"
|
||||
#include "settings/viewsettingsfactory.h"
|
||||
#include "settings/widgetexplorerview.h"
|
||||
#include "../apptypes.h"
|
||||
#include "../lattecorona.h"
|
||||
#include "../data/layoutdata.h"
|
||||
@ -178,6 +179,7 @@ View::View(Plasma::Corona *corona, QScreen *targetScreen, bool byPassWM)
|
||||
}
|
||||
|
||||
connect(this->containment(), SIGNAL(statusChanged(Plasma::Types::ItemStatus)), SLOT(statusChanged(Plasma::Types::ItemStatus)));
|
||||
connect(this->containment(), &Plasma::Containment::showAddWidgetsInterface, this, &View::showWidgetExplorer);
|
||||
connect(this->containment(), &Plasma::Containment::userConfiguringChanged, this, [&]() {
|
||||
emit inEditModeChanged();
|
||||
});
|
||||
@ -523,6 +525,16 @@ void View::showConfigurationInterface(Plasma::Applet *applet)
|
||||
}
|
||||
}
|
||||
|
||||
void View::showWidgetExplorer(const QPointF &point)
|
||||
{
|
||||
auto widgetExplorerView = m_corona->viewSettingsFactory()->widgetExplorerView(this);
|
||||
|
||||
if (!widgetExplorerView->isVisible()) {
|
||||
// widgetExplorerView->syncSlideEffect();
|
||||
widgetExplorerView->showAfter(400);
|
||||
}
|
||||
}
|
||||
|
||||
QRect View::localGeometry() const
|
||||
{
|
||||
return m_localGeometry;
|
||||
|
@ -279,6 +279,7 @@ public slots:
|
||||
|
||||
protected slots:
|
||||
void showConfigurationInterface(Plasma::Applet *applet) override;
|
||||
void showWidgetExplorer(const QPointF &point);
|
||||
|
||||
protected:
|
||||
bool event(QEvent *ev) override;
|
||||
|
@ -88,14 +88,15 @@ void Menu::makeActions()
|
||||
});
|
||||
|
||||
m_addWidgetsAction = new QAction(QIcon::fromTheme("list-add"), i18n("&Add Widgets..."), this);
|
||||
m_addWidgetsAction->setStatusTip(i18n("Show Plasma Widget Explorer"));
|
||||
connect(m_addWidgetsAction, &QAction::triggered, [ = ]() {
|
||||
m_addWidgetsAction->setStatusTip(i18n("Show Widget Explorer"));
|
||||
connect(m_addWidgetsAction, &QAction::triggered, this, &Menu::requestWidgetExplorer);
|
||||
/*connect(m_addWidgetsAction, &QAction::triggered, [ = ]() {
|
||||
QDBusInterface iface("org.kde.plasmashell", "/PlasmaShell", "", QDBusConnection::sessionBus());
|
||||
|
||||
if (iface.isValid()) {
|
||||
iface.call("toggleWidgetExplorer");
|
||||
}
|
||||
});
|
||||
});*/
|
||||
|
||||
m_configureAction = new QAction(QIcon::fromTheme("document-edit"), i18nc("view settings window", "View &Settings..."), this);
|
||||
connect(m_configureAction, &QAction::triggered, this, &Menu::requestConfiguration);
|
||||
@ -137,6 +138,12 @@ void Menu::requestConfiguration()
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::requestWidgetExplorer()
|
||||
{
|
||||
if (this->containment()) {
|
||||
emit this->containment()->showAddWidgetsInterface(QPointF());
|
||||
}
|
||||
}
|
||||
|
||||
QList<QAction *> Menu::contextualActions()
|
||||
{
|
||||
|
@ -43,8 +43,9 @@ public:
|
||||
private Q_SLOTS:
|
||||
void makeActions();
|
||||
void populateLayouts();
|
||||
void requestConfiguration();
|
||||
void quitApplication();
|
||||
void requestConfiguration();
|
||||
void requestWidgetExplorer();
|
||||
void switchToLayout(QAction *action);
|
||||
|
||||
|
||||
|
236
shell/package/contents/views/AppletDelegate.qml
Normal file
236
shell/package/contents/views/AppletDelegate.qml
Normal file
@ -0,0 +1,236 @@
|
||||
/*
|
||||
* Copyright 2011 Marco Martin <mart@kde.org>
|
||||
* Copyright 2015 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||
* Copyright 2021 Michail Vourlakos <mvourlakos@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Library General Public License as
|
||||
* published by the Free Software Foundation; either version 2 or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Library General Public
|
||||
* License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import QtQuick 2.4
|
||||
import QtQuick.Layouts 1.1
|
||||
|
||||
import org.kde.plasma.components 2.0 as PlasmaComponents
|
||||
import org.kde.plasma.extras 2.0 as PlasmaExtras
|
||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||
import org.kde.draganddrop 2.0
|
||||
|
||||
Item {
|
||||
id: delegate
|
||||
|
||||
readonly property string pluginName: model.pluginName
|
||||
readonly property bool pendingUninstall: pendingUninstallTimer.applets.indexOf(pluginName) > -1
|
||||
|
||||
width: list.cellWidth
|
||||
height: list.cellHeight
|
||||
|
||||
DragArea {
|
||||
anchors.fill: parent
|
||||
supportedActions: Qt.MoveAction | Qt.LinkAction
|
||||
//onDragStarted: tooltipDialog.visible = false
|
||||
delegateImage: decoration
|
||||
enabled: !delegate.pendingUninstall
|
||||
mimeData {
|
||||
source: parent
|
||||
}
|
||||
Component.onCompleted: mimeData.setData("text/x-plasmoidservicename", pluginName)
|
||||
|
||||
onDragStarted: {
|
||||
kwindowsystem.showingDesktop = true;
|
||||
main.draggingWidget = true;
|
||||
}
|
||||
onDrop: {
|
||||
main.draggingWidget = false;
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onDoubleClicked: {
|
||||
if (!delegate.pendingUninstall) {
|
||||
widgetExplorer.addApplet(pluginName)
|
||||
}
|
||||
}
|
||||
onEntered: delegate.GridView.view.currentIndex = index
|
||||
onExited: delegate.GridView.view.currentIndex = - 1
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: mainLayout
|
||||
spacing: units.smallSpacing
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
//bottom: parent.bottom
|
||||
margins: units.smallSpacing * 2
|
||||
rightMargin: units.smallSpacing * 2 // don't cram the text to the border too much
|
||||
top: parent.top
|
||||
}
|
||||
|
||||
Item {
|
||||
id: iconContainer
|
||||
width: units.iconSizes.enormous
|
||||
height: width
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
opacity: delegate.pendingUninstall ? 0.6 : 1
|
||||
Behavior on opacity {
|
||||
OpacityAnimator {
|
||||
duration: units.longDuration
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: iconWidget
|
||||
anchors.fill: parent
|
||||
PlasmaCore.IconItem {
|
||||
anchors.fill: parent
|
||||
source: model.decoration
|
||||
visible: model.screenshot === ""
|
||||
}
|
||||
Image {
|
||||
width: units.iconSizes.enormous
|
||||
height: width
|
||||
anchors.fill: parent
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: model.screenshot
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: badgeMask
|
||||
anchors.fill: parent
|
||||
|
||||
Rectangle {
|
||||
x: Math.round(-units.smallSpacing * 1.5 / 2)
|
||||
y: x
|
||||
width: runningBadge.width + Math.round(units.smallSpacing * 1.5)
|
||||
height: width
|
||||
radius: height
|
||||
visible: running && delegate.GridView.isCurrentItem
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: runningBadge
|
||||
width: height
|
||||
height: Math.round(theme.mSize(countLabel.font).height * 1.3)
|
||||
radius: height
|
||||
color: theme.highlightColor
|
||||
visible: running && delegate.GridView.isCurrentItem
|
||||
onVisibleChanged: maskShaderSource.scheduleUpdate()
|
||||
|
||||
PlasmaComponents.Label {
|
||||
id: countLabel
|
||||
anchors.fill: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
text: running
|
||||
}
|
||||
}
|
||||
|
||||
ShaderEffect {
|
||||
anchors.fill: parent
|
||||
property var source: ShaderEffectSource {
|
||||
sourceItem: iconWidget
|
||||
hideSource: true
|
||||
live: false
|
||||
}
|
||||
property var mask: ShaderEffectSource {
|
||||
id: maskShaderSource
|
||||
sourceItem: badgeMask
|
||||
hideSource: true
|
||||
live: false
|
||||
}
|
||||
|
||||
supportsAtlasTextures: true
|
||||
|
||||
fragmentShader: "
|
||||
varying highp vec2 qt_TexCoord0;
|
||||
uniform highp float qt_Opacity;
|
||||
uniform lowp sampler2D source;
|
||||
uniform lowp sampler2D mask;
|
||||
void main() {
|
||||
gl_FragColor = texture2D(source, qt_TexCoord0.st) * (1.0 - (texture2D(mask, qt_TexCoord0.st).a)) * qt_Opacity;
|
||||
}
|
||||
"
|
||||
}
|
||||
|
||||
PlasmaComponents.ToolButton {
|
||||
id: uninstallButton
|
||||
anchors {
|
||||
top: parent.top
|
||||
right: parent.right
|
||||
}
|
||||
iconSource: delegate.pendingUninstall ? "edit-undo" : "edit-delete"
|
||||
// we don't really "undo" anything but we'll pretend to the user that we do
|
||||
tooltip: delegate.pendingUninstall ? i18nd("plasma_shell_org.kde.plasma.desktop", "Undo uninstall")
|
||||
: i18nd("plasma_shell_org.kde.plasma.desktop", "Uninstall widget")
|
||||
flat: false
|
||||
visible: model.local && delegate.GridView.isCurrentItem
|
||||
|
||||
onHoveredChanged: {
|
||||
if (hovered) {
|
||||
// hovering the uninstall button triggers onExited of the main mousearea
|
||||
delegate.GridView.view.currentIndex = index
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
var pending = installTimer.applets
|
||||
if (delegate.pendingUninstall) {
|
||||
var index = pending.indexOf(pluginName)
|
||||
if (index > -1) {
|
||||
pending.splice(index, 1)
|
||||
}
|
||||
} else {
|
||||
pending.push(pluginName)
|
||||
}
|
||||
pendingUninstallTimer.applets = pending
|
||||
|
||||
if (pending.length) {
|
||||
pendingUninstallTimer.restart()
|
||||
} else {
|
||||
pendingUninstallTimer.stop()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
PlasmaExtras.Heading {
|
||||
id: heading
|
||||
Layout.fillWidth: true
|
||||
level: 4
|
||||
text: model.name
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.WordWrap
|
||||
maximumLineCount: 2
|
||||
lineHeight: 0.95
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
PlasmaComponents.Label {
|
||||
Layout.fillWidth: true
|
||||
// otherwise causes binding loop due to the way the Plasma sets the height
|
||||
height: implicitHeight
|
||||
text: model.description
|
||||
font: theme.smallestFont
|
||||
wrapMode: Text.WordWrap
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: heading.lineCount === 1 ? 3 : 2
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
389
shell/package/contents/views/WidgetExplorer.qml
Normal file
389
shell/package/contents/views/WidgetExplorer.qml
Normal file
@ -0,0 +1,389 @@
|
||||
/*
|
||||
* Copyright 2011 Marco Martin <mart@kde.org>
|
||||
* Copyright 2021 Michail Vourlakos <mvourlakos@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Library General Public License as
|
||||
* published by the Free Software Foundation; either version 2 or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Library General Public
|
||||
* License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.5 as QQC2
|
||||
|
||||
import org.kde.plasma.components 2.0 as PlasmaComponents
|
||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||
import org.kde.plasma.extras 2.0 as PlasmaExtras
|
||||
import org.kde.kquickcontrolsaddons 2.0
|
||||
import org.kde.kwindowsystem 1.0
|
||||
|
||||
import QtQuick.Window 2.1
|
||||
import QtQuick.Layouts 1.1
|
||||
|
||||
import org.kde.plasma.private.shell 2.0 as PlasmaShell
|
||||
|
||||
Item {
|
||||
id: main
|
||||
width: Math.max(heading.paintedWidth, units.iconSizes.enormous * 3 + units.smallSpacing * 4 + units.gridUnit * 2)
|
||||
// height: 800//Screen.height
|
||||
|
||||
opacity: draggingWidget ? 0.3 : 1
|
||||
|
||||
//property QtObject containment
|
||||
|
||||
//external drop events can cause a raise event causing us to lose focus and
|
||||
//therefore get deleted whilst we are still in a drag exec()
|
||||
//this is a clue to the owning dialog that hideOnWindowDeactivate should be deleted
|
||||
//See https://bugs.kde.org/show_bug.cgi?id=332733
|
||||
property bool preventWindowHide: draggingWidget
|
||||
|| categoriesDialog.status !== PlasmaComponents.DialogStatus.Closed
|
||||
|| getWidgetsDialog.status !== PlasmaComponents.DialogStatus.Closed
|
||||
|
||||
property bool outputOnly: draggingWidget
|
||||
|
||||
property Item categoryButton
|
||||
|
||||
property bool draggingWidget: false
|
||||
|
||||
signal closed();
|
||||
|
||||
onClosed: {
|
||||
if (main.preventWindowHide) {
|
||||
return;
|
||||
}
|
||||
|
||||
viewConfig.hideConfigWindow();
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
if (!visible) {
|
||||
kwindowsystem.showingDesktop = false
|
||||
}
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
if (pendingUninstallTimer.running) {
|
||||
// we're not being destroyed so at least reset the filters
|
||||
widgetExplorer.widgetsModel.filterQuery = ""
|
||||
widgetExplorer.widgetsModel.filterType = ""
|
||||
widgetExplorer.widgetsModel.searchTerm = ""
|
||||
}
|
||||
}
|
||||
|
||||
function addCurrentApplet() {
|
||||
var pluginName = list.currentItem ? list.currentItem.pluginName : ""
|
||||
if (pluginName) {
|
||||
widgetExplorer.addApplet(pluginName)
|
||||
}
|
||||
}
|
||||
|
||||
KWindowSystem {
|
||||
id: kwindowsystem
|
||||
}
|
||||
|
||||
QQC2.Action {
|
||||
shortcut: "Escape"
|
||||
onTriggered: {
|
||||
if (searchInput.length > 0) {
|
||||
searchInput.text = ""
|
||||
} else {
|
||||
main.closed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.Action {
|
||||
shortcut: "Up"
|
||||
onTriggered: list.currentIndex = (list.count + list.currentIndex - 1) % list.count
|
||||
}
|
||||
|
||||
QQC2.Action {
|
||||
shortcut: "Down"
|
||||
onTriggered: list.currentIndex = (list.currentIndex + 1) % list.count
|
||||
}
|
||||
|
||||
QQC2.Action {
|
||||
shortcut: "Enter"
|
||||
onTriggered: addCurrentApplet()
|
||||
}
|
||||
|
||||
QQC2.Action {
|
||||
shortcut: "Return"
|
||||
onTriggered: addCurrentApplet()
|
||||
}
|
||||
|
||||
PlasmaCore.FrameSvgItem{
|
||||
id: backgroundFrameSvgItem
|
||||
anchors.fill: parent
|
||||
imagePath: "dialogs/background"
|
||||
enabledBorders: viewConfig.enabledBorders
|
||||
|
||||
onEnabledBordersChanged: viewConfig.updateEffects()
|
||||
Component.onCompleted: viewConfig.updateEffects()
|
||||
}
|
||||
|
||||
|
||||
PlasmaShell.WidgetExplorer {
|
||||
id:widgetExplorer
|
||||
//view: desktop
|
||||
containment: containmentFromView
|
||||
onShouldClose: main.closed();
|
||||
}
|
||||
|
||||
|
||||
PlasmaComponents.ModelContextMenu {
|
||||
id: categoriesDialog
|
||||
visualParent: categoryButton
|
||||
// model set on first invocation
|
||||
|
||||
onClicked: {
|
||||
list.contentX = 0
|
||||
list.contentY = 0
|
||||
categoryButton.text = (model.filterData ? model.display : i18nd("plasma_shell_org.kde.plasma.desktop", "All Widgets"))
|
||||
widgetExplorer.widgetsModel.filterQuery = model.filterData
|
||||
widgetExplorer.widgetsModel.filterType = model.filterType
|
||||
}
|
||||
}
|
||||
|
||||
PlasmaComponents.ModelContextMenu {
|
||||
id: getWidgetsDialog
|
||||
visualParent: getWidgetsButton
|
||||
placement: PlasmaCore.Types.TopPosedLeftAlignedPopup
|
||||
// model set on first invocation
|
||||
onClicked: model.trigger()
|
||||
}
|
||||
/*
|
||||
PlasmaCore.Dialog {
|
||||
id: tooltipDialog
|
||||
property Item appletDelegate
|
||||
location: PlasmaCore.Types.RightEdge //actually we want this to be the opposite location of the explorer itself
|
||||
type: PlasmaCore.Dialog.Tooltip
|
||||
flags:Qt.Window|Qt.WindowStaysOnTopHint|Qt.X11BypassWindowManagerHint
|
||||
onAppletDelegateChanged: {
|
||||
if (!appletDelegate) {
|
||||
toolTipHideTimer.restart()
|
||||
toolTipShowTimer.running = false
|
||||
} else if (tooltipDialog.visible) {
|
||||
tooltipDialog.visualParent = appletDelegate
|
||||
} else {
|
||||
tooltipDialog.visualParent = appletDelegate
|
||||
toolTipShowTimer.restart()
|
||||
toolTipHideTimer.running = false
|
||||
}
|
||||
}
|
||||
mainItem: Tooltip { id: tooltipWidget }
|
||||
Behavior on y {
|
||||
NumberAnimation { duration: units.longDuration }
|
||||
}
|
||||
}
|
||||
Timer {
|
||||
id: toolTipShowTimer
|
||||
interval: 500
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
tooltipDialog.visible = true
|
||||
}
|
||||
}
|
||||
Timer {
|
||||
id: toolTipHideTimer
|
||||
interval: 1000
|
||||
repeat: false
|
||||
onTriggered: tooltipDialog.visible = false
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
PlasmaExtras.PlasmoidHeading {
|
||||
id: topArea
|
||||
implicitWidth: header.implicitWidth
|
||||
implicitHeight: header.implicitHeight
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: header
|
||||
anchors.fill: parent
|
||||
|
||||
RowLayout {
|
||||
PlasmaExtras.Heading {
|
||||
id: heading
|
||||
level: 1
|
||||
text: i18nd("plasma_shell_org.kde.plasma.desktop", "Widgets")
|
||||
elide: Text.ElideRight
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
PlasmaComponents.ToolButton {
|
||||
id: getWidgetsButton
|
||||
iconSource: "get-hot-new-stuff"
|
||||
text: i18nd("plasma_shell_org.kde.plasma.desktop", "Get New Widgets...")
|
||||
onClicked: {
|
||||
getWidgetsDialog.model = widgetExplorer.widgetsMenuActions
|
||||
getWidgetsDialog.openRelative()
|
||||
}
|
||||
}
|
||||
PlasmaComponents.ToolButton {
|
||||
id: closeButton
|
||||
iconSource: "window-close"
|
||||
onClicked: main.closed()
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
PlasmaComponents.TextField {
|
||||
id: searchInput
|
||||
Layout.fillWidth: true
|
||||
clearButtonShown: true
|
||||
placeholderText: i18nd("plasma_shell_org.kde.plasma.desktop", "Search...")
|
||||
onTextChanged: {
|
||||
list.positionViewAtBeginning()
|
||||
list.currentIndex = -1
|
||||
widgetExplorer.widgetsModel.searchTerm = text
|
||||
}
|
||||
|
||||
Component.onCompleted: forceActiveFocus()
|
||||
}
|
||||
PlasmaComponents.ToolButton {
|
||||
id: categoryButton
|
||||
tooltip: i18nd("plasma_shell_org.kde.plasma.desktop", "Categories")
|
||||
text: i18nd("plasma_shell_org.kde.plasma.desktop", "All Widgets")
|
||||
iconSource: "view-filter"
|
||||
onClicked: {
|
||||
categoriesDialog.model = widgetExplorer.filterModel
|
||||
categoriesDialog.open(0, categoryButton.height)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
height: units.smallSpacing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: setModelTimer
|
||||
interval: 20
|
||||
running: true
|
||||
onTriggered: list.model = widgetExplorer.widgetsModel
|
||||
}
|
||||
|
||||
PlasmaExtras.ScrollArea {
|
||||
anchors {
|
||||
top: topArea.bottom
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
|
||||
verticalScrollBarPolicy: Qt.ScrollBarAlwaysOn
|
||||
|
||||
// hide the flickering by fading in nicely
|
||||
opacity: setModelTimer.running ? 0 : 1
|
||||
Behavior on opacity {
|
||||
OpacityAnimator {
|
||||
duration: units.longDuration
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
|
||||
GridView {
|
||||
id: list
|
||||
|
||||
// model set delayed by Timer above
|
||||
|
||||
activeFocusOnTab: true
|
||||
keyNavigationWraps: true
|
||||
cellWidth: Math.floor((width - units.smallSpacing) / 3)
|
||||
cellHeight: cellWidth + units.gridUnit * 4 + units.smallSpacing * 2
|
||||
|
||||
delegate: AppletDelegate {}
|
||||
highlight: PlasmaComponents.Highlight {}
|
||||
highlightMoveDuration: 0
|
||||
//highlightResizeDuration: 0
|
||||
|
||||
//slide in to view from the left
|
||||
add: Transition {
|
||||
NumberAnimation {
|
||||
properties: "x"
|
||||
from: -list.width
|
||||
duration: units.shortDuration
|
||||
}
|
||||
}
|
||||
|
||||
//slide out of view to the right
|
||||
remove: Transition {
|
||||
NumberAnimation {
|
||||
properties: "x"
|
||||
to: list.width
|
||||
duration: units.shortDuration
|
||||
}
|
||||
}
|
||||
|
||||
//if we are adding other items into the view use the same animation as normal adding
|
||||
//this makes everything slide in together
|
||||
//if we make it move everything ends up weird
|
||||
addDisplaced: list.add
|
||||
|
||||
//moved due to filtering
|
||||
displaced: Transition {
|
||||
NumberAnimation {
|
||||
properties: "x,y"
|
||||
duration: units.shortDuration
|
||||
}
|
||||
}
|
||||
|
||||
PlasmaExtras.Heading {
|
||||
anchors.fill: parent
|
||||
anchors.margins: units.largeSpacing
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
wrapMode: Text.WordWrap
|
||||
level: 2
|
||||
text: searchInput.text.length > 0 ? i18n("No widgets matched the search terms") : i18n("No widgets available")
|
||||
enabled: false
|
||||
visible: list.count == 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//! Bindings
|
||||
Binding{
|
||||
target: viewConfig
|
||||
property: "hideOnWindowDeactivate"
|
||||
value: !main.preventWindowHide
|
||||
}
|
||||
|
||||
//! Timers
|
||||
Timer {
|
||||
id: pendingUninstallTimer
|
||||
// keeps track of the applets the user wants to uninstall
|
||||
property var applets: []
|
||||
|
||||
interval: 60000 // one minute
|
||||
onTriggered: {
|
||||
for (var i = 0, length = applets.length; i < length; ++i) {
|
||||
widgetExplorer.uninstall(applets[i])
|
||||
}
|
||||
applets = []
|
||||
|
||||
/*if (sidePanelStack.state !== "widgetExplorer" && widgetExplorer) {
|
||||
widgetExplorer.destroy()
|
||||
widgetExplorer = null
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user