add tray icons
This commit is contained in:
@@ -1,6 +1,11 @@
|
||||
#include "widgets/tray.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <gtk/gtk.h>
|
||||
#include <gdkmm/rectangle.h>
|
||||
#include <gio/gmenu.h>
|
||||
#include <utility>
|
||||
#include <iostream>
|
||||
|
||||
TrayIconWidget::TrayIconWidget(TrayService &service, std::string id)
|
||||
: m_service(service), m_id(std::move(id)), m_container(Gtk::Orientation::HORIZONTAL)
|
||||
@@ -41,6 +46,24 @@ TrayIconWidget::TrayIconWidget(TrayService &service, std::string id)
|
||||
|
||||
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();
|
||||
}
|
||||
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 (item.iconPaintable)
|
||||
{
|
||||
m_picture.set_paintable(item.iconPaintable);
|
||||
@@ -78,15 +101,186 @@ void TrayIconWidget::on_primary_clicked()
|
||||
m_service.activate(m_id, 0, 0);
|
||||
}
|
||||
|
||||
void TrayIconWidget::on_secondary_released(int /*n_press*/, double /*x*/, double /*y*/)
|
||||
void TrayIconWidget::on_secondary_released(int /*n_press*/, double x, double y)
|
||||
{
|
||||
m_service.contextMenu(m_id, 0, 0);
|
||||
m_service.contextMenu(m_id, static_cast<int32_t>(x), static_cast<int32_t>(y));
|
||||
|
||||
if (!ensure_menu())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_pendingX = x;
|
||||
m_pendingY = y;
|
||||
m_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();
|
||||
}
|
||||
if (m_menuPopover)
|
||||
{
|
||||
remove_action_group("dbusmenu");
|
||||
m_menuPopover->set_menu_model({});
|
||||
m_menuPopover->unparent();
|
||||
m_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);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_menuModel = menu;
|
||||
m_menuActions = actions;
|
||||
|
||||
if (!m_menuPopover)
|
||||
{
|
||||
auto *rawPopover = Gtk::make_managed<Gtk::PopoverMenu>();
|
||||
m_menuPopover = Glib::make_refptr_for_instance<Gtk::PopoverMenu>(rawPopover);
|
||||
if (!m_menuPopover)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_menuPopover->set_has_arrow(false);
|
||||
m_menuPopover->set_autohide(true);
|
||||
m_menuPopover->set_parent(*this);
|
||||
}
|
||||
|
||||
m_menuPopover->remove_action_group("dbusmenu");
|
||||
m_menuPopover->insert_action_group("dbusmenu", m_menuActions);
|
||||
|
||||
if (m_menuChangedConnection.connected())
|
||||
{
|
||||
m_menuChangedConnection.disconnect();
|
||||
}
|
||||
m_menuChangedConnection = m_menuModel->signal_items_changed().connect(sigc::mem_fun(*this, &TrayIconWidget::on_menu_items_changed));
|
||||
|
||||
m_menuPopover->set_menu_model(m_menuModel);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TrayIconWidget::on_menu_items_changed(guint /*position*/, guint /*removed*/, guint /*added*/)
|
||||
{
|
||||
if (!m_menuModel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto count = m_menuModel->get_n_items();
|
||||
std::cout << "[TrayIconWidget] items changed for " << m_id << ": " << count << " entries" << std::endl;
|
||||
try_popup();
|
||||
}
|
||||
|
||||
void TrayIconWidget::try_popup()
|
||||
{
|
||||
if (!m_menuPopupPending || !m_menuPopover || !m_menuModel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
auto section = Gio::Menu::create();
|
||||
menu->append_section("", section);
|
||||
continue;
|
||||
}
|
||||
|
||||
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());
|
||||
submenuItem->set_submenu(submenu);
|
||||
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 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));
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
TrayWidget::TrayWidget(TrayService &service)
|
||||
: Gtk::Box(Gtk::Orientation::HORIZONTAL), m_service(service)
|
||||
{
|
||||
set_spacing(6);
|
||||
set_spacing(4);
|
||||
set_valign(Gtk::Align::CENTER);
|
||||
set_halign(Gtk::Align::CENTER);
|
||||
set_visible(false);
|
||||
|
||||
Reference in New Issue
Block a user