Merge pull request #4913 from khaneliman/tray
fix(tray): complete attention/overlay implementation
This commit is contained in:
@@ -16,7 +16,7 @@ class Host {
|
|||||||
public:
|
public:
|
||||||
Host(const std::size_t id, const Json::Value&, const Bar&,
|
Host(const std::size_t id, const Json::Value&, const Bar&,
|
||||||
const std::function<void(std::unique_ptr<Item>&)>&,
|
const std::function<void(std::unique_ptr<Item>&)>&,
|
||||||
const std::function<void(std::unique_ptr<Item>&)>&);
|
const std::function<void(std::unique_ptr<Item>&)>&, const std::function<void()>&);
|
||||||
~Host();
|
~Host();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -28,6 +28,10 @@ class Host {
|
|||||||
static void registerHost(GObject*, GAsyncResult*, gpointer);
|
static void registerHost(GObject*, GAsyncResult*, gpointer);
|
||||||
static void itemRegistered(SnWatcher*, const gchar*, gpointer);
|
static void itemRegistered(SnWatcher*, const gchar*, gpointer);
|
||||||
static void itemUnregistered(SnWatcher*, const gchar*, gpointer);
|
static void itemUnregistered(SnWatcher*, const gchar*, gpointer);
|
||||||
|
void itemReady(Item&);
|
||||||
|
void itemInvalidated(Item&);
|
||||||
|
void removeItem(std::vector<std::unique_ptr<Item>>::iterator);
|
||||||
|
void clearItems();
|
||||||
|
|
||||||
std::tuple<std::string, std::string> getBusNameAndObjectPath(const std::string);
|
std::tuple<std::string, std::string> getBusNameAndObjectPath(const std::string);
|
||||||
void addRegisteredItem(const std::string& service);
|
void addRegisteredItem(const std::string& service);
|
||||||
@@ -43,6 +47,7 @@ class Host {
|
|||||||
const Bar& bar_;
|
const Bar& bar_;
|
||||||
const std::function<void(std::unique_ptr<Item>&)> on_add_;
|
const std::function<void(std::unique_ptr<Item>&)> on_add_;
|
||||||
const std::function<void(std::unique_ptr<Item>&)> on_remove_;
|
const std::function<void(std::unique_ptr<Item>&)> on_remove_;
|
||||||
|
const std::function<void()> on_update_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace waybar::modules::SNI
|
} // namespace waybar::modules::SNI
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include <libdbusmenu-gtk/dbusmenu-gtk.h>
|
#include <libdbusmenu-gtk/dbusmenu-gtk.h>
|
||||||
#include <sigc++/trackable.h>
|
#include <sigc++/trackable.h>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
@@ -25,9 +26,13 @@ struct ToolTip {
|
|||||||
|
|
||||||
class Item : public sigc::trackable {
|
class Item : public sigc::trackable {
|
||||||
public:
|
public:
|
||||||
Item(const std::string&, const std::string&, const Json::Value&, const Bar&);
|
Item(const std::string&, const std::string&, const Json::Value&, const Bar&,
|
||||||
|
const std::function<void(Item&)>&, const std::function<void(Item&)>&,
|
||||||
|
const std::function<void()>&);
|
||||||
~Item();
|
~Item();
|
||||||
|
|
||||||
|
bool isReady() const;
|
||||||
|
|
||||||
std::string bus_name;
|
std::string bus_name;
|
||||||
std::string object_path;
|
std::string object_path;
|
||||||
|
|
||||||
@@ -43,7 +48,9 @@ class Item : public sigc::trackable {
|
|||||||
Glib::RefPtr<Gdk::Pixbuf> icon_pixmap;
|
Glib::RefPtr<Gdk::Pixbuf> icon_pixmap;
|
||||||
Glib::RefPtr<Gtk::IconTheme> icon_theme;
|
Glib::RefPtr<Gtk::IconTheme> icon_theme;
|
||||||
std::string overlay_icon_name;
|
std::string overlay_icon_name;
|
||||||
|
Glib::RefPtr<Gdk::Pixbuf> overlay_icon_pixmap;
|
||||||
std::string attention_icon_name;
|
std::string attention_icon_name;
|
||||||
|
Glib::RefPtr<Gdk::Pixbuf> attention_icon_pixmap;
|
||||||
std::string attention_movie_name;
|
std::string attention_movie_name;
|
||||||
std::string icon_theme_path;
|
std::string icon_theme_path;
|
||||||
std::string menu;
|
std::string menu;
|
||||||
@@ -62,6 +69,8 @@ class Item : public sigc::trackable {
|
|||||||
void proxyReady(Glib::RefPtr<Gio::AsyncResult>& result);
|
void proxyReady(Glib::RefPtr<Gio::AsyncResult>& result);
|
||||||
void setProperty(const Glib::ustring& name, Glib::VariantBase& value);
|
void setProperty(const Glib::ustring& name, Glib::VariantBase& value);
|
||||||
void setStatus(const Glib::ustring& value);
|
void setStatus(const Glib::ustring& value);
|
||||||
|
void setReady();
|
||||||
|
void invalidate();
|
||||||
void setCustomIcon(const std::string& id);
|
void setCustomIcon(const std::string& id);
|
||||||
void getUpdatedProperties();
|
void getUpdatedProperties();
|
||||||
void processUpdatedProperties(Glib::RefPtr<Gio::AsyncResult>& result);
|
void processUpdatedProperties(Glib::RefPtr<Gio::AsyncResult>& result);
|
||||||
@@ -69,8 +78,13 @@ class Item : public sigc::trackable {
|
|||||||
const Glib::VariantContainerBase& arguments);
|
const Glib::VariantContainerBase& arguments);
|
||||||
|
|
||||||
void updateImage();
|
void updateImage();
|
||||||
Glib::RefPtr<Gdk::Pixbuf> extractPixBuf(GVariant* variant);
|
static Glib::RefPtr<Gdk::Pixbuf> extractPixBuf(GVariant* variant);
|
||||||
Glib::RefPtr<Gdk::Pixbuf> getIconPixbuf();
|
Glib::RefPtr<Gdk::Pixbuf> getIconPixbuf();
|
||||||
|
Glib::RefPtr<Gdk::Pixbuf> getAttentionIconPixbuf();
|
||||||
|
Glib::RefPtr<Gdk::Pixbuf> getOverlayIconPixbuf();
|
||||||
|
Glib::RefPtr<Gdk::Pixbuf> loadIconFromNameOrFile(const std::string& name, bool log_failure);
|
||||||
|
static Glib::RefPtr<Gdk::Pixbuf> overlayPixbufs(const Glib::RefPtr<Gdk::Pixbuf>&,
|
||||||
|
const Glib::RefPtr<Gdk::Pixbuf>&);
|
||||||
Glib::RefPtr<Gdk::Pixbuf> getIconByName(const std::string& name, int size);
|
Glib::RefPtr<Gdk::Pixbuf> getIconByName(const std::string& name, int size);
|
||||||
double getScaledIconSize();
|
double getScaledIconSize();
|
||||||
static void onMenuDestroyed(Item* self, GObject* old_menu_pointer);
|
static void onMenuDestroyed(Item* self, GObject* old_menu_pointer);
|
||||||
@@ -86,8 +100,13 @@ class Item : public sigc::trackable {
|
|||||||
gdouble distance_scrolled_y_ = 0;
|
gdouble distance_scrolled_y_ = 0;
|
||||||
// visibility of items with Status == Passive
|
// visibility of items with Status == Passive
|
||||||
bool show_passive_ = false;
|
bool show_passive_ = false;
|
||||||
|
bool ready_ = false;
|
||||||
|
Glib::ustring status_ = "active";
|
||||||
|
|
||||||
const Bar& bar_;
|
const Bar& bar_;
|
||||||
|
const std::function<void(Item&)> on_ready_;
|
||||||
|
const std::function<void(Item&)> on_invalidate_;
|
||||||
|
const std::function<void()> on_updated_;
|
||||||
|
|
||||||
Glib::RefPtr<Gio::DBus::Proxy> proxy_;
|
Glib::RefPtr<Gio::DBus::Proxy> proxy_;
|
||||||
Glib::RefPtr<Gio::Cancellable> cancellable_;
|
Glib::RefPtr<Gio::Cancellable> cancellable_;
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ class Tray : public AModule {
|
|||||||
private:
|
private:
|
||||||
void onAdd(std::unique_ptr<Item>& item);
|
void onAdd(std::unique_ptr<Item>& item);
|
||||||
void onRemove(std::unique_ptr<Item>& item);
|
void onRemove(std::unique_ptr<Item>& item);
|
||||||
|
void queueUpdate();
|
||||||
|
|
||||||
static inline std::size_t nb_hosts_ = 0;
|
static inline std::size_t nb_hosts_ = 0;
|
||||||
bool show_passive_ = false;
|
bool show_passive_ = false;
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ namespace waybar::modules::SNI {
|
|||||||
|
|
||||||
Host::Host(const std::size_t id, const Json::Value& config, const Bar& bar,
|
Host::Host(const std::size_t id, const Json::Value& config, const Bar& bar,
|
||||||
const std::function<void(std::unique_ptr<Item>&)>& on_add,
|
const std::function<void(std::unique_ptr<Item>&)>& on_add,
|
||||||
const std::function<void(std::unique_ptr<Item>&)>& on_remove)
|
const std::function<void(std::unique_ptr<Item>&)>& on_remove,
|
||||||
|
const std::function<void()>& on_update)
|
||||||
: bus_name_("org.kde.StatusNotifierHost-" + std::to_string(getpid()) + "-" +
|
: bus_name_("org.kde.StatusNotifierHost-" + std::to_string(getpid()) + "-" +
|
||||||
std::to_string(id)),
|
std::to_string(id)),
|
||||||
object_path_("/StatusNotifierHost/" + std::to_string(id)),
|
object_path_("/StatusNotifierHost/" + std::to_string(id)),
|
||||||
@@ -17,7 +18,8 @@ Host::Host(const std::size_t id, const Json::Value& config, const Bar& bar,
|
|||||||
config_(config),
|
config_(config),
|
||||||
bar_(bar),
|
bar_(bar),
|
||||||
on_add_(on_add),
|
on_add_(on_add),
|
||||||
on_remove_(on_remove) {}
|
on_remove_(on_remove),
|
||||||
|
on_update_(on_update) {}
|
||||||
|
|
||||||
Host::~Host() {
|
Host::~Host() {
|
||||||
if (bus_name_id_ > 0) {
|
if (bus_name_id_ > 0) {
|
||||||
@@ -54,7 +56,7 @@ void Host::nameVanished(const Glib::RefPtr<Gio::DBus::Connection>& conn, const G
|
|||||||
g_cancellable_cancel(cancellable_);
|
g_cancellable_cancel(cancellable_);
|
||||||
g_clear_object(&cancellable_);
|
g_clear_object(&cancellable_);
|
||||||
g_clear_object(&watcher_);
|
g_clear_object(&watcher_);
|
||||||
items_.clear();
|
clearItems();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Host::proxyReady(GObject* src, GAsyncResult* res, gpointer data) {
|
void Host::proxyReady(GObject* src, GAsyncResult* res, gpointer data) {
|
||||||
@@ -117,13 +119,50 @@ void Host::itemUnregistered(SnWatcher* watcher, const gchar* service, gpointer d
|
|||||||
auto [bus_name, object_path] = host->getBusNameAndObjectPath(service);
|
auto [bus_name, object_path] = host->getBusNameAndObjectPath(service);
|
||||||
for (auto it = host->items_.begin(); it != host->items_.end(); ++it) {
|
for (auto it = host->items_.begin(); it != host->items_.end(); ++it) {
|
||||||
if ((*it)->bus_name == bus_name && (*it)->object_path == object_path) {
|
if ((*it)->bus_name == bus_name && (*it)->object_path == object_path) {
|
||||||
host->on_remove_(*it);
|
host->removeItem(it);
|
||||||
host->items_.erase(it);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Host::itemReady(Item& item) {
|
||||||
|
auto it = std::find_if(items_.begin(), items_.end(),
|
||||||
|
[&item](const auto& candidate) { return candidate.get() == &item; });
|
||||||
|
if (it != items_.end() && (*it)->isReady()) {
|
||||||
|
on_add_(*it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Host::itemInvalidated(Item& item) {
|
||||||
|
auto it = std::find_if(items_.begin(), items_.end(),
|
||||||
|
[&item](const auto& candidate) { return candidate.get() == &item; });
|
||||||
|
if (it != items_.end()) {
|
||||||
|
removeItem(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Host::removeItem(std::vector<std::unique_ptr<Item>>::iterator it) {
|
||||||
|
if ((*it)->isReady()) {
|
||||||
|
on_remove_(*it);
|
||||||
|
}
|
||||||
|
items_.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Host::clearItems() {
|
||||||
|
bool removed_ready_item = false;
|
||||||
|
for (auto& item : items_) {
|
||||||
|
if (item->isReady()) {
|
||||||
|
on_remove_(item);
|
||||||
|
removed_ready_item = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool had_items = !items_.empty();
|
||||||
|
items_.clear();
|
||||||
|
if (had_items && !removed_ready_item) {
|
||||||
|
on_update_();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::tuple<std::string, std::string> Host::getBusNameAndObjectPath(const std::string service) {
|
std::tuple<std::string, std::string> Host::getBusNameAndObjectPath(const std::string service) {
|
||||||
auto it = service.find('/');
|
auto it = service.find('/');
|
||||||
if (it != std::string::npos) {
|
if (it != std::string::npos) {
|
||||||
@@ -139,8 +178,9 @@ void Host::addRegisteredItem(const std::string& service) {
|
|||||||
return bus_name == item->bus_name && object_path == item->object_path;
|
return bus_name == item->bus_name && object_path == item->object_path;
|
||||||
});
|
});
|
||||||
if (it == items_.end()) {
|
if (it == items_.end()) {
|
||||||
items_.emplace_back(new Item(bus_name, object_path, config_, bar_));
|
items_.emplace_back(new Item(
|
||||||
on_add_(items_.back());
|
bus_name, object_path, config_, bar_, [this](Item& item) { itemReady(item); },
|
||||||
|
[this](Item& item) { itemInvalidated(item); }, on_update_));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <gtkmm/tooltip.h>
|
#include <gtkmm/tooltip.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <map>
|
#include <map>
|
||||||
@@ -37,13 +38,18 @@ namespace waybar::modules::SNI {
|
|||||||
static const Glib::ustring SNI_INTERFACE_NAME = sn_item_interface_info()->name;
|
static const Glib::ustring SNI_INTERFACE_NAME = sn_item_interface_info()->name;
|
||||||
static const unsigned UPDATE_DEBOUNCE_TIME = 10;
|
static const unsigned UPDATE_DEBOUNCE_TIME = 10;
|
||||||
|
|
||||||
Item::Item(const std::string& bn, const std::string& op, const Json::Value& config, const Bar& bar)
|
Item::Item(const std::string& bn, const std::string& op, const Json::Value& config, const Bar& bar,
|
||||||
|
const std::function<void(Item&)>& on_ready,
|
||||||
|
const std::function<void(Item&)>& on_invalidate, const std::function<void()>& on_updated)
|
||||||
: bus_name(bn),
|
: bus_name(bn),
|
||||||
object_path(op),
|
object_path(op),
|
||||||
icon_size(16),
|
icon_size(16),
|
||||||
effective_icon_size(0),
|
effective_icon_size(0),
|
||||||
icon_theme(Gtk::IconTheme::create()),
|
icon_theme(Gtk::IconTheme::create()),
|
||||||
bar_(bar) {
|
bar_(bar),
|
||||||
|
on_ready_(on_ready),
|
||||||
|
on_invalidate_(on_invalidate),
|
||||||
|
on_updated_(on_updated) {
|
||||||
if (config["icon-size"].isUInt()) {
|
if (config["icon-size"].isUInt()) {
|
||||||
icon_size = config["icon-size"].asUInt();
|
icon_size = config["icon-size"].asUInt();
|
||||||
}
|
}
|
||||||
@@ -85,6 +91,8 @@ Item::~Item() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Item::isReady() const { return ready_; }
|
||||||
|
|
||||||
bool Item::handleMouseEnter(GdkEventCrossing* const& e) {
|
bool Item::handleMouseEnter(GdkEventCrossing* const& e) {
|
||||||
event_box.set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT);
|
event_box.set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT);
|
||||||
return false;
|
return false;
|
||||||
@@ -112,14 +120,18 @@ void Item::proxyReady(Glib::RefPtr<Gio::AsyncResult>& result) {
|
|||||||
|
|
||||||
if (this->id.empty() || this->category.empty()) {
|
if (this->id.empty() || this->category.empty()) {
|
||||||
spdlog::error("Invalid Status Notifier Item: {}, {}", bus_name, object_path);
|
spdlog::error("Invalid Status Notifier Item: {}, {}", bus_name, object_path);
|
||||||
|
invalidate();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->updateImage();
|
this->updateImage();
|
||||||
|
setReady();
|
||||||
|
|
||||||
} catch (const Glib::Error& err) {
|
} catch (const Glib::Error& err) {
|
||||||
spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, err.what());
|
spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, err.what());
|
||||||
|
invalidate();
|
||||||
} catch (const std::exception& err) {
|
} catch (const std::exception& err) {
|
||||||
spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, err.what());
|
spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, err.what());
|
||||||
|
invalidate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,11 +196,11 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) {
|
|||||||
} else if (name == "OverlayIconName") {
|
} else if (name == "OverlayIconName") {
|
||||||
overlay_icon_name = get_variant<std::string>(value);
|
overlay_icon_name = get_variant<std::string>(value);
|
||||||
} else if (name == "OverlayIconPixmap") {
|
} else if (name == "OverlayIconPixmap") {
|
||||||
// TODO: overlay_icon_pixmap
|
overlay_icon_pixmap = extractPixBuf(value.gobj());
|
||||||
} else if (name == "AttentionIconName") {
|
} else if (name == "AttentionIconName") {
|
||||||
attention_icon_name = get_variant<std::string>(value);
|
attention_icon_name = get_variant<std::string>(value);
|
||||||
} else if (name == "AttentionIconPixmap") {
|
} else if (name == "AttentionIconPixmap") {
|
||||||
// TODO: attention_icon_pixmap
|
attention_icon_pixmap = extractPixBuf(value.gobj());
|
||||||
} else if (name == "AttentionMovieName") {
|
} else if (name == "AttentionMovieName") {
|
||||||
attention_movie_name = get_variant<std::string>(value);
|
attention_movie_name = get_variant<std::string>(value);
|
||||||
} else if (name == "ToolTip") {
|
} else if (name == "ToolTip") {
|
||||||
@@ -217,18 +229,35 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Item::setStatus(const Glib::ustring& value) {
|
void Item::setStatus(const Glib::ustring& value) {
|
||||||
Glib::ustring lower = value.lowercase();
|
status_ = value.lowercase();
|
||||||
event_box.set_visible(show_passive_ || lower.compare("passive") != 0);
|
event_box.set_visible(show_passive_ || status_.compare("passive") != 0);
|
||||||
|
|
||||||
auto style = event_box.get_style_context();
|
auto style = event_box.get_style_context();
|
||||||
for (const auto& class_name : style->list_classes()) {
|
for (const auto& class_name : style->list_classes()) {
|
||||||
style->remove_class(class_name);
|
style->remove_class(class_name);
|
||||||
}
|
}
|
||||||
if (lower.compare("needsattention") == 0) {
|
auto css_class = status_;
|
||||||
|
if (css_class.compare("needsattention") == 0) {
|
||||||
// convert status to dash-case for CSS
|
// convert status to dash-case for CSS
|
||||||
lower = "needs-attention";
|
css_class = "needs-attention";
|
||||||
}
|
}
|
||||||
style->add_class(lower);
|
style->add_class(css_class);
|
||||||
|
on_updated_();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Item::setReady() {
|
||||||
|
if (ready_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ready_ = true;
|
||||||
|
on_ready_(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Item::invalidate() {
|
||||||
|
if (ready_) {
|
||||||
|
ready_ = false;
|
||||||
|
}
|
||||||
|
on_invalidate_(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Item::setCustomIcon(const std::string& id) {
|
void Item::setCustomIcon(const std::string& id) {
|
||||||
@@ -287,8 +316,8 @@ void Item::processUpdatedProperties(Glib::RefPtr<Gio::AsyncResult>& _result) {
|
|||||||
static const std::map<std::string_view, std::set<std::string_view>> signal2props = {
|
static const std::map<std::string_view, std::set<std::string_view>> signal2props = {
|
||||||
{"NewTitle", {"Title"}},
|
{"NewTitle", {"Title"}},
|
||||||
{"NewIcon", {"IconName", "IconPixmap"}},
|
{"NewIcon", {"IconName", "IconPixmap"}},
|
||||||
// {"NewAttentionIcon", {"AttentionIconName", "AttentionIconPixmap", "AttentionMovieName"}},
|
{"NewAttentionIcon", {"AttentionIconName", "AttentionIconPixmap", "AttentionMovieName"}},
|
||||||
// {"NewOverlayIcon", {"OverlayIconName", "OverlayIconPixmap"}},
|
{"NewOverlayIcon", {"OverlayIconName", "OverlayIconPixmap"}},
|
||||||
{"NewIconThemePath", {"IconThemePath"}},
|
{"NewIconThemePath", {"IconThemePath"}},
|
||||||
{"NewToolTip", {"ToolTip"}},
|
{"NewToolTip", {"ToolTip"}},
|
||||||
{"NewStatus", {"Status"}},
|
{"NewStatus", {"Status"}},
|
||||||
@@ -378,36 +407,24 @@ void Item::updateImage() {
|
|||||||
pixbuf = pixbuf->scale_simple(width, scaled_icon_size, Gdk::InterpType::INTERP_BILINEAR);
|
pixbuf = pixbuf->scale_simple(width, scaled_icon_size, Gdk::InterpType::INTERP_BILINEAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pixbuf = overlayPixbufs(pixbuf, getOverlayIconPixbuf());
|
||||||
|
|
||||||
auto surface =
|
auto surface =
|
||||||
Gdk::Cairo::create_surface_from_pixbuf(pixbuf, image.get_scale_factor(), image.get_window());
|
Gdk::Cairo::create_surface_from_pixbuf(pixbuf, image.get_scale_factor(), image.get_window());
|
||||||
image.set(surface);
|
image.set(surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
Glib::RefPtr<Gdk::Pixbuf> Item::getIconPixbuf() {
|
Glib::RefPtr<Gdk::Pixbuf> Item::getIconPixbuf() {
|
||||||
if (!icon_name.empty()) {
|
if (status_ == "needsattention") {
|
||||||
try {
|
if (auto attention_pixbuf = getAttentionIconPixbuf()) {
|
||||||
std::ifstream temp(icon_name);
|
return attention_pixbuf;
|
||||||
if (temp.is_open()) {
|
|
||||||
return Gdk::Pixbuf::create_from_file(icon_name);
|
|
||||||
}
|
|
||||||
} catch (Glib::Error& e) {
|
|
||||||
// Ignore because we want to also try different methods of getting an icon.
|
|
||||||
//
|
|
||||||
// But a warning is logged, as the file apparently exists, but there was
|
|
||||||
// a failure in creating a pixbuf out of it.
|
|
||||||
|
|
||||||
spdlog::warn("Item '{}': {}", id, static_cast<std::string>(e.what()));
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Will throw if it can not find an icon.
|
|
||||||
return getIconByName(icon_name, getScaledIconSize());
|
|
||||||
} catch (Glib::Error& e) {
|
|
||||||
spdlog::trace("Item '{}': {}", id, static_cast<std::string>(e.what()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the pixmap only if an icon for the given name could not be found.
|
if (auto pixbuf = loadIconFromNameOrFile(icon_name, true)) {
|
||||||
|
return pixbuf;
|
||||||
|
}
|
||||||
|
|
||||||
if (icon_pixmap) {
|
if (icon_pixmap) {
|
||||||
return icon_pixmap;
|
return icon_pixmap;
|
||||||
}
|
}
|
||||||
@@ -422,6 +439,77 @@ Glib::RefPtr<Gdk::Pixbuf> Item::getIconPixbuf() {
|
|||||||
return getIconByName("image-missing", getScaledIconSize());
|
return getIconByName("image-missing", getScaledIconSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Glib::RefPtr<Gdk::Pixbuf> Item::getAttentionIconPixbuf() {
|
||||||
|
if (auto pixbuf = loadIconFromNameOrFile(attention_icon_name, false)) {
|
||||||
|
return pixbuf;
|
||||||
|
}
|
||||||
|
if (auto pixbuf = loadIconFromNameOrFile(attention_movie_name, false)) {
|
||||||
|
return pixbuf;
|
||||||
|
}
|
||||||
|
return attention_icon_pixmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
Glib::RefPtr<Gdk::Pixbuf> Item::getOverlayIconPixbuf() {
|
||||||
|
if (auto pixbuf = loadIconFromNameOrFile(overlay_icon_name, false)) {
|
||||||
|
return pixbuf;
|
||||||
|
}
|
||||||
|
return overlay_icon_pixmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
Glib::RefPtr<Gdk::Pixbuf> Item::loadIconFromNameOrFile(const std::string& name, bool log_failure) {
|
||||||
|
if (name.empty()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
std::ifstream temp(name);
|
||||||
|
if (temp.is_open()) {
|
||||||
|
return Gdk::Pixbuf::create_from_file(name);
|
||||||
|
}
|
||||||
|
} catch (const Glib::Error& e) {
|
||||||
|
if (log_failure) {
|
||||||
|
spdlog::warn("Item '{}': {}", id, static_cast<std::string>(e.what()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return getIconByName(name, getScaledIconSize());
|
||||||
|
} catch (const Glib::Error& e) {
|
||||||
|
if (log_failure) {
|
||||||
|
spdlog::trace("Item '{}': {}", id, static_cast<std::string>(e.what()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Glib::RefPtr<Gdk::Pixbuf> Item::overlayPixbufs(const Glib::RefPtr<Gdk::Pixbuf>& base,
|
||||||
|
const Glib::RefPtr<Gdk::Pixbuf>& overlay) {
|
||||||
|
if (!base || !overlay) {
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto composed = base->copy();
|
||||||
|
if (!composed) {
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
int overlay_target_size =
|
||||||
|
std::max(1, std::min(composed->get_width(), composed->get_height()) / 2);
|
||||||
|
auto scaled_overlay = overlay->scale_simple(overlay_target_size, overlay_target_size,
|
||||||
|
Gdk::InterpType::INTERP_BILINEAR);
|
||||||
|
if (!scaled_overlay) {
|
||||||
|
return composed;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dest_x = std::max(0, composed->get_width() - scaled_overlay->get_width());
|
||||||
|
int dest_y = std::max(0, composed->get_height() - scaled_overlay->get_height());
|
||||||
|
scaled_overlay->composite(composed, dest_x, dest_y, scaled_overlay->get_width(),
|
||||||
|
scaled_overlay->get_height(), dest_x, dest_y, 1.0, 1.0,
|
||||||
|
Gdk::InterpType::INTERP_BILINEAR, 255);
|
||||||
|
return composed;
|
||||||
|
}
|
||||||
|
|
||||||
Glib::RefPtr<Gdk::Pixbuf> Item::getIconByName(const std::string& name, int request_size) {
|
Glib::RefPtr<Gdk::Pixbuf> Item::getIconByName(const std::string& name, int request_size) {
|
||||||
if (!icon_theme_path.empty()) {
|
if (!icon_theme_path.empty()) {
|
||||||
auto icon_info = icon_theme->lookup_icon(name.c_str(), request_size,
|
auto icon_info = icon_theme->lookup_icon(name.c_str(), request_size,
|
||||||
@@ -464,6 +552,9 @@ void Item::makeMenu() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Item::handleClick(GdkEventButton* const& ev) {
|
bool Item::handleClick(GdkEventButton* const& ev) {
|
||||||
|
if (!proxy_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
auto parameters = Glib::VariantContainerBase::create_tuple(
|
auto parameters = Glib::VariantContainerBase::create_tuple(
|
||||||
{Glib::Variant<int>::create(ev->x_root + bar_.x_global),
|
{Glib::Variant<int>::create(ev->x_root + bar_.x_global),
|
||||||
Glib::Variant<int>::create(ev->y_root + bar_.y_global)});
|
Glib::Variant<int>::create(ev->y_root + bar_.y_global)});
|
||||||
@@ -491,6 +582,9 @@ bool Item::handleClick(GdkEventButton* const& ev) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Item::handleScroll(GdkEventScroll* const& ev) {
|
bool Item::handleScroll(GdkEventScroll* const& ev) {
|
||||||
|
if (!proxy_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
int dx = 0, dy = 0;
|
int dx = 0, dy = 0;
|
||||||
switch (ev->direction) {
|
switch (ev->direction) {
|
||||||
case GDK_SCROLL_UP:
|
case GDK_SCROLL_UP:
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ Tray::Tray(const std::string& id, const Bar& bar, const Json::Value& config)
|
|||||||
box_(bar.orientation, 0),
|
box_(bar.orientation, 0),
|
||||||
watcher_(SNI::Watcher::getInstance()),
|
watcher_(SNI::Watcher::getInstance()),
|
||||||
host_(nb_hosts_, config, bar, std::bind(&Tray::onAdd, this, std::placeholders::_1),
|
host_(nb_hosts_, config, bar, std::bind(&Tray::onAdd, this, std::placeholders::_1),
|
||||||
std::bind(&Tray::onRemove, this, std::placeholders::_1)) {
|
std::bind(&Tray::onRemove, this, std::placeholders::_1),
|
||||||
|
std::bind(&Tray::queueUpdate, this)) {
|
||||||
box_.set_name("tray");
|
box_.set_name("tray");
|
||||||
event_box_.add(box_);
|
event_box_.add(box_);
|
||||||
if (!id.empty()) {
|
if (!id.empty()) {
|
||||||
@@ -33,6 +34,8 @@ Tray::Tray(const std::string& id, const Bar& bar, const Json::Value& config)
|
|||||||
dp.emit();
|
dp.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Tray::queueUpdate() { dp.emit(); }
|
||||||
|
|
||||||
void Tray::onAdd(std::unique_ptr<Item>& item) {
|
void Tray::onAdd(std::unique_ptr<Item>& item) {
|
||||||
if (config_["reverse-direction"].isBool() && config_["reverse-direction"].asBool()) {
|
if (config_["reverse-direction"].isBool() && config_["reverse-direction"].asBool()) {
|
||||||
box_.pack_end(item->event_box);
|
box_.pack_end(item->event_box);
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ gboolean Watcher::handleRegisterHost(Watcher* obj, GDBusMethodInvocation* invoca
|
|||||||
if (watch != nullptr) {
|
if (watch != nullptr) {
|
||||||
g_warning("Status Notifier Host with bus name '%s' and object path '%s' is already registered",
|
g_warning("Status Notifier Host with bus name '%s' and object path '%s' is already registered",
|
||||||
bus_name, object_path);
|
bus_name, object_path);
|
||||||
sn_watcher_complete_register_item(obj->watcher_, invocation);
|
sn_watcher_complete_register_host(obj->watcher_, invocation);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
watch = gfWatchNew(GF_WATCH_TYPE_HOST, service, bus_name, object_path, obj);
|
watch = gfWatchNew(GF_WATCH_TYPE_HOST, service, bus_name, object_path, obj);
|
||||||
@@ -98,8 +98,8 @@ gboolean Watcher::handleRegisterItem(Watcher* obj, GDBusMethodInvocation* invoca
|
|||||||
}
|
}
|
||||||
auto watch = gfWatchFind(obj->items_, bus_name, object_path);
|
auto watch = gfWatchFind(obj->items_, bus_name, object_path);
|
||||||
if (watch != nullptr) {
|
if (watch != nullptr) {
|
||||||
g_warning("Status Notifier Item with bus name '%s' and object path '%s' is already registered",
|
spdlog::debug("Ignoring duplicate Status Notifier Item registration for '{}' at '{}'", bus_name,
|
||||||
bus_name, object_path);
|
object_path);
|
||||||
sn_watcher_complete_register_item(obj->watcher_, invocation);
|
sn_watcher_complete_register_item(obj->watcher_, invocation);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
@@ -158,7 +158,7 @@ void Watcher::nameVanished(GDBusConnection* connection, const char* name, gpoint
|
|||||||
watch->watcher->hosts_ = g_slist_remove(watch->watcher->hosts_, watch);
|
watch->watcher->hosts_ = g_slist_remove(watch->watcher->hosts_, watch);
|
||||||
if (watch->watcher->hosts_ == nullptr) {
|
if (watch->watcher->hosts_ == nullptr) {
|
||||||
sn_watcher_set_is_host_registered(watch->watcher->watcher_, FALSE);
|
sn_watcher_set_is_host_registered(watch->watcher->watcher_, FALSE);
|
||||||
sn_watcher_emit_host_registered(watch->watcher->watcher_);
|
sn_watcher_emit_host_unregistered(watch->watcher->watcher_);
|
||||||
}
|
}
|
||||||
} else if (watch->type == GF_WATCH_TYPE_ITEM) {
|
} else if (watch->type == GF_WATCH_TYPE_ITEM) {
|
||||||
watch->watcher->items_ = g_slist_remove(watch->watcher->items_, watch);
|
watch->watcher->items_ = g_slist_remove(watch->watcher->items_, watch);
|
||||||
@@ -167,6 +167,7 @@ void Watcher::nameVanished(GDBusConnection* connection, const char* name, gpoint
|
|||||||
sn_watcher_emit_item_unregistered(watch->watcher->watcher_, tmp);
|
sn_watcher_emit_item_unregistered(watch->watcher->watcher_, tmp);
|
||||||
g_free(tmp);
|
g_free(tmp);
|
||||||
}
|
}
|
||||||
|
gfWatchFree(watch);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Watcher::updateRegisteredItems(SnWatcher* obj) {
|
void Watcher::updateRegisteredItems(SnWatcher* obj) {
|
||||||
|
|||||||
Reference in New Issue
Block a user