From 7fac2afb85dfa4081a373564e0a21b072157a1be Mon Sep 17 00:00:00 2001 From: Erik Reider <35975961+ErikReider@users.noreply.github.com> Date: Sat, 19 Mar 2022 17:09:55 +0100 Subject: [PATCH] Added custom tooltip with device icon, device name and battery status --- include/modules/upower/upower.hpp | 11 +- include/modules/upower/upower_tooltip.hpp | 29 ++++ meson.build | 1 + src/factory.cpp | 2 +- src/modules/upower/upower.cpp | 46 +++++-- src/modules/upower/upower_tooltip.cpp | 158 ++++++++++++++++++++++ 6 files changed, 229 insertions(+), 18 deletions(-) create mode 100644 include/modules/upower/upower_tooltip.hpp create mode 100644 src/modules/upower/upower_tooltip.cpp diff --git a/include/modules/upower/upower.hpp b/include/modules/upower/upower.hpp index 6145a15b..c73a0728 100644 --- a/include/modules/upower/upower.hpp +++ b/include/modules/upower/upower.hpp @@ -10,8 +10,9 @@ #include "gtkmm/box.h" #include "gtkmm/image.h" #include "gtkmm/label.h" +#include "modules/upower/upower_tooltip.hpp" -namespace waybar::modules { +namespace waybar::modules::upower { class UPower : public AModule { public: @@ -19,9 +20,9 @@ class UPower : public AModule { ~UPower(); auto update() -> void; + private: typedef std::unordered_map Devices; - private: static void deviceAdded_cb(UpClient *client, UpDevice *device, gpointer data); static void deviceRemoved_cb(UpClient *client, const gchar *objectPath, gpointer data); static void deviceNotify_cb(UpDevice *device, GParamSpec *pspec, gpointer user_data); @@ -34,6 +35,7 @@ class UPower : public AModule { void setDisplayDevice(); void resetDevices(); void removeDevices(); + bool show_tooltip_callback(int, int, bool, const Glib::RefPtr &tooltip); Gtk::Box box_; Gtk::Image icon_; @@ -41,6 +43,8 @@ class UPower : public AModule { // Config bool hideIfEmpty = true; + bool tooltip_enabled = true; + uint tooltip_spacing = 4; uint iconSize = 20; Devices devices; @@ -49,6 +53,7 @@ class UPower : public AModule { UpDevice *displayDevice; guint login1_id; GDBusConnection *login1_connection; + UPowerTooltip *upower_tooltip; }; -} // namespace waybar::modules +} // namespace waybar::modules::upower diff --git a/include/modules/upower/upower_tooltip.hpp b/include/modules/upower/upower_tooltip.hpp new file mode 100644 index 00000000..09f6e60b --- /dev/null +++ b/include/modules/upower/upower_tooltip.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include "gtkmm/box.h" +#include "gtkmm/label.h" +#include "gtkmm/window.h" + +namespace waybar::modules::upower { + +class UPowerTooltip : public Gtk::Window { + private: + typedef std::unordered_map Devices; + + std::string getDeviceIcon(UpDeviceKind& kind); + + Gtk::Box* contentBox; + + uint iconSize; + uint tooltipSpacing; + + public: + UPowerTooltip(uint iconSize, uint tooltipSpacing); + ~UPowerTooltip(); + + uint updateTooltip(Devices& devices); +}; + +} // namespace waybar::modules::upower diff --git a/meson.build b/meson.build index 5e76181f..e9daeca6 100644 --- a/meson.build +++ b/meson.build @@ -207,6 +207,7 @@ endif if (upower_glib.found() and giounix.found() and not get_option('logind').disabled()) add_project_arguments('-DHAVE_UPOWER', language: 'cpp') src_files += 'src/modules/upower/upower.cpp' + src_files += 'src/modules/upower/upower_tooltip.cpp' endif if libpulse.found() diff --git a/src/factory.cpp b/src/factory.cpp index a866c373..ab0dc435 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -14,7 +14,7 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { #endif #ifdef HAVE_UPOWER if (ref == "upower") { - return new waybar::modules::UPower(id, config_[name]); + return new waybar::modules::upower::UPower(id, config_[name]); } #endif #ifdef HAVE_SWAY diff --git a/src/modules/upower/upower.cpp b/src/modules/upower/upower.cpp index 1573796d..aef874b9 100644 --- a/src/modules/upower/upower.cpp +++ b/src/modules/upower/upower.cpp @@ -1,8 +1,11 @@ #include "modules/upower/upower.hpp" #include "gtkmm/icontheme.h" +#include "gtkmm/label.h" +#include "gtkmm/tooltip.h" +#include "modules/upower/upower_tooltip.hpp" -namespace waybar::modules { +namespace waybar::modules::upower { UPower::UPower(const std::string& id, const Json::Value& config) : AModule(config, "upower", id), box_(Gtk::ORIENTATION_HORIZONTAL, 0), @@ -26,6 +29,23 @@ UPower::UPower(const std::string& id, const Json::Value& config) hideIfEmpty = config_["hide-if-empty"].asBool(); } + // Tooltip Spacing + if (config_["tooltip-spacing"].isUInt()) { + tooltip_spacing = config_["tooltip-spacing"].asUInt(); + } + + // Tooltip + if (config_["tooltip"].isBool()) { + tooltip_enabled = config_["tooltip"].asBool(); + } + box_.set_has_tooltip(tooltip_enabled); + if (tooltip_enabled) { + // Sets the window to use when showing the tooltip + upower_tooltip = new UPowerTooltip(iconSize, tooltip_spacing); + box_.set_tooltip_window(*upower_tooltip); + box_.signal_query_tooltip().connect(sigc::mem_fun(*this, &UPower::show_tooltip_callback)); + } + GError* error = NULL; client = up_client_new_full(NULL, &error); if (client == NULL) { @@ -174,6 +194,10 @@ void UPower::resetDevices() { dp.emit(); } +bool UPower::show_tooltip_callback(int, int, bool, const Glib::RefPtr& tooltip) { + return true; +} + auto UPower::update() -> void { std::lock_guard guard(m_Mutex); @@ -204,7 +228,7 @@ auto UPower::update() -> void { std::string percentString = ""; - std::string tooltip = ""; + uint tooltipCount = 0; if (devices.size() == 0 && !displayDeviceValid && hideIfEmpty) { event_box_.set_visible(false); @@ -213,18 +237,12 @@ auto UPower::update() -> void { event_box_.set_visible(true); - // TODO: Tooltip - if (!devices.empty()) { - for (auto& e : devices) { - const gchar* objectPath = up_device_get_object_path(e.second); - double percentage; - g_object_get(e.second, "percentage", &percentage, NULL); - printf("Device: %s, VALID: %f\n", objectPath, percentage); - } - } else { - printf("No devices\n"); + // Tooltip + if (tooltip_enabled) { + tooltipCount = upower_tooltip->updateTooltip(devices); + // Disable the tooltip if there aren't any devices in the tooltip + box_.set_has_tooltip(!devices.empty() && tooltipCount > 0); } - // box_.set_tooltip // Set percentage if (displayDeviceValid) { @@ -243,4 +261,4 @@ update: AModule::update(); } -} // namespace waybar::modules +} // namespace waybar::modules::upower diff --git a/src/modules/upower/upower_tooltip.cpp b/src/modules/upower/upower_tooltip.cpp new file mode 100644 index 00000000..2bff69c0 --- /dev/null +++ b/src/modules/upower/upower_tooltip.cpp @@ -0,0 +1,158 @@ +#include "modules/upower/upower_tooltip.hpp" + +#include "gtkmm/box.h" +#include "gtkmm/enums.h" +#include "gtkmm/icontheme.h" +#include "gtkmm/image.h" +#include "gtkmm/label.h" + +namespace waybar::modules::upower { +UPowerTooltip::UPowerTooltip(uint iconSize_, uint tooltipSpacing_) + : Gtk::Window(), iconSize(iconSize_), tooltipSpacing(tooltipSpacing_) { + contentBox = new Gtk::Box(Gtk::ORIENTATION_VERTICAL); + add(*contentBox); + contentBox->show(); +} + +UPowerTooltip::~UPowerTooltip() {} + +uint UPowerTooltip::updateTooltip(Devices& devices) { + // Removes all old devices + for (auto child : contentBox->get_children()) { + child->~Widget(); + } + + uint deviceCount = 0; + // Adds all valid devices + for (auto pair : devices) { + UpDevice* device = pair.second; + std::string objectPath = pair.first; + + if (!G_IS_OBJECT(device)) continue; + + Gtk::Box* box = new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, tooltipSpacing); + + UpDeviceKind kind; + double percentage; + gchar* native_path; + gchar* model; + gchar* icon_name; + + g_object_get(device, + "kind", + &kind, + "percentage", + &percentage, + "native-path", + &native_path, + "model", + &model, + "icon-name", + &icon_name, + NULL); + + // Skip Line_Power and BAT0 devices + if (kind == UP_DEVICE_KIND_LINE_POWER || strcmp(native_path, "BAT0") == 0) continue; + + Gtk::Box* modelBox = new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL); + box->add(*modelBox); + // Set device icon + std::string deviceIconName = getDeviceIcon(kind); + Gtk::Image* deviceIcon = new Gtk::Image(); + deviceIcon->set_pixel_size(iconSize); + if (!Gtk::IconTheme::get_default()->has_icon(deviceIconName)) { + deviceIconName = "battery-missing-symbolic"; + } + deviceIcon->set_from_icon_name(deviceIconName, Gtk::ICON_SIZE_INVALID); + modelBox->add(*deviceIcon); + + // Set model + Gtk::Label* modelLabel = new Gtk::Label(model); + modelBox->add(*modelLabel); + + Gtk::Box* chargeBox = new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL); + box->add(*chargeBox); + + // Set icon + Gtk::Image* icon = new Gtk::Image(); + icon->set_pixel_size(iconSize); + if (!Gtk::IconTheme::get_default()->has_icon(icon_name)) { + icon_name = (char*)"battery-missing-symbolic"; + } + icon->set_from_icon_name(icon_name, Gtk::ICON_SIZE_INVALID); + chargeBox->add(*icon); + + // Set percentage + std::string percentString = std::to_string(int(percentage + 0.5)) + "%"; + Gtk::Label* percentLabel = new Gtk::Label(percentString); + chargeBox->add(*percentLabel); + + contentBox->add(*box); + + deviceCount++; + } + + contentBox->show_all(); + return deviceCount; +} + +std::string UPowerTooltip::getDeviceIcon(UpDeviceKind& kind) { + switch (kind) { + case UP_DEVICE_KIND_LINE_POWER: + return "ac-adapter-symbolic"; + case UP_DEVICE_KIND_BATTERY: + return "battery"; + case UP_DEVICE_KIND_UPS: + return "uninterruptible-power-supply-symbolic"; + case UP_DEVICE_KIND_MONITOR: + return "video-display-symbolic"; + case UP_DEVICE_KIND_MOUSE: + return "input-mouse-symbolic"; + case UP_DEVICE_KIND_KEYBOARD: + return "input-keyboard-symbolic"; + case UP_DEVICE_KIND_PDA: + return "pda-symbolic"; + case UP_DEVICE_KIND_PHONE: + return "phone-symbolic"; + case UP_DEVICE_KIND_MEDIA_PLAYER: + return "multimedia-player-symbolic"; + case UP_DEVICE_KIND_TABLET: + return "computer-apple-ipad-symbolic"; + case UP_DEVICE_KIND_COMPUTER: + return "computer-symbolic"; + case UP_DEVICE_KIND_GAMING_INPUT: + return "input-gaming-symbolic"; + case UP_DEVICE_KIND_PEN: + return "input-tablet-symbolic"; + case UP_DEVICE_KIND_TOUCHPAD: + return "input-touchpad-symbolic"; + case UP_DEVICE_KIND_MODEM: + return "modem-symbolic"; + case UP_DEVICE_KIND_NETWORK: + return "network-wired-symbolic"; + case UP_DEVICE_KIND_HEADSET: + return "audio-headset-symbolic"; + case UP_DEVICE_KIND_HEADPHONES: + return "audio-headphones-symbolic"; + case UP_DEVICE_KIND_OTHER_AUDIO: + case UP_DEVICE_KIND_SPEAKERS: + return "audio-speakers-symbolic"; + case UP_DEVICE_KIND_VIDEO: + return "camera-web-symbolic"; + case UP_DEVICE_KIND_PRINTER: + return "printer-symbolic"; + case UP_DEVICE_KIND_SCANNER: + return "scanner-symbolic"; + case UP_DEVICE_KIND_CAMERA: + return "camera-photo-symbolic"; + case UP_DEVICE_KIND_BLUETOOTH_GENERIC: + return "bluetooth-active-symbolic"; + case UP_DEVICE_KIND_TOY: + case UP_DEVICE_KIND_REMOTE_CONTROL: + case UP_DEVICE_KIND_WEARABLE: + case UP_DEVICE_KIND_LAST: + default: + return "battery-symbolic"; + } +} +} // namespace waybar::modules::upower