Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
popup-menu.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
6/*
7 * Authors:
8 * Daniel Boles <dboles.src+inkscape@gmail.com>
9 *
10 * Copyright (C) 2023 Daniel Boles
11 *
12 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
13 */
14
15#include "popup-menu.h"
16
17#include <utility>
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>
25
26#include "controller.h"
27#include "ui/util.h"
28
29namespace Inkscape::UI {
30
31static bool on_key_pressed(unsigned const keyval, unsigned /*keycode*/, Gdk::ModifierType state,
32 PopupMenuSlot const &slot)
33{
34 if (keyval == GDK_KEY_Menu) {
35 return slot(std::nullopt);
36 }
37
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);
41 }
42
43 return false;
44}
45
46static void on_click_pressed(int n_press, double x, double y, Gtk::GestureClick &click, PopupMenuSlot const &slot)
47{
48 auto const event = click.get_current_event();
49 if (event->triggers_context_menu()) {
50 auto const mc = PopupMenuClick{n_press, x, y};
51 if (slot(mc)) {
52 click.set_state(Gtk::EventSequenceState::CLAIMED);
53 }
54 }
55}
56
57void on_popup_menu(Gtk::Widget &widget, PopupMenuSlot slot)
58{
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);
62
63 auto click = Gtk::GestureClick::create();
64 click->set_button(0);
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);
68}
69
70static void popup_at(Gtk::Popover &popover, Gtk::Widget &widget,
71 double const x_offset, double const y_offset,
72 int width, int height)
73{
74 popover.set_visible(false);
75
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));
79
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();
83 double x{}, y{};
84 widget.translate_coordinates(*parent, 0, 0, x, y);
85 x += x_offset;
86 y += y_offset;
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});
89
90 popover.popup();
91}
92
93void popup_at(Gtk::Popover &popover, Gtk::Widget &widget,
94 double const x_offset, double const y_offset)
95{
96 popup_at(popover, widget, x_offset, y_offset, 0, 0);
97}
98
99void popup_at(Gtk::Popover &popover, Gtk::Widget &widget,
100 std::optional<Geom::Point> const &offset)
101{
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);
105}
106
107void popup_at_center(Gtk::Popover &popover, Gtk::Widget &widget)
108{
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);
112}
113
114void popup_at(Gtk::Popover &popover, Gtk::Widget &widget, Gdk::Rectangle const &rect)
115{
116 popup_at(popover, widget, rect.get_x(), rect.get_y(), rect.get_width(), rect.get_height());
117}
118
119} // namespace Inkscape::UI
120
121/*
122 Local Variables:
123 mode:c++
124 c-file-style:"stroustrup"
125 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
126 indent-tabs-mode:nil
127 fill-column:99
128 End:
129*/
130// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
Cartesian point / 2D vector and related operations.
Utilities to more easily use Gtk::EventController & subclasses like Gesture.
static char const *const parent
Definition dir-util.cpp:70
double offset
bool has_flag(Gdk::ModifierType const state, Gdk::ModifierType const flags)
Helper to query if ModifierType state contains one or more of given flag(s).
Definition controller.h:25
User interface code.
Definition desktop.h:113
static void on_click_pressed(int n_press, double x, double y, Gtk::GestureClick &click, PopupMenuSlot const &slot)
void popup_at_center(Gtk::Popover &popover, Gtk::Widget &widget)
As popup_at() but point to center of widget.
bool is_descendant_of(Gtk::Widget const &descendant, Gtk::Widget const &ancestor)
Returns if widget is a descendant of given ancestor, i.e.: itself, a child, or a childʼs child.
Definition util.cpp:195
void on_popup_menu(Gtk::Widget &widget, PopupMenuSlot slot)
Connect slot to a widgetʼs key and button events that traditionally trigger a popup menu,...
sigc::slot< bool(PopupMenuOptionalClick)> PopupMenuSlot
Return whether a popup was activated.
Definition popup-menu.h:45
static void popup_at(Gtk::Popover &popover, Gtk::Widget &widget, double const x_offset, double const y_offset, int width, int height)
static bool on_key_pressed(unsigned const keyval, unsigned, Gdk::ModifierType state, PopupMenuSlot const &slot)
static cairo_user_data_key_t key
Helpers to connect signals to events that popup a menu in both GTK3 and GTK4.
Information from a GestureClick if a popup menu was opened by click.
Definition popup-menu.h:39
double height
double width