diff --git a/SysTray.cxx b/SysTray.cxx
index bb5c9d1..d61277c 100644
--- a/SysTray.cxx
+++ b/SysTray.cxx
@@ -1,265 +1,286 @@
// SPDX-License-Identifier: GPL-3.0-or-later
/*
- Copyright 2017 Martin Koller, kollix@aon.at
+ Copyright 2017 - 2020 Martin Koller, kollix@aon.at
This file is part of liquidshell.
liquidshell 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 3 of the License, or
(at your option) any later version.
liquidshell 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 liquidshell. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#ifdef WITH_PACKAGEKIT
#include
#endif
#include
#include
#include
#include
#include
#include
+#include
+
//--------------------------------------------------------------------------------
static const QLatin1String WATCHER_SERVICE("org.kde.StatusNotifierWatcher");
//--------------------------------------------------------------------------------
SysTray::SysTray(DesktopPanel *parent)
: QFrame(parent)
{
qRegisterMetaType("KDbusImageStruct");
qRegisterMetaType("KDbusImageVector");
qDBusRegisterMetaType();
qDBusRegisterMetaType();
qDBusRegisterMetaType();
setFrameShape(QFrame::StyledPanel);
+ setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
connect(parent, &DesktopPanel::rowsChanged, this, &SysTray::fill);
QHBoxLayout *hbox = new QHBoxLayout(this);
hbox->setContentsMargins(QMargins(4, 0, 4, 0));
vbox = new QVBoxLayout;
vbox->setContentsMargins(QMargins());
vbox->setSpacing(4);
QFrame *separator = new QFrame;
separator->setFrameStyle(QFrame::Plain);
separator->setFrameShape(QFrame::VLine);
appsVbox = new QVBoxLayout;
appsVbox->setContentsMargins(QMargins());
appsVbox->setSpacing(4);
hbox->addLayout(vbox);
hbox->addWidget(separator);
hbox->addLayout(appsVbox);
if ( QDBusConnection::sessionBus().isConnected() )
{
serviceName = QString("org.kde.StatusNotifierHost-%1").arg(QApplication::applicationPid());
QDBusConnection::sessionBus().registerService(serviceName);
registerWatcher();
}
fill();
}
//--------------------------------------------------------------------------------
void SysTray::fill()
{
// delete all internal widgets
QLayoutItem *child;
while ( (child = vbox->takeAt(0)) )
{
if ( child->layout() )
{
while ( QLayoutItem *widgetItem = child->layout()->takeAt(0) )
delete widgetItem->widget();
}
delete child;
}
const int MAX_ROWS = qobject_cast(parentWidget())->getRows();
QVector rowsLayout(MAX_ROWS);
for (int i = 0; i < MAX_ROWS; i++)
{
rowsLayout[i] = new QHBoxLayout;
rowsLayout[i]->setContentsMargins(QMargins());
rowsLayout[i]->setSpacing(4);
vbox->addLayout(rowsLayout[i]);
}
- if ( MAX_ROWS == 1 )
- {
- rowsLayout[0]->addWidget(new NotificationServer(this), 0, Qt::AlignLeft);
- rowsLayout[0]->addWidget(new Network(this), 0, Qt::AlignLeft);
- rowsLayout[0]->addWidget(new DeviceNotifier(this), 0, Qt::AlignLeft);
- rowsLayout[0]->addWidget(new Battery(this), 0, Qt::AlignLeft);
- rowsLayout[0]->addWidget(new Bluetooth(this), 0, Qt::AlignLeft);
-#ifdef WITH_PACKAGEKIT
- rowsLayout[0]->addWidget(new PkUpdates(this), 0, Qt::AlignLeft);
-#endif
- }
- else if ( MAX_ROWS >= 2 )
+ QVector internalWidgets =
{
- rowsLayout[0]->addWidget(new NotificationServer(this), 0, Qt::AlignLeft);
- rowsLayout[0]->addWidget(new DeviceNotifier(this), 0, Qt::AlignLeft);
- rowsLayout[0]->addWidget(new Bluetooth(this), 0, Qt::AlignLeft);
- rowsLayout[1]->addWidget(new Network(this), 0, Qt::AlignLeft);
- rowsLayout[1]->addWidget(new Battery(this), 0, Qt::AlignLeft);
+ new NotificationServer(this),
+ new Network(this),
+ new DeviceNotifier(this),
+ new Battery(this),
+ new Bluetooth(this),
#ifdef WITH_PACKAGEKIT
- rowsLayout[1]->addWidget(new PkUpdates(this), 0, Qt::AlignLeft);
+ new PkUpdates(this)
#endif
- }
+ };
+
+ for (int i = 0; i < internalWidgets.count(); i++)
+ rowsLayout[i % MAX_ROWS]->addWidget(internalWidgets[i], 0, Qt::AlignLeft);
+
+ for (int i = 0; i < MAX_ROWS; i++)
+ rowsLayout[i]->addStretch();
// notifier items
qDeleteAll(appsRows);
appsRows.clear();
appsRows.resize(MAX_ROWS);
for (int i = 0; i < MAX_ROWS; i++)
{
appsRows[i] = new QHBoxLayout;
appsRows[i]->setContentsMargins(QMargins());
appsRows[i]->setSpacing(4);
appsVbox->addLayout(appsRows[i]);
}
- for (SysTrayNotifyItem *item : items)
- itemInitialized(item);
+ arrangeNotifyItems();
}
//--------------------------------------------------------------------------------
void SysTray::registerWatcher()
{
QDBusMessage msg =
QDBusMessage::createMethodCall(WATCHER_SERVICE, "/StatusNotifierWatcher",
"org.kde.StatusNotifierWatcher",
"RegisterStatusNotifierHost");
msg << serviceName;
QDBusConnection::sessionBus().send(msg);
// get list of currently existing items
msg = QDBusMessage::createMethodCall(WATCHER_SERVICE, "/StatusNotifierWatcher",
"org.freedesktop.DBus.Properties",
"Get");
msg << "org.kde.StatusNotifierWatcher" << "RegisteredStatusNotifierItems";
QDBusPendingCall call = QDBusConnection::sessionBus().asyncCall(msg);
QDBusPendingCallWatcher *pendingCallWatcher = new QDBusPendingCallWatcher(call, this);
connect(pendingCallWatcher, &QDBusPendingCallWatcher::finished, this,
[this](QDBusPendingCallWatcher *w)
{
w->deleteLater();
QDBusPendingReply reply = *w;
QStringList items = reply.value().variant().toStringList();
for (const QString &item : items)
itemRegistered(item);
}
);
// connect for new items
QDBusConnection::sessionBus().
connect(WATCHER_SERVICE, "/StatusNotifierWatcher",
"org.kde.StatusNotifierWatcher",
"StatusNotifierItemRegistered",
this, SLOT(itemRegistered(QString)));
// connect for removed items
QDBusConnection::sessionBus().
connect(WATCHER_SERVICE, "/StatusNotifierWatcher",
"org.kde.StatusNotifierWatcher",
"StatusNotifierItemUnregistered",
this, SLOT(itemUnregistered(QString)));
}
//--------------------------------------------------------------------------------
void SysTray::itemRegistered(QString item)
{
//qDebug() << "itemRegistered" << item;
int slash = item.indexOf('/');
if ( slash < 1 )
return;
QString service = item.left(slash);
QString path = item.mid(slash);
// create item but insert it into the layout just when it was initialized
// since it might be an invalid item which has no StatusNotifierItem path
// and will delete itself on error
SysTrayNotifyItem *sysItem = new SysTrayNotifyItem(this, service, path);
sysItem->setObjectName(item);
sysItem->setFixedSize(QSize(22, 22));
connect(sysItem, &SysTrayNotifyItem::initialized, this, &SysTray::itemInitialized);
}
//--------------------------------------------------------------------------------
void SysTray::itemInitialized(SysTrayNotifyItem *item)
{
- // TODO count only visible items
- int lowestCount = 0;
- int lowestCountAt = 0;
- for (int i = 0; i < appsRows.count(); i++)
- {
- if ( i == 0 )
- lowestCount = appsRows[i]->count();
- else if ( appsRows[i]->count() < lowestCount )
- {
- lowestCount = appsRows[i]->count();
- lowestCountAt = i;
- }
- }
-
- appsRows[lowestCountAt]->addWidget(item, 0, Qt::AlignLeft);
-
if ( !items.contains(item->objectName()) )
- {
items.insert(item->objectName(), item);
- disconnect(item, &SysTrayNotifyItem::initialized, this, &SysTray::itemInitialized);
- }
+
+ arrangeNotifyItems(); // show/hide icons
}
//--------------------------------------------------------------------------------
void SysTray::itemUnregistered(QString item)
{
//qDebug() << "itemUnregistered" << item;
if ( items.contains(item) )
+ {
delete items.take(item);
+ arrangeNotifyItems();
+ }
+}
+
+//--------------------------------------------------------------------------------
+
+void SysTray::arrangeNotifyItems()
+{
+ // cut all items from all layouts
+ foreach (QHBoxLayout *row, appsRows)
+ {
+ while ( QLayoutItem *item = row->itemAt(0) )
+ {
+ if ( item->widget() )
+ row->removeWidget(item->widget()); // take widget out but do not delete it
+ else
+ delete row->takeAt(0); // spacer items
+ }
+ }
+
+ // rearrange visible items - "row first" layout
+ int visibleItems = 0;
+ foreach (SysTrayNotifyItem *item, items)
+ if ( item->isVisible() )
+ visibleItems++;
+
+ const int MAX_COLUMNS = static_cast(std::ceil(visibleItems / float(appsRows.count())));
+ int row = 0;
+ foreach (SysTrayNotifyItem *item, items)
+ {
+ if ( item->isVisible() )
+ {
+ appsRows[row]->addWidget(item, 0, Qt::AlignLeft);
+ if ( appsRows[row]->count() == MAX_COLUMNS )
+ row++;
+ }
+ }
+
+ foreach (QHBoxLayout *row, appsRows)
+ row->addStretch();
}
//--------------------------------------------------------------------------------
diff --git a/SysTray.hxx b/SysTray.hxx
index 3106321..70a3a34 100644
--- a/SysTray.hxx
+++ b/SysTray.hxx
@@ -1,57 +1,58 @@
// SPDX-License-Identifier: GPL-3.0-or-later
/*
- Copyright 2017 Martin Koller, kollix@aon.at
+ Copyright 2017 - 2020 Martin Koller, kollix@aon.at
This file is part of liquidshell.
liquidshell 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 3 of the License, or
(at your option) any later version.
liquidshell 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 liquidshell. If not, see .
*/
#ifndef _SysTray_H_
#define _SysTray_H_
#include
#include
#include
#include
#include
#include
#include
#include
class SysTray : public QFrame
{
Q_OBJECT
public:
SysTray(DesktopPanel *parent);
private Q_SLOTS:
void fill();
void itemRegistered(QString service);
void itemUnregistered(QString service);
void itemInitialized(SysTrayNotifyItem *item);
private:
void registerWatcher();
+ void arrangeNotifyItems();
private:
QVBoxLayout *vbox, *appsVbox;
QVector appsRows;
QString serviceName;
QMap> items;
};
#endif