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"
6#include <QtCore/qobject.h>
7#include <QtCore/qlist.h>
8#include <QtGui/qicon.h>
9#include <QtGui/QWindow>
10#include <atomic>
11#include <cstdint>
12#include <qpa/qplatformtheme.h>
13#include <string>
14
16
17namespace {
18
20 QtOhos::JsState &jsState, QOhosConsumer<std::string> clickedMenuCodeConsumer)
21{
22 return QtOhos::registerOnOffMethodsBasedEventHandler(
23 jsState.eval<QNapi::Object>("@kit.StatusBarExtensionKit.statusBarManager"),
24 "rightMenuClick",
25 [clickedMenuCodeConsumer = std::move(clickedMenuCodeConsumer)](const QtOhos::CallbackInfo &info) {
26 auto eventData = info.getFirstArg<QNapi::Object>(Q_FUNC_INFO);
27 auto optMenuCode = QNapi::getOptionalPropOrEmpty<QNapi::String>(
28 QNapi::getOptionalPropOrEmpty<QNapi::Object>(eventData, "data"),
29 "menuCode");
30 if (!optMenuCode.IsEmpty()) {
31 clickedMenuCodeConsumer(optMenuCode);
32 } else {
33 qOhosPrintfDebug(
34 "%s: no 'menuCode' in the event (%s), ignoring it",
35 Q_FUNC_INFO, QNapi::toJsonString(eventData).c_str());
36 }
37 });
38}
39
41 QtOhos::JsState &jsState, const std::string &menuCode)
42{
43 return QNapi::makeObject(
44 jsState.env(),
45 {
46 {"abilityName", "invalidAbility"},
47 {"notifyOnly", true},
48 {"menuCode", menuCode},
49 });
50}
51
53 QtOhos::JsState &jsState, const std::string &title, const std::string &menuCode)
54{
55 return QNapi::makeObject(
56 jsState.env(),
57 {
58 {"title", title},
59 {"menuAction", makeNotifyOnlyJsStatusBarMenuAction(jsState, menuCode)},
60 });
61}
62
64 QtOhos::JsState &jsState, const std::string &title, QNapi::Array jsStatusBarSubMenuItems)
65{
66 return QNapi::makeObject(
67 jsState.env(),
68 {
69 {"title", title},
70 {"subMenu", jsStatusBarSubMenuItems},
71 });
72}
73
75 QtOhos::JsState &jsState, const std::string &subTitle, const std::string &menuCode)
76{
77 return QNapi::makeObject(
78 jsState.env(),
79 {
80 {"subTitle", subTitle},
81 {"menuAction", makeNotifyOnlyJsStatusBarMenuAction(jsState, menuCode)},
82 });
83}
84
86
88{
90
91public:
93
94 void setText(const QString &text) override;
95 QString text() const;
96 void setIcon(const QIcon &icon) override;
97
98 void setMenu(QPlatformMenu *menu) override;
99
100 void setVisible(bool isVisible) override;
101 void setIsSeparator(bool isSeparator) override;
102 void setFont(const QFont &font) override;
103 void setRole(MenuRole role) override;
104 void setCheckable(bool checkable) override;
105 void setChecked(bool isChecked) override;
106 void setShortcut(const QKeySequence &shortcut) override;
107 void setEnabled(bool enabled) override;
108 void setIconSize(int size) override;
109
110 std::string menuCode() const;
111
114
115private:
116 QString m_text;
117 std::string m_menuCode;
118 bool m_isSeparator = false;
119 QOhosStatusBarMenuImpl *m_menu = nullptr;
120
121 static std::string generateNextMenuCode();
122};
123
125{
127
128public:
130
131 void insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before) override;
132 void removeMenuItem(QPlatformMenuItem *menuItem) override;
133 void syncMenuItem(QPlatformMenuItem *menuItem) override;
135
136 void setText(const QString &text) override;
137 void setIcon(const QIcon &icon) override;
138 void setEnabled(bool enabled) override;
139 void setVisible(bool visible) override;
140
141 void showPopup(const QWindow *parentWindow, const QRect &targetRect, const QPlatformMenuItem *item) override;
143
144 QPlatformMenuItem *menuItemAt(int position) const override;
145 QPlatformMenuItem *menuItemForTag(quintptr tag) const override;
146
148 QPlatformMenu *createSubMenu() const override;
149
151
153
154private:
155 QPlatformMenuItem *findItemByMenuCode(const std::string &menuCode) const;
156 void handleRightClickEvent(const std::string &menuCode);
157
158 QString m_text;
159 QList<QPlatformMenuItem *> m_menuItems;
160
161 struct JsScopeData
162 {
163 std::shared_ptr<void> m_rightMenuClickListenerHandle;
164 };
165
166 std::shared_ptr<JsScopeData> m_jsScopeData;
167};
168
169QOhosStatusBarMenuItem::QOhosStatusBarMenuItem()
170 : QPlatformMenuItem()
171 , m_menuCode(generateNextMenuCode())
172{
173}
174
175void QOhosStatusBarMenuItem::setText(const QString &text)
176{
177 m_text = text;
178}
179
181{
182 return m_text;
183}
184
185void QOhosStatusBarMenuItem::setIcon(const QIcon &icon)
186{
187 Q_UNUSED(icon);
188}
189
190void QOhosStatusBarMenuItem::setMenu(QPlatformMenu *menu)
191{
192 auto *ohosMenu = qobject_cast<QOhosStatusBarMenuImpl *>(menu);
193 if (menu != nullptr && ohosMenu == nullptr)
194 qOhosPrintfWarning("%s: got menu object of incompatible type", Q_FUNC_INFO);
195
196 m_menu = ohosMenu;
197}
198
200{
201 Q_UNUSED(isVisible);
202}
203
205{
206 m_isSeparator = isSeparator;
207}
208
209void QOhosStatusBarMenuItem::setFont(const QFont &font)
210{
211 Q_UNUSED(font);
212}
213
214void QOhosStatusBarMenuItem::setRole(MenuRole role)
215{
216 Q_UNUSED(role);
217}
218
220{
221 Q_UNUSED(checkable);
222}
223
225{
226 Q_UNUSED(isChecked);
227}
228
229void QOhosStatusBarMenuItem::setShortcut(const QKeySequence &shortcut)
230{
231 Q_UNUSED(shortcut);
232}
233
235{
236 Q_UNUSED(enabled);
237}
238
240{
241 Q_UNUSED(size);
242}
243
245{
246 return m_menuCode;
247}
248
250{
251 if (m_isSeparator) {
252 return [](QtOhos::JsState &) {
253 return QOhosOptional<QNapi::Object>();
254 };
255 }
256
257 auto title = QPlatformTheme::removeMnemonics(m_text).toStdString();
258
259 if (m_menu == nullptr) {
260 return [title, menuCode = m_menuCode](QtOhos::JsState &jsState) {
261 return makeQOhosOptional(
262 makeJsStatusBarMenuItemWithAction(jsState, title, menuCode));
263 };
264 } else {
265 return [title, jsSubMenuItemsFactory = m_menu->makeJsStatusBarSubMenuItemsFactory()](QtOhos::JsState &jsState) {
266 return makeQOhosOptional(
267 makeJsStatusBarMenuItemWithSubMenu(jsState, title, jsSubMenuItemsFactory(jsState)));
268 };
269 }
270}
271
273{
274 if (m_isSeparator) {
275 qOhosPrintfWarning(
276 "%s: separator item %p used in sub-menu, which is unsupported on OHOS, ignoring",
277 Q_FUNC_INFO, this);
278 return [](QtOhos::JsState &) {
279 return QOhosOptional<QNapi::Object>();
280 };
281 }
282
283 if (m_menu != nullptr) {
284 qOhosPrintfWarning(
285 "%s: nested-menu item %p used in sub-menu, which is unsupported on OHOS, ignoring",
286 Q_FUNC_INFO, this);
287 return [](QtOhos::JsState &) {
288 return QOhosOptional<QNapi::Object>();
289 };
290 }
291
292 auto subTitle = QPlatformTheme::removeMnemonics(m_text).toStdString();
293
294 return [subTitle, menuCode = m_menuCode](QtOhos::JsState &jsState) {
295 return makeQOhosOptional(
296 makeJsStatusBarSubMenuItem(jsState, subTitle, menuCode));
297 };
298}
299
300std::string QOhosStatusBarMenuItem::generateNextMenuCode()
301{
302 static std::atomic<std::uint64_t> menuCodeCounter(0);
303
304 auto counterValue = ++menuCodeCounter;
305 return std::to_string(counterValue);
306}
307
308QOhosStatusBarMenuImpl::QOhosStatusBarMenuImpl()
310 , m_jsScopeData(
311 QtOhos::makeProxyWithJsThreadDeleter(
312 std::make_shared<JsScopeData>()))
313{
314 auto selfRef = QtOhos::makeQThreadSafeRef(this);
316 [&](QtOhos::JsState &jsState) {
317 m_jsScopeData->m_rightMenuClickListenerHandle = registerOhosRightMenuClickListener(
318 jsState,
319 [selfRef](std::string menuCode) {
320 selfRef.visitInQtThreadIfAlive(
321 [menuCode](auto &self) {
322 self.handleRightClickEvent(menuCode);
323 });
324 });
325 },
326 Q_FUNC_INFO);
327}
328
329void QOhosStatusBarMenuImpl::insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before)
330{
331 if (menuItem == nullptr)
332 return;
333
334 if (before == nullptr) {
335 m_menuItems.append(menuItem);
336 return;
337 }
338
339 const int idx = m_menuItems.indexOf(before);
340 if (idx >= 0)
341 m_menuItems.insert(idx, menuItem);
342 else
343 m_menuItems.append(menuItem);
344}
345
346void QOhosStatusBarMenuImpl::removeMenuItem(QPlatformMenuItem *menuItem)
347{
348 if (menuItem == nullptr)
349 return;
350 m_menuItems.removeAll(menuItem);
351}
352
353void QOhosStatusBarMenuImpl::syncMenuItem(QPlatformMenuItem *menuItem)
354{
355 Q_UNUSED(menuItem);
356}
357
359{
360 Q_UNUSED(enable);
361}
362
363void QOhosStatusBarMenuImpl::setText(const QString &text)
364{
365 m_text = text;
366}
367
368void QOhosStatusBarMenuImpl::setIcon(const QIcon &icon)
369{
370 Q_UNUSED(icon);
371}
372
374{
375 Q_UNUSED(enabled);
376}
377
379{
380 Q_UNUSED(visible);
381}
382
383void QOhosStatusBarMenuImpl::showPopup(const QWindow *parentWindow, const QRect &targetRect, const QPlatformMenuItem *item)
384{
385 Q_UNUSED(parentWindow);
386 Q_UNUSED(targetRect);
387 Q_UNUSED(item);
388}
389
393
395{
396 if (position < 0 || position >= m_menuItems.size())
397 return nullptr;
398 return m_menuItems.at(position);
399}
400
402{
403 for (auto *item : m_menuItems) {
404 if (item && item->tag() == tag)
405 return item;
406 }
407 return nullptr;
408}
409
414
419
421{
422 std::vector<std::function<QOhosOptional<QNapi::Object>(QtOhos::JsState &)>> jsMenuItemsFactories;
423 for (auto *item : m_menuItems) {
424 auto *ohosItem = qobject_cast<QOhosStatusBarMenuItem *>(item);
425 jsMenuItemsFactories.push_back(
426 ohosItem != nullptr
427 ? ohosItem->makeJsStatusBarMenuItemFactory()
428 : [](QtOhos::JsState &) {
429 return QOhosOptional<QNapi::Object>();
430 });
431 }
432
433 return [jsMenuItemsFactories = std::move(jsMenuItemsFactories)](QtOhos::JsState &jsState) {
434 std::vector<QNapi::ValueWrapper> jsGroupMenusArray;
435
436 std::vector<QNapi::ValueWrapper> currentJsMenuItemsArray;
437
438 for (const auto &jsMenuItemFactory : jsMenuItemsFactories) {
439 QOhosOptional<QNapi::Object> optJsMenuItem = jsMenuItemFactory(jsState);
440 if (optJsMenuItem.has_value()) {
441 currentJsMenuItemsArray.push_back(optJsMenuItem.value());
442 } else {
443 jsGroupMenusArray.push_back(QNapi::makeArray(jsState.env(), currentJsMenuItemsArray));
444 currentJsMenuItemsArray.clear();
445 }
446 }
447
448 jsGroupMenusArray.push_back(QNapi::makeArray(jsState.env(), currentJsMenuItemsArray));
449 currentJsMenuItemsArray.clear();
450
451 return QNapi::makeArray(jsState.env(), jsGroupMenusArray);
452 };
453}
454
456{
457 std::vector<std::function<QOhosOptional<QNapi::Object>(QtOhos::JsState &)>> jsSubMenuItemsFactories;
458 for (auto *item : m_menuItems) {
459 auto *ohosItem = qobject_cast<QOhosStatusBarMenuItem *>(item);
460 if (ohosItem != nullptr)
461 jsSubMenuItemsFactories.push_back(ohosItem->makeJsStatusBarSubMenuItemFactory());
462 }
463
464 return [jsSubMenuItemsFactories = std::move(jsSubMenuItemsFactories)](QtOhos::JsState &jsState) {
465 std::vector<QNapi::ValueWrapper> jsSubMenuItems;
466 for (const auto &jsSubMenuItemsFactory : jsSubMenuItemsFactories) {
467 QOhosOptional<QNapi::Object> optJsSubMenuItem = jsSubMenuItemsFactory(jsState);
468 if (optJsSubMenuItem.has_value())
469 jsSubMenuItems.push_back(optJsSubMenuItem.value());
470 }
471 return QNapi::makeArray(jsState.env(), jsSubMenuItems);
472 };
473}
474
475QPlatformMenuItem *QOhosStatusBarMenuImpl::findItemByMenuCode(const std::string &menuCode) const
476{
477 for (auto *item : m_menuItems) {
478 auto *ohosItem = qobject_cast<QOhosStatusBarMenuItem *>(item);
479 if (ohosItem != nullptr && ohosItem->menuCode() == menuCode)
480 return item;
481 }
482 return nullptr;
483}
484
485void QOhosStatusBarMenuImpl::handleRightClickEvent(const std::string &menuCode)
486{
487 QPlatformMenuItem *item = findItemByMenuCode(menuCode);
488 if (item == nullptr) {
489 qOhosPrintfWarning("%s: Menu item with code '%s' not found", Q_FUNC_INFO, menuCode.c_str());
490 return;
491 }
492
493 QOhosStatusBarMenuItem *ohosItem = qobject_cast<QOhosStatusBarMenuItem *>(item);
494 if (ohosItem != nullptr)
495 Q_EMIT ohosItem->activated();
496}
497
498}
499
501
506
507QT_END_NAMESPACE
508
509#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< QOhosOptional< QNapi::Object >(QtOhos::JsState &)> makeJsStatusBarSubMenuItemFactory() const
std::function< QOhosOptional< QNapi::Object >(QtOhos::JsState &)> makeJsStatusBarMenuItemFactory() const
void setText(const QString &text) override
Combined button and popup list for selecting options.
QNapi::Object makeJsStatusBarSubMenuItem(QtOhos::JsState &jsState, const std::string &subTitle, const std::string &menuCode)
QNapi::Object makeNotifyOnlyJsStatusBarMenuAction(QtOhos::JsState &jsState, const std::string &menuCode)
QNapi::Object makeJsStatusBarMenuItemWithSubMenu(QtOhos::JsState &jsState, const std::string &title, QNapi::Array jsStatusBarSubMenuItems)
QNapi::Object makeJsStatusBarMenuItemWithAction(QtOhos::JsState &jsState, const std::string &title, const std::string &menuCode)
std::shared_ptr< void > registerOhosRightMenuClickListener(QtOhos::JsState &jsState, QOhosConsumer< std::string > clickedMenuCodeConsumer)
void runInJsThreadAndWait(const std::function< void(JsState &)> &task, std::string callerContextName={})
std::unique_ptr< QOhosStatusBarMenu > makeQOhosStatusBarMenu()