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