From 47f052f91396a35939f732deaa56795ef0c8df9c Mon Sep 17 00:00:00 2001 From: Arif Hasanic Date: Sat, 20 Dec 2025 20:52:04 +0100 Subject: [PATCH] add popover component --- CMakeLists.txt | 1 + include/components/popover.hpp | 19 ++++++++ include/widgets/webWidget.hpp | 7 +-- src/bar/bar.cpp | 2 +- src/components/popover.cpp | 35 +++++++++++++ src/services/hyprland.cpp | 21 ++++---- src/services/notifications.cpp | 89 +++++++++++++++++++--------------- src/widgets/webWidget.cpp | 34 +------------ 8 files changed, 119 insertions(+), 89 deletions(-) create mode 100644 include/components/popover.hpp create mode 100644 src/components/popover.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6365ce7..057a467 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,7 @@ target_sources(bar_lib src/services/tray.cpp src/services/notifications.cpp src/widgets/tray.cpp + src/components/popover.cpp ) include_directories(bar_lib PRIVATE include diff --git a/include/components/popover.hpp b/include/components/popover.hpp new file mode 100644 index 0000000..6ecd684 --- /dev/null +++ b/include/components/popover.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +#include + +class Popover: public Gtk::Button { +public: + Popover(std::string icon, std::string name); + ~Popover() override; + +protected: + void on_toggle_window(); + Gtk::Popover* popover = nullptr; + void set_popover_child(Gtk::Widget& child) { + gtk_popover_set_child(popover->gobj(), child.gobj()); + } +}; \ No newline at end of file diff --git a/include/widgets/webWidget.hpp b/include/widgets/webWidget.hpp index b6f30fb..4241ca7 100644 --- a/include/widgets/webWidget.hpp +++ b/include/widgets/webWidget.hpp @@ -1,14 +1,11 @@ #pragma once +#include "components/popover.hpp" #include #include -class WebWidget : public Gtk::Button { +class WebWidget : public Popover { public: WebWidget(std::string icon, std::string title, std::string url); - ~WebWidget() override; - private: - void on_toggle_window(); - Gtk::Popover* popover = nullptr; }; diff --git a/src/bar/bar.cpp b/src/bar/bar.cpp index dd257d8..b467165 100644 --- a/src/bar/bar.cpp +++ b/src/bar/bar.cpp @@ -70,7 +70,7 @@ void Bar::setup_ui() { right_box.set_valign(Gtk::Align::CENTER); workspaceIndicator = Gtk::make_managed(hyprlandService, monitorId); - + left_box.append(*workspaceIndicator); clock.set_name("clock-label"); diff --git a/src/components/popover.cpp b/src/components/popover.cpp new file mode 100644 index 0000000..af03dd4 --- /dev/null +++ b/src/components/popover.cpp @@ -0,0 +1,35 @@ +#include "components/popover.hpp" + +#include "gtkmm/label.h" +#include "gtkmm/object.h" + +Popover::Popover(std::string icon, std::string name) { + auto label = Gtk::make_managed(icon); + label->add_css_class("icon-label"); + set_child(*label); + signal_clicked().connect( + sigc::mem_fun(*this, &Popover::on_toggle_window)); + + popover = new Gtk::Popover(); + popover->set_parent(*this); + popover->set_autohide(true); + + popover->signal_closed().connect([this]() { + this->add_css_class("minimized"); + this->remove_css_class("restored"); + }); +} + +Popover::~Popover() { + delete popover; +} + +void Popover::on_toggle_window() { + if (popover->get_visible()) { + popover->popdown(); + } else { + this->remove_css_class("minimized"); + this->add_css_class("restored"); + popover->popup(); + } +} diff --git a/src/services/hyprland.cpp b/src/services/hyprland.cpp index 8d79317..98ffbf3 100644 --- a/src/services/hyprland.cpp +++ b/src/services/hyprland.cpp @@ -31,7 +31,7 @@ HyprlandService::~HyprlandService() { } void HyprlandService::on_hyprland_event(std::string event, std::string data) { - + if (event == "urgent") { onUrgentEvent(data); } @@ -39,7 +39,7 @@ void HyprlandService::on_hyprland_event(std::string event, std::string data) { if (event == "activewindowv2") { onActiveWindowEvent(data); } - + if (event == "workspace" || event == "movewindow") { refresh_workspaces(); } @@ -47,7 +47,7 @@ void HyprlandService::on_hyprland_event(std::string event, std::string data) { // use for // event == "focusedmon" - if (event == "monitoradded" || event == "monitorremoved") { + if (event == "monitoradded" || event == "monitorremoved") { refresh_monitors(); } } @@ -179,9 +179,9 @@ void HyprlandService::refresh_monitors() { wsState.label = std::to_string(slot); wsState.monitorId = monitor.id; - int id = slot + monitor.id * HyprlandService::kWorkspaceSlotCount; - wsState.hyprId = id; - this->workspaces[id] = new WorkspaceState(wsState); + int id = slot + monitor.id * HyprlandService::kWorkspaceSlotCount; + wsState.hyprId = id; + this->workspaces[id] = new WorkspaceState(wsState); if (monitor.id >= 0) { this->monitors[monitor.id].workspaceStates[slot] = this->workspaces[id]; } @@ -220,14 +220,14 @@ void HyprlandService::refresh_workspaces() { } for (const auto &workspaceJson : workspacesJson) { - const int workspaceId = workspaceJson.value("id", -1); + const int workspaceId = workspaceJson.value("id", -1); auto workspaceStateIt = this->workspaces.find(workspaceId); if (workspaceStateIt == this->workspaces.end()) { continue; } WorkspaceState *workspaceState = workspaceStateIt->second; - auto mit = this->monitors.find(workspaceState->monitorId); + auto mit = this->monitors.find(workspaceState->monitorId); if (mit != this->monitors.end()) { workspaceState->focused = mit->second.focusedWorkspaceId == workspaceId; } else { @@ -270,7 +270,6 @@ void HyprlandService::onUrgentEvent(std::string windowAddress) { it->second->urgentWindows.push_back(windowAddress); workspaceStateChanged.emit(); } - } break; @@ -287,10 +286,10 @@ void HyprlandService::onActiveWindowEvent(std::string windowAddress) { if (addr == "0x" + windowAddress) { int workspaceId = clientJson["workspace"]["id"]; - auto it = this->workspaces.find(workspaceId); + auto it = this->workspaces.find(workspaceId); if (it != this->workspaces.end() && it->second) { WorkspaceState *ws = it->second; - auto uit = std::find(ws->urgentWindows.begin(), ws->urgentWindows.end(), windowAddress); + auto uit = std::find(ws->urgentWindows.begin(), ws->urgentWindows.end(), windowAddress); if (uit != ws->urgentWindows.end()) { ws->urgentWindows.erase(uit); workspaceStateChanged.emit(); diff --git a/src/services/notifications.cpp b/src/services/notifications.cpp index f7412e1..9f5b24c 100644 --- a/src/services/notifications.cpp +++ b/src/services/notifications.cpp @@ -3,10 +3,10 @@ #include #include -static constexpr const char* kNotificationsObjectPath = "/org/freedesktop/Notifications"; -static constexpr const char* kNotificationsInterface = "org.freedesktop.Notifications"; +static constexpr const char *kNotificationsObjectPath = "/org/freedesktop/Notifications"; +static constexpr const char *kNotificationsInterface = "org.freedesktop.Notifications"; -static const char* kNotificationsIntrospectionXml = R"XML( +static const char *kNotificationsIntrospectionXml = R"XML( @@ -47,15 +47,15 @@ static const char* kNotificationsIntrospectionXml = R"XML( )XML"; -static void on_method_call(GDBusConnection* /*connection*/, - const gchar* /*sender*/, - const gchar* /*object_path*/, - const gchar* interface_name, - const gchar* method_name, - GVariant* parameters, - GDBusMethodInvocation* invocation, +static void on_method_call(GDBusConnection * /*connection*/, + const gchar * /*sender*/, + const gchar * /*object_path*/, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, gpointer user_data) { - auto* self = static_cast(user_data); + auto *self = static_cast(user_data); if (g_strcmp0(interface_name, kNotificationsInterface) != 0) { g_dbus_method_invocation_return_dbus_error( @@ -66,13 +66,13 @@ static void on_method_call(GDBusConnection* /*connection*/, } if (g_strcmp0(method_name, "Notify") == 0) { - const gchar* app_name = ""; - guint32 replaces_id = 0; - const gchar* app_icon = ""; - const gchar* summary = ""; - const gchar* body = ""; - GVariant* actions = nullptr; - GVariant* hints = nullptr; + const gchar *app_name = ""; + guint32 replaces_id = 0; + const gchar *app_icon = ""; + const gchar *summary = ""; + const gchar *body = ""; + GVariant *actions = nullptr; + GVariant *hints = nullptr; gint32 expire_timeout = -1; g_variant_get(parameters, "(&su&s&s&s@as@a{sv}i)", @@ -90,8 +90,10 @@ static void on_method_call(GDBusConnection* /*connection*/, std::cout << "Title: " << (summary ? summary : "") << std::endl; std::cout << "Body: " << (body ? body : "") << std::endl; - if (actions) g_variant_unref(actions); - if (hints) g_variant_unref(hints); + if (actions) + g_variant_unref(actions); + if (hints) + g_variant_unref(hints); guint32 id = self->allocateNotificationId(replaces_id); g_dbus_method_invocation_return_value(invocation, g_variant_new("(u)", id)); @@ -101,15 +103,14 @@ static void on_method_call(GDBusConnection* /*connection*/, if (g_strcmp0(method_name, "GetCapabilities") == 0) { // Advertise common capabilities so clients don't disable notifications. // (Many apps probe this first and may skip Notify if it's empty.) - const gchar* caps[] = { + const gchar *caps[] = { "body", "actions", "body-markup", "icon-static", "persistence", - nullptr - }; - GVariant* capsV = g_variant_new_strv(caps, -1); + nullptr}; + GVariant *capsV = g_variant_new_strv(caps, -1); g_dbus_method_invocation_return_value(invocation, g_variant_new("(@as)", capsV)); return; } @@ -128,7 +129,7 @@ static void on_method_call(GDBusConnection* /*connection*/, // reason: 3 = closed by call to CloseNotification if (self && self->getConnection()) { g_dbus_connection_emit_signal( - self->getConnection(), + self->getConnection(), nullptr, kNotificationsObjectPath, kNotificationsInterface, @@ -148,12 +149,13 @@ static void on_method_call(GDBusConnection* /*connection*/, } guint32 NotificationService::allocateNotificationId(guint32 replacesId) { - if (replacesId != 0) return replacesId; + if (replacesId != 0) + return replacesId; return this->nextNotificationId++; } static const GDBusInterfaceVTable kVTable = { - .method_call = on_method_call, + .method_call = on_method_call, .get_property = nullptr, .set_property = nullptr, }; @@ -162,8 +164,8 @@ NotificationService::~NotificationService() { if (this->connection) { // Best-effort release of the well-known name. { - GError* error = nullptr; - GVariant* releaseResult = g_dbus_connection_call_sync( + GError *error = nullptr; + GVariant *releaseResult = g_dbus_connection_call_sync( this->connection, "org.freedesktop.DBus", "/org/freedesktop/DBus", @@ -175,8 +177,10 @@ NotificationService::~NotificationService() { -1, nullptr, &error); - if (releaseResult) g_variant_unref(releaseResult); - if (error) g_error_free(error); + if (releaseResult) + g_variant_unref(releaseResult); + if (error) + g_error_free(error); } if (this->registrationId != 0) { @@ -195,16 +199,17 @@ NotificationService::~NotificationService() { } void NotificationService::intialize() { - GError* error = nullptr; + GError *error = nullptr; if (this->connection) { return; } - gchar* address = g_dbus_address_get_for_bus_sync(G_BUS_TYPE_SESSION, nullptr, &error); + gchar *address = g_dbus_address_get_for_bus_sync(G_BUS_TYPE_SESSION, nullptr, &error); if (!address) { std::cerr << "Failed to get session bus address: " << (error ? error->message : "unknown error") << std::endl; - if (error) g_error_free(error); + if (error) + g_error_free(error); return; } @@ -225,7 +230,8 @@ void NotificationService::intialize() { if (!this->connection) { std::cerr << "Failed to connect to session bus: " << (error ? error->message : "unknown error") << std::endl; - if (error) g_error_free(error); + if (error) + g_error_free(error); return; } @@ -237,11 +243,12 @@ void NotificationService::intialize() { this->nodeInfo = g_dbus_node_info_new_for_xml(kNotificationsIntrospectionXml, &error); if (!this->nodeInfo) { std::cerr << "Failed to create introspection data: " << (error ? error->message : "unknown error") << std::endl; - if (error) g_error_free(error); + if (error) + g_error_free(error); return; } - GDBusInterfaceInfo* iface = g_dbus_node_info_lookup_interface(this->nodeInfo, kNotificationsInterface); + GDBusInterfaceInfo *iface = g_dbus_node_info_lookup_interface(this->nodeInfo, kNotificationsInterface); if (!iface) { std::cerr << "Missing interface info for org.freedesktop.Notifications" << std::endl; return; @@ -258,13 +265,14 @@ void NotificationService::intialize() { if (this->registrationId == 0) { std::cerr << "Failed to register notifications object: " << (error ? error->message : "unknown error") << std::endl; - if (error) g_error_free(error); + if (error) + g_error_free(error); return; } // Request the well-known name synchronously so we can detect conflicts. // Reply codes: 1=PRIMARY_OWNER, 2=IN_QUEUE, 3=EXISTS, 4=ALREADY_OWNER - GVariant* requestResult = g_dbus_connection_call_sync( + GVariant *requestResult = g_dbus_connection_call_sync( this->connection, "org.freedesktop.DBus", "/org/freedesktop/DBus", @@ -280,7 +288,8 @@ void NotificationService::intialize() { if (!requestResult) { std::cerr << "Failed to RequestName(org.freedesktop.Notifications): " << (error ? error->message : "unknown error") << std::endl; - if (error) g_error_free(error); + if (error) + g_error_free(error); return; } diff --git a/src/widgets/webWidget.cpp b/src/widgets/webWidget.cpp index e3db8ad..c497c0e 100644 --- a/src/widgets/webWidget.cpp +++ b/src/widgets/webWidget.cpp @@ -3,23 +3,7 @@ #include #include -WebWidget::WebWidget(std::string icon, std::string title, std::string url) { - auto label = Gtk::make_managed(icon); - label->add_css_class("icon-label"); - set_child(*label); - - signal_clicked().connect( - sigc::mem_fun(*this, &WebWidget::on_toggle_window)); - - popover = new Gtk::Popover(); - popover->set_parent(*this); - popover->set_autohide(true); - - popover->signal_closed().connect([this]() { - this->add_css_class("minimized"); - this->remove_css_class("restored"); - }); - +WebWidget::WebWidget(std::string icon, std::string name, std::string url) : Popover(icon, name) { auto webview = webkit_web_view_new(); gtk_widget_set_hexpand(webview, true); gtk_widget_set_vexpand(webview, true); @@ -31,19 +15,5 @@ WebWidget::WebWidget(std::string icon, std::string title, std::string url) { webkit_web_view_load_uri(WEBKIT_WEB_VIEW(webview), url.c_str()); - gtk_popover_set_child(popover->gobj(), webview); -} - -WebWidget::~WebWidget() { - delete popover; -} - -void WebWidget::on_toggle_window() { - if (popover->get_visible()) { - popover->popdown(); - } else { - popover->popup(); - this->remove_css_class("minimized"); - this->add_css_class("restored"); - } + this->set_popover_child(*Glib::wrap(webview)); }