Compare commits
4 Commits
5a429a3b8b
...
2d5b492da8
| Author | SHA1 | Date | |
|---|---|---|---|
| 2d5b492da8 | |||
| 9b5db719cb | |||
| a06c96f648 | |||
| 8613024f8d |
@@ -39,9 +39,11 @@ target_sources(bar_lib
|
|||||||
src/widgets/tray.cpp
|
src/widgets/tray.cpp
|
||||||
src/widgets/todo.cpp
|
src/widgets/todo.cpp
|
||||||
src/widgets/bluetooth.cpp
|
src/widgets/bluetooth.cpp
|
||||||
|
src/widgets/controlCenter.cpp
|
||||||
|
|
||||||
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
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "bar/bar.hpp"
|
#include "bar/bar.hpp"
|
||||||
#include "services/bluetooth.hpp"
|
|
||||||
#include "services/hyprland.hpp"
|
#include "services/hyprland.hpp"
|
||||||
#include "services/notifications.hpp"
|
#include "services/notifications.hpp"
|
||||||
#include "services/tray.hpp"
|
#include "services/tray.hpp"
|
||||||
@@ -20,10 +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();
|
||||||
BluetoothService bluetoothService;
|
|
||||||
|
|
||||||
void setupServices();
|
void setupServices();
|
||||||
};
|
};
|
||||||
@@ -4,42 +4,35 @@
|
|||||||
#include <gtkmm.h>
|
#include <gtkmm.h>
|
||||||
|
|
||||||
#include "icons.hpp"
|
#include "icons.hpp"
|
||||||
#include "services/bluetooth.hpp"
|
|
||||||
#include "services/hyprland.hpp"
|
|
||||||
#include "services/tray.hpp"
|
|
||||||
#include "widgets/bluetooth.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"
|
||||||
#include "widgets/volumeWidget.hpp"
|
#include "widgets/volumeWidget.hpp"
|
||||||
#include "widgets/webWidget.hpp"
|
#include "widgets/webWidget.hpp"
|
||||||
#include "widgets/workspaceIndicator.hpp"
|
#include "widgets/workspaceIndicator.hpp"
|
||||||
|
#include "widgets/controlCenter.hpp"
|
||||||
|
|
||||||
class Bar : public Gtk::Window {
|
class Bar : public Gtk::Window {
|
||||||
public:
|
public:
|
||||||
Bar(GdkMonitor *monitor, HyprlandService &hyprlandService, TrayService &trayService, BluetoothService &bluetoothService, 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"};
|
||||||
|
ControlCenter controlCenter{"\ue8bb", "Control Center"};
|
||||||
|
|
||||||
WorkspaceIndicator *workspaceIndicator = nullptr;
|
WorkspaceIndicator *workspaceIndicator = nullptr;
|
||||||
TrayWidget *trayWidget = nullptr;
|
TrayWidget *trayWidget = nullptr;
|
||||||
VolumeWidget *volumeWidget = nullptr;
|
VolumeWidget *volumeWidget = nullptr;
|
||||||
BluetoothWidget *bluetoothWidget = nullptr;
|
|
||||||
|
|
||||||
TrayService &trayService;
|
|
||||||
HyprlandService &hyprlandService;
|
|
||||||
BluetoothService &bluetoothService;
|
|
||||||
|
|
||||||
void setup_ui();
|
void setup_ui();
|
||||||
void setup_left_box();
|
void setup_left_box();
|
||||||
|
|||||||
18
include/components/base/button.hpp
Normal file
18
include/components/base/button.hpp
Normal 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();
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -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:
|
||||||
|
|||||||
@@ -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; }
|
||||||
|
|||||||
@@ -2,26 +2,40 @@
|
|||||||
|
|
||||||
#include <gio/gio.h>
|
#include <gio/gio.h>
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "sigc++/signal.h"
|
#include "sigc++/signal.h"
|
||||||
|
|
||||||
class BluetoothService {
|
class BluetoothService {
|
||||||
public:
|
inline static BluetoothService *instance = nullptr;
|
||||||
BluetoothService();
|
|
||||||
|
|
||||||
|
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();
|
||||||
void setPowerState(bool state);
|
|
||||||
|
|
||||||
bool getIsDiscovering();
|
bool getIsDiscovering();
|
||||||
void setIsDiscovering(bool state);
|
|
||||||
|
|
||||||
private:
|
void togglePowerState();
|
||||||
|
void toggleIsDiscovering();
|
||||||
|
|
||||||
|
static BluetoothService *getInstance() {
|
||||||
|
if (BluetoothService::instance == nullptr) {
|
||||||
|
|
||||||
|
BluetoothService::instance = new BluetoothService();
|
||||||
|
}
|
||||||
|
return BluetoothService::instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
BluetoothService();
|
||||||
|
|
||||||
GDBusProxy *adapter_proxy = nullptr;
|
GDBusProxy *adapter_proxy = nullptr;
|
||||||
bool powerState = false;
|
|
||||||
bool isDiscovering = false;
|
std::vector<std::string> getDeviceObjectPaths();
|
||||||
|
bool powerState = false;
|
||||||
|
bool isDiscovering = false;
|
||||||
|
|
||||||
void onPropertyChanged(GDBusProxy *proxy,
|
void onPropertyChanged(GDBusProxy *proxy,
|
||||||
GVariant *changed_properties,
|
GVariant *changed_properties,
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -1,31 +1,68 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <gtkmm/box.h>
|
#include <gtkmm/box.h>
|
||||||
|
#include <gtkmm/button.h>
|
||||||
|
#include <gtkmm/label.h>
|
||||||
|
|
||||||
#include "components/popover.hpp"
|
#include "components/base/button.hpp"
|
||||||
|
|
||||||
#include "gtkmm/button.h"
|
|
||||||
|
|
||||||
class BluetoothWidget : public Popover {
|
|
||||||
|
class BluetoothEntry : Gtk::Box {
|
||||||
public:
|
public:
|
||||||
BluetoothWidget(std::string icon, std::string title);
|
BluetoothEntry(std::string name, std::string address) {
|
||||||
|
this->set_orientation(Gtk::Orientation::VERTICAL);
|
||||||
|
this->add_css_class("bluetooth-entry-box");
|
||||||
|
|
||||||
|
auto nameLabel = Gtk::make_managed<Gtk::Label>(name);
|
||||||
|
nameLabel->set_halign(Gtk::Align::START);
|
||||||
|
nameLabel->add_css_class("bluetooth-entry-name");
|
||||||
|
this->append(*nameLabel);
|
||||||
|
|
||||||
|
auto addressLabel = Gtk::make_managed<Gtk::Label>(address);
|
||||||
|
addressLabel->set_halign(Gtk::Align::START);
|
||||||
|
addressLabel->add_css_class("bluetooth-entry-address");
|
||||||
|
this->append(*addressLabel);
|
||||||
|
|
||||||
|
this->add_css_class("bluetooth-entry");
|
||||||
|
|
||||||
|
auto connectButton = Gtk::make_managed<Button>("Connect");
|
||||||
|
connectButton->set_halign(Gtk::Align::END);
|
||||||
|
connectButton->set_tooltip_text("Connect to Device");
|
||||||
|
connectButton->onClickedSignal.connect([this, address]() {
|
||||||
|
this->connect_clicked.emit(address);
|
||||||
|
});
|
||||||
|
this->append(*connectButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
sigc::signal<void(std::string)> connect_clicked;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class BluetoothWidget : public Gtk::Box {
|
||||||
|
public:
|
||||||
|
BluetoothWidget();
|
||||||
|
|
||||||
void setPowerState(bool state);
|
void setPowerState(bool state);
|
||||||
void setIsDiscovering(bool state);
|
void setIsDiscovering(bool state);
|
||||||
|
|
||||||
sigc::signal<void(bool)> powerStateChangedSignal;
|
sigc::signal<void()> onPowerStateButtonClickedSignal;
|
||||||
sigc::signal<void(bool)> isDiscoveringChangedSignal;
|
sigc::signal<void()> onIsDiscoveringButtonClickedSignal;
|
||||||
|
|
||||||
void update();
|
void update();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool isPowered = false;
|
bool isPowered = false;
|
||||||
bool isDiscovering = false;
|
bool isDiscovering = false;
|
||||||
|
|
||||||
Gtk::Box container;
|
|
||||||
Gtk::Box statusArea;
|
Gtk::Box statusArea;
|
||||||
|
std::map<std::string, BluetoothEntry *> deviceEntries;
|
||||||
|
Gtk::Box devicesArea;
|
||||||
|
Button *scanButton = nullptr;
|
||||||
|
Button *powerButton = nullptr;
|
||||||
|
|
||||||
Gtk::Box *deviceList = nullptr;
|
void onPowerButtonClicked();
|
||||||
|
void onScanButtonClicked();
|
||||||
|
|
||||||
Gtk::Button *scanButton = nullptr;
|
void toggleButton(Button *button, bool state);
|
||||||
Gtk::Button *toggleButton = nullptr;
|
|
||||||
};
|
};
|
||||||
17
include/widgets/controlCenter.hpp
Normal file
17
include/widgets/controlCenter.hpp
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "components/popover.hpp"
|
||||||
|
#include "services/bluetooth.hpp"
|
||||||
|
#include "widgets/bluetooth.hpp"
|
||||||
|
#include "gtkmm/box.h"
|
||||||
|
|
||||||
|
class ControlCenter : public Popover {
|
||||||
|
public:
|
||||||
|
ControlCenter(std::string icon, std::string name);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Gtk::Box container;
|
||||||
|
|
||||||
|
BluetoothWidget *bluetoothWidget = nullptr;
|
||||||
|
BluetoothService *bluetoothService = BluetoothService::getInstance();
|
||||||
|
};
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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:
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,138 +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: rgb(30, 30, 30);
|
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);
|
||||||
|
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
|
||||||
|
border: 1px solid rgba(57, 57, 57, 0.71);
|
||||||
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
tooltip {
|
tooltip {
|
||||||
background-color: rgba(50, 50, 50, 0.9);
|
background-color: #222222;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
font-family: "IBMPlexSans-Regular", sans-serif;
|
padding: 4px 8px;
|
||||||
padding: 5px 10px;
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-label {
|
button {
|
||||||
font-family: "Material Icons, Font Awesome 7 Brands, Hack Nerd Font Mono";
|
font-family: "Material Icons", sans-serif;
|
||||||
font-size: 19px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.todo-popover-container {
|
#spacer {
|
||||||
background-color: #1e1e1e;
|
font-weight: 900;
|
||||||
|
padding: 0 5px;
|
||||||
|
text-shadow: 0 0 5px #ffffffaa;
|
||||||
}
|
}
|
||||||
|
|
||||||
.todo-input-area {
|
.button {
|
||||||
margin-bottom: 10px;
|
padding: 4px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: "Material Icons", sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
.todo-input {
|
.button:hover {
|
||||||
padding: 5px;
|
background-color: #111111;
|
||||||
border-radius: 4px;
|
|
||||||
border: 1px solid #555555;
|
|
||||||
background-color: #222222;
|
|
||||||
color: #ffffff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.toggle-button {
|
.workspace-pill {
|
||||||
border-radius: 4px;
|
padding: 2px 5px;
|
||||||
|
margin-right: 6px;
|
||||||
|
border-radius: 5px;
|
||||||
|
text-shadow: 0 0 2px #646464;
|
||||||
|
transition:
|
||||||
|
background-color 0.2s,
|
||||||
|
color 0.2s,
|
||||||
|
box-shadow 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* use background to highlight using same dark colors as in file */
|
.workspace-pill:hover {
|
||||||
.toggle-button-on {
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
background-color: rgba(0, 150, 136, 0.2);
|
color: #ffffff;
|
||||||
border: 1px solid #009688;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.toggle-button-off {
|
.workspace-pill-active {
|
||||||
background-color: rgba(244, 67, 54, 0.2);
|
background-color: #666666;
|
||||||
border: 1px solid #f44336;
|
}
|
||||||
|
|
||||||
|
.workspace-pill-focused {
|
||||||
|
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: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-pill-seven {
|
||||||
|
animation: workspace-updown 1.2s ease-in-out infinite;
|
||||||
|
animation-delay: 0.6s;
|
||||||
|
margin-right: -4px;
|
||||||
|
margin-top: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
18
src/app.cpp
18
src/app.cpp
@@ -22,17 +22,14 @@ 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;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto bar = new Bar(monitor->gobj(), hyprlandMonitor->id);
|
||||||
|
|
||||||
auto bar = new Bar(monitor->gobj(), this->hyprlandService,
|
|
||||||
this->trayService, this->bluetoothService, hyprlandMonitor->id);
|
|
||||||
|
|
||||||
bar->set_application(app);
|
bar->set_application(app);
|
||||||
bar->show();
|
bar->show();
|
||||||
@@ -41,22 +38,23 @@ App::App() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
app->signal_shutdown().connect([&]() {
|
app->signal_shutdown().connect([&]() {
|
||||||
for (auto bar : bars) {
|
for (auto bar : bars) {
|
||||||
delete bar;
|
delete bar;
|
||||||
}
|
}
|
||||||
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(); }
|
||||||
|
|||||||
@@ -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, BluetoothService &bluetoothService, int monitorId)
|
: monitorId(monitorId) {
|
||||||
: hyprlandService(hyprlandService), trayService(trayService), bluetoothService(bluetoothService),
|
|
||||||
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,21 +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>();
|
||||||
this->bluetoothWidget = Gtk::make_managed<BluetoothWidget>("\ue8bb", "Bluetooth");
|
|
||||||
|
|
||||||
bluetoothService.powerStateChangedSignal.connect(
|
|
||||||
sigc::mem_fun(*this->bluetoothWidget, &BluetoothWidget::setPowerState));
|
|
||||||
bluetoothWidget->powerStateChangedSignal.connect(
|
|
||||||
sigc::mem_fun(bluetoothService, &BluetoothService::setPowerState));
|
|
||||||
bluetoothWidget->setPowerState(bluetoothService.getPowerState());
|
|
||||||
|
|
||||||
bluetoothService.isDiscoveringChangedSignal.connect(
|
|
||||||
sigc::mem_fun(*this->bluetoothWidget, &BluetoothWidget::setIsDiscovering));
|
|
||||||
bluetoothWidget->isDiscoveringChangedSignal.connect(
|
|
||||||
sigc::mem_fun(bluetoothService, &BluetoothService::setIsDiscovering));
|
|
||||||
bluetoothWidget->setIsDiscovering(bluetoothService.getIsDiscovering());
|
|
||||||
|
|
||||||
load_css();
|
load_css();
|
||||||
setup_ui();
|
setup_ui();
|
||||||
@@ -93,7 +78,7 @@ void Bar::setup_center_box() {
|
|||||||
void Bar::setup_right_box() {
|
void Bar::setup_right_box() {
|
||||||
right_box.append(*this->trayWidget);
|
right_box.append(*this->trayWidget);
|
||||||
right_box.append(this->homeAssistant);
|
right_box.append(this->homeAssistant);
|
||||||
right_box.append(*this->bluetoothWidget);
|
right_box.append(this->controlCenter);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bar::load_css() {
|
void Bar::load_css() {
|
||||||
|
|||||||
19
src/components/base/button.cpp
Normal file
19
src/components/base/button.cpp
Normal 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");
|
||||||
|
}
|
||||||
@@ -3,12 +3,8 @@
|
|||||||
#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);
|
signal_clicked().connect(sigc::mem_fun(*this, &Popover::on_toggle_window));
|
||||||
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 = new Gtk::Popover();
|
||||||
popover->set_parent(*this);
|
popover->set_parent(*this);
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
@@ -2,9 +2,10 @@
|
|||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "glib.h"
|
#include "glib.h"
|
||||||
#include "sigc++/signal.h"
|
|
||||||
|
|
||||||
BluetoothService::BluetoothService() {
|
BluetoothService::BluetoothService() {
|
||||||
GError *error = nullptr;
|
GError *error = nullptr;
|
||||||
@@ -24,8 +25,6 @@ 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();
|
||||||
@@ -72,8 +71,9 @@ bool BluetoothService::getIsDiscovering() {
|
|||||||
return discovering;
|
return discovering;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BluetoothService::setPowerState(bool state) {
|
void BluetoothService::togglePowerState() {
|
||||||
GError *error = nullptr;
|
GError *error = nullptr;
|
||||||
|
bool state = !this->powerState;
|
||||||
|
|
||||||
GDBusConnection *connection = g_dbus_proxy_get_connection(this->adapter_proxy);
|
GDBusConnection *connection = g_dbus_proxy_get_connection(this->adapter_proxy);
|
||||||
GVariant *reply = g_dbus_connection_call_sync(
|
GVariant *reply = g_dbus_connection_call_sync(
|
||||||
@@ -108,17 +108,11 @@ void BluetoothService::setPowerState(bool state) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BluetoothService::setIsDiscovering(bool state) {
|
void BluetoothService::toggleIsDiscovering() {
|
||||||
const bool currently_discovering = this->getIsDiscovering();
|
bool newState = !this->isDiscovering;
|
||||||
this->isDiscovering = currently_discovering;
|
|
||||||
|
|
||||||
if (currently_discovering == state) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
GError *error = nullptr;
|
GError *error = nullptr;
|
||||||
|
|
||||||
const char *method = state ? "StartDiscovery" : "StopDiscovery";
|
const char *method = newState ? "StartDiscovery" : "StopDiscovery";
|
||||||
GVariant *reply = g_dbus_proxy_call_sync(
|
GVariant *reply = g_dbus_proxy_call_sync(
|
||||||
this->adapter_proxy,
|
this->adapter_proxy,
|
||||||
method,
|
method,
|
||||||
@@ -140,7 +134,7 @@ void BluetoothService::setIsDiscovering(bool state) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->isDiscovering = state;
|
this->isDiscovering = newState;
|
||||||
|
|
||||||
if (reply) {
|
if (reply) {
|
||||||
g_variant_unref(reply);
|
g_variant_unref(reply);
|
||||||
@@ -157,8 +151,10 @@ void BluetoothService::onPropertyChanged(GDBusProxy *proxy,
|
|||||||
|
|
||||||
if (g_variant_lookup(changed_properties, "Powered", "b", &is_powered)) {
|
if (g_variant_lookup(changed_properties, "Powered", "b", &is_powered)) {
|
||||||
|
|
||||||
this->setIsDiscovering(false);
|
if (!is_powered) {
|
||||||
isDiscoveringChangedSignal.emit(isDiscovering);
|
this->isDiscovering = is_discovering;
|
||||||
|
isDiscoveringChangedSignal.emit(isDiscovering);
|
||||||
|
}
|
||||||
|
|
||||||
this->powerState = is_powered;
|
this->powerState = is_powered;
|
||||||
powerStateChangedSignal.emit(powerState);
|
powerStateChangedSignal.emit(powerState);
|
||||||
@@ -167,6 +163,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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,3 +180,58 @@ void BluetoothService::onPropertyChangedStatic(GDBusProxy *proxy,
|
|||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> BluetoothService::getDeviceObjectPaths() {
|
||||||
|
std::vector<std::string> device_paths;
|
||||||
|
|
||||||
|
GError *error = nullptr;
|
||||||
|
GDBusConnection *connection = g_dbus_proxy_get_connection(this->adapter_proxy);
|
||||||
|
GVariant *reply = g_dbus_connection_call_sync(
|
||||||
|
connection,
|
||||||
|
"org.bluez",
|
||||||
|
"/",
|
||||||
|
"org.freedesktop.DBus.ObjectManager",
|
||||||
|
"GetManagedObjects",
|
||||||
|
nullptr,
|
||||||
|
G_VARIANT_TYPE("(a{oa{sa{sv}}})"),
|
||||||
|
G_DBUS_CALL_FLAGS_NONE,
|
||||||
|
-1,
|
||||||
|
nullptr,
|
||||||
|
&error);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
std::cerr << "Error calling GetManagedObjects: " << error->message << std::endl;
|
||||||
|
g_error_free(error);
|
||||||
|
return device_paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!reply) {
|
||||||
|
return device_paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
GVariant *objects = g_variant_get_child_value(reply, 0);
|
||||||
|
g_variant_unref(reply);
|
||||||
|
|
||||||
|
if (!objects) {
|
||||||
|
return device_paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
GVariantIter iter;
|
||||||
|
g_variant_iter_init(&iter, objects);
|
||||||
|
|
||||||
|
const gchar *object_path = nullptr;
|
||||||
|
GVariant *interfaces = nullptr;
|
||||||
|
while (g_variant_iter_next(&iter, "{&o@a{sa{sv}}}", &object_path, &interfaces)) {
|
||||||
|
GVariant *device_props = nullptr;
|
||||||
|
if (g_variant_lookup(interfaces, "org.bluez.Device1", "@a{sv}", &device_props)) {
|
||||||
|
device_paths.emplace_back(object_path);
|
||||||
|
g_variant_unref(device_props);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_variant_unref(interfaces);
|
||||||
|
interfaces = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_variant_unref(objects);
|
||||||
|
return device_paths;
|
||||||
|
}
|
||||||
@@ -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 |
|
||||||
@@ -263,10 +258,10 @@ void HyprlandService::onUrgentEvent(std::string windowAddress) {
|
|||||||
auto it = this->workspaces.find(workspaceId);
|
auto it = this->workspaces.find(workspaceId);
|
||||||
if (it != this->workspaces.end() && it->second) {
|
if (it != this->workspaces.end() && it->second) {
|
||||||
|
|
||||||
if (std::find(it->second->urgentWindows.begin(),
|
WorkspaceState *ws = it->second;
|
||||||
it->second->urgentWindows.end(), windowAddress) ==
|
auto uit = std::find(ws->urgentWindows.begin(), ws->urgentWindows.end(), windowAddress);
|
||||||
it->second->urgentWindows.end()) {
|
if (uit == ws->urgentWindows.end()) {
|
||||||
it->second->urgentWindows.push_back(windowAddress);
|
ws->urgentWindows.push_back(windowAddress);
|
||||||
workspaceStateChanged.emit();
|
workspaceStateChanged.emit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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()) {
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -1,67 +1,69 @@
|
|||||||
#include "widgets/bluetooth.hpp"
|
#include "widgets/bluetooth.hpp"
|
||||||
|
#include "gtkmm/label.h"
|
||||||
|
|
||||||
BluetoothWidget::BluetoothWidget(std::string icon, std::string title) : Popover(icon, title) {
|
|
||||||
this->popover->set_size_request(100, -1);
|
|
||||||
set_popover_child(this->container);
|
|
||||||
|
|
||||||
this->container.set_orientation(Gtk::Orientation::VERTICAL);
|
BluetoothWidget::BluetoothWidget() : Gtk::Box() {
|
||||||
this->container.set_spacing(10);
|
this->set_orientation(Gtk::Orientation::VERTICAL);
|
||||||
this->container.add_css_class("bluetooth-popover-container");
|
this->add_css_class("bluetooth-popover-container");
|
||||||
|
|
||||||
this->statusArea.add_css_class("bluetooth-status-area");
|
this->statusArea.add_css_class("bluetooth-status-area");
|
||||||
this->statusArea.set_hexpand(true);
|
this->statusArea.set_hexpand(true);
|
||||||
this->statusArea.set_halign(Gtk::Align::FILL);
|
this->statusArea.set_halign(Gtk::Align::FILL);
|
||||||
this->container.append(this->statusArea);
|
this->append(this->statusArea);
|
||||||
|
|
||||||
this->toggleButton = Gtk::make_managed<Gtk::Button>("\ue1a8");
|
this->powerButton = Gtk::make_managed<Button>("\ue1a8");
|
||||||
this->toggleButton->add_css_class("bluetooth-toggle-button");
|
this->powerButton->set_tooltip_text("Turn Bluetooth Off");
|
||||||
this->toggleButton->set_tooltip_text("Turn Bluetooth Off");
|
this->powerButton->signal_clicked().connect(sigc::mem_fun(*this, &BluetoothWidget::onPowerButtonClicked));
|
||||||
this->toggleButton->signal_clicked().connect([this]() {
|
this->powerButton->add_css_class("toggle-button");
|
||||||
const bool newState = !isPowered;
|
|
||||||
setPowerState(newState);
|
|
||||||
powerStateChangedSignal.emit(newState);
|
|
||||||
});
|
|
||||||
this->toggleButton->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("bluetooth-scan-button");
|
this->scanButton->add_css_class("toggle-button");
|
||||||
this->scanButton->signal_clicked().connect([this]() {
|
this->scanButton->signal_clicked().connect(sigc::mem_fun(*this, &BluetoothWidget::onScanButtonClicked));
|
||||||
const bool newState = !isDiscovering;
|
|
||||||
setIsDiscovering(newState);
|
|
||||||
isDiscoveringChangedSignal.emit(newState);
|
|
||||||
});
|
|
||||||
|
|
||||||
this->statusArea.append(*this->toggleButton);
|
this->statusArea.append(*this->powerButton);
|
||||||
this->statusArea.append(*this->scanButton);
|
this->statusArea.append(*this->scanButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BluetoothWidget::onPowerButtonClicked() {
|
||||||
|
onPowerStateButtonClickedSignal.emit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BluetoothWidget::onScanButtonClicked() {
|
||||||
|
onIsDiscoveringButtonClickedSignal.emit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BluetoothWidget::toggleButton(Button *button, bool state) {
|
||||||
|
if (state) {
|
||||||
|
button->add_css_class("toggle-button-on");
|
||||||
|
button->remove_css_class("toggle-button-off");
|
||||||
|
} else {
|
||||||
|
button->add_css_class("toggle-button-off");
|
||||||
|
button->remove_css_class("toggle-button-on");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void BluetoothWidget::setPowerState(bool state) {
|
void BluetoothWidget::setPowerState(bool state) {
|
||||||
this->isPowered = state;
|
this->isPowered = state;
|
||||||
|
|
||||||
if (state) {
|
this->scanButton->set_sensitive(state);
|
||||||
this->toggleButton->set_label("\ue1a8");
|
|
||||||
this->toggleButton->add_css_class("toggle-button-on");
|
if (!state) {
|
||||||
this->toggleButton->remove_css_class("toggle-button-off");
|
this->scanButton->add_css_class("toggle-button-disabled");
|
||||||
|
// this->add_css_class("disabled-popover-icon");
|
||||||
|
this->setIsDiscovering(false);
|
||||||
} else {
|
} else {
|
||||||
this->toggleButton->set_label("\ue1a9");
|
this->scanButton->remove_css_class("toggle-button-disabled");
|
||||||
this->toggleButton->add_css_class("toggle-button-off");
|
// this->remove_css_class("disabled-popover-icon");
|
||||||
this->toggleButton->remove_css_class("toggle-button-on");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this->setIsDiscovering(false);
|
this->toggleButton(this->powerButton, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BluetoothWidget::setIsDiscovering(bool state) {
|
void BluetoothWidget::setIsDiscovering(bool state) {
|
||||||
this->isDiscovering = state;
|
this->isDiscovering = state;
|
||||||
|
|
||||||
if (state) {
|
this->toggleButton(this->scanButton, state);
|
||||||
this->scanButton->add_css_class("toggle-button-on");
|
|
||||||
this->scanButton->remove_css_class("toggle-button-off");
|
|
||||||
} else {
|
|
||||||
this->scanButton->add_css_class("toggle-button-off");
|
|
||||||
this->scanButton->remove_css_class("toggle-button-on");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BluetoothWidget::update() {
|
void BluetoothWidget::update() {
|
||||||
|
|||||||
24
src/widgets/controlCenter.cpp
Normal file
24
src/widgets/controlCenter.cpp
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#include "widgets/controlCenter.hpp"
|
||||||
|
|
||||||
|
ControlCenter::ControlCenter(std::string icon, std::string name)
|
||||||
|
: Popover(icon, name) {
|
||||||
|
this->popover->set_size_request(200, -1);
|
||||||
|
set_popover_child(this->container);
|
||||||
|
|
||||||
|
this->bluetoothWidget = Gtk::make_managed<BluetoothWidget>();
|
||||||
|
this->container.append(*this->bluetoothWidget);
|
||||||
|
|
||||||
|
bluetoothService->powerStateChangedSignal.connect(
|
||||||
|
sigc::mem_fun(*this->bluetoothWidget, &BluetoothWidget::setPowerState));
|
||||||
|
|
||||||
|
bluetoothWidget->onPowerStateButtonClickedSignal.connect(
|
||||||
|
sigc::mem_fun(*this->bluetoothService, &BluetoothService::togglePowerState));
|
||||||
|
|
||||||
|
bluetoothWidget->setPowerState(bluetoothService->getPowerState());
|
||||||
|
|
||||||
|
bluetoothService->isDiscoveringChangedSignal.connect(
|
||||||
|
sigc::mem_fun(*this->bluetoothWidget, &BluetoothWidget::setIsDiscovering));
|
||||||
|
bluetoothWidget->onIsDiscoveringButtonClickedSignal.connect(
|
||||||
|
sigc::mem_fun(*this->bluetoothService, &BluetoothService::toggleIsDiscovering));
|
||||||
|
bluetoothWidget->setIsDiscovering(bluetoothService->getIsDiscovering());
|
||||||
|
}
|
||||||
@@ -9,7 +9,6 @@ TodoPopover::TodoPopover(std::string icon, std::string title) : Popover(icon, ti
|
|||||||
this->popover->set_size_request(300, -1);
|
this->popover->set_size_request(300, -1);
|
||||||
|
|
||||||
container.set_orientation(Gtk::Orientation::VERTICAL);
|
container.set_orientation(Gtk::Orientation::VERTICAL);
|
||||||
container.set_spacing(10);
|
|
||||||
container.add_css_class("todo-popover-container");
|
container.add_css_class("todo-popover-container");
|
||||||
|
|
||||||
auto entry = Gtk::make_managed<Gtk::Entry>();
|
auto entry = Gtk::make_managed<Gtk::Entry>();
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -3,39 +3,57 @@
|
|||||||
#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);
|
||||||
|
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 +73,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user