kde-workspace/plasma/applets/pager/pager.cpp

509 lines
16 KiB
C++
Raw Normal View History

/* This file is part of the KDE project
Copyright (C) 2023 Ivailo Monev <xakepa10@gmail.com>
2014-11-13 19:30:51 +02:00
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2, as published by the Free Software Foundation.
2014-11-13 19:30:51 +02:00
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
2014-11-13 19:30:51 +02:00
#include "pager.h"
#include <QX11Info>
#include <QGridLayout>
#include <QLabel>
#include <Plasma/Svg>
2014-11-13 19:30:51 +02:00
#include <Plasma/FrameSvg>
#include <Plasma/SvgWidget>
2014-11-13 19:30:51 +02:00
#include <Plasma/Theme>
#include <Plasma/ToolTipManager>
#include <KCModuleInfo>
#include <KWindowSystem>
#include <KIcon>
#include <KDebug>
#include <netwm.h>
2014-11-13 19:30:51 +02:00
// standard issue margin/spacing
static const int s_spacing = 4;
static PagerApplet::PagerMode s_defaultpagermode = PagerApplet::ShowName;
2014-11-13 19:30:51 +02:00
static QString kElementPrefixForDesktop(const int desktop, const bool hovered)
2014-11-13 19:30:51 +02:00
{
if (hovered) {
return QString::fromLatin1("hover");
} else if (KWindowSystem::currentDesktop() == desktop) {
return QString::fromLatin1("active");
}
return QString::fromLatin1("normal");
2014-11-13 19:30:51 +02:00
}
static QRectF kAdjustRect(const QRectF &rect, const bool vertical)
{
QRectF result = rect;
if (vertical) {
result.setHeight(result.height() - (s_spacing * 2));
qreal resultx = 0.0;
qreal resulty = 0.0;
qreal resultw = 0.0;
qreal resulth = 0.0;
result.getRect(&resultx, &resulty, &resultw, &resulth);
result.setX(-(resultw / resulth));
result.setY(-resultw);
result.setWidth(resulth);
result.setHeight(resultw);
} else {
result.setWidth(result.width() - (s_spacing * 2));
}
return result;
}
static QFont kGetFont()
{
QFont font = KGlobalSettings::smallestReadableFont();
font.setBold(true);
return font;
}
static bool kHandleMouseEvent(QGraphicsSceneMouseEvent *event)
{
if (event->button() == Qt::MiddleButton) {
NETRootInfo netrootinfo(QX11Info::display(), NET::WM2ShowingDesktop | NET::Supported);
if (!netrootinfo.isSupported(NET::WM2ShowingDesktop)) {
kWarning() << "NET::WM2ShowingDesktop is not supported";
return false;
}
// that is how the showdesktop does it - it tracks the state internally
static bool s_didshow = false;
if (netrootinfo.showingDesktop() || s_didshow) {
s_didshow = false;
netrootinfo.setShowingDesktop(false);
} else {
s_didshow = true;
netrootinfo.setShowingDesktop(true);
}
return true;
}
return false;
}
class PagerSvg : public Plasma::SvgWidget
{
Q_OBJECT
public:
PagerSvg(const int desktop, const PagerApplet::PagerMode pagermode, QGraphicsItem *parent = nullptr);
void setup(const PagerApplet::PagerMode pagermode);
2014-11-13 19:30:51 +02:00
protected:
QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint) const final;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) final;
void hoverEnterEvent(QGraphicsSceneHoverEvent *event) final;
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) final;
// handled here too
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) final;
2014-11-13 19:30:51 +02:00
private Q_SLOTS:
void slotClicked(const Qt::MouseButton button);
void slotUpdate();
void slotUpdateSvgAndToolTip();
2014-11-13 19:30:51 +02:00
private:
int m_desktop;
bool m_hovered;
Plasma::FrameSvg* m_framesvg;
PagerApplet::PagerMode m_pagermode;
// updateGeometry() is protected but size hint is based on orientation and geometry has to be
// updated on layout orientation change
friend PagerApplet;
};
2014-11-13 19:30:51 +02:00
PagerSvg::PagerSvg(const int desktop, const PagerApplet::PagerMode pagermode, QGraphicsItem *parent)
: Plasma::SvgWidget(parent),
m_desktop(desktop),
m_hovered(false),
m_framesvg(nullptr),
m_pagermode(pagermode)
2014-11-13 19:30:51 +02:00
{
setAcceptHoverEvents(true);
slotUpdateSvgAndToolTip();
connect(
this, SIGNAL(clicked(Qt::MouseButton)),
this, SLOT(slotClicked(Qt::MouseButton))
);
connect(
KWindowSystem::self(), SIGNAL(desktopNamesChanged()),
this, SLOT(slotUpdate())
);
connect(
KWindowSystem::self(), SIGNAL(currentDesktopChanged(int)),
this, SLOT(slotUpdate())
);
connect(
Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()),
this, SLOT(slotUpdateSvgAndToolTip())
);
}
void PagerSvg::setup(const PagerApplet::PagerMode pagermode)
{
m_pagermode = pagermode;
update();
}
QSizeF PagerSvg::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
{
const PagerApplet* pagerapplet = qobject_cast<PagerApplet*>(parentObject());
bool inpanel = false;
switch (pagerapplet->formFactor()) {
case Plasma::FormFactor::Planar:
case Plasma::FormFactor::MediaCenter:
case Plasma::FormFactor::Application: {
break;
}
default: {
inpanel = true;
break;
}
}
// the panel containment completely ignores minimum size so no hint for that
if (inpanel && which == Qt::MinimumSize) {
return Plasma::SvgWidget::sizeHint(which, constraint);
}
if (which != Qt::MaximumSize) {
const QSizeF currentsize = size();
const bool vertical = (currentsize.width() < currentsize.height());
// hints are based on the mode and longest text of all virtual desktops
qreal textwidth = 0;
const int numberofdesktops = KWindowSystem::numberOfDesktops();
QFontMetricsF fontmetricsf(kGetFont());
for (int i = 0; i < numberofdesktops; i++) {
QString desktoptext;
if (m_pagermode == PagerApplet::ShowNumber) {
desktoptext = QString::number(i);
} else {
desktoptext = KWindowSystem::desktopName(i);
}
textwidth = qMax(textwidth, fontmetricsf.width(desktoptext));
}
// the applet layout spacing + the text spacing + s_spacing for good measure
static const int spacingx4 = (s_spacing * 4);
QSizeF result;
if (vertical) {
result.setWidth(fontmetricsf.height() + spacingx4);
result.setHeight(textwidth + spacingx4);
} else {
result.setWidth(textwidth + spacingx4);
result.setHeight(fontmetricsf.height() + spacingx4);
}
return result;
}
return Plasma::SvgWidget::sizeHint(which, constraint);
}
2014-11-13 19:30:51 +02:00
void PagerSvg::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(option);
Q_UNUSED(widget);
painter->setRenderHint(QPainter::Antialiasing);
painter->setFont(kGetFont());
const QRectF brect = boundingRect();
const QSizeF brectsize = brect.size();
m_framesvg->setElementPrefix(kElementPrefixForDesktop(m_desktop, m_hovered));
m_framesvg->resizeFrame(brectsize);
m_framesvg->paintFrame(painter, brect);
QString pagertext;
switch (m_pagermode) {
case PagerApplet::ShowNumber: {
pagertext = QString::number(m_desktop);
break;
}
case PagerApplet::ShowName: {
pagertext = KWindowSystem::desktopName(m_desktop);
break;
}
}
const bool vertical = (brectsize.width() < brectsize.height());
if (vertical) {
painter->rotate(90);
}
painter->translate(s_spacing, 0);
painter->drawText(
kAdjustRect(brect, vertical),
pagertext,
QTextOption(Qt::AlignCenter)
);
}
void PagerSvg::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
Q_UNUSED(event);
m_hovered = true;
update();
}
void PagerSvg::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
Q_UNUSED(event);
m_hovered = false;
update();
2014-11-13 19:30:51 +02:00
}
void PagerSvg::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
if (kHandleMouseEvent(event)) {
event->accept();
return;
}
Plasma::SvgWidget::mouseReleaseEvent(event);
}
void PagerSvg::slotClicked(const Qt::MouseButton button)
{
if (button == Qt::LeftButton) {
KWindowSystem::setCurrentDesktop(m_desktop);
}
}
void PagerSvg::slotUpdate()
{
update();
}
void PagerSvg::slotUpdateSvgAndToolTip()
2014-11-13 19:30:51 +02:00
{
if (m_framesvg) {
delete m_framesvg;
2014-11-13 19:30:51 +02:00
}
m_framesvg = new Plasma::FrameSvg(this);
m_framesvg->setImagePath("widgets/pager");
setSvg(m_framesvg);
Plasma::ToolTipContent plasmatooltip;
plasmatooltip.setMainText(QString::fromLatin1("<center>%1</center>").arg(KWindowSystem::desktopName(m_desktop)));
Plasma::ToolTipManager::self()->setContent(this, plasmatooltip);
}
PagerApplet::PagerApplet(QObject *parent, const QVariantList &args)
: Plasma::Applet(parent, args),
m_layout(nullptr),
m_adddesktopaction(nullptr),
m_removedesktopaction(nullptr),
m_pagermode(s_defaultpagermode),
m_pagermodebox(nullptr),
m_spacer(nullptr),
m_kcmdesktopproxy(nullptr)
{
KGlobal::locale()->insertCatalog("plasma_applet_pager");
setAspectRatioMode(Plasma::AspectRatioMode::IgnoreAspectRatio);
setHasConfigurationInterface(true);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
2014-11-13 19:30:51 +02:00
m_layout = new QGraphicsLinearLayout(Qt::Horizontal, this);
m_layout->setContentsMargins(s_spacing, s_spacing, s_spacing, s_spacing);
m_layout->setSpacing(s_spacing);
// early setup to get proper initial size
slotUpdateLayout();
adjustSize();
2014-11-13 19:30:51 +02:00
}
void PagerApplet::init()
2014-11-13 19:30:51 +02:00
{
KConfigGroup configgroup = config();
const PagerApplet::PagerMode oldpagermode = m_pagermode;
m_pagermode = static_cast<PagerApplet::PagerMode>(configgroup.readEntry("pagerMode", static_cast<int>(s_defaultpagermode)));
if (oldpagermode != m_pagermode) {
// layout again with the new mode for size hints to apply correctly
slotUpdateLayout();
}
connect(
KWindowSystem::self(), SIGNAL(numberOfDesktopsChanged(int)),
this, SLOT(slotUpdateLayout())
);
2014-11-13 19:30:51 +02:00
}
void PagerApplet::createConfigurationInterface(KConfigDialog *parent)
2014-11-13 19:30:51 +02:00
{
QWidget* widget = new QWidget();
QGridLayout* widgetlayout = new QGridLayout(widget);
QLabel* pagermodelabel = new QLabel(widget);
pagermodelabel->setText(i18n("Text:"));
widgetlayout->addWidget(pagermodelabel, 0, 0);
m_pagermodebox = new QComboBox(widget);
m_pagermodebox->addItem(i18n("Desktop number"), static_cast<int>(PagerApplet::ShowNumber));
m_pagermodebox->addItem(i18n("Desktop name"), static_cast<int>(PagerApplet::ShowName));
m_pagermodebox->setCurrentIndex(static_cast<int>(m_pagermode));
widgetlayout->addWidget(m_pagermodebox, 0, 1);
m_spacer = new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Expanding);
widgetlayout->addItem(m_spacer, 1, 0, 1, 2);
widget->setLayout(widgetlayout);
// doesn't look like media visualization but ok.. (that's what the clock applet uses)
parent->addPage(widget, i18n("Appearance"), "view-media-visualization");
m_kcmdesktopproxy = new KCModuleProxy("desktop");
parent->addPage(
m_kcmdesktopproxy, m_kcmdesktopproxy->moduleInfo().moduleName(),
m_kcmdesktopproxy->moduleInfo().icon()
);
connect(parent, SIGNAL(applyClicked()), this, SLOT(slotConfigAccepted()));
connect(parent, SIGNAL(okClicked()), this, SLOT(slotConfigAccepted()));
connect(m_pagermodebox, SIGNAL(currentIndexChanged(int)), parent, SLOT(settingsModified()));
connect(m_kcmdesktopproxy, SIGNAL(changed(bool)), parent, SLOT(settingsModified()));
2014-11-13 19:30:51 +02:00
}
QList<QAction*> PagerApplet::contextualActions()
2014-11-13 19:30:51 +02:00
{
return m_actions;
2014-11-13 19:30:51 +02:00
}
void PagerApplet::wheelEvent(QGraphicsSceneWheelEvent *event)
{
const int currentdesktop = KWindowSystem::currentDesktop();
if (event->delta() < 0) {
KWindowSystem::setCurrentDesktop(currentdesktop + 1);
} else {
KWindowSystem::setCurrentDesktop(currentdesktop - 1);
}
}
void PagerApplet::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
if (kHandleMouseEvent(event)) {
event->accept();
return;
}
Plasma::Applet::mouseReleaseEvent(event);
}
void PagerApplet::constraintsEvent(Plasma::Constraints constraints)
2014-11-13 19:30:51 +02:00
{
// perfect size finder
// qDebug() << Q_FUNC_INFO << size();
if (constraints & Plasma::SizeConstraint || constraints & Plasma::FormFactorConstraint) {
switch (formFactor()) {
case Plasma::FormFactor::Horizontal: {
m_layout->setOrientation(Qt::Horizontal);
updatePolicyAndPagers();
return;
2014-11-13 19:30:51 +02:00
}
case Plasma::FormFactor::Vertical: {
m_layout->setOrientation(Qt::Vertical);
updatePolicyAndPagers();
return;
2014-11-13 19:30:51 +02:00
}
default: {
break;
2014-11-13 19:30:51 +02:00
}
}
const QSizeF appletsize = size();
if (appletsize.width() >= appletsize.height()) {
m_layout->setOrientation(Qt::Horizontal);
2014-11-13 19:30:51 +02:00
} else {
m_layout->setOrientation(Qt::Vertical);
2014-11-13 19:30:51 +02:00
}
updatePolicyAndPagers();
}
}
void PagerApplet::updatePolicyAndPagers()
{
if (m_layout->orientation() == Qt::Horizontal) {
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
} else {
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
2014-11-13 19:30:51 +02:00
}
QMutexLocker locker(&m_mutex);
foreach (PagerSvg* pagersvg, m_pagersvgs) {
pagersvg->updateGeometry();
}
2014-11-13 19:30:51 +02:00
}
void PagerApplet::slotUpdateLayout()
2014-11-13 19:30:51 +02:00
{
QMutexLocker locker(&m_mutex);
foreach (PagerSvg* pagersvg, m_pagersvgs) {
m_layout->removeItem(pagersvg);
2014-11-13 19:30:51 +02:00
}
qDeleteAll(m_pagersvgs);
m_pagersvgs.clear();
2014-11-13 19:30:51 +02:00
const int numberofdesktops = KWindowSystem::numberOfDesktops();
for (int i = 0; i < numberofdesktops; i++) {
PagerSvg* pagersvg = new PagerSvg(i + 1, m_pagermode, this);
m_layout->addItem(pagersvg);
m_pagersvgs.append(pagersvg);
2014-11-13 19:30:51 +02:00
}
m_actions.clear();
if (!m_adddesktopaction) {
m_adddesktopaction = new QAction(
KIcon("list-add"), i18n("&Add Virtual Desktop"),
this
);
2014-11-13 19:30:51 +02:00
}
m_actions.append(m_adddesktopaction);
connect(
m_adddesktopaction, SIGNAL(triggered(bool)),
this , SLOT(slotAddDesktop())
);
if (numberofdesktops > 1) {
if (!m_removedesktopaction) {
m_removedesktopaction = new QAction(
KIcon("list-remove"), i18n("&Remove Last Virtual Desktop"),
this
);
}
m_actions.append(m_removedesktopaction);
connect(
m_removedesktopaction, SIGNAL(triggered(bool)),
this , SLOT(slotRemoveDesktop())
);
2014-11-13 19:30:51 +02:00
}
}
void PagerApplet::slotAddDesktop()
2014-11-13 19:30:51 +02:00
{
NETRootInfo netrootinfo(QX11Info::display(), NET::NumberOfDesktops);
netrootinfo.setNumberOfDesktops(netrootinfo.numberOfDesktops() + 1);
2014-11-13 19:30:51 +02:00
}
void PagerApplet::slotRemoveDesktop()
2014-11-13 19:30:51 +02:00
{
NETRootInfo netrootinfo(QX11Info::display(), NET::NumberOfDesktops);
const int numberofdesktops = netrootinfo.numberOfDesktops();
if (numberofdesktops > 1) {
netrootinfo.setNumberOfDesktops(netrootinfo.numberOfDesktops() - 1);
2014-11-13 19:30:51 +02:00
} else {
kWarning() << "there is only one desktop";
2014-11-13 19:30:51 +02:00
}
}
void PagerApplet::slotConfigAccepted()
{
Q_ASSERT(m_pagermodebox != nullptr);
const int pagermodeindex = m_pagermodebox->currentIndex();
m_pagermode = static_cast<PagerApplet::PagerMode>(pagermodeindex);
KConfigGroup configgroup = config();
configgroup.writeEntry("pagerMode", pagermodeindex);
slotUpdateLayout();
m_kcmdesktopproxy->save();
emit configNeedsSaving();
}
2015-02-27 09:28:46 +00:00
#include "moc_pager.cpp"
#include "pager.moc"