Members of the KDE Community are recommended to subscribe to the kde-community mailing list at https://mail.kde.org/mailman/listinfo/kde-community to allow them to participate in important discussions and receive other important announcements

Commit 7b3b06a4 authored by Marco Martin's avatar Marco Martin

Panel spacer that can center things

Summary:
* Make the panel spacer take a lot more space when is in expanding mode, so much
to "win" against the taskbar and collapse it to its minimum size (seems the
most expected behavior after some discussions in vdg channel)
* make it paint a background when in edit mode
* when two spacers are present in the panel, they try to center all the content
that is in between them (if possible), so that is always at the center of the panel,
even if there is more content on one side rather than the other

Test Plan:
{F8110981}
{F8112648}

Reviewers: #plasma, #vdg, davidedmundson

Reviewed By: #plasma, davidedmundson

Subscribers: davidedmundson, davidre, broulik, ngraham, plasma-devel

Tags: #plasma

Differential Revision: https://phabricator.kde.org/D27481
parent 05353f12
......@@ -3,7 +3,6 @@ add_subdirectory(icon)
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(calendar)
add_subdirectory(devicenotifier)
add_subdirectory(digital-clock)
add_subdirectory(kicker)
add_subdirectory(panelspacer)
plasma_install_package(clipboard org.kde.plasma.clipboard)
......
plasma_install_package(package org.kde.plasma.panelspacer)
add_subdirectory(plugin)
#! /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
/*
* Copyright 2014 Marco Martin <mart@kde.org>
*
* 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 <http://www.gnu.org/licenses/>
*/
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");
}
}
......@@ -11,7 +11,8 @@
<default>true</default>
</entry>
<entry name="length" type="Int">
<label>length in pixels of the spacer. Configuration effective only if expanding is set to false.</label>
<label>length in pixels of the spacer. Configuration effective only if expanding is set to false. A negative value means an invalid value that should be completely ignored.</label>
<default>-1</default>
</entry>
</group>
......
/*
* Copyright 2014 Marco Martin <mart@kde.org>
*
* 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 <http://www.gnu.org/licenses/>
*/
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
}
}
......@@ -139,6 +139,7 @@ NoDisplay=true
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
......
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)
/***************************************************************************
* Copyright (C) 2020 Marco Martin <mart@kde.org> *
* *
* 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 <QDebug>
#include <QProcess>
#include <QtQml>
#include <Plasma/Containment>
#include <Plasma/Corona>
#include <PlasmaQuick/AppletQuickItem>
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<PlasmaQuick::AppletQuickItem *>());
lay2->setTwinSpacer(lay1->property("_plasma_graphicObject").value<PlasmaQuick::AppletQuickItem *>());
} 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<PlasmaQuick::AppletQuickItem *>());
lay2->setTwinSpacer(lay1->property("_plasma_graphicObject").value<PlasmaQuick::AppletQuickItem *>());
} 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<PlasmaQuick::AppletQuickItem *>();
}
K_EXPORT_PLASMA_APPLET_WITH_JSON(panelspacer, PanelSpacer, "metadata.json")
#include "panelspacer.moc"
/***************************************************************************
* Copyright (C) 2020 Marco Martin <mart@kde.org> *
*
* *
* 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 <Plasma/Applet>
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<Plasma::Containment *, QList<PanelSpacer *> > 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;
};
......@@ -106,7 +106,20 @@ void PanelConfigView::showAddWidgetDialog()
void PanelConfigView::addPanelSpacer()
{
m_containment->createApplet(QStringLiteral("org.kde.plasma.panelspacer"));
ShellCorona *c = qobject_cast<ShellCorona *>(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()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment