Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qohosstatusbarmenu.cpp
Go to the documentation of this file.
1// Copyright (C) 2025 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
5#include "qohosjsutils.h"
7#include <QtCore/qobject.h>
8#include <QtCore/qlist.h>
9#include <QtGui/qicon.h>
10#include <QtGui/qwindow.h>
11#include <atomic>
12#include <cstdint>
13#include <optional>
14#include <qpa/qplatformtheme.h>
15#include <string>
16
18
19namespace {
20
22
24 QtOhos::JsState &jsState, QOhosConsumer<std::string> clickedMenuCodeConsumer)
25{
26 return registerQOhosOnOffMethodsBasedEventHandler(
27 jsState.eval<QNapi::Object>("@kit.StatusBarExtensionKit.statusBarManager"),
28 "rightMenuClick",
29 [clickedMenuCodeConsumer = std::move(clickedMenuCodeConsumer)](const QtOhos::CallbackInfo &info) {
30 auto eventData = info.getFirstArg<QNapi::Object>(Q_FUNC_INFO);
31 auto optMenuCode = QNapi::getOptionalPropOrEmpty<QNapi::String>(
32 QNapi::getOptionalPropOrEmpty<QNapi::Object>(eventData, "data"),
33 "menuCode");
34 if (!optMenuCode.IsEmpty()) {
35 clickedMenuCodeConsumer(optMenuCode);
36 } else {
37 qOhosPrintfDebug(
38 "%s: no 'menuCode' in the event (%s), ignoring it",
39 Q_FUNC_INFO, QNapi::toJsonString(eventData).c_str());
40 }
41 });
42}
43
45 QtOhos::JsState &jsState, const QIcon &icon, const QSize &iconSize)
46{
47 if (icon.isNull())
48 return std::nullopt;
49
50 QImage iconImage = icon.pixmap(iconSize).toImage();
51 if (iconImage.isNull())
52 return std::nullopt;
53
54 auto *env = jsState.env();
55 auto menuIcon =
56 makeDisplayDensityScaledJsPixelMapFromQImage(jsState, iconImage);
57
58 auto imageSize = menuIcon.eval<QNapi::Object>("getImageInfoSync().size");
59
60 int imageWidth = imageSize.get<QNapi::Number>("width");
61 int imageHeight = imageSize.get<QNapi::Number>("height");
62 qOhosPrintfDebug("%s: created PixelMap size: %dx%d", Q_FUNC_INFO, imageWidth, imageHeight);
63
64 return QNapi::makeObject(
65 env,
66 {
67 {"white", menuIcon},
68 {"black", menuIcon},
69 });
70}
71
73 QtOhos::JsState &jsState, const QIcon &icon, const QSize &iconSize)
74{
75 auto itemIcon = makeJsStatusBarItemIcon(jsState, icon, iconSize);
76 if (!itemIcon)
77 return std::nullopt;
78
79 return QNapi::makeObject(
80 jsState.env(),
81 {
82 {"icon", *itemIcon},
83 });
84}
85
87 QtOhos::JsState &jsState, const std::string &menuCode)
88{
89 return QNapi::makeObject(
90 jsState.env(),
91 {
92 {"abilityName", "invalidAbility"},
93 {"notifyOnly", true},
94 {"menuCode", menuCode},
95 });
96}
97
99 QtOhos::JsState &jsState, const std::string &title, const std::string &menuCode,
100 const QIcon &icon, const QSize &iconSize)
101{
102 auto statusBarMenuItem = QNapi::makeObject(
103 jsState.env(),
104 {
105 {"title", title},
106 {"menuAction", makeNotifyOnlyJsStatusBarMenuAction(jsState, menuCode)},
107 });
108
109 if (auto options = makeJsStatusBarMenuItemOptions(jsState, icon, iconSize))
110 statusBarMenuItem.set("options", *options);
111
112 return statusBarMenuItem;
113}
114
116 QtOhos::JsState &jsState, const std::string &title, QNapi::Array jsStatusBarSubMenuItems,
117 const QIcon &icon, const QSize &iconSize)
118{
119 auto statusBarMenuItem = QNapi::makeObject(
120 jsState.env(),
121 {
122 {"title", title},
123 {"subMenu", jsStatusBarSubMenuItems},
124 });
125
126 if (auto options = makeJsStatusBarMenuItemOptions(jsState, icon, iconSize))
127 statusBarMenuItem.set("options", *options);
128
129 return statusBarMenuItem;
130}
131
133 QtOhos::JsState &jsState, const std::string &subTitle, const std::string &menuCode,
134 const QIcon &icon, const QSize &iconSize)
135{
136 auto statusBarSubMenuItem = QNapi::makeObject(
137 jsState.env(),
138 {
139 {"subTitle", subTitle},
140 {"menuAction", makeNotifyOnlyJsStatusBarMenuAction(jsState, menuCode)},
141 });
142
143 if (auto options = makeJsStatusBarMenuItemOptions(jsState, icon, iconSize))
144 statusBarSubMenuItem.set("options", *options);
145
146 return statusBarSubMenuItem;
147}
148
150
152{
154
155public:
157
158 void setText(const QString &text) override;
159 QString text() const;
160 void setIcon(const QIcon &icon) override;
161
162 void setMenu(QPlatformMenu *menu) override;
163
164 void setVisible(bool isVisible) override;
165 void setIsSeparator(bool isSeparator) override;
166 void setFont(const QFont &font) override;
167 void setRole(MenuRole role) override;
168 void setCheckable(bool checkable) override;
169 void setChecked(bool isChecked) override;
170 void setShortcut(const QKeySequence &shortcut) override;
171 void setEnabled(bool enabled) override;
172 void setIconSize(int size) override;
173
174 std::string menuCode() const;
175
178
179private:
180 QString m_text;
181 std::string m_menuCode;
182 QIcon m_icon;
183 bool m_isSeparator = false;
184 QOhosStatusBarMenuImpl *m_menu = nullptr;
185
186 static std::string generateNextMenuCode();
187};
188
190{
192
193public:
195
196 void insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before) override;
197 void removeMenuItem(QPlatformMenuItem *menuItem) override;
198 void syncMenuItem(QPlatformMenuItem *menuItem) override;
200
201 void setText(const QString &text) override;
202 void setIcon(const QIcon &icon) override;
203 void setEnabled(bool enabled) override;
204 void setVisible(bool visible) override;
205
206 void showPopup(const QWindow *parentWindow, const QRect &targetRect, const QPlatformMenuItem *item) override;
208
209 QPlatformMenuItem *menuItemAt(int position) const override;
210 QPlatformMenuItem *menuItemForTag(quintptr tag) const override;
211
213 QPlatformMenu *createSubMenu() const override;
214
216
218
219private:
220 QPlatformMenuItem *findItemByMenuCode(const std::string &menuCode) const;
221 void handleRightClickEvent(const std::string &menuCode);
222
223 QString m_text;
224 QList<QPlatformMenuItem *> m_menuItems;
225
226 struct JsScopeData
227 {
228 std::shared_ptr<void> m_rightMenuClickListenerHandle;
229 };
230
231 std::shared_ptr<JsScopeData> m_jsScopeData;
232};
233
234QOhosStatusBarMenuItem::QOhosStatusBarMenuItem()
235 : QPlatformMenuItem()
236 , m_menuCode(generateNextMenuCode())
237{
238}
239
240void QOhosStatusBarMenuItem::setText(const QString &text)
241{
242 m_text = text;
243}
244
246{
247 return m_text;
248}
249
250void QOhosStatusBarMenuItem::setIcon(const QIcon &icon)
251{
252 m_icon = icon;
253}
254
255void QOhosStatusBarMenuItem::setMenu(QPlatformMenu *menu)
256{
257 auto *ohosMenu = qobject_cast<QOhosStatusBarMenuImpl *>(menu);
258 if (menu != nullptr && ohosMenu == nullptr)
259 qOhosPrintfWarning("%s: got menu object of incompatible type", Q_FUNC_INFO);
260
261 m_menu = ohosMenu;
262}
263
265{
266 Q_UNUSED(isVisible);
267}
268
270{
271 m_isSeparator = isSeparator;
272}
273
274void QOhosStatusBarMenuItem::setFont(const QFont &font)
275{
276 Q_UNUSED(font);
277}
278
279void QOhosStatusBarMenuItem::setRole(MenuRole role)
280{
281 Q_UNUSED(role);
282}
283
285{
286 Q_UNUSED(checkable);
287}
288
290{
291 Q_UNUSED(isChecked);
292}
293
294void QOhosStatusBarMenuItem::setShortcut(const QKeySequence &shortcut)
295{
296 Q_UNUSED(shortcut);
297}
298
300{
301 Q_UNUSED(enabled);
302}
303
305{
306 Q_UNUSED(size);
307}
308
310{
311 return m_menuCode;
312}
313
315{
316 if (m_isSeparator) {
317 return [](QtOhos::JsState &) {
318 return std::optional<QNapi::Object>();
319 };
320 }
321
322 auto title = QPlatformTheme::removeMnemonics(m_text).toStdString();
323
324 if (m_menu == nullptr) {
325 return [title, menuCode = m_menuCode, icon = m_icon](QtOhos::JsState &jsState) {
326 return std::optional(
327 makeJsStatusBarMenuItemWithAction(
328 jsState, title, menuCode, icon, defaultStatusBarMenuItemIconSize));
329 };
330 } else {
331 return [title, jsSubMenuItemsFactory = m_menu->makeJsStatusBarSubMenuItemsFactory(),
332 icon = m_icon](QtOhos::JsState &jsState) {
333 return std::optional(
334 makeJsStatusBarMenuItemWithSubMenu(
335 jsState, title, jsSubMenuItemsFactory(jsState), icon, defaultStatusBarMenuItemIconSize));
336 };
337 }
338}
339
341{
342 if (m_isSeparator) {
343 qOhosPrintfWarning(
344 "%s: separator item %p used in sub-menu, which is unsupported on OHOS, ignoring",
345 Q_FUNC_INFO, this);
346 return [](QtOhos::JsState &) {
347 return std::optional<QNapi::Object>();
348 };
349 }
350
351 if (m_menu != nullptr) {
352 qOhosPrintfWarning(
353 "%s: nested-menu item %p used in sub-menu, which is unsupported on OHOS, ignoring",
354 Q_FUNC_INFO, this);
355 return [](QtOhos::JsState &) {
356 return std::optional<QNapi::Object>();
357 };
358 }
359
360 auto subTitle = QPlatformTheme::removeMnemonics(m_text).toStdString();
361
362 return [subTitle, menuCode = m_menuCode, icon = m_icon](QtOhos::JsState &jsState) {
363 return std::optional(
364 makeJsStatusBarSubMenuItem(
365 jsState, subTitle, menuCode, icon, defaultStatusBarMenuItemIconSize));
366 };
367}
368
369std::string QOhosStatusBarMenuItem::generateNextMenuCode()
370{
371 static std::atomic<std::uint64_t> menuCodeCounter(0);
372
373 auto counterValue = ++menuCodeCounter;
374 return std::to_string(counterValue);
375}
376
377QOhosStatusBarMenuImpl::QOhosStatusBarMenuImpl()
379{
380 auto selfRef = QtOhos::makeQThreadSafeRef(this);
381 m_jsScopeData = QtOhos::evalInJsThread(
382 [&](QtOhos::JsState &jsState) {
383 return QtOhos::makeProxyWithJsThreadDeleter(
384 QtOhos::moveToSharedPtr(
385 JsScopeData {
386 .m_rightMenuClickListenerHandle = registerOhosRightMenuClickListener(
387 jsState,
388 [selfRef](std::string menuCode) {
389 selfRef.visitInQtThreadIfAlive(
390 [menuCode](auto &self) {
391 self.handleRightClickEvent(menuCode);
392 });
393 }),
394 }));
395 },
396 Q_FUNC_INFO);
397}
398
399void QOhosStatusBarMenuImpl::insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before)
400{
401 if (menuItem == nullptr)
402 return;
403
404 if (before == nullptr) {
405 m_menuItems.append(menuItem);
406 return;
407 }
408
409 const int idx = m_menuItems.indexOf(before);
410 if (idx >= 0)
411 m_menuItems.insert(idx, menuItem);
412 else
413 m_menuItems.append(menuItem);
414}
415
416void QOhosStatusBarMenuImpl::removeMenuItem(QPlatformMenuItem *menuItem)
417{
418 if (menuItem == nullptr)
419 return;
420 m_menuItems.removeAll(menuItem);
421}
422
423void QOhosStatusBarMenuImpl::syncMenuItem(QPlatformMenuItem *menuItem)
424{
425 Q_UNUSED(menuItem);
426}
427
429{
430 Q_UNUSED(enable);
431}
432
433void QOhosStatusBarMenuImpl::setText(const QString &text)
434{
435 m_text = text;
436}
437
438void QOhosStatusBarMenuImpl::setIcon(const QIcon &icon)
439{
440 Q_UNUSED(icon);
441}
442
444{
445 Q_UNUSED(enabled);
446}
447
449{
450 Q_UNUSED(visible);
451}
452
453void QOhosStatusBarMenuImpl::showPopup(const QWindow *parentWindow, const QRect &targetRect, const QPlatformMenuItem *item)
454{
455 Q_UNUSED(parentWindow);
456 Q_UNUSED(targetRect);
457 Q_UNUSED(item);
458}
459
463
465{
466 if (position < 0 || position >= m_menuItems.size())
467 return nullptr;
468 return m_menuItems.at(position);
469}
470
472{
473 for (auto *item : m_menuItems) {
474 if (item && item->tag() == tag)
475 return item;
476 }
477 return nullptr;
478}
479
484
489
491{
492 std::vector<std::function<std::optional<QNapi::Object>(QtOhos::JsState &)>> jsMenuItemsFactories;
493 for (auto *item : m_menuItems) {
494 auto *ohosItem = qobject_cast<QOhosStatusBarMenuItem *>(item);
495 jsMenuItemsFactories.push_back(
496 ohosItem != nullptr
497 ? ohosItem->makeJsStatusBarMenuItemFactory()
498 : [](QtOhos::JsState &) {
499 return std::optional<QNapi::Object>();
500 });
501 }
502
503 return [jsMenuItemsFactories = std::move(jsMenuItemsFactories)](QtOhos::JsState &jsState) {
504 std::vector<QNapi::ValueWrapper> jsGroupMenusArray;
505
506 std::vector<QNapi::ValueWrapper> currentJsMenuItemsArray;
507
508 for (const auto &jsMenuItemFactory : jsMenuItemsFactories) {
509 std::optional<QNapi::Object> optJsMenuItem = jsMenuItemFactory(jsState);
510 if (optJsMenuItem.has_value()) {
511 currentJsMenuItemsArray.push_back(optJsMenuItem.value());
512 } else {
513 jsGroupMenusArray.push_back(QNapi::makeArray(jsState.env(), currentJsMenuItemsArray));
514 currentJsMenuItemsArray.clear();
515 }
516 }
517
518 jsGroupMenusArray.push_back(QNapi::makeArray(jsState.env(), currentJsMenuItemsArray));
519 currentJsMenuItemsArray.clear();
520
521 return QNapi::makeArray(jsState.env(), jsGroupMenusArray);
522 };
523}
524
526{
527 std::vector<std::function<std::optional<QNapi::Object>(QtOhos::JsState &)>> jsSubMenuItemsFactories;
528 for (auto *item : m_menuItems) {
529 auto *ohosItem = qobject_cast<QOhosStatusBarMenuItem *>(item);
530 if (ohosItem != nullptr)
531 jsSubMenuItemsFactories.push_back(ohosItem->makeJsStatusBarSubMenuItemFactory());
532 }
533
534 return [jsSubMenuItemsFactories = std::move(jsSubMenuItemsFactories)](QtOhos::JsState &jsState) {
535 std::vector<QNapi::ValueWrapper> jsSubMenuItems;
536 for (const auto &jsSubMenuItemsFactory : jsSubMenuItemsFactories) {
537 std::optional<QNapi::Object> optJsSubMenuItem = jsSubMenuItemsFactory(jsState);
538 if (optJsSubMenuItem.has_value())
539 jsSubMenuItems.push_back(optJsSubMenuItem.value());
540 }
541 return QNapi::makeArray(jsState.env(), jsSubMenuItems);
542 };
543}
544
545QPlatformMenuItem *QOhosStatusBarMenuImpl::findItemByMenuCode(const std::string &menuCode) const
546{
547 for (auto *item : m_menuItems) {
548 auto *ohosItem = qobject_cast<QOhosStatusBarMenuItem *>(item);
549 if (ohosItem != nullptr && ohosItem->menuCode() == menuCode)
550 return item;
551 }
552 return nullptr;
553}
554
555void QOhosStatusBarMenuImpl::handleRightClickEvent(const std::string &menuCode)
556{
557 QPlatformMenuItem *item = findItemByMenuCode(menuCode);
558 if (item == nullptr) {
559 qOhosPrintfWarning("%s: Menu item with code '%s' not found", Q_FUNC_INFO, menuCode.c_str());
560 return;
561 }
562
563 QOhosStatusBarMenuItem *ohosItem = qobject_cast<QOhosStatusBarMenuItem *>(item);
564 if (ohosItem != nullptr)
565 Q_EMIT ohosItem->activated();
566}
567
568}
569
571
576
577QT_END_NAMESPACE
578
579#include "qohosstatusbarmenu.moc"
void setText(const QString &text) override
void showPopup(const QWindow *parentWindow, const QRect &targetRect, const QPlatformMenuItem *item) override
QPlatformMenuItem * menuItemAt(int position) const override
std::function< QNapi::Array(QtOhos::JsState &)> makeJsStatusBarGroupMenusFactory() const override
QPlatformMenuItem * menuItemForTag(quintptr tag) const override
void syncMenuItem(QPlatformMenuItem *menuItem) override
void insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before) override
void removeMenuItem(QPlatformMenuItem *menuItem) override
QPlatformMenu * createSubMenu() const override
QPlatformMenuItem * createMenuItem() const override
std::function< QNapi::Array(QtOhos::JsState &)> makeJsStatusBarSubMenuItemsFactory() const
void setIsSeparator(bool isSeparator) override
void setMenu(QPlatformMenu *menu) override
void setShortcut(const QKeySequence &shortcut) override
std::function< std::optional< QNapi::Object >(QtOhos::JsState &)> makeJsStatusBarMenuItemFactory() const
std::function< std::optional< QNapi::Object >(QtOhos::JsState &)> makeJsStatusBarSubMenuItemFactory() const
void setText(const QString &text) override
Combined button and popup list for selecting options.
std::optional< QNapi::Object > makeJsStatusBarMenuItemOptions(QtOhos::JsState &jsState, const QIcon &icon, const QSize &iconSize)
QNapi::Object makeJsStatusBarMenuItemWithSubMenu(QtOhos::JsState &jsState, const std::string &title, QNapi::Array jsStatusBarSubMenuItems, const QIcon &icon, const QSize &iconSize)
QNapi::Object makeNotifyOnlyJsStatusBarMenuAction(QtOhos::JsState &jsState, const std::string &menuCode)
QNapi::Object makeJsStatusBarMenuItemWithAction(QtOhos::JsState &jsState, const std::string &title, const std::string &menuCode, const QIcon &icon, const QSize &iconSize)
std::optional< QNapi::Object > makeJsStatusBarItemIcon(QtOhos::JsState &jsState, const QIcon &icon, const QSize &iconSize)
std::shared_ptr< void > registerOhosRightMenuClickListener(QtOhos::JsState &jsState, QOhosConsumer< std::string > clickedMenuCodeConsumer)
constexpr QSize defaultStatusBarMenuItemIconSize(48, 48)
QNapi::Object makeJsStatusBarSubMenuItem(QtOhos::JsState &jsState, const std::string &subTitle, const std::string &menuCode, const QIcon &icon, const QSize &iconSize)
std::unique_ptr< QOhosStatusBarMenu > makeQOhosStatusBarMenu()