summaryrefslogtreecommitdiffstats
path: root/src/ui/desktop/menu-icon-shift.cpp
blob: 0da36d5358e154d2c4dc7ed07d14def67f04c14b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// SPDX-License-Identifier: GPL-2.0-or-later
/**
 * @file
 * Shift Gtk::MenuItems with icons to align with Toggle and Radio buttons.
 */
/*
 * Authors:
 *   Tavmjong Bah       <tavmjong@free.fr>
 *   Patrick Storz      <eduard.braun2@gmx.de>
 *
 * 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 <iostream>
#include <gtkmm.h>

#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<Gtk::CssProvider> 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<Gtk::MenuItem *>(child);
        if (menuitem) {

            auto box = dynamic_cast<Gtk::Box *>(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<Gtk::Image *>(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<Gtk::MenuShell *>(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<Gtk::MenuItem *>(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<Gtk::Box *>(container);
                    if (box) {
                        std::vector<Gtk::Widget *> children = box->get_children();
                        if (children.size() == 2) {
                            auto label_widget = dynamic_cast<Gtk::Label *>(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 :