diff --git a/applets/CMakeLists.txt b/applets/CMakeLists.txt --- a/applets/CMakeLists.txt +++ b/applets/CMakeLists.txt @@ -3,7 +3,6 @@ plasma_install_package(analog-clock org.kde.plasma.analogclock) plasma_install_package(mediacontroller org.kde.plasma.mediacontroller) -plasma_install_package(panelspacer org.kde.plasma.panelspacer) plasma_install_package(lock_logout org.kde.plasma.lock_logout) add_subdirectory(appmenu) @@ -13,6 +12,7 @@ add_subdirectory(devicenotifier) add_subdirectory(digital-clock) add_subdirectory(kicker) +add_subdirectory(panelspacer) plasma_install_package(clipboard org.kde.plasma.clipboard) diff --git a/applets/panelspacer/CMakeLists.txt b/applets/panelspacer/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/applets/panelspacer/CMakeLists.txt @@ -0,0 +1,5 @@ + +plasma_install_package(package org.kde.plasma.panelspacer) + +add_subdirectory(plugin) + diff --git a/applets/panelspacer/Messages.sh b/applets/panelspacer/Messages.sh deleted file mode 100755 --- a/applets/panelspacer/Messages.sh +++ /dev/null @@ -1,4 +0,0 @@ -#! /usr/bin/env bash -$EXTRACTRC `find . -name \*.rc -o -name \*.ui -o -name \*.kcfg` >> rc.cpp -$XGETTEXT `find . -name \*.js -o -name \*.qml -o -name \*.cpp` -o $podir/plasma_applet_org.kde.plasma.panelspacer.pot -rm -f rc.cpp diff --git a/applets/panelspacer/contents/ui/main.qml b/applets/panelspacer/contents/ui/main.qml deleted file mode 100644 --- a/applets/panelspacer/contents/ui/main.qml +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2014 Marco Martin - * - * This program 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) version 3 or any later version - * accepted by the membership of KDE e.V. (or its successor approved - * by the membership of KDE e.V.), which shall act as a proxy - * defined in Section 14 of version 3 of the license. - * - * 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 General Public License - * along with this program. If not, see - */ - -import QtQuick 2.0 -import QtQuick.Layouts 1.1 -import org.kde.plasma.core 2.0 as PlasmaCore -import org.kde.plasma.plasmoid 2.0 -import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddons - -Item { - id: root - - z: 9999 - property bool horizontal: plasmoid.formFactor !== PlasmaCore.Types.Vertical - - Layout.fillWidth: plasmoid.configuration.expanding - Layout.fillHeight: plasmoid.configuration.expanding - - Layout.minimumWidth: 1 - Layout.minimumHeight: 1 - Layout.preferredWidth: horizontal ? plasmoid.configuration.length : 0 - Layout.preferredHeight: horizontal ? 0 : plasmoid.configuration.length - - Plasmoid.preferredRepresentation: Plasmoid.fullRepresentation - - function action_expanding() { - plasmoid.configuration.expanding = plasmoid.action("expanding").checked; - } - Component.onCompleted: { - plasmoid.setAction("expanding", i18n("Set flexible size")); - var action = plasmoid.action("expanding"); - action.checkable = true; - action.checked = plasmoid.configuration.expanding; - - plasmoid.removeAction("configure"); - } -} diff --git a/applets/panelspacer/contents/config/main.xml b/applets/panelspacer/package/contents/config/main.xml rename from applets/panelspacer/contents/config/main.xml rename to applets/panelspacer/package/contents/config/main.xml --- a/applets/panelspacer/contents/config/main.xml +++ b/applets/panelspacer/package/contents/config/main.xml @@ -11,7 +11,8 @@ true - + + -1 diff --git a/applets/panelspacer/package/contents/ui/main.qml b/applets/panelspacer/package/contents/ui/main.qml new file mode 100644 --- /dev/null +++ b/applets/panelspacer/package/contents/ui/main.qml @@ -0,0 +1,172 @@ +/* + * Copyright 2014 Marco Martin + * + * This program 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) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * 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 chenterX 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 org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.plasmoid 2.0 +import org.kde.kirigami 2.10 as Kirigami + +Item { + id: root + + property bool horizontal: plasmoid.formFactor !== PlasmaCore.Types.Vertical + + Layout.fillWidth: plasmoid.configuration.expanding + Layout.fillHeight: plasmoid.configuration.expanding + + Layout.minimumWidth: plasmoid.nativeInterface.containment.editMode ? units.gridUnit * 2 : 1 + Layout.minimumHeight: plasmoid.nativeInterface.containment.editMode ? units.gridUnit * 2 : 1 + Layout.preferredWidth: horizontal + ? (plasmoid.configuration.expanding ? optimalSize : plasmoid.configuration.length) + : 0 + Layout.preferredHeight: horizontal + ? 0 + : (plasmoid.configuration.expanding ? optimalSize : plasmoid.configuration.length) + + Plasmoid.preferredRepresentation: Plasmoid.fullRepresentation + + property int optimalSize: units.largeSpacing + + function action_expanding() { + plasmoid.configuration.expanding = plasmoid.action("expanding").checked; + } + + // Search the actual gridLayout of the panel + property GridLayout panelLayout: { + var candidate = root.parent; + while (candidate) { + if (candidate instanceof GridLayout) { + return candidate; + } + candidate = candidate.parent; + } + } + + Component.onCompleted: { + plasmoid.setAction("expanding", i18n("Set flexible size")); + var action = plasmoid.action("expanding"); + action.checkable = true; + action.checked = Qt.binding(function() {return plasmoid.configuration.expanding}); + + plasmoid.removeAction("configure"); + } + + property real leftItemsSizeHint: 0 + property real rightItemsSizeHint: 0 + property real middleItemsSizeHint: { + // Every time this binding gets reevaluated we want to queue a recomputation of the size hints + Qt.callLater(root.updateHints) + if (!twinSpacer || !panelLayout || !leftTwin || !rightTwin) { + return 0; + } + + var leftTwinParent = leftTwin.parent; + var rightTwinParent = rightTwin.parent; + if (!leftTwinParent || !rightTwinParent) { + return 0; + } + var firstSpacerFound = false; + var secondSpacerFound = false; + var leftItemsHint = 0; + var middleItemsHint = 0; + var rightItemsHint = 0; + + // Children order is guaranteed to be the same as the visual order of items in the layout + for (var i in panelLayout.children) { + var child = panelLayout.children[i]; + if (!child.visible) { + continue; + } else if (child == leftTwinParent) { + firstSpacerFound = true; + } else if (child == rightTwinParent) { + secondSpacerFound = true; + } else if (secondSpacerFound) { + if (root.horizontal) { + rightItemsHint += Math.min(child.Layout.maximumWidth, Math.max(child.Layout.minimumWidth, child.Layout.preferredWidth)) + panelLayout.rowSpacing; + } else { + rightItemsHint += Math.min(child.Layout.maximumWidth, Math.max(child.Layout.minimumHeight, child.Layout.preferredHeight)) + panelLayout.columnSpacing; + } + } else if (firstSpacerFound) { + if (root.horizontal) { + middleItemsHint += Math.min(child.Layout.maximumWidth, Math.max(child.Layout.minimumWidth, child.Layout.preferredWidth)) + panelLayout.rowSpacing; + } else { + middleItemsHint += Math.min(child.Layout.maximumWidth, Math.max(child.Layout.minimumHeight, child.Layout.preferredHeight)) + panelLayout.columnSpacing; + } + } else { + if (root.horizontal) { + leftItemsHint += Math.min(child.Layout.maximumWidth, Math.max(child.Layout.minimumWidth, child.Layout.preferredWidth)) + panelLayout.rowSpacing; + } else { + leftItemsHint += Math.min(child.Layout.maximumHeight, Math.max(child.Layout.minimumHeight, child.Layout.preferredHeight)) + panelLayout.columnSpacing; + } + } + } + + rightItemsSizeHint = rightItemsHint; + leftItemsSizeHint = leftItemsHint; + return middleItemsHint; + } + + readonly property Item twinSpacer: plasmoid.configuration.expanding && plasmoid.nativeInterface.twinSpacer && plasmoid.nativeInterface.twinSpacer.configuration.expanding ? plasmoid.nativeInterface.twinSpacer : null + readonly property Item leftTwin: { + if (!twinSpacer) { + return null; + } + + if (root.horizontal) { + return root.Kirigami.ScenePosition.x < twinSpacer.Kirigami.ScenePosition.x ? plasmoid : twinSpacer; + } else { + return root.Kirigami.ScenePosition.y < twinSpacer.Kirigami.ScenePosition.y ? plasmoid : twinSpacer; + } + } + readonly property Item rightTwin: { + if (!twinSpacer) { + return null; + } + + if (root.horizontal) { + return root.Kirigami.ScenePosition.x >= twinSpacer.Kirigami.ScenePosition.x ? plasmoid : twinSpacer; + } else { + return root.Kirigami.ScenePosition.y >= twinSpacer.Kirigami.ScenePosition.y ? plasmoid : twinSpacer; + } + } + + function updateHints() { + if (!twinSpacer || !panelLayout || !leftTwin || !rightTwin) { + root.optimalSize = root.horizontal ? plasmoid.nativeInterface.containment.width : plasmoid.nativeInterface.containment.height; + return; + } + + var halfContainment = root.horizontal ?plasmoid.nativeInterface.containment.width/2 : plasmoid.nativeInterface.containment.height/2; + + if (leftTwin == plasmoid) { + root.optimalSize = Math.max(units.smallSpacing, halfContainment - middleItemsSizeHint/2 - leftItemsSizeHint) + } else { + root.optimalSize = Math.max(units.smallSpacing, halfContainment - middleItemsSizeHint/2 - rightItemsSizeHint) + } + } + + + Rectangle { + anchors.fill: parent + color: theme.highlightColor + visible: plasmoid.nativeInterface.containment.editMode + } +} diff --git a/applets/panelspacer/metadata.desktop b/applets/panelspacer/package/metadata.desktop rename from applets/panelspacer/metadata.desktop rename to applets/panelspacer/package/metadata.desktop --- a/applets/panelspacer/metadata.desktop +++ b/applets/panelspacer/package/metadata.desktop @@ -139,6 +139,7 @@ X-KDE-PluginInfo-Author=The Plasma Team X-KDE-PluginInfo-Email=plasma-devel@kde.org X-KDE-PluginInfo-Name=org.kde.plasma.panelspacer +X-KDE-Library=org.kde.plasma.panelspacer X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-Website=https://www.kde.org/plasma-desktop X-KDE-PluginInfo-Category=System Information diff --git a/applets/panelspacer/plugin/CMakeLists.txt b/applets/panelspacer/plugin/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/applets/panelspacer/plugin/CMakeLists.txt @@ -0,0 +1,15 @@ +kde_enable_exceptions() + +add_definitions(-DTRANSLATION_DOMAIN=\"panelspacer\") + +set(panelspacer_SRCS + panelspacer.cpp) + +add_library(org.kde.plasma.panelspacer MODULE ${panelspacer_SRCS}) + +kcoreaddons_desktop_to_json(org.kde.plasma.panelspacer ../package/metadata.desktop) + +target_link_libraries(org.kde.plasma.panelspacer Qt5::Gui Qt5::Core Qt5::Qml Qt5::Quick KF5::Plasma KF5::PlasmaQuick KF5::I18n) + +install(TARGETS org.kde.plasma.panelspacer DESTINATION ${KDE_INSTALL_PLUGINDIR}/plasma/applets) + diff --git a/applets/panelspacer/plugin/panelspacer.h b/applets/panelspacer/plugin/panelspacer.h new file mode 100644 --- /dev/null +++ b/applets/panelspacer/plugin/panelspacer.h @@ -0,0 +1,78 @@ +/*************************************************************************** + * Copyright (C) 2020 Marco Martin * + * + * * + * This program 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. * + * * + * 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 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 . * + ***************************************************************************/ + +#pragma once + + +#include + +namespace PlasmaQuick { + class AppletQuickItem; +} + +namespace Plasma { + class Containment; +} + +class PanelSpacer; + +class SpacersTracker : public QObject +{ + Q_OBJECT + +public: + SpacersTracker( QObject *parent = nullptr ); + ~SpacersTracker() override; + + static SpacersTracker *self(); + + void insertSpacer(Plasma::Containment *containment, PanelSpacer *spacer); + void removeSpacer(Plasma::Containment *containment, PanelSpacer *spacer); + +private: + QHash > m_spacers; +}; + +class PanelSpacer : public Plasma::Applet +{ + Q_OBJECT + Q_PROPERTY(PlasmaQuick::AppletQuickItem *twinSpacer READ twinSpacer NOTIFY twinSpacerChanged) + Q_PROPERTY(PlasmaQuick::AppletQuickItem *containment READ containmentGraphicObject CONSTANT) + +public: + PanelSpacer( QObject *parent, const QVariantList &args ); + ~PanelSpacer() override; + + void init() override; + void constraintsEvent(Plasma::Types::Constraints constraints) override; + + void setTwinSpacer(PlasmaQuick::AppletQuickItem *spacer); + PlasmaQuick::AppletQuickItem *twinSpacer() const; + + PlasmaQuick::AppletQuickItem *containmentGraphicObject() const; + +Q_SIGNALS: + void twinSpacerChanged(); + +private: + PlasmaQuick::AppletQuickItem *m_twinSpacer; +}; + + diff --git a/applets/panelspacer/plugin/panelspacer.cpp b/applets/panelspacer/plugin/panelspacer.cpp new file mode 100644 --- /dev/null +++ b/applets/panelspacer/plugin/panelspacer.cpp @@ -0,0 +1,146 @@ +/*************************************************************************** + * Copyright (C) 2020 Marco Martin * + * * + * This program 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. * + * * + * 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 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 . * + ***************************************************************************/ + +#include "panelspacer.h" + +#include +#include +#include + +#include +#include +#include + +class SpacersTrackerSingleton +{ +public: + SpacersTracker self; +}; + +Q_GLOBAL_STATIC(SpacersTrackerSingleton, privateSpacersTrackerSelf) + +SpacersTracker::SpacersTracker(QObject *parent) + : QObject(parent) +{ +} + +SpacersTracker::~SpacersTracker() +{ +} + +SpacersTracker *SpacersTracker::self() +{ + return &privateSpacersTrackerSelf()->self; +} + +void SpacersTracker::insertSpacer(Plasma::Containment *containment, PanelSpacer *spacer) +{ + const bool wasTwin = m_spacers[containment].count() == 2; + m_spacers[containment] << (spacer); + const bool isTwin = m_spacers[containment].count() == 2; + + if (isTwin) { + auto *lay1 = m_spacers[containment].first(); + auto *lay2 = m_spacers[containment].last(); + lay1->setTwinSpacer(lay2->property("_plasma_graphicObject").value()); + lay2->setTwinSpacer(lay1->property("_plasma_graphicObject").value()); + } else if (wasTwin) { + for (auto *lay : m_spacers[containment]) { + lay->setTwinSpacer(nullptr); + } + } +} + +void SpacersTracker::removeSpacer(Plasma::Containment *containment, PanelSpacer *spacer) +{ + const bool wasTwin = m_spacers[containment].count() == 2; + m_spacers[containment].removeAll(spacer); + const bool isTwin = m_spacers[containment].count() == 2; + + if (isTwin) { + auto *lay1 = m_spacers[containment].first(); + auto *lay2 = m_spacers[containment].last(); + lay1->setTwinSpacer(lay2->property("_plasma_graphicObject").value()); + lay2->setTwinSpacer(lay1->property("_plasma_graphicObject").value()); + + } else if (wasTwin) { + for (auto *lay : m_spacers[containment]) { + lay->setTwinSpacer(nullptr); + } + } + + if (m_spacers[containment].isEmpty()) { + m_spacers.remove(containment); + } +} + +///////////////////////////////////////////////////////////////////// + +PanelSpacer::PanelSpacer(QObject *parent, const QVariantList &args) + : Plasma::Applet(parent, args) +{ +} + +PanelSpacer::~PanelSpacer() +{ + SpacersTracker::self()->removeSpacer(containment(), this); +} + +void PanelSpacer::init() +{ + +} + +void PanelSpacer::constraintsEvent(Plasma::Types::Constraints constraints) +{ + // At this point we're sure the AppletQuickItem has been created already + if (constraints & Plasma::Types::UiReadyConstraint) { + Q_ASSERT(containment()); + Q_ASSERT(containment()->corona()); + + SpacersTracker::self()->insertSpacer(containment(), this); + } + + Plasma::Applet::constraintsEvent(constraints); +} + +void PanelSpacer::setTwinSpacer(PlasmaQuick::AppletQuickItem *spacer) +{ + if (m_twinSpacer == spacer) { + return; + } + + m_twinSpacer = spacer; + emit twinSpacerChanged(); +} + +PlasmaQuick::AppletQuickItem *PanelSpacer::twinSpacer() const +{ + return m_twinSpacer; +} + +PlasmaQuick::AppletQuickItem *PanelSpacer::containmentGraphicObject() const +{ + return containment()->property("_plasma_graphicObject").value(); +} + + +K_EXPORT_PLASMA_APPLET_WITH_JSON(panelspacer, PanelSpacer, "metadata.json") + +#include "panelspacer.moc" diff --git a/shell/panelconfigview.cpp b/shell/panelconfigview.cpp --- a/shell/panelconfigview.cpp +++ b/shell/panelconfigview.cpp @@ -106,7 +106,20 @@ void PanelConfigView::addPanelSpacer() { - m_containment->createApplet(QStringLiteral("org.kde.plasma.panelspacer")); + ShellCorona *c = qobject_cast(m_containment->corona()); + if (!c) { + return; + } + // Add a spacer at the end *except* if there is exactly one spacer already + // this to trigger the panel centering mode of the spacer in a slightly more discoverable way + c->evaluateScript(QStringLiteral("panel = panelById(") + QString::number(m_containment->id()) + + QStringLiteral(");" + "var spacers = panel.widgets(\"org.kde.plasma.panelspacer\");" + "if (spacers.length === 1) {" + " panel.addWidget(\"org.kde.plasma.panelspacer\", 0,0,1,1);" + "} else {" + " panel.addWidget(\"org.kde.plasma.panelspacer\");" + "}")); } void PanelConfigView::syncGeometry()