// SPDX-License-Identifier: GPL-2.0-or-later /** * @file * Shift Gtk::MenuItems with icons to align with Toggle and Radio buttons. */ /* * Authors: * Tavmjong Bah * Patrick Storz * * Copyright (C) 2020 Authors * * The contents of this file may be used under the GNU General Public License Version 2 or later. * Read the file 'COPYING' for more information. * */ #include "menu-icon-shift.h" #include #include #include "inkscape-application.h" // Action extra data // Could be used to update status bar. // bool on_enter_notify(GdkEventCrossing* crossing_event, Gtk::MenuItem* menuitem) // { // return false; // } /* * Install CSS to shift icons into the space reserved for toggles (i.e. check and radio items). * The CSS will apply to all menu icons but is updated as each menu is shown. */ void shift_icons(Gtk::MenuShell* menu) { static Glib::RefPtr provider; if (!provider) { provider = Gtk::CssProvider::create(); auto const screen = Gdk::Screen::get_default(); Gtk::StyleContext::add_provider_for_screen(screen, provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); } // Calculate required shift. We need an example! // Search for Gtk::MenuItem -> Gtk::Box -> Gtk::Image for (auto child : menu->get_children()) { auto menuitem = dynamic_cast(child); if (menuitem) { auto box = dynamic_cast(menuitem->get_child()); if (box) { box->set_spacing(0); // Match ImageMenuItem auto children = box->get_children(); if (children.size() == 2) { // Image + Label auto image = dynamic_cast(box->get_children()[0]); if (image) { // OK, we have an example, do calculation. auto allocation_menuitem = menuitem->get_allocation(); auto allocation_image = image->get_allocation(); int shift = -allocation_image.get_x(); if (menuitem->get_direction() == Gtk::TEXT_DIR_RTL) { shift += (allocation_menuitem.get_width() - allocation_image.get_width()); } static int current_shift = 0; if (std::abs(current_shift - shift) > 2) { // Only do this once per menu, and only if there is a large change. current_shift = shift; std::string css_str; if (menuitem->get_direction() == Gtk::TEXT_DIR_RTL) { css_str = "menuitem box image {margin-right:" + std::to_string(shift) + "px;}"; } else { css_str = "menuitem box image {margin-left:" + std::to_string(shift) + "px;}"; } provider->load_from_data(css_str); } } } } } } // If we get here, it means there were no examples... but we don't care as there are no icons to shift anyway. } /* * Find all submenus to add "shift_icons" callback. We need to do this for * all submenus as some submenus are children of other submenus without icons. */ void shift_icons_recursive(Gtk::MenuShell *menu) { if (menu) { // Connect signal menu->signal_map().connect(sigc::bind(sigc::ptr_fun(&shift_icons), menu)); static auto app = InkscapeApplication::instance(); auto label_to_tooltip_map = app->get_menu_label_to_tooltip_map(); // Look for descendent menus. auto children = menu->get_children(); // Should be Gtk::MenuItem's for (auto child : children) { auto menuitem = dynamic_cast(child); if (menuitem) { // Use label as alternative way to figure out tooltip. auto label = menuitem->get_label(); if (label.empty()) { auto container = menuitem->get_child(); auto box = dynamic_cast(container); if (box) { std::vector children = box->get_children(); if (children.size() == 2) { auto label_widget = dynamic_cast(children[1]); if (label_widget) { label = label_widget->get_label(); } } } } auto it = label_to_tooltip_map.find(label); if (it != label_to_tooltip_map.end()) { menuitem->set_tooltip_text(it->second); } auto submenu = menuitem->get_submenu(); if (submenu) { shift_icons_recursive(submenu); } } } } } /* Local Variables: mode:c++ c-file-style:"stroustrup" c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :