refactor and shizz

This commit is contained in:
2025-12-25 21:13:00 +01:00
parent a06c96f648
commit 9b5db719cb
27 changed files with 286 additions and 312 deletions

View File

@@ -43,6 +43,7 @@ target_sources(bar_lib
src/components/popover.cpp src/components/popover.cpp
src/components/todoEntry.cpp src/components/todoEntry.cpp
src/components/base/button.cpp
) )
include_directories(bar_lib PRIVATE include_directories(bar_lib PRIVATE
include include

View File

@@ -19,9 +19,9 @@ class App {
private: private:
Glib::RefPtr<Gtk::Application> app; Glib::RefPtr<Gtk::Application> app;
std::vector<Bar *> bars; std::vector<Bar *> bars;
HyprlandService hyprlandService; HyprlandService *hyprlandService = HyprlandService::getInstance();
NotificationService notificationService; NotificationService notificationService;
TrayService trayService; TrayService *trayService = TrayService::getInstance();
void setupServices(); void setupServices();
}; };

View File

@@ -4,8 +4,6 @@
#include <gtkmm.h> #include <gtkmm.h>
#include "icons.hpp" #include "icons.hpp"
#include "services/hyprland.hpp"
#include "services/tray.hpp"
#include "widgets/clock.hpp" #include "widgets/clock.hpp"
#include "widgets/date.hpp" #include "widgets/date.hpp"
#include "widgets/tray.hpp" #include "widgets/tray.hpp"
@@ -16,17 +14,16 @@
class Bar : public Gtk::Window { class Bar : public Gtk::Window {
public: public:
Bar(GdkMonitor *monitor, HyprlandService &hyprlandService, TrayService &trayService, int monitorId); Bar(GdkMonitor *monitor, int monitorId);
private:
int monitorId;
protected:
Gtk::CenterBox main_box{}; Gtk::CenterBox main_box{};
Gtk::Box left_box{Gtk::Orientation::HORIZONTAL}; Gtk::Box left_box{Gtk::Orientation::HORIZONTAL};
Gtk::Box center_box{Gtk::Orientation::HORIZONTAL}; Gtk::Box center_box{Gtk::Orientation::HORIZONTAL};
Gtk::Box right_box{Gtk::Orientation::HORIZONTAL}; Gtk::Box right_box{Gtk::Orientation::HORIZONTAL};
private:
int monitorId;
Clock clock; Clock clock;
Date date; Date date;
WebWidget homeAssistant{ICON_HOME, "Home Assistant", "https://home.rivercry.com"}; WebWidget homeAssistant{ICON_HOME, "Home Assistant", "https://home.rivercry.com"};
@@ -36,8 +33,6 @@ class Bar : public Gtk::Window {
TrayWidget *trayWidget = nullptr; TrayWidget *trayWidget = nullptr;
VolumeWidget *volumeWidget = nullptr; VolumeWidget *volumeWidget = nullptr;
TrayService &trayService;
HyprlandService &hyprlandService;
void setup_ui(); void setup_ui();
void setup_left_box(); void setup_left_box();

View File

@@ -0,0 +1,18 @@
#pragma once
#include <gtkmm/button.h>
#include "gtkmm/image.h"
#include "sigc++/signal.h"
class Button : public Gtk::Button {
public:
Button(const std::string label);
Button(Gtk::Image &image);
sigc::signal<void()> onClickedSignal;
private:
void on_clicked() {
onClickedSignal.emit();
}
};

View File

@@ -3,10 +3,11 @@
#include <gtkmm/button.h> #include <gtkmm/button.h>
#include <gtkmm/popover.h> #include <gtkmm/popover.h>
#include <string> #include <string>
#include "components/base/button.hpp"
class Popover : public Gtk::Button { class Popover : public Button {
public: public:
Popover(std::string icon, std::string name); Popover(const std::string icon, std::string name);
~Popover() override; ~Popover() override;
protected: protected:

View File

@@ -7,7 +7,6 @@
class TodoEntry : public Gtk::Box { class TodoEntry : public Gtk::Box {
public: public:
TodoEntry(int id, std::string text, sigc::signal<void(int)> signal_dismissed, sigc::signal<void(int, std::string)> signal_edited); TodoEntry(int id, std::string text, sigc::signal<void(int)> signal_dismissed, sigc::signal<void(int, std::string)> signal_edited);
~TodoEntry() override;
int get_id() const { return id; } int get_id() const { return id; }
std::string get_text() const { return text; } std::string get_text() const { return text; }

View File

@@ -8,15 +8,13 @@
#include "sigc++/signal.h" #include "sigc++/signal.h"
class BluetoothService { class BluetoothService {
static BluetoothService *instance; inline static BluetoothService *instance = nullptr;
public: public:
sigc::signal<void(bool)> powerStateChangedSignal; sigc::signal<void(bool)> powerStateChangedSignal;
sigc::signal<void(bool)> isDiscoveringChangedSignal; sigc::signal<void(bool)> isDiscoveringChangedSignal;
bool getPowerState(); bool getPowerState();
bool getIsDiscovering(); bool getIsDiscovering();
void togglePowerState(); void togglePowerState();

View File

@@ -5,9 +5,12 @@
#include <map> #include <map>
#include <sigc++/sigc++.h> #include <sigc++/sigc++.h>
#include <string> #include <string>
#include <sys/stat.h>
#include <vector> #include <vector>
class HyprlandService { class HyprlandService {
inline static HyprlandService *instance = nullptr;
public: public:
static constexpr int kWorkspaceSlotCount = 7; static constexpr int kWorkspaceSlotCount = 7;
const char *kMonitorCommand = "hyprctl monitors -j"; const char *kMonitorCommand = "hyprctl monitors -j";
@@ -36,9 +39,6 @@ class HyprlandService {
int focusedWorkspaceId = -1; int focusedWorkspaceId = -1;
}; };
HyprlandService();
~HyprlandService();
void start(); void start();
void on_hyprland_event(std::string event, std::string data); void on_hyprland_event(std::string event, std::string data);
void printMonitor(const Monitor &mon) const; void printMonitor(const Monitor &mon) const;
@@ -55,11 +55,21 @@ class HyprlandService {
return this->workspaces; return this->workspaces;
} }
static HyprlandService *getInstance() {
if (HyprlandService::instance == nullptr) {
HyprlandService::instance = new HyprlandService();
}
return HyprlandService::instance;
}
private: private:
HyprlandService();
~HyprlandService();
int fd = -1; int fd = -1;
std::map<int, Monitor> monitors; std::map<int, Monitor> monitors;
std::map<int, WorkspaceState *> workspaces; std::map<int, WorkspaceState *> workspaces;
// persistent buffer for socket reads to handle partial messages
std::string socket_buffer; std::string socket_buffer;
std::string get_socket_path(); std::string get_socket_path();

View File

@@ -16,6 +16,7 @@
#include <vector> #include <vector>
class TrayService { class TrayService {
inline static TrayService *instance = nullptr;
public: public:
struct Item { struct Item {
std::string id; std::string id;
@@ -29,9 +30,6 @@ class TrayService {
Glib::RefPtr<Gdk::Paintable> iconPaintable; Glib::RefPtr<Gdk::Paintable> iconPaintable;
}; };
TrayService();
~TrayService();
void start(); void start();
void stop(); void stop();
@@ -44,7 +42,6 @@ class TrayService {
Glib::RefPtr<Gio::MenuModel> get_menu_model(const std::string &id); Glib::RefPtr<Gio::MenuModel> get_menu_model(const std::string &id);
Glib::RefPtr<Gio::ActionGroup> get_menu_action_group(const std::string &id); Glib::RefPtr<Gio::ActionGroup> get_menu_action_group(const std::string &id);
void debug_dump_menu_layout(const std::string &id);
struct MenuNode { struct MenuNode {
int id = 0; int id = 0;
std::string label; std::string label;
@@ -60,7 +57,18 @@ class TrayService {
sigc::signal<void(const std::string &)> &signal_item_removed(); sigc::signal<void(const std::string &)> &signal_item_removed();
sigc::signal<void(const Item &)> &signal_item_updated(); sigc::signal<void(const Item &)> &signal_item_updated();
static TrayService *getInstance() {
if (TrayService::instance == nullptr) {
TrayService::instance = new TrayService();
}
return TrayService::instance;
}
private: private:
TrayService();
~TrayService();
struct TrackedItem { struct TrackedItem {
Item publicData; Item publicData;
guint signalSubscriptionId = 0; guint signalSubscriptionId = 0;

View File

@@ -2,6 +2,7 @@
#include <gtkmm/box.h> #include <gtkmm/box.h>
#include <gtkmm/button.h> #include <gtkmm/button.h>
#include "components/base/button.hpp"
class BluetoothWidget : public Gtk::Box { class BluetoothWidget : public Gtk::Box {
public: public:
@@ -21,11 +22,11 @@ class BluetoothWidget : public Gtk::Box {
Gtk::Box statusArea; Gtk::Box statusArea;
Gtk::Box *deviceList = nullptr; Gtk::Box *deviceList = nullptr;
Gtk::Button *scanButton = nullptr; Button *scanButton = nullptr;
Gtk::Button *powerButton = nullptr; Button *powerButton = nullptr;
void onPowerButtonClicked(); void onPowerButtonClicked();
void onScanButtonClicked(); void onScanButtonClicked();
void toggleButton(Gtk::Button *button, bool state); void toggleButton(Button *button, bool state);
}; };

View File

@@ -16,15 +16,16 @@
#include <string> #include <string>
#include "services/tray.hpp" #include "services/tray.hpp"
#include "components/base/button.hpp"
class TrayIconWidget : public Gtk::Button { class TrayIconWidget : public Button {
public: public:
TrayIconWidget(TrayService &service, std::string id); TrayIconWidget(std::string id);
void update(const TrayService::Item &item); void update(const TrayService::Item &item);
private: private:
TrayService &service; TrayService &service = *TrayService::getInstance();
std::string id; std::string id;
Gtk::Box container; Gtk::Box container;
Gtk::Picture picture; Gtk::Picture picture;
@@ -53,11 +54,11 @@ class TrayIconWidget : public Gtk::Button {
class TrayWidget : public Gtk::Box { class TrayWidget : public Gtk::Box {
public: public:
explicit TrayWidget(TrayService &service); explicit TrayWidget();
~TrayWidget() override; ~TrayWidget() override;
private: private:
TrayService &service; TrayService *service = TrayService::getInstance();
std::map<std::string, std::unique_ptr<TrayIconWidget>> icons; std::map<std::string, std::unique_ptr<TrayIconWidget>> icons;
sigc::connection addConnection; sigc::connection addConnection;

View File

@@ -8,6 +8,4 @@
class WebWidget : public Popover { class WebWidget : public Popover {
public: public:
WebWidget(std::string icon, std::string title, std::string url); WebWidget(std::string icon, std::string title, std::string url);
private:
}; };

View File

@@ -6,22 +6,23 @@
#include <sigc++/connection.h> #include <sigc++/connection.h>
#include "services/hyprland.hpp" #include "services/hyprland.hpp"
#include "gtkmm/overlay.h"
class WorkspaceIndicator : public Gtk::Box { class WorkspaceIndicator : public Gtk::Box {
public: public:
WorkspaceIndicator(HyprlandService &service, int monitorId); WorkspaceIndicator(int monitorId);
~WorkspaceIndicator() override; ~WorkspaceIndicator() override;
private: private:
HyprlandService &service; HyprlandService *service = HyprlandService::getInstance();
int monitorId; int monitorId;
sigc::connection workspaceConnection; sigc::connection workspaceConnection;
sigc::connection monitorConnection; sigc::connection monitorConnection;
std::map<int, Gtk::Label *> workspaceLabels; std::map<int, Gtk::Overlay *> workspaceIndicators;
std::map<int, Glib::RefPtr<Gtk::GestureClick>> workspaceGestures; std::map<int, Glib::RefPtr<Gtk::GestureClick>> workspaceGestures;
void rebuild(); void rebuild();
void on_workspace_update(); void on_workspace_update();
void on_monitor_update(); void on_monitor_update();
void refreshLabel(Gtk::Label *label, const HyprlandService::WorkspaceState &state); void refreshLabel(Gtk::Overlay *overlay, const HyprlandService::WorkspaceState &state);
}; };

View File

@@ -1,142 +1,138 @@
/** biome-ignore-all lint/correctness/noUnknownTypeSelector: gtk css has more valid identifiers */
* { * {
all: unset; all: unset;
} }
window { window {
background-color: rgba(30, 30, 30, 0.8); background-color: #191919c6;
color: #ffffff; color: #ffffff;
font-family: "IBMPlexSans-Regular", sans-serif; padding-left: 4px;
font-size: 14px; padding-right: 4px;
padding: 2px 7px; padding-top: 2px;
} padding-bottom: 2px;
font-size: 14px;
#clock-label { font-family:
font-weight: bold; "Hack Nerd Font Mono", "Font Awesome 7 Brands", "Font Awesome 7 Free",
font-family: monospace; sans-serif;
}
.workspace-pill {
padding: 2px 5px;
margin-right: 6px;
border-radius: 5px;
}
.workspace-pill:hover {
background-color: rgba(255, 255, 255, 0.1);
}
.workspace-pill-focused {
background-color: #ffffff;
color: #1e1e1e;
font-weight: bold;
box-shadow: 0 0 6px rgba(255, 255, 255, 0.8);
}
.workspace-pill-focused:hover {
box-shadow: none;
}
.workspace-pill-active {
background-color: rgba(255, 255, 255, 0.2);
}
.workspace-pill-urgent {
background-color: #ff5555;
color: #fff;
animation: workspace-blink 1s linear infinite;
}
/* blinking animation for urgent workspaces */
@keyframes workspace-blink {
0% {
opacity: 1;
}
50% {
opacity: 0.5;
}
100% {
opacity: 1;
}
}
.workspace-pill:last-child {
margin-right: 0;
}
button {
padding: 2px 5px;
margin: 0 2px;
border-radius: 3px;
background-color: transparent;
color: #ffffff;
border: none;
font-size: 20px;
}
button:hover {
background-color: #111111;
}
#spacer {
color: rgba(255, 255, 255, 0.3);
padding: 0 5px;
} }
popover { popover {
background-color: rgba(30, 30, 30, 0.3); margin-top: 4px;
color: #ffffff; font-family:
font-family: "IBMPlexSans-Regular", sans-serif; "Hack Nerd Font Mono", "Material Icons", "Font Awesome 7 Free", sans-serif;
padding: 5px; padding: 6px;
border-radius: 8px; border-radius: 8px;
border: 1px solid #444444;
margin-top: 5px; background: rgba(25, 25, 25, 0.8);
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.5)); box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
border: 1px solid rgba(57, 57, 57, 0.71);
font-size: 14px;
} }
.icon-label { tooltip {
font-family: "Material Icons, Font Awesome 7 Brands, Hack Nerd Font Mono"; background-color: #222222;
font-size: 19px; color: #ffffff;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
} }
.todo-popover-container { button {
background-color: #1e1e1e; font-family: "Material Icons", sans-serif;
font-size: 20px;
} }
.todo-input-area { #spacer {
margin-bottom: 10px; font-weight: 900;
padding: 0 5px;
text-shadow: 0 0 5px #ffffffaa;
} }
.todo-input { .button {
padding: 5px; padding: 4px 8px;
border-radius: 4px; border-radius: 4px;
border: 1px solid #555555; font-family: "Material Icons", sans-serif;
background-color: #222222;
color: #ffffff;
} }
.toggle-button { .button:hover {
border-radius: 4px; background-color: #111111;
} }
/* use background to highlight using same dark colors as in file */ .workspace-pill {
.toggle-button-on { padding: 2px 5px;
background-color: rgba(0, 150, 136, 0.9); margin-right: 6px;
border: 1px solid #009688; border-radius: 5px;
text-shadow: 0 0 2px #646464;
transition:
background-color 0.2s,
color 0.2s,
box-shadow 0.2s;
} }
.toggle-button-off { .workspace-pill:hover {
background-color: rgba(244, 67, 54, 0.9); background-color: rgba(255, 255, 255, 0.1);
border: 1px solid #f44336; color: #ffffff;
} }
.toggle-button-disabled { .workspace-pill-active {
background-color: rgba(100, 100, 100, 0.9); background-color: #666666;
border: 1px solid #666666;
color: #888888;
} }
.disabled-popover-icon { .workspace-pill-focused {
color: #888888; background-color: #ffffff;
} color: #1e1e1e;
box-shadow: 0 0 6px rgba(255, 255, 255, 0.8);
}
.workspace-pill-focused:hover {
box-shadow: none;
}
.workspace-pill-urgent {
background-color: #ff5555;
color: #fff;
animation: workspace-blink 1s linear infinite;
}
.workspace-pill-six {
animation: workspace-updown 1.2s ease-in-out infinite;
margin-left: -4px;
margin-top: 4px;
font-size: 10px;
}
.workspace-pill-seven {
animation: workspace-updown 1.2s ease-in-out infinite;
animation-delay: 0.6s;
margin-right: -4px;
margin-top: 4px;
font-size: 10px;
}
@keyframes workspace-updown {
0% {
transform: translateY(4px);
}
50% {
transform: translateY(0px);
}
100% {
transform: translateY(4px);
}
}
@keyframes workspace-blink {
0% {
opacity: 1;
}
50% {
opacity: 0.5;
}
100% {
opacity: 1;
}
}

View File

@@ -22,7 +22,7 @@ App::App() {
try { try {
hyprlandMonitor = hyprlandMonitor =
this->hyprlandService.getMonitorByIndex(i); this->hyprlandService->getMonitorByIndex(i);
} catch (const std::exception &ex) { } catch (const std::exception &ex) {
std::cerr << "[App] Failed to fetch Hyprland monitor: " std::cerr << "[App] Failed to fetch Hyprland monitor: "
<< ex.what() << std::endl; << ex.what() << std::endl;
@@ -31,8 +31,7 @@ App::App() {
auto bar = new Bar(monitor->gobj(), this->hyprlandService, auto bar = new Bar(monitor->gobj(), hyprlandMonitor->id);
this->trayService, hyprlandMonitor->id);
bar->set_application(app); bar->set_application(app);
bar->show(); bar->show();
@@ -47,16 +46,16 @@ App::App() {
} }
bars.clear(); bars.clear();
this->trayService.stop(); this->trayService->stop();
}); });
} }
void App::setupServices() { void App::setupServices() {
this->hyprlandService.socketEventSignal.connect(sigc::mem_fun( this->hyprlandService->socketEventSignal.connect(sigc::mem_fun(
this->hyprlandService, &HyprlandService::on_hyprland_event)); *this->hyprlandService, &HyprlandService::on_hyprland_event));
this->hyprlandService.start(); this->hyprlandService->start();
this->trayService.start(); this->trayService->start();
} }
int App::run() { return this->app->run(); } int App::run() { return this->app->run(); }

View File

@@ -16,10 +16,8 @@
#include "gtk/gtk.h" #include "gtk/gtk.h"
#include "sigc++/functors/mem_fun.h" #include "sigc++/functors/mem_fun.h"
Bar::Bar(GdkMonitor *monitor, HyprlandService &hyprlandService, Bar::Bar(GdkMonitor *monitor, int monitorId)
TrayService &trayService, int monitorId) : monitorId(monitorId) {
: hyprlandService(hyprlandService), trayService(trayService),
monitorId(monitorId) {
set_name("bar-window"); set_name("bar-window");
gtk_layer_init_for_window(this->gobj()); gtk_layer_init_for_window(this->gobj());
@@ -38,9 +36,8 @@ Bar::Bar(GdkMonitor *monitor, HyprlandService &hyprlandService,
set_child(main_box); set_child(main_box);
this->volumeWidget = Gtk::make_managed<VolumeWidget>(); this->volumeWidget = Gtk::make_managed<VolumeWidget>();
this->workspaceIndicator = Gtk::make_managed<WorkspaceIndicator>(hyprlandService, monitorId); this->workspaceIndicator = Gtk::make_managed<WorkspaceIndicator>(monitorId);
this->trayWidget = Gtk::make_managed<TrayWidget>(trayService); this->trayWidget = Gtk::make_managed<TrayWidget>();
load_css(); load_css();
setup_ui(); setup_ui();

View File

@@ -0,0 +1,19 @@
#include "components/base/button.hpp"
#include "sigc++/functors/mem_fun.h"
Button::Button(const std::string label)
: Gtk::Button(label) {
signal_clicked().connect(
sigc::mem_fun(*this, &Button::on_clicked));
this->add_css_class("button");
}
Button::Button(Gtk::Image &image)
: Gtk::Button() {
set_child(image);
signal_clicked().connect(
sigc::mem_fun(*this, &Button::on_clicked));
this->add_css_class("button");
}

View File

@@ -3,10 +3,7 @@
#include "gtkmm/label.h" #include "gtkmm/label.h"
#include "gtkmm/object.h" #include "gtkmm/object.h"
Popover::Popover(std::string icon, std::string name) { Popover::Popover(const std::string icon, std::string name): Button(icon) {
auto label = Gtk::make_managed<Gtk::Label>(icon);
label->add_css_class("icon-label");
set_child(*label);
signal_clicked().connect(sigc::mem_fun(*this, &Popover::on_toggle_window)); signal_clicked().connect(sigc::mem_fun(*this, &Popover::on_toggle_window));
popover = new Gtk::Popover(); popover = new Gtk::Popover();

View File

@@ -2,8 +2,7 @@
#include <gtkmm/label.h> #include <gtkmm/label.h>
#include <string> #include <string>
#include "components/base/button.hpp"
#include "gtkmm/button.h"
TodoEntry::TodoEntry(int id, std::string text, sigc::signal<void(int)> signal_dismissed, sigc::signal<void(int, std::string)> signal_edited) TodoEntry::TodoEntry(int id, std::string text, sigc::signal<void(int)> signal_dismissed, sigc::signal<void(int, std::string)> signal_edited)
: Gtk::Box(Gtk::Orientation::HORIZONTAL) { : Gtk::Box(Gtk::Orientation::HORIZONTAL) {
@@ -30,22 +29,19 @@ TodoEntry::TodoEntry(int id, std::string text, sigc::signal<void(int)> signal_di
buttonBox->set_valign(Gtk::Align::CENTER); buttonBox->set_valign(Gtk::Align::CENTER);
append(*buttonBox); append(*buttonBox);
auto dismissButton = Gtk::make_managed<Gtk::Button>("\uf00d"); auto dismissButton = Gtk::make_managed<Button>("\uf00d");
dismissButton->set_valign(Gtk::Align::CENTER); dismissButton->set_valign(Gtk::Align::CENTER);
dismissButton->set_tooltip_text("Dismiss"); dismissButton->set_tooltip_text("Dismiss");
dismissButton->signal_clicked().connect(sigc::mem_fun(*this, &TodoEntry::on_dismiss_clicked)); dismissButton->signal_clicked().connect(sigc::mem_fun(*this, &TodoEntry::on_dismiss_clicked));
auto editButton = Gtk::make_managed<Gtk::Button>("\uf044"); auto editButton = Gtk::make_managed<Button>("\uf044");
editButton->set_valign(Gtk::Align::CENTER); editButton->set_valign(Gtk::Align::CENTER);
buttonBox->append(*editButton); buttonBox->append(*editButton);
buttonBox->append(*dismissButton); buttonBox->append(*dismissButton);
} }
TodoEntry::~TodoEntry() {
}
void TodoEntry::on_dismiss_clicked() { void TodoEntry::on_dismiss_clicked() {
this->signal_dismissed.emit(this->id); this->signal_dismissed.emit(this->id);
} }

View File

@@ -8,8 +8,6 @@
#include "glib.h" #include "glib.h"
#include "sigc++/signal.h" #include "sigc++/signal.h"
BluetoothService *BluetoothService::instance = nullptr;
BluetoothService::BluetoothService() { BluetoothService::BluetoothService() {
GError *error = nullptr; GError *error = nullptr;
@@ -28,9 +26,7 @@ BluetoothService::BluetoothService() {
<< error->message << std::endl; << error->message << std::endl;
g_error_free(error); g_error_free(error);
assert(false); assert(false);
} else { }
std::cout << "Bluetooth adapter proxy created successfully." << std::endl;
}
this->powerState = this->getPowerState(); this->powerState = this->getPowerState();
this->isDiscovering = this->getIsDiscovering(); this->isDiscovering = this->getIsDiscovering();
@@ -168,7 +164,7 @@ void BluetoothService::onPropertyChanged(GDBusProxy *proxy,
if (g_variant_lookup(changed_properties, "Discovering", "b", &is_discovering)) { if (g_variant_lookup(changed_properties, "Discovering", "b", &is_discovering)) {
this->isDiscovering = is_discovering; this->isDiscovering = is_discovering;
isDiscoveringChangedSignal.emit(isDiscovering); isDiscoveringChangedSignal.emit(isDiscovering);
// getDeviceObjectPaths(); getDeviceObjectPaths();
} }
} }
@@ -229,8 +225,6 @@ std::vector<std::string> BluetoothService::getDeviceObjectPaths() {
while (g_variant_iter_next(&iter, "{&o@a{sa{sv}}}", &object_path, &interfaces)) { while (g_variant_iter_next(&iter, "{&o@a{sa{sv}}}", &object_path, &interfaces)) {
GVariant *device_props = nullptr; GVariant *device_props = nullptr;
if (g_variant_lookup(interfaces, "org.bluez.Device1", "@a{sv}", &device_props)) { if (g_variant_lookup(interfaces, "org.bluez.Device1", "@a{sv}", &device_props)) {
std::cout << "Found device: " << object_path << std::endl;
device_paths.emplace_back(object_path); device_paths.emplace_back(object_path);
g_variant_unref(device_props); g_variant_unref(device_props);
} }

View File

@@ -43,9 +43,6 @@ void HyprlandService::on_hyprland_event(std::string event, std::string data) {
refresh_workspaces(); refresh_workspaces();
} }
// use for
// event == "focusedmon"
if (event == "monitoradded" || event == "monitorremoved") { if (event == "monitoradded" || event == "monitorremoved") {
refresh_monitors(); refresh_monitors();
} }
@@ -77,8 +74,6 @@ void HyprlandService::start() {
return; return;
} }
std::cout << "[Hyprland] Connected to event socket." << std::endl;
Glib::signal_io().connect( Glib::signal_io().connect(
sigc::mem_fun(*this, &HyprlandService::on_socket_read), fd, sigc::mem_fun(*this, &HyprlandService::on_socket_read), fd,
Glib::IOCondition::IO_IN | Glib::IOCondition::IO_HUP | Glib::IOCondition::IO_IN | Glib::IOCondition::IO_HUP |
@@ -300,45 +295,6 @@ void HyprlandService::onActiveWindowEvent(std::string windowAddress) {
} }
} }
void HyprlandService::printMonitor(const Monitor &mon) const {
std::cout << "=== Monitor Info ===\n";
std::cout << "Name: " << mon.name << " (ID: " << mon.id << ")\n";
std::cout << "Position: (" << mon.x << ", " << mon.y << ")\n";
std::cout << "Focused Workspace ID: " << mon.focusedWorkspaceId << "\n";
std::cout << "Workspaces:\n";
if (mon.workspaceStates.empty()) {
std::cout << " (None)\n";
return;
}
for (int slot = 1; slot <= HyprlandService::kWorkspaceSlotCount; ++slot) {
const auto it = mon.workspaceStates.find(slot);
if (it == mon.workspaceStates.end()) {
std::cout << " - [Slot: " << slot << " | HyprID: n/a]"
<< " Label: <none> | Active: No | Focused: No | "
"Urgent: No\n";
continue;
}
const WorkspaceState *ws = it->second;
std::cout << " - [HyprID: "
<< (ws->hyprId >= 0 ? std::to_string(ws->hyprId)
: std::string("n/a"))
<< "] "
<< "Label: " << (ws->label.empty() ? "<none>" : ws->label)
<< " | "
<< "Active: " << (ws->active ? "Yes" : "No") << " | "
<< "Focused: " << (ws->focused ? "Yes" : "No") << " | "
<< "Urgent: " << (ws->urgentWindows.size() > 0 ? "Yes" : "No")
<< "\n";
}
std::cout << "====================\n";
}
HyprlandService::Monitor *HyprlandService::getMonitorById(int id) { HyprlandService::Monitor *HyprlandService::getMonitorById(int id) {
auto it = monitors.find(id); auto it = monitors.find(id);
if (it == monitors.end()) { if (it == monitors.end()) {

View File

@@ -302,6 +302,4 @@ void NotificationService::intialize() {
<< "). Stop your existing notification daemon (e.g. dunst/mako/swaync) or allow replacement." << std::endl; << "). Stop your existing notification daemon (e.g. dunst/mako/swaync) or allow replacement." << std::endl;
return; return;
} }
std::cout << "Notifications daemon active (org.freedesktop.Notifications)." << std::endl;
} }

View File

@@ -485,34 +485,6 @@ TrayService::get_menu_action_group(const std::string &id) {
return item.menuActions; return item.menuActions;
} }
void TrayService::debug_dump_menu_layout(const std::string &id) {
auto it = items.find(id);
if (it == items.end() || !connection) {
return;
}
const auto &item = *it->second;
if (!item.publicData.menuAvailable || item.publicData.menuPath.empty()) {
return;
}
GVariant *result = call_get_layout(connection, item.publicData.busName,
item.publicData.menuPath);
if (!result) {
return;
}
gchar *printed = g_variant_print(result, TRUE);
if (printed) {
std::cout << "[TrayService] GetLayout for " << id << ":\n"
<< printed << std::endl;
g_free(printed);
}
g_variant_unref(result);
}
std::optional<TrayService::MenuNode> std::optional<TrayService::MenuNode>
TrayService::get_menu_layout(const std::string &id) { TrayService::get_menu_layout(const std::string &id) {
auto it = items.find(id); auto it = items.find(id);

View File

@@ -9,12 +9,12 @@ BluetoothWidget::BluetoothWidget() : Gtk::Box() {
this->statusArea.set_halign(Gtk::Align::FILL); this->statusArea.set_halign(Gtk::Align::FILL);
this->append(this->statusArea); this->append(this->statusArea);
this->powerButton = Gtk::make_managed<Gtk::Button>("\ue1a8"); this->powerButton = Gtk::make_managed<Button>("\ue1a8");
this->powerButton->set_tooltip_text("Turn Bluetooth Off"); this->powerButton->set_tooltip_text("Turn Bluetooth Off");
this->powerButton->signal_clicked().connect(sigc::mem_fun(*this, &BluetoothWidget::onPowerButtonClicked)); this->powerButton->signal_clicked().connect(sigc::mem_fun(*this, &BluetoothWidget::onPowerButtonClicked));
this->powerButton->add_css_class("toggle-button"); this->powerButton->add_css_class("toggle-button");
this->scanButton = Gtk::make_managed<Gtk::Button>("\ue1aa"); this->scanButton = Gtk::make_managed<Button>("\ue1aa");
this->scanButton->set_tooltip_text("Scan for Devices"); this->scanButton->set_tooltip_text("Scan for Devices");
this->scanButton->add_css_class("toggle-button"); this->scanButton->add_css_class("toggle-button");
this->scanButton->signal_clicked().connect(sigc::mem_fun(*this, &BluetoothWidget::onScanButtonClicked)); this->scanButton->signal_clicked().connect(sigc::mem_fun(*this, &BluetoothWidget::onScanButtonClicked));
@@ -31,7 +31,7 @@ void BluetoothWidget::onScanButtonClicked() {
onIsDiscoveringButtonClickedSignal.emit(); onIsDiscoveringButtonClickedSignal.emit();
} }
void BluetoothWidget::toggleButton(Gtk::Button *button, bool state) { void BluetoothWidget::toggleButton(Button *button, bool state) {
if (state) { if (state) {
button->add_css_class("toggle-button-on"); button->add_css_class("toggle-button-on");
button->remove_css_class("toggle-button-off"); button->remove_css_class("toggle-button-off");

View File

@@ -10,9 +10,11 @@ ControlCenter::ControlCenter(std::string icon, std::string name)
bluetoothService->powerStateChangedSignal.connect( bluetoothService->powerStateChangedSignal.connect(
sigc::mem_fun(*this->bluetoothWidget, &BluetoothWidget::setPowerState)); sigc::mem_fun(*this->bluetoothWidget, &BluetoothWidget::setPowerState));
bluetoothWidget->onPowerStateButtonClickedSignal.connect(
bluetoothWidget->onPowerStateButtonClickedSignal.connect(
sigc::mem_fun(*this->bluetoothService, &BluetoothService::togglePowerState)); sigc::mem_fun(*this->bluetoothService, &BluetoothService::togglePowerState));
bluetoothWidget->setPowerState(bluetoothService->getPowerState());
bluetoothWidget->setPowerState(bluetoothService->getPowerState());
bluetoothService->isDiscoveringChangedSignal.connect( bluetoothService->isDiscoveringChangedSignal.connect(
sigc::mem_fun(*this->bluetoothWidget, &BluetoothWidget::setIsDiscovering)); sigc::mem_fun(*this->bluetoothWidget, &BluetoothWidget::setIsDiscovering));

View File

@@ -5,9 +5,10 @@
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include <iostream> #include <iostream>
#include <utility> #include <utility>
#include "components/base/button.hpp"
TrayIconWidget::TrayIconWidget(TrayService &service, std::string id) TrayIconWidget::TrayIconWidget( std::string id)
: service(service), id(std::move(id)), : Button(id), id(std::move(id)),
container(Gtk::Orientation::HORIZONTAL) { container(Gtk::Orientation::HORIZONTAL) {
set_has_frame(false); set_has_frame(false);
set_focusable(false); set_focusable(false);
@@ -131,10 +132,8 @@ bool TrayIconWidget::ensure_menu() {
populate_menu_items(layout.children, menu, actions); populate_menu_items(layout.children, menu, actions);
const auto itemCount = menu->get_n_items(); const auto itemCount = menu->get_n_items();
std::cout << "[TrayIconWidget] menu update for " << id
<< ", items: " << itemCount << std::endl;
if (itemCount == 0) { if (itemCount == 0) {
service.debug_dump_menu_layout(id);
return false; return false;
} }
@@ -174,9 +173,6 @@ void TrayIconWidget::on_menu_items_changed(guint /*position*/,
return; return;
} }
const auto count = menuModel->get_n_items();
std::cout << "[TrayIconWidget] items changed for " << id << ": " << count
<< " entries" << std::endl;
try_popup(); try_popup();
} }
@@ -251,17 +247,17 @@ void TrayIconWidget::on_menu_action(const Glib::VariantBase & /*parameter*/,
} }
} }
TrayWidget::TrayWidget(TrayService &service) TrayWidget::TrayWidget()
: Gtk::Box(Gtk::Orientation::HORIZONTAL), service(service) { : Gtk::Box(Gtk::Orientation::HORIZONTAL) {
set_valign(Gtk::Align::CENTER); set_valign(Gtk::Align::CENTER);
set_halign(Gtk::Align::CENTER); set_halign(Gtk::Align::CENTER);
set_visible(false); set_visible(false);
addConnection = service.signal_item_added().connect( addConnection = service->signal_item_added().connect(
sigc::mem_fun(*this, &TrayWidget::on_item_added)); sigc::mem_fun(*this, &TrayWidget::on_item_added));
removeConnection = service.signal_item_removed().connect( removeConnection = service->signal_item_removed().connect(
sigc::mem_fun(*this, &TrayWidget::on_item_removed)); sigc::mem_fun(*this, &TrayWidget::on_item_removed));
updateConnection = service.signal_item_updated().connect( updateConnection = service->signal_item_updated().connect(
sigc::mem_fun(*this, &TrayWidget::on_item_updated)); sigc::mem_fun(*this, &TrayWidget::on_item_updated));
rebuild_existing(); rebuild_existing();
@@ -280,7 +276,7 @@ TrayWidget::~TrayWidget() {
} }
void TrayWidget::rebuild_existing() { void TrayWidget::rebuild_existing() {
auto items = service.snapshotItems(); auto items = service->snapshotItems();
for (const auto &item : items) { for (const auto &item : items) {
on_item_added(item); on_item_added(item);
} }
@@ -295,7 +291,7 @@ void TrayWidget::on_item_added(const TrayService::Item &item) {
return; return;
} }
auto icon = std::make_unique<TrayIconWidget>(service, item.id); auto icon = std::make_unique<TrayIconWidget>(item.id);
icon->update(item); icon->update(item);
auto *raw = icon.get(); auto *raw = icon.get();
append(*raw); append(*raw);

View File

@@ -3,39 +3,58 @@
#include <cassert> #include <cassert>
#include <gdk/gdk.h> #include <gdk/gdk.h>
#include <gtkmm/gestureclick.h> #include <gtkmm/gestureclick.h>
#include <gtkmm/overlay.h>
#include <gtkmm/widget.h> #include <gtkmm/widget.h>
#include <sigc++/functors/mem_fun.h> #include <sigc++/functors/mem_fun.h>
#include "services/hyprland.hpp" #include "services/hyprland.hpp"
WorkspaceIndicator::WorkspaceIndicator(HyprlandService &service, int monitorId) #include "gtkmm/box.h"
: Gtk::Box(Gtk::Orientation::HORIZONTAL), service(service), #include "gtkmm/label.h"
WorkspaceIndicator::WorkspaceIndicator(int monitorId)
: Gtk::Box(Gtk::Orientation::HORIZONTAL),
monitorId(monitorId) { monitorId(monitorId) {
set_margin_top(2); set_margin_top(2);
set_margin_bottom(2); set_margin_bottom(2);
workspaceConnection = service.workspaceStateChanged.connect( workspaceConnection = service->workspaceStateChanged.connect(
sigc::mem_fun(*this, &WorkspaceIndicator::on_workspace_update)); sigc::mem_fun(*this, &WorkspaceIndicator::on_workspace_update));
monitorConnection = service.monitorStateChanged.connect( monitorConnection = service->monitorStateChanged.connect(
sigc::mem_fun(*this, &WorkspaceIndicator::on_monitor_update)); sigc::mem_fun(*this, &WorkspaceIndicator::on_monitor_update));
for (int i = 1; i <= HyprlandService::kWorkspaceSlotCount; ++i) { for (int i = 1; i <= HyprlandService::kWorkspaceSlotCount; ++i) {
auto label = Gtk::make_managed<Gtk::Label>(std::to_string(i)); auto overlay = Gtk::make_managed<Gtk::Overlay>();
label->add_css_class("workspace-pill"); auto numLabel = Gtk::make_managed<Gtk::Label>(std::to_string(i));
auto pillContainer = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
// create and store a gesture controller once to avoid duplicate handlers
auto gesture = Gtk::GestureClick::create(); auto gesture = Gtk::GestureClick::create();
gesture->set_button(GDK_BUTTON_PRIMARY); gesture->set_button(GDK_BUTTON_PRIMARY);
gesture->signal_released().connect( gesture->signal_released().connect([this, i](int, double, double) {
[this, i](int, double, double) { this->service->switchToWorkspace(
this->service.switchToWorkspace( i + this->monitorId * HyprlandService::kWorkspaceSlotCount);
i + this->monitorId * HyprlandService::kWorkspaceSlotCount); });
});
label->add_controller(gesture);
workspaceGestures[i] = gesture;
workspaceLabels[i] = label; workspaceGestures[i] = gesture;
append(*label); workspaceIndicators[i] = overlay;
overlay->add_controller(gesture);
overlay->add_css_class("workspace-pill");
// if (i == 6 || i == 7) {
// auto indicator = Gtk::make_managed<Gtk::Label>(i == 6 ? "🫱🏻" : "🫲🏻");
// indicator->add_css_class(i == 6 ? "workspace-pill-six" : "workspace-pill-seven");
// indicator->set_valign(Gtk::Align::END);
// indicator->set_halign(i == 6 ? Gtk::Align::START : Gtk::Align::END);
// overlay->set_child(*indicator);
// overlay->add_overlay(*numLabel);
// pillContainer->append(*overlay);
// } else {
overlay->set_child(*numLabel);
pillContainer->append(*overlay);
// }
append(*pillContainer);
} }
rebuild(); rebuild();
@@ -55,30 +74,32 @@ void WorkspaceIndicator::on_workspace_update() {
rebuild(); rebuild();
} }
void WorkspaceIndicator::on_monitor_update() { rebuild(); } void WorkspaceIndicator::on_monitor_update() {
rebuild();
}
void WorkspaceIndicator::refreshLabel(Gtk::Label *label, const HyprlandService::WorkspaceState &state) { void WorkspaceIndicator::refreshLabel(Gtk::Overlay *overlay, const HyprlandService::WorkspaceState &state) {
label->remove_css_class("workspace-pill-active"); overlay->remove_css_class("workspace-pill-active");
label->remove_css_class("workspace-pill-focused"); overlay->remove_css_class("workspace-pill-focused");
label->remove_css_class("workspace-pill-urgent"); overlay->remove_css_class("workspace-pill-urgent");
// controller created once in constructor and reused // controller created once in constructor and reused
if (state.urgentWindows.size() > 0) { if (state.urgentWindows.size() > 0) {
label->add_css_class("workspace-pill-urgent"); overlay->add_css_class("workspace-pill-urgent");
} else { } else {
if (state.focused) { if (state.focused) {
label->add_css_class("workspace-pill-focused"); overlay->add_css_class("workspace-pill-focused");
} else if (state.active) { } else if (state.active) {
label->add_css_class("workspace-pill-active"); overlay->add_css_class("workspace-pill-active");
} }
} }
} }
void WorkspaceIndicator::rebuild() { void WorkspaceIndicator::rebuild() {
HyprlandService::Monitor *mon = service.getMonitorById(this->monitorId); HyprlandService::Monitor *mon = service->getMonitorById(this->monitorId);
for (auto [id, workspaceState] : mon->workspaceStates) { for (auto [id, workspaceState] : mon->workspaceStates) {
Gtk::Label *label = workspaceLabels[id]; Gtk::Overlay *overlay = workspaceIndicators[id];
this->refreshLabel(label, *workspaceState); this->refreshLabel(overlay, *workspaceState);
} }
} }