add tray icons

This commit is contained in:
2025-12-10 00:25:49 +01:00
parent d53dfa27f1
commit 7bd4c72763
9 changed files with 1268 additions and 9 deletions

174
src/widgets/tray.cpp Normal file
View File

@@ -0,0 +1,174 @@
#include "widgets/tray.hpp"
#include <utility>
TrayIconWidget::TrayIconWidget(TrayService &service, std::string id)
: m_service(service), m_id(std::move(id)), m_container(Gtk::Orientation::HORIZONTAL)
{
set_has_frame(false);
set_focusable(false);
set_valign(Gtk::Align::CENTER);
set_halign(Gtk::Align::CENTER);
add_css_class("tray-icon");
m_picture.set_halign(Gtk::Align::CENTER);
m_picture.set_valign(Gtk::Align::CENTER);
m_picture.set_content_fit(Gtk::ContentFit::CONTAIN);
m_picture.set_can_shrink(true);
m_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);
m_container.set_spacing(0);
m_container.set_halign(Gtk::Align::CENTER);
m_container.set_valign(Gtk::Align::CENTER);
m_container.append(m_picture);
m_container.append(m_image);
m_picture.set_visible(false);
m_image.set_visible(true);
set_child(m_container);
signal_clicked().connect(sigc::mem_fun(*this, &TrayIconWidget::on_primary_clicked));
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);
}
void TrayIconWidget::update(const TrayService::Item &item)
{
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.title.empty())
{
set_tooltip_text(item.title);
}
else
{
set_tooltip_text("");
}
set_sensitive(item.status != "Passive");
}
void TrayIconWidget::on_primary_clicked()
{
m_service.activate(m_id, 0, 0);
}
void TrayIconWidget::on_secondary_released(int /*n_press*/, double /*x*/, double /*y*/)
{
m_service.contextMenu(m_id, 0, 0);
}
TrayWidget::TrayWidget(TrayService &service)
: Gtk::Box(Gtk::Orientation::HORIZONTAL), m_service(service)
{
set_spacing(6);
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));
rebuild_existing();
}
TrayWidget::~TrayWidget()
{
if (m_addConnection.connected())
{
m_addConnection.disconnect();
}
if (m_removeConnection.connected())
{
m_removeConnection.disconnect();
}
if (m_updateConnection.connected())
{
m_updateConnection.disconnect();
}
}
void TrayWidget::rebuild_existing()
{
auto items = m_service.snapshotItems();
for (const auto &item : items)
{
on_item_added(item);
}
set_visible(!m_icons.empty());
}
void TrayWidget::on_item_added(const TrayService::Item &item)
{
auto it = m_icons.find(item.id);
if (it != m_icons.end())
{
it->second->update(item);
return;
}
auto icon = std::make_unique<TrayIconWidget>(m_service, item.id);
icon->update(item);
auto *raw = icon.get();
append(*raw);
m_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())
{
return;
}
remove(*it->second);
it->second->unparent();
m_icons.erase(it);
if (m_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())
{
on_item_added(item);
return;
}
it->second->update(item);
}