diff --git a/.clang-format b/.clang-format index 44598e4..d32bc89 100644 --- a/.clang-format +++ b/.clang-format @@ -1,3 +1,12 @@ IndentWidth: 4 ColumnLimit: 0 -AlignConsecutiveAssignments: true \ No newline at end of file +AlignConsecutiveAssignments: true + +IncludeCategories: + - Regex: '^(<.+>)$' + Priority: 1 + - Regex: '^"(.+\.hpp)"$' + Priority: 2 + - Regex: '.*' + Priority: 3 +IncludeBlocks: Regroup \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index f26e6b9..38036e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,9 +34,11 @@ target_sources(bar_lib src/services/hyprland.cpp src/services/tray.cpp src/services/notifications.cpp + src/services/bluetooth.cpp src/widgets/tray.cpp - src/widgets/todoPopover.cpp + src/widgets/todo.cpp + src/widgets/bluetooth.cpp src/components/popover.cpp src/components/todoEntry.cpp diff --git a/include/app.hpp b/include/app.hpp index 9170361..69211c7 100644 --- a/include/app.hpp +++ b/include/app.hpp @@ -3,12 +3,14 @@ #include #include "bar/bar.hpp" -#include "glibmm/refptr.h" -#include "gtkmm/application.h" +#include "services/bluetooth.hpp" #include "services/hyprland.hpp" #include "services/notifications.hpp" #include "services/tray.hpp" +#include "glibmm/refptr.h" +#include "gtkmm/application.h" + class App { public: App(); @@ -21,6 +23,7 @@ class App { HyprlandService hyprlandService; NotificationService notificationService; TrayService trayService; + BluetoothService bluetoothService; void setupServices(); }; \ No newline at end of file diff --git a/include/bar/bar.hpp b/include/bar/bar.hpp index abd71bd..b272304 100644 --- a/include/bar/bar.hpp +++ b/include/bar/bar.hpp @@ -2,9 +2,12 @@ #include #include + #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/date.hpp" #include "widgets/tray.hpp" @@ -14,7 +17,7 @@ class Bar : public Gtk::Window { public: - Bar(GdkMonitor *monitor, HyprlandService &hyprlandService, TrayService &trayService, int monitorId); + Bar(GdkMonitor *monitor, HyprlandService &hyprlandService, TrayService &trayService, BluetoothService &bluetoothService, int monitorId); protected: Gtk::CenterBox main_box{}; @@ -30,11 +33,13 @@ class Bar : public Gtk::Window { WebWidget homeAssistant{ICON_HOME, "Home Assistant", "https://home.rivercry.com"}; WorkspaceIndicator *workspaceIndicator = nullptr; - TrayWidget *trayWidget = nullptr; - VolumeWidget *volumeWidget = nullptr; + TrayWidget *trayWidget = nullptr; + VolumeWidget *volumeWidget = nullptr; + BluetoothWidget *bluetoothWidget = nullptr; TrayService &trayService; HyprlandService &hyprlandService; + BluetoothService &bluetoothService; void setup_ui(); void setup_left_box(); diff --git a/include/components/popover.hpp b/include/components/popover.hpp index 6ecd684..2ab92e7 100644 --- a/include/components/popover.hpp +++ b/include/components/popover.hpp @@ -2,18 +2,17 @@ #include #include - #include -class Popover: public Gtk::Button { -public: +class Popover : public Gtk::Button { + public: Popover(std::string icon, std::string name); ~Popover() override; -protected: + protected: void on_toggle_window(); - Gtk::Popover* popover = nullptr; - void set_popover_child(Gtk::Widget& child) { + Gtk::Popover *popover = nullptr; + void set_popover_child(Gtk::Widget &child) { gtk_popover_set_child(popover->gobj(), child.gobj()); } }; \ No newline at end of file diff --git a/include/components/todoEntry.hpp b/include/components/todoEntry.hpp index 4a010d8..b9e588b 100644 --- a/include/components/todoEntry.hpp +++ b/include/components/todoEntry.hpp @@ -1,20 +1,22 @@ #pragma once -#include "gtkmm/box.h" #include +#include "gtkmm/box.h" class TodoEntry : public Gtk::Box { -public: - TodoEntry(int id, std::string text, sigc::signal signal_dismissed); + public: + TodoEntry(int id, std::string text, sigc::signal signal_dismissed, sigc::signal signal_edited); ~TodoEntry() override; int get_id() const { return id; } std::string get_text() const { return text; } -private: + + private: int id; std::string text; sigc::signal signal_dismissed; + sigc::signal signal_edited; void on_dismiss_clicked(); }; \ No newline at end of file diff --git a/include/interface/updateable.ipp b/include/interface/updateable.ipp index 69196dc..ffee07c 100644 --- a/include/interface/updateable.ipp +++ b/include/interface/updateable.ipp @@ -1,7 +1,7 @@ #pragma once class IUpdatable { -public: - virtual ~IUpdatable() = default; + public: + virtual ~IUpdatable() = default; virtual bool onUpdate() = 0; }; \ No newline at end of file diff --git a/include/services/bluetooth.hpp b/include/services/bluetooth.hpp new file mode 100644 index 0000000..ace3100 --- /dev/null +++ b/include/services/bluetooth.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include +#include + +#include "sigc++/signal.h" + +class BluetoothService { + public: + BluetoothService(); + + sigc::signal powerStateChangedSignal; + sigc::signal isDiscoveringChangedSignal; + + bool getPowerState(); + void setPowerState(bool state); + + bool getIsDiscovering(); + void setIsDiscovering(bool state); + +private: + GDBusProxy *adapter_proxy = nullptr; + bool powerState = false; + bool isDiscovering = false; + + void onPropertyChanged(GDBusProxy *proxy, + GVariant *changed_properties, + const gchar *const *invalidated_properties, + gpointer user_data); + + static void onPropertyChangedStatic(GDBusProxy *proxy, + GVariant *changed_properties, + const gchar *const *invalidated_properties, + gpointer user_data); +}; \ No newline at end of file diff --git a/include/services/notifications.hpp b/include/services/notifications.hpp index c3e63ef..e13c8e5 100644 --- a/include/services/notifications.hpp +++ b/include/services/notifications.hpp @@ -3,18 +3,18 @@ #include class NotificationService { -public: + public: void intialize(); NotificationService() = default; ~NotificationService(); guint32 allocateNotificationId(guint32 replacesId); - GDBusConnection* getConnection() const { return connection; } + GDBusConnection *getConnection() const { return connection; } -private: - GDBusConnection* connection = nullptr; - guint registrationId = 0; - GDBusNodeInfo* nodeInfo = nullptr; - guint32 nextNotificationId = 1; + private: + GDBusConnection *connection = nullptr; + guint registrationId = 0; + GDBusNodeInfo *nodeInfo = nullptr; + guint32 nextNotificationId = 1; }; diff --git a/include/services/todo.hpp b/include/services/todo.hpp index b5eae55..4569df2 100644 --- a/include/services/todo.hpp +++ b/include/services/todo.hpp @@ -1,9 +1,10 @@ #pragma once +#include +#include + #include "components/todoEntry.hpp" #include "services/todoAdapter.hpp" -#include -#include class TodoService { public: diff --git a/include/services/todoAdapter.hpp b/include/services/todoAdapter.hpp index c1336d1..ad499cd 100644 --- a/include/services/todoAdapter.hpp +++ b/include/services/todoAdapter.hpp @@ -12,10 +12,10 @@ class ITodoAdapter { public: virtual ~ITodoAdapter() = default; - virtual bool init() = 0; + virtual bool init() = 0; virtual std::vector listTodos() = 0; - virtual int addTodo(const std::string &text) = 0; - virtual bool removeTodo(int id) = 0; + virtual int addTodo(const std::string &text) = 0; + virtual bool removeTodo(int id) = 0; virtual bool updateTodo(int id, const std::string &text) = 0; }; diff --git a/include/services/tray.hpp b/include/services/tray.hpp index b2c3335..2ed5d39 100644 --- a/include/services/tray.hpp +++ b/include/services/tray.hpp @@ -1,18 +1,17 @@ #pragma once #include +#include #include #include #include #include #include #include -#include - -#include #include #include #include +#include #include #include @@ -49,8 +48,8 @@ class TrayService { struct MenuNode { int id = 0; std::string label; - bool enabled = true; - bool visible = true; + bool enabled = true; + bool visible = true; bool separator = false; std::vector children; }; @@ -65,7 +64,7 @@ class TrayService { struct TrackedItem { Item publicData; guint signalSubscriptionId = 0; - guint ownerWatchId = 0; + guint ownerWatchId = 0; Glib::RefPtr menuModel; Glib::RefPtr menuActions; }; @@ -74,9 +73,9 @@ class TrayService { Glib::RefPtr nodeInfo; Gio::DBus::InterfaceVTable vtable; - guint nameOwnerId = 0; + guint nameOwnerId = 0; guint registrationId = 0; - bool hostRegistered = false; + bool hostRegistered = false; std::map> items; diff --git a/include/widgets/bluetooth.hpp b/include/widgets/bluetooth.hpp new file mode 100644 index 0000000..c1416ba --- /dev/null +++ b/include/widgets/bluetooth.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include + +#include "components/popover.hpp" + +#include "gtkmm/button.h" + +class BluetoothWidget : public Popover { + public: + BluetoothWidget(std::string icon, std::string title); + + void setPowerState(bool state); + void setIsDiscovering(bool state); + + sigc::signal powerStateChangedSignal; + sigc::signal isDiscoveringChangedSignal; + + void update(); + private: + bool isPowered = false; + bool isDiscovering = false; + + Gtk::Box container; + Gtk::Box statusArea; + + Gtk::Box *deviceList = nullptr; + + Gtk::Button *scanButton = nullptr; + Gtk::Button *toggleButton = nullptr; +}; \ No newline at end of file diff --git a/include/widgets/todoPopover.hpp b/include/widgets/todo.hpp similarity index 74% rename from include/widgets/todoPopover.hpp rename to include/widgets/todo.hpp index 434f17e..7964aba 100644 --- a/include/widgets/todoPopover.hpp +++ b/include/widgets/todo.hpp @@ -1,19 +1,21 @@ #pragma once +#include + #include "components/popover.hpp" #include "services/todo.hpp" -#include class TodoPopover : public Popover { -public: + public: TodoPopover(std::string icon, std::string title); void update(); -private: + + private: std::string name; - TodoService* todoService = nullptr; + TodoService *todoService = nullptr; Gtk::Box container; Gtk::Box inputArea; - Gtk::Box* todoList = nullptr; + Gtk::Box *todoList = nullptr; }; \ No newline at end of file diff --git a/include/widgets/tray.hpp b/include/widgets/tray.hpp index ec7796f..2d39dc2 100644 --- a/include/widgets/tray.hpp +++ b/include/widgets/tray.hpp @@ -11,7 +11,6 @@ #include #include #include - #include #include #include @@ -37,8 +36,8 @@ class TrayIconWidget : public Gtk::Button { Glib::RefPtr menuModel; sigc::connection menuChangedConnection; bool menuPopupPending = false; - double pendingX = 0.0; - double pendingY = 0.0; + double pendingX = 0.0; + double pendingY = 0.0; void on_primary_released(int n_press, double x, double y); void on_secondary_released(int n_press, double x, double y); diff --git a/include/widgets/webWidget.hpp b/include/widgets/webWidget.hpp index 4241ca7..ee80605 100644 --- a/include/widgets/webWidget.hpp +++ b/include/widgets/webWidget.hpp @@ -1,11 +1,13 @@ #pragma once -#include "components/popover.hpp" #include #include +#include "components/popover.hpp" + class WebWidget : public Popover { -public: + public: WebWidget(std::string icon, std::string title, std::string url); -private: + + private: }; diff --git a/include/widgets/workspaceIndicator.hpp b/include/widgets/workspaceIndicator.hpp index 671f78b..5722357 100644 --- a/include/widgets/workspaceIndicator.hpp +++ b/include/widgets/workspaceIndicator.hpp @@ -1,8 +1,8 @@ #pragma once #include -#include #include +#include #include #include "services/hyprland.hpp" @@ -18,7 +18,7 @@ class WorkspaceIndicator : public Gtk::Box { sigc::connection workspaceConnection; sigc::connection monitorConnection; std::map workspaceLabels; - std::map> workspaceGestures; + std::map> workspaceGestures; void rebuild(); void on_workspace_update(); diff --git a/resources/bar.css b/resources/bar.css index 969af68..cdcea46 100644 --- a/resources/bar.css +++ b/resources/bar.css @@ -2,7 +2,6 @@ all: unset; } - window { background-color: rgba(30, 30, 30, 0.8); color: #ffffff; @@ -73,6 +72,7 @@ button { background-color: transparent; color: #ffffff; border: none; + font-size: 20px; } button:hover { @@ -88,9 +88,10 @@ popover { background-color: rgb(30, 30, 30); color: #ffffff; font-family: "IBMPlexSans-Regular", sans-serif; - padding: 10px; + padding: 5px; border-radius: 8px; border: 1px solid #444444; + margin-top: 5px; } tooltip { @@ -119,4 +120,19 @@ tooltip { border: 1px solid #555555; background-color: #222222; color: #ffffff; +} + +.toggle-button { + border-radius: 4px; +} + +/* use background to highlight using same dark colors as in file */ +.toggle-button-on { + background-color: rgba(0, 150, 136, 0.2); + border: 1px solid #009688; +} + +.toggle-button-off { + background-color: rgba(244, 67, 54, 0.2); + border: 1px solid #f44336; } \ No newline at end of file diff --git a/src/app.cpp b/src/app.cpp index be64910..2ca17be 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -29,8 +29,10 @@ App::App() { continue; } + + auto bar = new Bar(monitor->gobj(), this->hyprlandService, - this->trayService, hyprlandMonitor->id); + this->trayService, this->bluetoothService, hyprlandMonitor->id); bar->set_application(app); bar->show(); diff --git a/src/bar/bar.cpp b/src/bar/bar.cpp index fee9ac3..d5e3342 100644 --- a/src/bar/bar.cpp +++ b/src/bar/bar.cpp @@ -1,26 +1,24 @@ #include "bar/bar.hpp" -#include "gtk/gtk.h" - -#include "widgets/date.hpp" -#include "widgets/spacer.hpp" -#include "widgets/todoPopover.hpp" -#include "widgets/volumeWidget.hpp" -#include "widgets/workspaceIndicator.hpp" - -#include "helpers/systemHelper.hpp" - #include #include #include #include +#include "helpers/systemHelper.hpp" +#include "widgets/date.hpp" +#include "widgets/spacer.hpp" +#include "widgets/todo.hpp" +#include "widgets/volumeWidget.hpp" +#include "widgets/workspaceIndicator.hpp" + #include "glibmm/main.h" +#include "gtk/gtk.h" #include "sigc++/functors/mem_fun.h" Bar::Bar(GdkMonitor *monitor, HyprlandService &hyprlandService, - TrayService &trayService, int monitorId) - : hyprlandService(hyprlandService), trayService(trayService), + TrayService &trayService, BluetoothService &bluetoothService, int monitorId) + : hyprlandService(hyprlandService), trayService(trayService), bluetoothService(bluetoothService), monitorId(monitorId) { set_name("bar-window"); @@ -39,20 +37,30 @@ Bar::Bar(GdkMonitor *monitor, HyprlandService &hyprlandService, set_child(main_box); - this->volumeWidget = Gtk::make_managed(); + this->volumeWidget = Gtk::make_managed(); this->workspaceIndicator = Gtk::make_managed(hyprlandService, monitorId); - this->trayWidget = Gtk::make_managed(trayService); + this->trayWidget = Gtk::make_managed(trayService); + this->bluetoothWidget = Gtk::make_managed("\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(); setup_ui(); clock.onUpdate(); - - Glib::signal_timeout().connect(sigc::mem_fun(clock, &Clock::onUpdate), - 1000); - date.onUpdate(); + Glib::signal_timeout().connect(sigc::mem_fun(clock, &Clock::onUpdate), 1000); Glib::signal_timeout().connect(sigc::mem_fun(date, &Date::onUpdate), 1000); } @@ -83,8 +91,9 @@ void Bar::setup_center_box() { } void Bar::setup_right_box() { - right_box.append(*trayWidget); - right_box.append(homeAssistant); + right_box.append(*this->trayWidget); + right_box.append(this->homeAssistant); + right_box.append(*this->bluetoothWidget); } void Bar::load_css() { diff --git a/src/components/todoEntry.cpp b/src/components/todoEntry.cpp index 89e2f6b..1eb0b52 100644 --- a/src/components/todoEntry.cpp +++ b/src/components/todoEntry.cpp @@ -1,15 +1,16 @@ #include "components/todoEntry.hpp" #include +#include #include "gtkmm/button.h" -TodoEntry::TodoEntry(int id, std::string text, sigc::signal signal_dismissed) +TodoEntry::TodoEntry(int id, std::string text, sigc::signal signal_dismissed, sigc::signal signal_edited) : Gtk::Box(Gtk::Orientation::HORIZONTAL) { this->id = id; this->text = text; this->signal_dismissed = signal_dismissed; - + this->signal_edited = signal_edited; auto box = Gtk::make_managed(Gtk::Orientation::HORIZONTAL); box->set_hexpand(true); @@ -35,6 +36,10 @@ TodoEntry::TodoEntry(int id, std::string text, sigc::signal signal_di dismissButton->signal_clicked().connect(sigc::mem_fun(*this, &TodoEntry::on_dismiss_clicked)); + auto editButton = Gtk::make_managed("\uf044"); + editButton->set_valign(Gtk::Align::CENTER); + + buttonBox->append(*editButton); buttonBox->append(*dismissButton); } diff --git a/src/services/bluetooth.cpp b/src/services/bluetooth.cpp new file mode 100644 index 0000000..5eda130 --- /dev/null +++ b/src/services/bluetooth.cpp @@ -0,0 +1,185 @@ +#include "services/bluetooth.hpp" + +#include +#include + +#include "glib.h" +#include "sigc++/signal.h" + +BluetoothService::BluetoothService() { + GError *error = nullptr; + + this->adapter_proxy = g_dbus_proxy_new_for_bus_sync( + G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_NONE, + nullptr, + "org.bluez", + "/org/bluez/hci0", + "org.bluez.Adapter1", + nullptr, + &error); + + if (error) { + std::cerr << "Error creating Bluetooth adapter proxy: " + << error->message << std::endl; + g_error_free(error); + assert(false); + } else { + std::cout << "Bluetooth adapter proxy created successfully." << std::endl; + } + + this->powerState = this->getPowerState(); + this->isDiscovering = this->getIsDiscovering(); + + g_signal_connect( + this->adapter_proxy, + "g-properties-changed", + G_CALLBACK(BluetoothService::onPropertyChangedStatic), + this); +} + +bool BluetoothService::getPowerState() { + GVariant *result = g_dbus_proxy_get_cached_property( + this->adapter_proxy, + "Powered"); + + if (!result) { + std::cerr << "Error getting Powered property." << std::endl; + return false; + } + + gboolean powered; + g_variant_get(result, "b", &powered); + g_variant_unref(result); + + return powered; +} + +bool BluetoothService::getIsDiscovering() { + GVariant *result = g_dbus_proxy_get_cached_property( + this->adapter_proxy, + "Discovering"); + + if (!result) { + std::cerr << "Error getting Discovering property." << std::endl; + return false; + } + + gboolean discovering; + g_variant_get(result, "b", &discovering); + g_variant_unref(result); + + return discovering; +} + +void BluetoothService::setPowerState(bool state) { + GError *error = nullptr; + + GDBusConnection *connection = g_dbus_proxy_get_connection(this->adapter_proxy); + GVariant *reply = g_dbus_connection_call_sync( + connection, + "org.bluez", + "/org/bluez/hci0", + "org.freedesktop.DBus.Properties", + "Set", + g_variant_new( + "(ssv)", + "org.bluez.Adapter1", + "Powered", + g_variant_new_boolean(state)), + nullptr, + G_DBUS_CALL_FLAGS_NONE, + -1, + nullptr, + &error); + + if (error) { + std::cerr << "Error setting Powered property: " + << error->message << std::endl; + g_error_free(error); + } + + if (reply) { + g_variant_unref(reply); + } + + if (!error) { + this->powerState = state; + } +} + +void BluetoothService::setIsDiscovering(bool state) { + const bool currently_discovering = this->getIsDiscovering(); + this->isDiscovering = currently_discovering; + + if (currently_discovering == state) { + return; + } + + GError *error = nullptr; + + const char *method = state ? "StartDiscovery" : "StopDiscovery"; + GVariant *reply = g_dbus_proxy_call_sync( + this->adapter_proxy, + method, + nullptr, + G_DBUS_CALL_FLAGS_NONE, + -1, + nullptr, + &error); + + if (error) { + std::cerr << "Error calling " << method << ": " + << error->message << std::endl; + g_error_free(error); + + if (reply) { + g_variant_unref(reply); + } + + return; + } + + this->isDiscovering = state; + + if (reply) { + g_variant_unref(reply); + } +} + +void BluetoothService::onPropertyChanged(GDBusProxy *proxy, + GVariant *changed_properties, + const gchar *const *invalidated_properties, + gpointer user_data) { + + gboolean is_powered = FALSE; + gboolean is_discovering = FALSE; + + if (g_variant_lookup(changed_properties, "Powered", "b", &is_powered)) { + + this->setIsDiscovering(false); + isDiscoveringChangedSignal.emit(isDiscovering); + + this->powerState = is_powered; + powerStateChangedSignal.emit(powerState); + } + + if (g_variant_lookup(changed_properties, "Discovering", "b", &is_discovering)) { + this->isDiscovering = is_discovering; + isDiscoveringChangedSignal.emit(isDiscovering); + } +} + +void BluetoothService::onPropertyChangedStatic(GDBusProxy *proxy, + GVariant *changed_properties, + const gchar *const *invalidated_properties, + gpointer user_data) { + BluetoothService *service = static_cast(user_data); + + if (service) { + service->onPropertyChanged(proxy, changed_properties, invalidated_properties, user_data); + } else { + std::cerr << "Error: BluetoothService instance is null in static callback." << std::endl; + assert(false); + } +} \ No newline at end of file diff --git a/src/services/hyprland.cpp b/src/services/hyprland.cpp index 98ffbf3..421c3e8 100644 --- a/src/services/hyprland.cpp +++ b/src/services/hyprland.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -10,8 +11,6 @@ #include #include -#include - #include "helpers/systemHelper.hpp" HyprlandService::HyprlandService() = default; diff --git a/src/services/sqliteTodoAdapter.cpp b/src/services/sqliteTodoAdapter.cpp index 43619bb..cbaf80d 100644 --- a/src/services/sqliteTodoAdapter.cpp +++ b/src/services/sqliteTodoAdapter.cpp @@ -1,10 +1,10 @@ -#include "services/todoAdapter.hpp" - #include #include #include #include +#include "services/todoAdapter.hpp" + namespace { std::string getDbPath() { const char *homeDir = getenv("HOME"); @@ -31,7 +31,6 @@ class SqliteTodoAdapter final : public ITodoAdapter { bool init() override { std::string dbPath = getDbPath(); - std::cout << "Opening database at: " << dbPath << std::endl; int rc = sqlite3_open(dbPath.c_str(), &db_); if (rc != SQLITE_OK) { @@ -42,8 +41,8 @@ class SqliteTodoAdapter final : public ITodoAdapter { const char *sql = "CREATE TABLE IF NOT EXISTS todos (" "id INTEGER PRIMARY KEY AUTOINCREMENT," "text TEXT NOT NULL);"; - char *zErrMsg = nullptr; - rc = sqlite3_exec(db_, sql, nullptr, nullptr, &zErrMsg); + char *zErrMsg = nullptr; + rc = sqlite3_exec(db_, sql, nullptr, nullptr, &zErrMsg); if (rc != SQLITE_OK) { std::cerr << "SQL error (create table): " << (zErrMsg ? zErrMsg : "") << std::endl; sqlite3_free(zErrMsg); diff --git a/src/services/todo.cpp b/src/services/todo.cpp index a98a77f..1d2d67b 100644 --- a/src/services/todo.cpp +++ b/src/services/todo.cpp @@ -1,8 +1,10 @@ #include "services/todo.hpp" -#include "components/todoEntry.hpp" -#include "gtkmm/object.h" + #include #include +#include + +#include "components/todoEntry.hpp" std::unique_ptr makeSqliteTodoAdapter(); @@ -62,9 +64,13 @@ TodoEntry *TodoService::addTodo(std::string text, bool emitSignal, bool persist) id = newId; } - auto signal = sigc::signal(); - signal.connect(sigc::mem_fun(*this, &TodoService::removeTodo)); - TodoEntry *todo = Gtk::make_managed(id, text, signal); + auto dismissSignal = sigc::signal(); + dismissSignal.connect(sigc::mem_fun(*this, &TodoService::removeTodo)); + + auto editSignal = sigc::signal(); + editSignal.connect(sigc::mem_fun(*this, &TodoService::updateTodo)); + + TodoEntry *todo = Gtk::make_managed(id, text, dismissSignal, editSignal); todos[id] = todo; @@ -79,14 +85,24 @@ TodoEntry *TodoService::addTodo(std::string text, bool emitSignal, bool persist) return todo; } -void TodoService::updateTodo(int id, std::string text) {} +void TodoService::updateTodo(int id, std::string text) { + if (todos.find(id) == todos.end()) { + std::cerr << "Todo with id " << id << " not found!" << std::endl; + assert(false); + } + + if (this->adapter) { + this->adapter->updateTodo(id, text); + } + + this->refreshSignal.emit(); +} void TodoService::load() { if (!this->adapter) { return; } - std::cout << "Loading todos from database..." << std::endl; auto rows = this->adapter->listTodos(); int count = 0; @@ -96,14 +112,17 @@ void TodoService::load() { int id = row.id; std::string text = row.text; - auto signal = sigc::signal(); - signal.connect(sigc::mem_fun(*this, &TodoService::removeTodo)); - TodoEntry *todo = Gtk::make_managed(id, text, signal); + auto dismissSignal = sigc::signal(); + dismissSignal.connect(sigc::mem_fun(*this, &TodoService::removeTodo)); + + auto editSignal = sigc::signal(); + editSignal.connect(sigc::mem_fun(*this, &TodoService::updateTodo)); + + TodoEntry *todo = Gtk::make_managed(id, text, dismissSignal, editSignal); todos[id] = todo; if (id >= nextId) { nextId = id + 1; } } - std::cout << "Loaded " << count << " todos." << std::endl; } diff --git a/src/services/tray.cpp b/src/services/tray.cpp index f969e95..f7621f7 100644 --- a/src/services/tray.cpp +++ b/src/services/tray.cpp @@ -1,5 +1,7 @@ #include "services/tray.hpp" +#include +#include #include #include #include @@ -7,9 +9,6 @@ #include #include #include - -#include -#include #include #include #include diff --git a/src/widgets/bluetooth.cpp b/src/widgets/bluetooth.cpp new file mode 100644 index 0000000..9636e52 --- /dev/null +++ b/src/widgets/bluetooth.cpp @@ -0,0 +1,70 @@ +#include "widgets/bluetooth.hpp" + +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); + this->container.set_spacing(10); + this->container.add_css_class("bluetooth-popover-container"); + + this->statusArea.add_css_class("bluetooth-status-area"); + this->statusArea.set_hexpand(true); + this->statusArea.set_halign(Gtk::Align::FILL); + this->container.append(this->statusArea); + + this->toggleButton = Gtk::make_managed("\ue1a8"); + this->toggleButton->add_css_class("bluetooth-toggle-button"); + this->toggleButton->set_tooltip_text("Turn Bluetooth Off"); + this->toggleButton->signal_clicked().connect([this]() { + const bool newState = !isPowered; + setPowerState(newState); + powerStateChangedSignal.emit(newState); + }); + this->toggleButton->add_css_class("toggle-button"); + + this->scanButton = Gtk::make_managed("\ue1aa"); + this->scanButton->set_tooltip_text("Scan for Devices"); + this->scanButton->add_css_class("bluetooth-scan-button"); + this->scanButton->signal_clicked().connect([this]() { + const bool newState = !isDiscovering; + setIsDiscovering(newState); + isDiscoveringChangedSignal.emit(newState); + }); + + this->statusArea.append(*this->toggleButton); + this->statusArea.append(*this->scanButton); +} + +void BluetoothWidget::setPowerState(bool state) { + this->isPowered = state; + + if (state) { + this->toggleButton->set_label("\ue1a8"); + this->toggleButton->add_css_class("toggle-button-on"); + this->toggleButton->remove_css_class("toggle-button-off"); + } else { + this->toggleButton->set_label("\ue1a9"); + this->toggleButton->add_css_class("toggle-button-off"); + this->toggleButton->remove_css_class("toggle-button-on"); + } + + this->setIsDiscovering(false); +} + +void BluetoothWidget::setIsDiscovering(bool state) { + this->isDiscovering = state; + + if (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() { + setPowerState(isPowered); + setIsDiscovering(isDiscovering); +} \ No newline at end of file diff --git a/src/widgets/todoPopover.cpp b/src/widgets/todo.cpp similarity index 88% rename from src/widgets/todoPopover.cpp rename to src/widgets/todo.cpp index 0eb8c15..b37f997 100644 --- a/src/widgets/todoPopover.cpp +++ b/src/widgets/todo.cpp @@ -1,4 +1,4 @@ -#include "widgets/todoPopover.hpp" +#include "widgets/todo.hpp" #include #include @@ -12,24 +12,23 @@ TodoPopover::TodoPopover(std::string icon, std::string title) : Popover(icon, ti container.set_spacing(10); container.add_css_class("todo-popover-container"); - auto entry = Gtk::make_managed(); entry->set_placeholder_text("Enter your to-do item..."); entry->add_css_class("todo-input"); entry->set_hexpand(true); entry->set_halign(Gtk::Align::FILL); entry->set_valign(Gtk::Align::START); - + inputArea.append(*entry); inputArea.add_css_class("todo-input-area"); inputArea.set_hexpand(true); inputArea.set_halign(Gtk::Align::FILL); - + entry->signal_activate().connect([this, entry]() { std::string text = entry->get_text(); if (!text.empty()) { this->todoService->addTodo(text); - + entry->set_text(""); } }); @@ -39,12 +38,12 @@ TodoPopover::TodoPopover(std::string icon, std::string title) : Popover(icon, ti this->todoList = Gtk::make_managed(Gtk::Orientation::VERTICAL); container.append(*todoList); todoList->add_css_class("todo-list"); - + auto signal = sigc::signal(); this->todoService = new TodoService(signal); signal.connect(sigc::mem_fun(*this, &TodoPopover::update)); - + this->set_popover_child(this->container); this->update(); } @@ -52,23 +51,23 @@ TodoPopover::TodoPopover(std::string icon, std::string title) : Popover(icon, ti void TodoPopover::update() { auto todos = this->todoService->getTodos(); - Gtk::Widget* child = todoList->get_first_child(); + Gtk::Widget *child = todoList->get_first_child(); while (child) { - Gtk::Widget* next = child->get_next_sibling(); - + Gtk::Widget *next = child->get_next_sibling(); + bool found = false; - for (auto& [id, todo] : todos) { + for (auto &[id, todo] : todos) { if (child == todo) { found = true; break; } } - + if (!found) { todoList->remove(*child); } - + child = next; } diff --git a/src/widgets/volumeWidget.cpp b/src/widgets/volumeWidget.cpp index 2581039..e733d9c 100644 --- a/src/widgets/volumeWidget.cpp +++ b/src/widgets/volumeWidget.cpp @@ -1,12 +1,12 @@ #include "widgets/volumeWidget.hpp" -#include "helpers/systemHelper.hpp" - #include #include #include #include +#include "helpers/systemHelper.hpp" + VolumeWidget::VolumeWidget() : Gtk::Box(Gtk::Orientation::HORIZONTAL) { set_valign(Gtk::Align::CENTER); set_halign(Gtk::Align::CENTER); diff --git a/src/widgets/webWidget.cpp b/src/widgets/webWidget.cpp index 726bc34..d2ab3b3 100644 --- a/src/widgets/webWidget.cpp +++ b/src/widgets/webWidget.cpp @@ -1,4 +1,5 @@ #include "widgets/webWidget.hpp" + #include #include diff --git a/src/widgets/workspaceIndicator.cpp b/src/widgets/workspaceIndicator.cpp index 046d6df..3e25231 100644 --- a/src/widgets/workspaceIndicator.cpp +++ b/src/widgets/workspaceIndicator.cpp @@ -1,5 +1,4 @@ #include "widgets/workspaceIndicator.hpp" -#include "services/hyprland.hpp" #include #include @@ -7,6 +6,8 @@ #include #include +#include "services/hyprland.hpp" + WorkspaceIndicator::WorkspaceIndicator(HyprlandService &service, int monitorId) : Gtk::Box(Gtk::Orientation::HORIZONTAL), service(service), monitorId(monitorId) {