1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Helpers to connect signals to events that popup a menu in both GTK3 and GTK4.
4 * Plus miscellaneous helpers primarily useful with widgets used as popop menus.
8 * Daniel Boles <dboles.src+inkscape@gmail.com>
10 * Copyright (C) 2023 Daniel Boles
12 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
15 #include "popup-menu.h"
18 #include <2geom/point.h>
19 #include <gtkmm/accelerator.h>
20 #include <gtkmm/eventcontrollerkey.h>
21 #include <gtkmm/gestureclick.h>
22 #include <gdkmm/rectangle.h>
23 #include <gtkmm/popover.h>
24 #include <gtkmm/widget.h>
26 #include "controller.h"
29 namespace Inkscape::UI
{
31 static bool on_key_pressed(unsigned const keyval
, unsigned /*keycode*/, Gdk::ModifierType state
,
32 PopupMenuSlot
const &slot
)
34 if (keyval
== GDK_KEY_Menu
) {
35 return slot(std::nullopt
);
38 state
&= Gtk::Accelerator::get_default_mod_mask();
39 if (keyval
== GDK_KEY_F10
&& Controller::has_flag(state
, Gdk::ModifierType::SHIFT_MASK
)) {
40 return slot(std::nullopt
);
46 static void on_click_pressed(int n_press
, double x
, double y
, Gtk::GestureClick
&click
, PopupMenuSlot
const &slot
)
48 auto const event
= click
.get_current_event();
49 if (event
->triggers_context_menu()) {
50 auto const mc
= PopupMenuClick
{n_press
, x
, y
};
52 click
.set_state(Gtk::EventSequenceState::CLAIMED
);
57 void on_popup_menu(Gtk::Widget
&widget
, PopupMenuSlot slot
)
59 auto key
= Gtk::EventControllerKey::create();
60 key
->signal_key_pressed().connect(sigc::bind(&on_key_pressed
, slot
), true); // after
61 widget
.add_controller(key
);
63 auto click
= Gtk::GestureClick::create();
65 click
->set_propagation_phase(Gtk::PropagationPhase::CAPTURE
); // before GTK's popup handler
66 click
->signal_pressed().connect(sigc::bind(&on_click_pressed
, std::ref(*click
), std::move(slot
)));
67 widget
.add_controller(click
);
70 static void popup_at(Gtk::Popover
&popover
, Gtk::Widget
&widget
,
71 double const x_offset
, double const y_offset
,
72 int width
, int height
)
74 popover
.set_visible(false);
76 auto const parent
= popover
.get_parent();
77 g_return_if_fail(parent
);
78 g_return_if_fail(&widget
== parent
|| is_descendant_of(widget
, *parent
));
80 auto const allocation
= widget
.get_allocation();
81 if (!width
) width
= x_offset
? 1 : allocation
.get_width ();
82 if (!height
) height
= y_offset
? 1 : allocation
.get_height();
84 widget
.translate_coordinates(*parent
, 0, 0, x
, y
);
87 auto const ix
= static_cast<int>(x
+ 0.5), iy
= static_cast<int>(y
+ 0.5);
88 popover
.set_pointing_to({ix
, iy
, width
, height
});
93 void popup_at(Gtk::Popover
&popover
, Gtk::Widget
&widget
,
94 double const x_offset
, double const y_offset
)
96 popup_at(popover
, widget
, x_offset
, y_offset
, 0, 0);
99 void popup_at(Gtk::Popover
&popover
, Gtk::Widget
&widget
,
100 std::optional
<Geom::Point
> const &offset
)
102 auto const x_offset
= offset
? offset
->x() : 0;
103 auto const y_offset
= offset
? offset
->y() : 0;
104 popup_at(popover
, widget
, x_offset
, y_offset
);
107 void popup_at_center(Gtk::Popover
&popover
, Gtk::Widget
&widget
)
109 auto const x_offset
= widget
.get_width () / 2;
110 auto const y_offset
= widget
.get_height() / 2;
111 popup_at(popover
, widget
, x_offset
, y_offset
);
114 void popup_at(Gtk::Popover
&popover
, Gtk::Widget
&widget
, Gdk::Rectangle
const &rect
)
116 popup_at(popover
, widget
, rect
.get_x(), rect
.get_y(), rect
.get_width(), rect
.get_height());
119 } // namespace Inkscape::UI
124 c-file-style:"stroustrup"
125 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
130 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :