Skip to content

Commit 2149e2a

Browse files
committed
(sni) Be able to control order for system tray items.
1 parent 1e965cc commit 2149e2a

File tree

8 files changed

+101
-37
lines changed

8 files changed

+101
-37
lines changed

include/modules/sni/host.hpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,18 @@ namespace waybar::modules::SNI {
1414

1515
class Host {
1616
public:
17-
Host(const std::size_t id, const Json::Value&, const Bar&,
17+
Host(std::size_t id, const Json::Value&, const Bar&,
1818
const std::function<void(std::unique_ptr<Item>&)>&,
1919
const std::function<void(std::unique_ptr<Item>&)>&, const std::function<void()>&);
2020
~Host();
2121

22+
void reorderItems();
23+
2224
private:
23-
void busAcquired(const Glib::RefPtr<Gio::DBus::Connection>&, Glib::ustring);
24-
void nameAppeared(const Glib::RefPtr<Gio::DBus::Connection>&, Glib::ustring,
25+
void busAcquired(const Glib::RefPtr<Gio::DBus::Connection>&, const Glib::ustring&);
26+
void nameAppeared(const Glib::RefPtr<Gio::DBus::Connection>&, const Glib::ustring&,
2527
const Glib::ustring&);
26-
void nameVanished(const Glib::RefPtr<Gio::DBus::Connection>&, Glib::ustring);
28+
void nameVanished(const Glib::RefPtr<Gio::DBus::Connection>&, const Glib::ustring&);
2729
static void proxyReady(GObject*, GAsyncResult*, gpointer);
2830
static void registerHost(GObject*, GAsyncResult*, gpointer);
2931
static void itemRegistered(SnWatcher*, const gchar*, gpointer);
@@ -33,7 +35,7 @@ class Host {
3335
void removeItem(std::vector<std::unique_ptr<Item>>::iterator);
3436
void clearItems();
3537

36-
std::tuple<std::string, std::string> getBusNameAndObjectPath(const std::string);
38+
static std::tuple<std::string, std::string> getBusNameAndObjectPath(const std::string&);
3739
void addRegisteredItem(const std::string& service);
3840

3941
std::vector<std::unique_ptr<Item>> items_;
@@ -47,6 +49,8 @@ class Host {
4749
const Bar& bar_;
4850
const std::function<void(std::unique_ptr<Item>&)> on_add_;
4951
const std::function<void(std::unique_ptr<Item>&)> on_remove_;
52+
53+
ItemOrderMap orders_;
5054
const std::function<void()> on_update_;
5155
};
5256

include/modules/sni/item.hpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,15 @@ struct ToolTip {
2424
Glib::ustring text;
2525
};
2626

27+
class Host;
28+
29+
using ItemOrderMap = std::unordered_map<std::string, int>;
30+
2731
class Item : public sigc::trackable {
2832
public:
2933
Item(const std::string&, const std::string&, const Json::Value&, const Bar&,
3034
const std::function<void(Item&)>&, const std::function<void(Item&)>&,
31-
const std::function<void()>&);
35+
const std::function<void()>&, Host&, const ItemOrderMap&);
3236
~Item();
3337

3438
bool isReady() const;
@@ -63,6 +67,7 @@ class Item : public sigc::trackable {
6367
* while compliant SNI implementation would always reset the flag to desired value.
6468
*/
6569
bool item_is_menu = true;
70+
int order_ = -1; // -1 means not set
6671

6772
private:
6873
void onConfigure(GdkEventConfigure* ev);
@@ -111,6 +116,9 @@ class Item : public sigc::trackable {
111116
Glib::RefPtr<Gio::DBus::Proxy> proxy_;
112117
Glib::RefPtr<Gio::Cancellable> cancellable_;
113118
std::set<std::string_view> update_pending_;
119+
120+
Host& host_;
121+
const ItemOrderMap& orders_;
114122
};
115123

116124
} // namespace waybar::modules::SNI

include/modules/sni/tray.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ namespace waybar::modules::SNI {
1313
class Tray : public AModule {
1414
public:
1515
Tray(const std::string&, const Bar&, const Json::Value&);
16-
virtual ~Tray() = default;
16+
~Tray() override = default ;
1717
auto update() -> void override;
1818

1919
private:

man/waybar-tray.5.scd

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@ Addressed by *tray*
4242
default: false ++
4343
Enables this module to consume all left over space dynamically.
4444

45+
*orders*: ++
46+
typeof: object ++
47+
Ordres for items to be placed in tray. When not set, orders are controlled by DBus.
48+
Key: name of item.
49+
Value: integer, higher value means to place this item to the right.
50+
51+
4552
# EXAMPLES
4653

4754
```
@@ -51,6 +58,9 @@ Addressed by *tray*
5158
"icons": {
5259
"blueman": "bluetooth",
5360
"TelegramDesktop": "$HOME/.local/share/icons/hicolor/16x16/apps/telegram.png"
61+
},
62+
"orders" : {
63+
"wechat" : 99
5464
}
5565
}
5666

src/modules/sni/host.cpp

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@
22

33
#include <spdlog/spdlog.h>
44

5+
#include <algorithm>
6+
7+
#include "modules/sni/item.hpp"
58
#include "util/scope_guard.hpp"
69

710
namespace waybar::modules::SNI {
811

9-
Host::Host(const std::size_t id, const Json::Value& config, const Bar& bar,
12+
Host::Host(std::size_t id, const Json::Value& config, const Bar& bar,
1013
const std::function<void(std::unique_ptr<Item>&)>& on_add,
1114
const std::function<void(std::unique_ptr<Item>&)>& on_remove,
1215
const std::function<void()>& on_update)
@@ -19,7 +22,18 @@ Host::Host(const std::size_t id, const Json::Value& config, const Bar& bar,
1922
bar_(bar),
2023
on_add_(on_add),
2124
on_remove_(on_remove),
22-
on_update_(on_update) {}
25+
on_update_(on_update) {
26+
auto orders = config["orders"];
27+
if (!orders.isNull()) {
28+
for (auto itr = orders.begin(); itr != orders.end(); ++itr) {
29+
auto key = itr.name();
30+
auto& value = *itr;
31+
assert(value.isInt());
32+
33+
orders_[key] = value.asInt();
34+
}
35+
}
36+
}
2337

2438
Host::~Host() {
2539
if (bus_name_id_ > 0) {
@@ -35,13 +49,13 @@ Host::~Host() {
3549
g_clear_object(&watcher_);
3650
}
3751

38-
void Host::busAcquired(const Glib::RefPtr<Gio::DBus::Connection>& conn, Glib::ustring name) {
52+
void Host::busAcquired(const Glib::RefPtr<Gio::DBus::Connection>& conn, const Glib::ustring& name) {
3953
watcher_id_ = Gio::DBus::watch_name(conn, "org.kde.StatusNotifierWatcher",
4054
sigc::mem_fun(*this, &Host::nameAppeared),
4155
sigc::mem_fun(*this, &Host::nameVanished));
4256
}
4357

44-
void Host::nameAppeared(const Glib::RefPtr<Gio::DBus::Connection>& conn, const Glib::ustring name,
58+
void Host::nameAppeared(const Glib::RefPtr<Gio::DBus::Connection>& conn, const Glib::ustring& name,
4559
const Glib::ustring& name_owner) {
4660
if (cancellable_ != nullptr) {
4761
// TODO
@@ -52,7 +66,8 @@ void Host::nameAppeared(const Glib::RefPtr<Gio::DBus::Connection>& conn, const G
5266
"/StatusNotifierWatcher", cancellable_, &Host::proxyReady, this);
5367
}
5468

55-
void Host::nameVanished(const Glib::RefPtr<Gio::DBus::Connection>& conn, const Glib::ustring name) {
69+
void Host::nameVanished(const Glib::RefPtr<Gio::DBus::Connection>& conn,
70+
const Glib::ustring& name) {
5671
g_cancellable_cancel(cancellable_);
5772
g_clear_object(&cancellable_);
5873
g_clear_object(&watcher_);
@@ -67,11 +82,11 @@ void Host::proxyReady(GObject* src, GAsyncResult* res, gpointer data) {
6782
}
6883
});
6984
SnWatcher* watcher = sn_watcher_proxy_new_finish(res, &error);
70-
if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
85+
if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED) != 0) {
7186
spdlog::error("Host: {}", error->message);
7287
return;
7388
}
74-
auto host = static_cast<SNI::Host*>(data);
89+
auto* host = static_cast<SNI::Host*>(data);
7590
host->watcher_ = watcher;
7691
if (error != nullptr) {
7792
spdlog::error("Host: {}", error->message);
@@ -89,18 +104,18 @@ void Host::registerHost(GObject* src, GAsyncResult* res, gpointer data) {
89104
}
90105
});
91106
sn_watcher_call_register_host_finish(SN_WATCHER(src), res, &error);
92-
if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
107+
if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED) != 0) {
93108
spdlog::error("Host: {}", error->message);
94109
return;
95110
}
96-
auto host = static_cast<SNI::Host*>(data);
111+
auto* host = static_cast<SNI::Host*>(data);
97112
if (error != nullptr) {
98113
spdlog::error("Host: {}", error->message);
99114
return;
100115
}
101116
g_signal_connect(host->watcher_, "item-registered", G_CALLBACK(&Host::itemRegistered), data);
102117
g_signal_connect(host->watcher_, "item-unregistered", G_CALLBACK(&Host::itemUnregistered), data);
103-
auto items = sn_watcher_dup_registered_items(host->watcher_);
118+
auto* items = sn_watcher_dup_registered_items(host->watcher_);
104119
if (items != nullptr) {
105120
for (uint32_t i = 0; items[i] != nullptr; i += 1) {
106121
host->addRegisteredItem(items[i]);
@@ -110,13 +125,13 @@ void Host::registerHost(GObject* src, GAsyncResult* res, gpointer data) {
110125
}
111126

112127
void Host::itemRegistered(SnWatcher* watcher, const gchar* service, gpointer data) {
113-
auto host = static_cast<SNI::Host*>(data);
128+
auto* host = static_cast<SNI::Host*>(data);
114129
host->addRegisteredItem(service);
115130
}
116131

117132
void Host::itemUnregistered(SnWatcher* watcher, const gchar* service, gpointer data) {
118-
auto host = static_cast<SNI::Host*>(data);
119-
auto [bus_name, object_path] = host->getBusNameAndObjectPath(service);
133+
auto* host = static_cast<SNI::Host*>(data);
134+
auto [bus_name, object_path] = waybar::modules::SNI::Host::getBusNameAndObjectPath(service);
120135
for (auto it = host->items_.begin(); it != host->items_.end(); ++it) {
121136
if ((*it)->bus_name == bus_name && (*it)->object_path == object_path) {
122137
host->removeItem(it);
@@ -163,7 +178,7 @@ void Host::clearItems() {
163178
}
164179
}
165180

166-
std::tuple<std::string, std::string> Host::getBusNameAndObjectPath(const std::string service) {
181+
std::tuple<std::string, std::string> Host::getBusNameAndObjectPath(const std::string& service) {
167182
auto it = service.find('/');
168183
if (it != std::string::npos) {
169184
return {service.substr(0, it), service.substr(it)};
@@ -172,16 +187,25 @@ std::tuple<std::string, std::string> Host::getBusNameAndObjectPath(const std::st
172187
}
173188

174189
void Host::addRegisteredItem(const std::string& service) {
175-
std::string bus_name, object_path;
190+
std::string bus_name;
191+
std::string object_path;
176192
std::tie(bus_name, object_path) = getBusNameAndObjectPath(service);
177-
auto it = std::find_if(items_.begin(), items_.end(), [&bus_name, &object_path](const auto& item) {
193+
auto it = std::ranges::find_if(items_, [&bus_name, &object_path](const auto& item) {
178194
return bus_name == item->bus_name && object_path == item->object_path;
179195
});
180196
if (it == items_.end()) {
181197
items_.emplace_back(new Item(
182198
bus_name, object_path, config_, bar_, [this](Item& item) { itemReady(item); },
183-
[this](Item& item) { itemInvalidated(item); }, on_update_));
199+
[this](Item& item) { itemInvalidated(item); }, on_update_, *this, orders_));
184200
}
185201
}
186202

203+
void Host::reorderItems() {
204+
std::ranges::for_each(items_, on_remove_);
205+
std::ranges::sort(items_, [](std::unique_ptr<Item>& item1, std::unique_ptr<Item>& item2) {
206+
return item1->order_ < item2->order_;
207+
});
208+
std::ranges::for_each(items_, on_add_);
209+
}
210+
187211
} // namespace waybar::modules::SNI

src/modules/sni/item.cpp

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,17 @@
55
#include <gtkmm/tooltip.h>
66
#include <spdlog/spdlog.h>
77

8+
#include <cassert>
89
#include <algorithm>
910
#include <filesystem>
1011
#include <fstream>
1112
#include <map>
13+
#include <unordered_map>
1214

1315
#include "gdk/gdk.h"
16+
#include "modules/sni/host.hpp"
1417
#include "modules/sni/icon_manager.hpp"
15-
#include "util/format.hpp"
18+
#include "util/format.hpp" // IWYU pragma: keep
1619
#include "util/gtk_icon.hpp"
1720

1821
template <>
@@ -40,7 +43,8 @@ static const unsigned UPDATE_DEBOUNCE_TIME = 10;
4043

4144
Item::Item(const std::string& bn, const std::string& op, const Json::Value& config, const Bar& bar,
4245
const std::function<void(Item&)>& on_ready,
43-
const std::function<void(Item&)>& on_invalidate, const std::function<void()>& on_updated)
46+
const std::function<void(Item&)>& on_invalidate, const std::function<void()>& on_updated,
47+
Host& host, const ItemOrderMap& orders)
4448
: bus_name(bn),
4549
object_path(op),
4650
icon_size(16),
@@ -49,7 +53,9 @@ Item::Item(const std::string& bn, const std::string& op, const Json::Value& conf
4953
bar_(bar),
5054
on_ready_(on_ready),
5155
on_invalidate_(on_invalidate),
52-
on_updated_(on_updated) {
56+
on_updated_(on_updated),
57+
host_(host),
58+
orders_(orders) {
5359
if (config["icon-size"].isUInt()) {
5460
icon_size = config["icon-size"].asUInt();
5561
}
@@ -263,6 +269,17 @@ void Item::invalidate() {
263269
void Item::setCustomIcon(const std::string& id) {
264270
spdlog::debug("SNI tray id: {}", id);
265271

272+
if (order_ == -1) {
273+
auto iter = orders_.find(id);
274+
if (iter != orders_.end()) {
275+
order_ = iter->second;
276+
spdlog::debug("reordering tray item {}, order: {}", id, order_);
277+
} else {
278+
order_ = 0;
279+
}
280+
host_.reorderItems();
281+
}
282+
266283
std::string custom_icon = IconManager::instance().getIconForApp(id);
267284
if (!custom_icon.empty()) {
268285
if (std::filesystem::exists(custom_icon)) {

src/modules/sni/tray.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ auto Tray::update() -> void {
5656
if (show_passive_) {
5757
event_box_.set_visible(!children.empty());
5858
} else {
59-
event_box_.set_visible(!std::all_of(children.begin(), children.end(), [](Gtk::Widget* child) {
59+
event_box_.set_visible(!std::ranges::all_of(children, [](Gtk::Widget* child) {
6060
return child->get_style_context()->has_class("passive");
6161
}));
6262
}

0 commit comments

Comments
 (0)