Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qgtk3menu.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qgtk3menu.h"
5
6#include <QtGui/qwindow.h>
7#include <QtGui/qpa/qplatformtheme.h>
8#include <QtGui/qpa/qplatformwindow.h>
9
10#undef signals
11#include <gtk/gtk.h>
12
14
15#if QT_CONFIG(shortcut)
16static guint qt_gdkKey(const QKeySequence &shortcut)
17{
18 if (shortcut.isEmpty())
19 return 0;
20
21 // TODO: proper mapping
23 return (shortcut[0].toCombined() ^ mods) & shortcut[0].toCombined();
24}
25
26static GdkModifierType qt_gdkModifiers(const QKeySequence &shortcut)
27{
28 if (shortcut.isEmpty())
29 return GdkModifierType(0);
30
31 guint mods = 0;
32 Qt::KeyboardModifiers m = shortcut[0].keyboardModifiers();
33 if (m & Qt::ShiftModifier)
34 mods |= GDK_SHIFT_MASK;
36 mods |= GDK_CONTROL_MASK;
37 if (m & Qt::AltModifier)
38 mods |= GDK_MOD1_MASK;
39 if (m & Qt::MetaModifier)
40 mods |= GDK_META_MASK;
41
42 return static_cast<GdkModifierType>(mods);
43}
44#endif
45
47 : m_visible(true),
48 m_separator(false),
49 m_checkable(false),
50 m_checked(false),
51 m_enabled(true),
52 m_exclusive(false),
53 m_underline(false),
54 m_invalid(true),
55 m_menu(nullptr),
56 m_item(nullptr)
57{
58}
59
63
65{
66 return m_invalid;
67}
68
70{
71 if (m_invalid) {
72 if (m_item) {
73 gtk_widget_destroy(m_item);
74 m_item = nullptr;
75 }
76 m_invalid = false;
77 }
78
79 if (!m_item) {
80 if (m_separator) {
81 m_item = gtk_separator_menu_item_new();
82 } else {
83 if (m_checkable) {
84 m_item = gtk_check_menu_item_new();
85 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(m_item), m_checked);
86 g_signal_connect(m_item, "toggled", G_CALLBACK(onToggle), this);
87 } else {
88 m_item = gtk_menu_item_new();
89 g_signal_connect(m_item, "activate", G_CALLBACK(onActivate), this);
90 }
91 gtk_menu_item_set_label(GTK_MENU_ITEM(m_item), m_text.toUtf8());
92 gtk_menu_item_set_use_underline(GTK_MENU_ITEM(m_item), m_underline);
93 if (m_menu)
94 gtk_menu_item_set_submenu(GTK_MENU_ITEM(m_item), m_menu->handle());
95 g_signal_connect(m_item, "select", G_CALLBACK(onSelect), this);
96#if QT_CONFIG(shortcut)
97 if (!m_shortcut.isEmpty()) {
98 GtkWidget *label = gtk_bin_get_child(GTK_BIN(m_item));
99 gtk_accel_label_set_accel(GTK_ACCEL_LABEL(label), qt_gdkKey(m_shortcut), qt_gdkModifiers(m_shortcut));
100 }
101#endif
102 }
103 gtk_widget_set_sensitive(m_item, m_enabled);
104 gtk_widget_set_visible(m_item, m_visible);
105 if (GTK_IS_CHECK_MENU_ITEM(m_item))
106 g_object_set(m_item, "draw-as-radio", m_exclusive, NULL);
107 }
108
109 return m_item;
110}
111
113{
114 return m_item;
115}
116
118{
119 return m_text;
120}
121
123{
124 *found = false;
125
126 qsizetype i = text.size() - 1;
127 while (i >= 0) {
128 const QChar c = text.at(i);
129 if (c == u'&') {
130 if (i == 0 || text.at(i - 1) != u'&') {
131 // convert Qt to GTK mnemonic
132 if (i < text.size() - 1 && !text.at(i + 1).isSpace()) {
133 text.replace(i, 1, u'_');
134 *found = true;
135 }
136 } else if (text.at(i - 1) == u'&') {
137 // unescape ampersand
138 text.replace(--i, 2, u'&');
139 }
140 } else if (c == u'_') {
141 // escape GTK mnemonic
142 text.insert(i, u'_');
143 }
144 --i;
145 }
146
147 return text;
148}
149
151{
152 m_text = convertMnemonics(text, &m_underline);
153 if (GTK_IS_MENU_ITEM(m_item)) {
154 gtk_menu_item_set_label(GTK_MENU_ITEM(m_item), m_text.toUtf8());
155 gtk_menu_item_set_use_underline(GTK_MENU_ITEM(m_item), m_underline);
156 }
157}
158
160{
161 return m_menu;
162}
163
165{
166 m_menu = qobject_cast<QGtk3Menu *>(menu);
167 if (GTK_IS_MENU_ITEM(m_item))
168 gtk_menu_item_set_submenu(GTK_MENU_ITEM(m_item), m_menu ? m_menu->handle() : nullptr);
169}
170
172{
173 return m_visible;
174}
175
177{
178 if (m_visible == visible)
179 return;
180
181 m_visible = visible;
182 if (GTK_IS_MENU_ITEM(m_item))
183 gtk_widget_set_visible(m_item, visible);
184}
185
187{
188 return m_separator;
189}
190
192{
193 if (m_separator == separator)
194 return;
195
196 m_invalid = true;
197 m_separator = separator;
198}
199
201{
202 return m_checkable;
203}
204
205void QGtk3MenuItem::setCheckable(bool checkable)
206{
207 if (m_checkable == checkable)
208 return;
209
210 m_invalid = true;
211 m_checkable = checkable;
212}
213
215{
216 return m_checked;
217}
218
220{
221 if (m_checked == checked)
222 return;
223
224 m_checked = checked;
225 if (GTK_IS_CHECK_MENU_ITEM(m_item))
226 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(m_item), checked);
227}
228
229#if QT_CONFIG(shortcut)
230QKeySequence QGtk3MenuItem::shortcut() const
231{
232 return m_shortcut;
233}
234
235void QGtk3MenuItem::setShortcut(const QKeySequence& shortcut)
236{
237 if (m_shortcut == shortcut)
238 return;
239
240 m_shortcut = shortcut;
241 if (GTK_IS_MENU_ITEM(m_item)) {
242 GtkWidget *label = gtk_bin_get_child(GTK_BIN(m_item));
243 gtk_accel_label_set_accel(GTK_ACCEL_LABEL(label), qt_gdkKey(m_shortcut), qt_gdkModifiers(m_shortcut));
244 }
245}
246#endif
247
249{
250 return m_enabled;
251}
252
254{
255 if (m_enabled == enabled)
256 return;
257
258 m_enabled = enabled;
259 if (m_item)
260 gtk_widget_set_sensitive(m_item, enabled);
261}
262
264{
265 return m_exclusive;
266}
267
269{
270 if (m_exclusive == exclusive)
271 return;
272
273 m_exclusive = exclusive;
274 if (GTK_IS_CHECK_MENU_ITEM(m_item))
275 g_object_set(m_item, "draw-as-radio", exclusive, NULL);
276}
277
279{
280 QGtk3MenuItem *item = static_cast<QGtk3MenuItem *>(data);
281 if (item)
282 emit item->hovered();
283}
284
286{
287 QGtk3MenuItem *item = static_cast<QGtk3MenuItem *>(data);
288 if (item)
289 emit item->activated();
290}
291
293{
294 QGtk3MenuItem *item = static_cast<QGtk3MenuItem *>(data);
295 if (item) {
296 bool active = gtk_check_menu_item_get_active(check);
297 if (active != item->isChecked()) {
298 item->setChecked(active);
299 emit item->activated();
300 }
301 }
302}
303
305{
306 m_menu = gtk_menu_new();
307
308 g_signal_connect(m_menu, "show", G_CALLBACK(onShow), this);
309 g_signal_connect(m_menu, "hide", G_CALLBACK(onHide), this);
310}
311
313{
314 if (GTK_IS_WIDGET(m_menu))
315 gtk_widget_destroy(m_menu);
316}
317
319{
320 return m_menu;
321}
322
324{
325 QGtk3MenuItem *gitem = static_cast<QGtk3MenuItem *>(item);
326 if (!gitem || m_items.contains(gitem))
327 return;
328
329 GtkWidget *handle = gitem->create();
330 int index = m_items.indexOf(static_cast<QGtk3MenuItem *>(before));
331 if (index < 0)
332 index = m_items.size();
333 m_items.insert(index, gitem);
334 gtk_menu_shell_insert(GTK_MENU_SHELL(m_menu), handle, index);
335}
336
338{
339 QGtk3MenuItem *gitem = static_cast<QGtk3MenuItem *>(item);
340 if (!gitem || !m_items.removeOne(gitem))
341 return;
342
343 GtkWidget *handle = gitem->handle();
344 if (handle)
345 gtk_container_remove(GTK_CONTAINER(m_menu), handle);
346}
347
349{
350 QGtk3MenuItem *gitem = static_cast<QGtk3MenuItem *>(item);
351 int index = m_items.indexOf(gitem);
352 if (index == -1 || !gitem->isInvalid())
353 return;
354
355 GtkWidget *handle = gitem->create();
356 if (handle)
357 gtk_menu_shell_insert(GTK_MENU_SHELL(m_menu), handle, index);
358}
359
364
366{
367 gtk_widget_set_sensitive(m_menu, enabled);
368}
369
370void QGtk3Menu::setVisible(bool visible)
371{
372 gtk_widget_set_visible(m_menu, visible);
373}
374
375static void qt_gtk_menu_position_func(GtkMenu *, gint *x, gint *y, gboolean *push_in, gpointer data)
376{
377 QGtk3Menu *menu = static_cast<QGtk3Menu *>(data);
378 QPoint targetPos = menu->targetPos();
379#if GTK_CHECK_VERSION(3, 10, 0)
380 targetPos /= gtk_widget_get_scale_factor(menu->handle());
381#endif
382 *x = targetPos.x();
383 *y = targetPos.y();
384 *push_in = true;
385}
386
388{
389 return m_targetPos;
390}
391
392void QGtk3Menu::showPopup(const QWindow *parentWindow, const QRect &targetRect, const QPlatformMenuItem *item)
393{
394 const QGtk3MenuItem *menuItem = static_cast<const QGtk3MenuItem *>(item);
395 if (menuItem)
396 gtk_menu_shell_select_item(GTK_MENU_SHELL(m_menu), menuItem->handle());
397
398 m_targetPos = QPoint(targetRect.x(), targetRect.y() + targetRect.height());
399
400 QPlatformWindow *pw = parentWindow ? parentWindow->handle() : nullptr;
401 if (pw)
402 m_targetPos = pw->mapToGlobal(m_targetPos);
403
404 gtk_menu_popup(GTK_MENU(m_menu), nullptr, nullptr, qt_gtk_menu_position_func, this, 0, gtk_get_current_event_time());
405}
406
408{
409 gtk_menu_popdown(GTK_MENU(m_menu));
410}
411
413{
414 return m_items.value(position);
415}
416
418{
419 for (QGtk3MenuItem *item : m_items) {
420 if (item->tag() == tag)
421 return item;
422 }
423 return nullptr;
424}
425
430
432{
433 return new QGtk3Menu;
434}
435
437{
438 QGtk3Menu *menu = static_cast<QGtk3Menu *>(data);
439 if (menu)
441}
442
444{
445 QGtk3Menu *menu = static_cast<QGtk3Menu *>(data);
446 if (menu)
448}
449
451
452#include "moc_qgtk3menu.cpp"
\inmodule QtCore
void setMenu(QPlatformMenu *menu) override
void setText(const QString &text) override
void setHasExclusiveGroup(bool exclusive) override
static void onToggle(GtkCheckMenuItem *item, void *data)
static void onSelect(GtkMenuItem *item, void *data)
bool isEnabled() const
bool hasExclusiveGroup() const
bool isCheckable() const
bool isSeparator() const
QString text() const
void setVisible(bool visible) override
void setEnabled(bool enabled) override
void setChecked(bool checked) override
static void onActivate(GtkMenuItem *item, void *data)
GtkWidget * handle() const
bool isInvalid() const
Definition qgtk3menu.cpp:64
void setCheckable(bool checkable) override
bool isChecked() const
void setIsSeparator(bool separator) override
bool isVisible() const
QGtk3Menu * menu() const
GtkWidget * create()
Definition qgtk3menu.cpp:69
void insertMenuItem(QPlatformMenuItem *item, QPlatformMenuItem *before) override
QPoint targetPos() const
QPlatformMenuItem * menuItemAt(int position) const override
void syncSeparatorsCollapsible(bool enable) override
QPlatformMenuItem * menuItemForTag(quintptr tag) const override
void dismiss() override
void syncMenuItem(QPlatformMenuItem *item) override
void showPopup(const QWindow *parentWindow, const QRect &targetRect, const QPlatformMenuItem *item) override
void setVisible(bool visible) override
QPlatformMenuItem * createMenuItem() const override
GtkWidget * handle() const
void setEnabled(bool enabled) override
static void onHide(GtkWidget *menu, void *data)
void removeMenuItem(QPlatformMenuItem *item) override
static void onShow(GtkWidget *menu, void *data)
QPlatformMenu * createSubMenu() const override
The QKeySequence class encapsulates a key sequence as used by shortcuts.
qsizetype size() const noexcept
Definition qlist.h:397
iterator insert(qsizetype i, parameter_type t)
Definition qlist.h:488
bool removeOne(const AT &t)
Definition qlist.h:598
T value(qsizetype i) const
Definition qlist.h:664
void aboutToShow()
This signal is emitted just before the menu is shown to the user.
void aboutToHide()
virtual quintptr tag() const
The QPlatformWindow class provides an abstraction for top-level windows.
\inmodule QtCore\reentrant
Definition qpoint.h:25
constexpr int x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:130
constexpr int y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:135
\inmodule QtCore\reentrant
Definition qrect.h:30
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QString & replace(qsizetype i, qsizetype len, QChar after)
Definition qstring.cpp:3824
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
Definition qstring.h:1226
QString & insert(qsizetype i, QChar c)
Definition qstring.cpp:3132
QByteArray toUtf8() const &
Definition qstring.h:634
\inmodule QtGui
Definition qwindow.h:63
QString text
Combined button and popup list for selecting options.
@ ShiftModifier
@ ControlModifier
@ MetaModifier
@ AltModifier
AudioChannelLayoutTag tag
struct _GtkWidget GtkWidget
static void qt_gtk_menu_position_func(GtkMenu *, gint *x, gint *y, gboolean *push_in, gpointer data)
static QString convertMnemonics(QString text, bool *found)
struct _GtkWidget GtkWidget
Definition qgtk3menu.h:9
struct _GtkMenuItem GtkMenuItem
Definition qgtk3menu.h:10
struct _GtkCheckMenuItem GtkCheckMenuItem
Definition qgtk3menu.h:11
GLuint64 GLenum void * handle
GLint GLint GLint GLint GLint x
[0]
const GLfloat * m
GLuint index
[2]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLenum GLsizei const GLuint GLboolean enabled
GLuint GLsizei const GLchar * label
[43]
GLboolean enable
GLint y
const GLubyte * c
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
#define emit
#define Q_UNUSED(x)
size_t quintptr
Definition qtypes.h:167
ptrdiff_t qsizetype
Definition qtypes.h:165
#define enabled
QObject::connect nullptr
QGraphicsItem * item
QMenu menu
[5]
qsizetype indexOf(const AT &t, qsizetype from=0) const noexcept
Definition qlist.h:962
bool contains(const AT &t) const noexcept
Definition qlist.h:45