fix code style
This commit is contained in:
@@ -3,9 +3,9 @@
|
||||
#include <chrono>
|
||||
#include <iomanip>
|
||||
|
||||
bool Clock::onUpdate()
|
||||
{
|
||||
auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
|
||||
bool Clock::onUpdate() {
|
||||
auto now =
|
||||
std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
|
||||
|
||||
std::stringstream ss;
|
||||
ss << std::put_time(std::localtime(&now), "%H:%M:%S");
|
||||
|
||||
@@ -1,363 +1,326 @@
|
||||
#include "widgets/tray.hpp"
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <gdkmm/rectangle.h>
|
||||
#include <gio/gmenu.h>
|
||||
#include <utility>
|
||||
#include <gtk/gtk.h>
|
||||
#include <iostream>
|
||||
#include <utility>
|
||||
|
||||
TrayIconWidget::TrayIconWidget(TrayService &service, std::string id)
|
||||
: m_service(service), m_id(std::move(id)), m_container(Gtk::Orientation::HORIZONTAL)
|
||||
{
|
||||
: service(service), id(std::move(id)),
|
||||
container(Gtk::Orientation::HORIZONTAL) {
|
||||
set_has_frame(false);
|
||||
set_focusable(false);
|
||||
set_valign(Gtk::Align::CENTER);
|
||||
set_halign(Gtk::Align::CENTER);
|
||||
|
||||
m_picture.set_halign(Gtk::Align::CENTER);
|
||||
m_picture.set_valign(Gtk::Align::CENTER);
|
||||
m_picture.set_can_shrink(true);
|
||||
m_picture.set_size_request(20, 20);
|
||||
picture.set_halign(Gtk::Align::CENTER);
|
||||
picture.set_valign(Gtk::Align::CENTER);
|
||||
picture.set_can_shrink(true);
|
||||
picture.set_size_request(20, 20);
|
||||
|
||||
m_image.set_pixel_size(20);
|
||||
m_image.set_halign(Gtk::Align::CENTER);
|
||||
m_image.set_valign(Gtk::Align::CENTER);
|
||||
image.set_pixel_size(20);
|
||||
image.set_halign(Gtk::Align::CENTER);
|
||||
image.set_valign(Gtk::Align::CENTER);
|
||||
|
||||
m_container.set_halign(Gtk::Align::CENTER);
|
||||
m_container.set_valign(Gtk::Align::CENTER);
|
||||
m_container.append(m_picture);
|
||||
m_container.append(m_image);
|
||||
container.set_halign(Gtk::Align::CENTER);
|
||||
container.set_valign(Gtk::Align::CENTER);
|
||||
container.append(picture);
|
||||
container.append(image);
|
||||
|
||||
m_picture.set_visible(false);
|
||||
m_image.set_visible(true);
|
||||
set_child(m_container);
|
||||
picture.set_visible(false);
|
||||
image.set_visible(true);
|
||||
set_child(container);
|
||||
|
||||
m_primaryGesture = Gtk::GestureClick::create();
|
||||
m_primaryGesture->set_button(GDK_BUTTON_PRIMARY);
|
||||
m_primaryGesture->signal_released().connect(sigc::mem_fun(*this, &TrayIconWidget::on_primary_released));
|
||||
add_controller(m_primaryGesture);
|
||||
primaryGesture = Gtk::GestureClick::create();
|
||||
primaryGesture->set_button(GDK_BUTTON_PRIMARY);
|
||||
primaryGesture->signal_released().connect(
|
||||
sigc::mem_fun(*this, &TrayIconWidget::on_primary_released));
|
||||
add_controller(primaryGesture);
|
||||
|
||||
m_secondaryGesture = Gtk::GestureClick::create();
|
||||
m_secondaryGesture->set_button(GDK_BUTTON_SECONDARY);
|
||||
m_secondaryGesture->signal_released().connect(sigc::mem_fun(*this, &TrayIconWidget::on_secondary_released));
|
||||
add_controller(m_secondaryGesture);
|
||||
secondaryGesture = Gtk::GestureClick::create();
|
||||
secondaryGesture->set_button(GDK_BUTTON_SECONDARY);
|
||||
secondaryGesture->signal_released().connect(
|
||||
sigc::mem_fun(*this, &TrayIconWidget::on_secondary_released));
|
||||
add_controller(secondaryGesture);
|
||||
}
|
||||
|
||||
void TrayIconWidget::update(const TrayService::Item &item)
|
||||
{
|
||||
if (!item.menuAvailable)
|
||||
{
|
||||
m_menuModel.reset();
|
||||
m_menuActions.reset();
|
||||
m_menuPopupPending = false;
|
||||
if (m_menuChangedConnection.connected())
|
||||
{
|
||||
m_menuChangedConnection.disconnect();
|
||||
void TrayIconWidget::update(const TrayService::Item &item) {
|
||||
if (!item.menuAvailable) {
|
||||
menuModel.reset();
|
||||
menuActions.reset();
|
||||
menuPopupPending = false;
|
||||
if (menuChangedConnection.connected()) {
|
||||
menuChangedConnection.disconnect();
|
||||
}
|
||||
if (m_menuPopover)
|
||||
{
|
||||
m_menuPopover->insert_action_group("dbusmenu", Glib::RefPtr<Gio::ActionGroup>());
|
||||
m_menuPopover->set_menu_model({});
|
||||
m_menuPopover->unparent();
|
||||
m_menuPopover.reset();
|
||||
if (menuPopover) {
|
||||
menuPopover->insert_action_group("dbusmenu",
|
||||
Glib::RefPtr<Gio::ActionGroup>());
|
||||
menuPopover->set_menu_model({});
|
||||
menuPopover->unparent();
|
||||
menuPopover.reset();
|
||||
}
|
||||
}
|
||||
|
||||
if (item.iconPaintable)
|
||||
{
|
||||
m_picture.set_paintable(item.iconPaintable);
|
||||
m_picture.set_visible(true);
|
||||
m_image.set_visible(false);
|
||||
}
|
||||
else if (!item.iconName.empty())
|
||||
{
|
||||
m_image.set_from_icon_name(item.iconName);
|
||||
m_image.set_pixel_size(20);
|
||||
m_image.set_visible(true);
|
||||
m_picture.set_visible(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_picture.set_paintable({});
|
||||
m_image.set_visible(false);
|
||||
m_picture.set_visible(false);
|
||||
if (item.iconPaintable) {
|
||||
picture.set_paintable(item.iconPaintable);
|
||||
picture.set_visible(true);
|
||||
image.set_visible(false);
|
||||
} else if (!item.iconName.empty()) {
|
||||
image.set_from_icon_name(item.iconName);
|
||||
image.set_pixel_size(20);
|
||||
image.set_visible(true);
|
||||
picture.set_visible(false);
|
||||
} else {
|
||||
picture.set_paintable({});
|
||||
image.set_visible(false);
|
||||
picture.set_visible(false);
|
||||
}
|
||||
|
||||
if (!item.title.empty())
|
||||
{
|
||||
if (!item.title.empty()) {
|
||||
set_tooltip_text(item.title);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
set_tooltip_text("");
|
||||
}
|
||||
|
||||
set_sensitive(item.status != "Passive");
|
||||
}
|
||||
|
||||
void TrayIconWidget::on_primary_released(int /*n_press*/, double x, double y)
|
||||
{
|
||||
m_service.activate(m_id, -1, -1);
|
||||
void TrayIconWidget::on_primary_released(int /*n_press*/, double x, double y) {
|
||||
service.activate(id, -1, -1);
|
||||
}
|
||||
|
||||
void TrayIconWidget::on_secondary_released(int /*n_press*/, double x, double y)
|
||||
{
|
||||
m_service.contextMenu(m_id, -1, -1);
|
||||
void TrayIconWidget::on_secondary_released(int /*n_press*/, double x,
|
||||
double y) {
|
||||
service.contextMenu(id, -1, -1);
|
||||
|
||||
if (!ensure_menu())
|
||||
{
|
||||
if (!ensure_menu()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_pendingX = x;
|
||||
m_pendingY = y;
|
||||
m_menuPopupPending = true;
|
||||
pendingX = x;
|
||||
pendingY = y;
|
||||
menuPopupPending = true;
|
||||
try_popup();
|
||||
}
|
||||
|
||||
bool TrayIconWidget::ensure_menu()
|
||||
{
|
||||
auto layoutOpt = m_service.get_menu_layout(m_id);
|
||||
if (!layoutOpt)
|
||||
{
|
||||
m_menuModel.reset();
|
||||
m_menuActions.reset();
|
||||
m_menuPopupPending = false;
|
||||
if (m_menuChangedConnection.connected())
|
||||
{
|
||||
m_menuChangedConnection.disconnect();
|
||||
bool TrayIconWidget::ensure_menu() {
|
||||
auto layoutOpt = service.get_menu_layout(id);
|
||||
if (!layoutOpt) {
|
||||
menuModel.reset();
|
||||
menuActions.reset();
|
||||
menuPopupPending = false;
|
||||
if (menuChangedConnection.connected()) {
|
||||
menuChangedConnection.disconnect();
|
||||
}
|
||||
if (m_menuPopover)
|
||||
{
|
||||
if (menuPopover) {
|
||||
remove_action_group("dbusmenu");
|
||||
m_menuPopover->set_menu_model({});
|
||||
m_menuPopover->unparent();
|
||||
m_menuPopover.reset();
|
||||
menuPopover->set_menu_model({});
|
||||
menuPopover->unparent();
|
||||
menuPopover.reset();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
const auto &layout = *layoutOpt;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
auto menu = Gio::Menu::create();
|
||||
auto actions = Gio::SimpleActionGroup::create();
|
||||
|
||||
populate_menu_items(layout.children, menu, actions);
|
||||
|
||||
const auto itemCount = menu->get_n_items();
|
||||
std::cout << "[TrayIconWidget] menu update for " << m_id << ", items: " << itemCount << std::endl;
|
||||
if (itemCount == 0)
|
||||
{
|
||||
m_service.debug_dump_menu_layout(m_id);
|
||||
std::cout << "[TrayIconWidget] menu update for " << id
|
||||
<< ", items: " << itemCount << std::endl;
|
||||
if (itemCount == 0) {
|
||||
service.debug_dump_menu_layout(id);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_menuModel = menu;
|
||||
m_menuActions = actions;
|
||||
menuModel = menu;
|
||||
menuActions = actions;
|
||||
|
||||
if (!m_menuPopover)
|
||||
{
|
||||
if (!menuPopover) {
|
||||
auto *rawPopover = Gtk::make_managed<Gtk::PopoverMenu>();
|
||||
m_menuPopover = Glib::make_refptr_for_instance<Gtk::PopoverMenu>(rawPopover);
|
||||
if (!m_menuPopover)
|
||||
{
|
||||
menuPopover =
|
||||
Glib::make_refptr_for_instance<Gtk::PopoverMenu>(rawPopover);
|
||||
if (!menuPopover) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_menuPopover->set_has_arrow(false);
|
||||
m_menuPopover->set_autohide(true);
|
||||
m_menuPopover->set_parent(*this);
|
||||
menuPopover->set_has_arrow(false);
|
||||
menuPopover->set_autohide(true);
|
||||
menuPopover->set_parent(*this);
|
||||
}
|
||||
|
||||
m_menuPopover->remove_action_group("dbusmenu");
|
||||
m_menuPopover->insert_action_group("dbusmenu", m_menuActions);
|
||||
menuPopover->remove_action_group("dbusmenu");
|
||||
menuPopover->insert_action_group("dbusmenu", menuActions);
|
||||
|
||||
if (m_menuChangedConnection.connected())
|
||||
{
|
||||
m_menuChangedConnection.disconnect();
|
||||
if (menuChangedConnection.connected()) {
|
||||
menuChangedConnection.disconnect();
|
||||
}
|
||||
m_menuChangedConnection = m_menuModel->signal_items_changed().connect(sigc::mem_fun(*this, &TrayIconWidget::on_menu_items_changed));
|
||||
menuChangedConnection = menuModel->signal_items_changed().connect(
|
||||
sigc::mem_fun(*this, &TrayIconWidget::on_menu_items_changed));
|
||||
|
||||
m_menuPopover->set_menu_model(m_menuModel);
|
||||
menuPopover->set_menu_model(menuModel);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TrayIconWidget::on_menu_items_changed(guint /*position*/, guint /*removed*/, guint /*added*/)
|
||||
{
|
||||
if (!m_menuModel)
|
||||
{
|
||||
void TrayIconWidget::on_menu_items_changed(guint /*position*/,
|
||||
guint /*removed*/, guint /*added*/) {
|
||||
if (!menuModel) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto count = m_menuModel->get_n_items();
|
||||
std::cout << "[TrayIconWidget] items changed for " << m_id << ": " << count << " entries" << std::endl;
|
||||
const auto count = menuModel->get_n_items();
|
||||
std::cout << "[TrayIconWidget] items changed for " << id << ": " << count
|
||||
<< " entries" << std::endl;
|
||||
try_popup();
|
||||
}
|
||||
|
||||
void TrayIconWidget::try_popup()
|
||||
{
|
||||
if (!m_menuPopupPending || !m_menuPopover || !m_menuModel)
|
||||
{
|
||||
void TrayIconWidget::try_popup() {
|
||||
if (!menuPopupPending || !menuPopover || !menuModel) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_menuModel->get_n_items() == 0)
|
||||
{
|
||||
if (menuModel->get_n_items() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Gdk::Rectangle rect(static_cast<int>(m_pendingX), static_cast<int>(m_pendingY), 1, 1);
|
||||
m_menuPopover->set_pointing_to(rect);
|
||||
m_menuPopover->popup();
|
||||
m_menuPopupPending = false;
|
||||
Gdk::Rectangle rect(static_cast<int>(pendingX), static_cast<int>(pendingY),
|
||||
1, 1);
|
||||
menuPopover->set_pointing_to(rect);
|
||||
menuPopover->popup();
|
||||
menuPopupPending = false;
|
||||
}
|
||||
|
||||
void TrayIconWidget::populate_menu_items(const std::vector<TrayService::MenuNode> &nodes,
|
||||
const Glib::RefPtr<Gio::Menu> &menu,
|
||||
const Glib::RefPtr<Gio::SimpleActionGroup> &actions)
|
||||
{
|
||||
for (const auto &node : nodes)
|
||||
{
|
||||
if (!node.visible)
|
||||
{
|
||||
void TrayIconWidget::populate_menu_items(
|
||||
const std::vector<TrayService::MenuNode> &nodes,
|
||||
const Glib::RefPtr<Gio::Menu> &menu,
|
||||
const Glib::RefPtr<Gio::SimpleActionGroup> &actions) {
|
||||
for (const auto &node : nodes) {
|
||||
if (!node.visible) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (node.separator)
|
||||
{
|
||||
if (node.separator) {
|
||||
auto section = Gio::Menu::create();
|
||||
menu->append_section("", section);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!node.children.empty())
|
||||
{
|
||||
if (!node.children.empty()) {
|
||||
auto submenu = Gio::Menu::create();
|
||||
populate_menu_items(node.children, submenu, actions);
|
||||
auto submenuItem = Gio::MenuItem::create(node.label, Glib::ustring());
|
||||
auto submenuItem =
|
||||
Gio::MenuItem::create(node.label, Glib::ustring());
|
||||
submenuItem->set_submenu(submenu);
|
||||
if (!node.enabled)
|
||||
{
|
||||
submenuItem->set_attribute_value("enabled", Glib::Variant<bool>::create(false));
|
||||
if (!node.enabled) {
|
||||
submenuItem->set_attribute_value(
|
||||
"enabled", Glib::Variant<bool>::create(false));
|
||||
}
|
||||
menu->append_item(submenuItem);
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::string actionName = "item" + std::to_string(node.id);
|
||||
auto menuItem = Gio::MenuItem::create(node.label, "dbusmenu." + actionName);
|
||||
if (!node.enabled)
|
||||
{
|
||||
menuItem->set_attribute_value("enabled", Glib::Variant<bool>::create(false));
|
||||
auto menuItem =
|
||||
Gio::MenuItem::create(node.label, "dbusmenu." + actionName);
|
||||
if (!node.enabled) {
|
||||
menuItem->set_attribute_value("enabled",
|
||||
Glib::Variant<bool>::create(false));
|
||||
}
|
||||
|
||||
auto action = Gio::SimpleAction::create(actionName);
|
||||
action->set_enabled(node.enabled);
|
||||
action->signal_activate().connect(sigc::bind(sigc::mem_fun(*this, &TrayIconWidget::on_menu_action), node.id));
|
||||
action->signal_activate().connect(sigc::bind(
|
||||
sigc::mem_fun(*this, &TrayIconWidget::on_menu_action), node.id));
|
||||
actions->add_action(action);
|
||||
|
||||
menu->append_item(menuItem);
|
||||
}
|
||||
}
|
||||
|
||||
void TrayIconWidget::on_menu_action(const Glib::VariantBase & /*parameter*/, int itemId)
|
||||
{
|
||||
m_service.activate_menu_item(m_id, itemId);
|
||||
if (m_menuPopover)
|
||||
{
|
||||
m_menuPopover->popdown();
|
||||
void TrayIconWidget::on_menu_action(const Glib::VariantBase & /*parameter*/,
|
||||
int itemId) {
|
||||
service.activate_menu_item(id, itemId);
|
||||
if (menuPopover) {
|
||||
menuPopover->popdown();
|
||||
}
|
||||
}
|
||||
|
||||
TrayWidget::TrayWidget(TrayService &service)
|
||||
: Gtk::Box(Gtk::Orientation::HORIZONTAL), m_service(service)
|
||||
{
|
||||
: Gtk::Box(Gtk::Orientation::HORIZONTAL), service(service) {
|
||||
set_valign(Gtk::Align::CENTER);
|
||||
set_halign(Gtk::Align::CENTER);
|
||||
set_visible(false);
|
||||
|
||||
m_addConnection = m_service.signal_item_added().connect(sigc::mem_fun(*this, &TrayWidget::on_item_added));
|
||||
m_removeConnection = m_service.signal_item_removed().connect(sigc::mem_fun(*this, &TrayWidget::on_item_removed));
|
||||
m_updateConnection = m_service.signal_item_updated().connect(sigc::mem_fun(*this, &TrayWidget::on_item_updated));
|
||||
addConnection = service.signal_item_added().connect(
|
||||
sigc::mem_fun(*this, &TrayWidget::on_item_added));
|
||||
removeConnection = service.signal_item_removed().connect(
|
||||
sigc::mem_fun(*this, &TrayWidget::on_item_removed));
|
||||
updateConnection = service.signal_item_updated().connect(
|
||||
sigc::mem_fun(*this, &TrayWidget::on_item_updated));
|
||||
|
||||
rebuild_existing();
|
||||
}
|
||||
|
||||
TrayWidget::~TrayWidget()
|
||||
{
|
||||
if (m_addConnection.connected())
|
||||
{
|
||||
m_addConnection.disconnect();
|
||||
TrayWidget::~TrayWidget() {
|
||||
if (addConnection.connected()) {
|
||||
addConnection.disconnect();
|
||||
}
|
||||
if (m_removeConnection.connected())
|
||||
{
|
||||
m_removeConnection.disconnect();
|
||||
if (removeConnection.connected()) {
|
||||
removeConnection.disconnect();
|
||||
}
|
||||
if (m_updateConnection.connected())
|
||||
{
|
||||
m_updateConnection.disconnect();
|
||||
if (updateConnection.connected()) {
|
||||
updateConnection.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
void TrayWidget::rebuild_existing()
|
||||
{
|
||||
auto items = m_service.snapshotItems();
|
||||
for (const auto &item : items)
|
||||
{
|
||||
void TrayWidget::rebuild_existing() {
|
||||
auto items = service.snapshotItems();
|
||||
for (const auto &item : items) {
|
||||
on_item_added(item);
|
||||
}
|
||||
|
||||
set_visible(!m_icons.empty());
|
||||
set_visible(!icons.empty());
|
||||
}
|
||||
|
||||
void TrayWidget::on_item_added(const TrayService::Item &item)
|
||||
{
|
||||
auto it = m_icons.find(item.id);
|
||||
if (it != m_icons.end())
|
||||
{
|
||||
void TrayWidget::on_item_added(const TrayService::Item &item) {
|
||||
auto it = icons.find(item.id);
|
||||
if (it != icons.end()) {
|
||||
it->second->update(item);
|
||||
return;
|
||||
}
|
||||
|
||||
auto icon = std::make_unique<TrayIconWidget>(m_service, item.id);
|
||||
auto icon = std::make_unique<TrayIconWidget>(service, item.id);
|
||||
icon->update(item);
|
||||
auto *raw = icon.get();
|
||||
append(*raw);
|
||||
m_icons.emplace(item.id, std::move(icon));
|
||||
icons.emplace(item.id, std::move(icon));
|
||||
|
||||
set_visible(true);
|
||||
}
|
||||
|
||||
void TrayWidget::on_item_removed(const std::string &id)
|
||||
{
|
||||
auto it = m_icons.find(id);
|
||||
if (it == m_icons.end())
|
||||
{
|
||||
void TrayWidget::on_item_removed(const std::string &id) {
|
||||
auto it = icons.find(id);
|
||||
if (it == icons.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
remove(*it->second);
|
||||
it->second->unparent();
|
||||
m_icons.erase(it);
|
||||
icons.erase(it);
|
||||
|
||||
if (m_icons.empty())
|
||||
{
|
||||
if (icons.empty()) {
|
||||
set_visible(false);
|
||||
}
|
||||
}
|
||||
|
||||
void TrayWidget::on_item_updated(const TrayService::Item &item)
|
||||
{
|
||||
auto it = m_icons.find(item.id);
|
||||
if (it == m_icons.end())
|
||||
{
|
||||
void TrayWidget::on_item_updated(const TrayService::Item &item) {
|
||||
auto it = icons.find(item.id);
|
||||
if (it == icons.end()) {
|
||||
on_item_added(item);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2,60 +2,57 @@
|
||||
|
||||
#include "helpers/systemHelper.hpp"
|
||||
|
||||
#include <sigc++/functors/mem_fun.h>
|
||||
#include <regex>
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
#include <sigc++/functors/mem_fun.h>
|
||||
|
||||
VolumeWidget::VolumeWidget()
|
||||
: Gtk::Box(Gtk::Orientation::HORIZONTAL)
|
||||
{
|
||||
VolumeWidget::VolumeWidget() : Gtk::Box(Gtk::Orientation::HORIZONTAL) {
|
||||
set_valign(Gtk::Align::CENTER);
|
||||
set_halign(Gtk::Align::CENTER);
|
||||
|
||||
m_label.set_halign(Gtk::Align::CENTER);
|
||||
m_label.set_valign(Gtk::Align::CENTER);
|
||||
m_label.set_text("Vol");
|
||||
label.set_halign(Gtk::Align::CENTER);
|
||||
label.set_valign(Gtk::Align::CENTER);
|
||||
label.set_text("Vol");
|
||||
|
||||
append(m_label);
|
||||
append(label);
|
||||
|
||||
// Click toggles mute using wpctl
|
||||
m_click = Gtk::GestureClick::create();
|
||||
m_click->set_button(GDK_BUTTON_PRIMARY);
|
||||
// signal_released provides (int, double, double) — use lambda to ignore args
|
||||
m_click->signal_released().connect([this](int /*n_press*/, double /*x*/, double /*y*/)
|
||||
{
|
||||
try
|
||||
{
|
||||
click = Gtk::GestureClick::create();
|
||||
click->set_button(GDK_BUTTON_PRIMARY);
|
||||
// signal_released provides (int, double, double) — use lambda to ignore
|
||||
// args
|
||||
click->signal_released().connect([this](int /*n_press*/, double /*x*/,
|
||||
double /*y*/) {
|
||||
try {
|
||||
// Toggle mute then refresh
|
||||
(void)SystemHelper::get_command_output("wpctl set-mute @DEFAULT_SINK@ toggle");
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
std::cerr << "[VolumeWidget] failed to toggle mute: " << ex.what() << std::endl;
|
||||
(void)SystemHelper::get_command_output(
|
||||
"wpctl set-mute @DEFAULT_SINK@ toggle");
|
||||
} catch (const std::exception &ex) {
|
||||
std::cerr << "[VolumeWidget] failed to toggle mute: " << ex.what()
|
||||
<< std::endl;
|
||||
}
|
||||
this->update();
|
||||
});
|
||||
add_controller(m_click);
|
||||
add_controller(click);
|
||||
|
||||
// Initial read
|
||||
update();
|
||||
|
||||
// Start polling every 1 second to keep the display up to date
|
||||
m_timeoutConn = Glib::signal_timeout().connect(sigc::mem_fun(*this, &VolumeWidget::on_timeout), 100);
|
||||
timeoutConn = Glib::signal_timeout().connect(
|
||||
sigc::mem_fun(*this, &VolumeWidget::on_timeout), 100);
|
||||
}
|
||||
|
||||
VolumeWidget::~VolumeWidget()
|
||||
{
|
||||
if (m_timeoutConn.connected())
|
||||
m_timeoutConn.disconnect();
|
||||
VolumeWidget::~VolumeWidget() {
|
||||
if (timeoutConn.connected())
|
||||
timeoutConn.disconnect();
|
||||
}
|
||||
|
||||
void VolumeWidget::update()
|
||||
{
|
||||
try
|
||||
{
|
||||
const std::string out = SystemHelper::get_command_output("wpctl get-volume @DEFAULT_SINK@");
|
||||
void VolumeWidget::update() {
|
||||
try {
|
||||
const std::string out =
|
||||
SystemHelper::get_command_output("wpctl get-volume @DEFAULT_SINK@");
|
||||
|
||||
// Attempt to parse a number (percentage or fraction)
|
||||
std::smatch m;
|
||||
@@ -65,12 +62,9 @@ void VolumeWidget::update()
|
||||
std::string text = out;
|
||||
int percent = -1;
|
||||
|
||||
if (std::regex_search(text, m, r_percent))
|
||||
{
|
||||
if (std::regex_search(text, m, r_percent)) {
|
||||
percent = static_cast<int>(std::round(std::stod(m[1].str())));
|
||||
}
|
||||
else if (std::regex_search(text, m, r_number))
|
||||
{
|
||||
} else if (std::regex_search(text, m, r_number)) {
|
||||
// If number looks like 0.8 treat as fraction
|
||||
const double v = std::stod(m[1].str());
|
||||
if (v <= 1.0)
|
||||
@@ -79,34 +73,26 @@ void VolumeWidget::update()
|
||||
percent = static_cast<int>(std::round(v));
|
||||
}
|
||||
|
||||
if (percent >= 0)
|
||||
{
|
||||
m_label.set_text(std::to_string(percent) + "%");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (percent >= 0) {
|
||||
label.set_text(std::to_string(percent) + "%");
|
||||
} else {
|
||||
// Fallback to raw output (trimmed)
|
||||
auto pos = text.find_first_not_of(" \t\n\r");
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
if (pos != std::string::npos) {
|
||||
auto end = text.find_last_not_of(" \t\n\r");
|
||||
m_label.set_text(text.substr(pos, end - pos + 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_label.set_text("?");
|
||||
label.set_text(text.substr(pos, end - pos + 1));
|
||||
} else {
|
||||
label.set_text("?");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
std::cerr << "[VolumeWidget] failed to read volume: " << ex.what() << std::endl;
|
||||
m_label.set_text("N/A");
|
||||
} catch (const std::exception &ex) {
|
||||
std::cerr << "[VolumeWidget] failed to read volume: " << ex.what()
|
||||
<< std::endl;
|
||||
label.set_text("N/A");
|
||||
}
|
||||
}
|
||||
|
||||
bool VolumeWidget::on_timeout()
|
||||
{
|
||||
bool VolumeWidget::on_timeout() {
|
||||
update();
|
||||
return true; // keep timeout active
|
||||
}
|
||||
|
||||
@@ -1,130 +1,104 @@
|
||||
#include "widgets/workspaceIndicator.hpp"
|
||||
|
||||
#include <exception>
|
||||
#include <gtkmm/widget.h>
|
||||
#include <gtkmm/gestureclick.h>
|
||||
#include <gdk/gdk.h>
|
||||
#include <gtkmm/gestureclick.h>
|
||||
#include <gtkmm/widget.h>
|
||||
#include <sigc++/functors/mem_fun.h>
|
||||
|
||||
WorkspaceIndicator::WorkspaceIndicator(HyprlandService &service, int monitorId)
|
||||
: Gtk::Box(Gtk::Orientation::HORIZONTAL), m_service(service), m_monitorId(monitorId)
|
||||
{
|
||||
: Gtk::Box(Gtk::Orientation::HORIZONTAL), service(service),
|
||||
monitorId(monitorId) {
|
||||
set_margin_top(2);
|
||||
set_margin_bottom(2);
|
||||
|
||||
m_workspaceConnection = m_service.workspaceStateChanged.connect(
|
||||
workspaceConnection = service.workspaceStateChanged.connect(
|
||||
sigc::mem_fun(*this, &WorkspaceIndicator::on_workspace_update));
|
||||
m_monitorConnection = m_service.monitorStateChanged.connect(
|
||||
monitorConnection = service.monitorStateChanged.connect(
|
||||
sigc::mem_fun(*this, &WorkspaceIndicator::on_monitor_update));
|
||||
|
||||
rebuild();
|
||||
}
|
||||
|
||||
WorkspaceIndicator::~WorkspaceIndicator()
|
||||
{
|
||||
if (m_workspaceConnection.connected())
|
||||
{
|
||||
m_workspaceConnection.disconnect();
|
||||
WorkspaceIndicator::~WorkspaceIndicator() {
|
||||
if (workspaceConnection.connected()) {
|
||||
workspaceConnection.disconnect();
|
||||
}
|
||||
|
||||
if (m_monitorConnection.connected())
|
||||
{
|
||||
m_monitorConnection.disconnect();
|
||||
if (monitorConnection.connected()) {
|
||||
monitorConnection.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
void WorkspaceIndicator::on_workspace_update(int monitorId)
|
||||
{
|
||||
if (monitorId != m_monitorId && monitorId != -1)
|
||||
{
|
||||
void WorkspaceIndicator::on_workspace_update(int monitorId) {
|
||||
if (monitorId != monitorId && monitorId != -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
rebuild();
|
||||
}
|
||||
|
||||
void WorkspaceIndicator::on_monitor_update()
|
||||
{
|
||||
rebuild();
|
||||
}
|
||||
void WorkspaceIndicator::on_monitor_update() { rebuild(); }
|
||||
|
||||
void WorkspaceIndicator::rebuild()
|
||||
{
|
||||
void WorkspaceIndicator::rebuild() {
|
||||
clear_children();
|
||||
|
||||
HyprlandService::Monitor *monitor = nullptr;
|
||||
try
|
||||
{
|
||||
monitor = m_service.getMonitorById(m_monitorId);
|
||||
}
|
||||
catch (const std::exception &)
|
||||
{
|
||||
try {
|
||||
monitor = service.getMonitorById(monitorId);
|
||||
} catch (const std::exception &) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (monitor == nullptr)
|
||||
{
|
||||
if (monitor == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int workspaceId = 1; workspaceId <= HyprlandService::kWorkspaceSlotCount; ++workspaceId)
|
||||
{
|
||||
for (int workspaceId = 1;
|
||||
workspaceId <= HyprlandService::kWorkspaceSlotCount; ++workspaceId) {
|
||||
const HyprlandService::WorkspaceState *state = nullptr;
|
||||
auto it = monitor->workspaceStates.find(workspaceId);
|
||||
if (it != monitor->workspaceStates.end())
|
||||
{
|
||||
|
||||
if (it != monitor->workspaceStates.end()) {
|
||||
state = &it->second;
|
||||
}
|
||||
|
||||
const std::string display = (state && !state->label.empty()) ? state->label : std::to_string(workspaceId);
|
||||
const std::string display = (state && !state->label.empty())
|
||||
? state->label
|
||||
: std::to_string(workspaceId);
|
||||
|
||||
auto label = Gtk::make_managed<Gtk::Label>(display);
|
||||
label->add_css_class("workspace-pill");
|
||||
|
||||
// Make the label clickable using a gesture click (primary button)
|
||||
auto gesture = Gtk::GestureClick::create();
|
||||
gesture->set_button(GDK_BUTTON_PRIMARY);
|
||||
// Use a lambda to capture the workspace slot id
|
||||
gesture->signal_released().connect([this, workspaceId](int /*n_press*/, double /*x*/, double /*y*/)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_service.switchToWorkspace(m_monitorId, workspaceId);
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
std::cerr << "[WorkspaceIndicator] switchToWorkspace failed: " << ex.what() << std::endl;
|
||||
}
|
||||
});
|
||||
gesture->signal_released().connect(
|
||||
[this, workspaceId](int /*n_press*/, double /*x*/, double /*y*/) {
|
||||
int realWorkspaceId = workspaceId + 5 * (monitorId);
|
||||
|
||||
service.switchToWorkspace(realWorkspaceId);
|
||||
});
|
||||
label->add_controller(gesture);
|
||||
|
||||
if (state != nullptr)
|
||||
{
|
||||
if (state->focused)
|
||||
{
|
||||
if (state != nullptr) {
|
||||
if (state->focused) {
|
||||
label->add_css_class("workspace-pill-focused");
|
||||
}
|
||||
else if (state->active)
|
||||
{
|
||||
} else if (state->active) {
|
||||
label->add_css_class("workspace-pill-active");
|
||||
}
|
||||
|
||||
if (state->urgent)
|
||||
{
|
||||
if (state->urgent) {
|
||||
label->add_css_class("workspace-pill-urgent");
|
||||
}
|
||||
}
|
||||
|
||||
append(*label);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void WorkspaceIndicator::clear_children()
|
||||
{
|
||||
void WorkspaceIndicator::clear_children() {
|
||||
Gtk::Widget *child = get_first_child();
|
||||
while (child != nullptr)
|
||||
{
|
||||
while (child != nullptr) {
|
||||
Gtk::Widget *next = child->get_next_sibling();
|
||||
remove(*child);
|
||||
child = next;
|
||||
|
||||
Reference in New Issue
Block a user