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}
327
328void QOhosStatusBarMenuImpl::insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before)
329{
330 if (menuItem == nullptr)
331 return;
332
333 if (before == nullptr) {
334 m_menuItems.append(menuItem);
335 return;
336 }
337
338 const int idx = m_menuItems.indexOf(before);
339 if (idx >= 0)
340 m_menuItems.insert(idx, menuItem);
341 else
342 m_menuItems.append(menuItem);
343}
344
345void QOhosStatusBarMenuImpl::removeMenuItem(QPlatformMenuItem *menuItem)
346{
347 if (menuItem == nullptr)
348 return;
349 m_menuItems.removeAll(menuItem);
350}
351
352void QOhosStatusBarMenuImpl::syncMenuItem(QPlatformMenuItem *menuItem)
353{
354 Q_UNUSED(menuItem);
355}
356
358{
359 Q_UNUSED(enable);
360}
361
362void QOhosStatusBarMenuImpl::setText(const QString &text)
363{
364 m_text = text;
365}
366
367void QOhosStatusBarMenuImpl::setIcon(const QIcon &icon)
368{
369 Q_UNUSED(icon);
370}
371
373{
374 Q_UNUSED(enabled);
375}
376
378{
379 Q_UNUSED(visible);
380}
381
382void QOhosStatusBarMenuImpl::showPopup(const QWindow *parentWindow, const QRect &targetRect, const QPlatformMenuItem *item)
383{
384 Q_UNUSED(parentWindow);
385 Q_UNUSED(targetRect);
386 Q_UNUSED(item);
387}
388
392
394{
395 if (position < 0 || position >= m_menuItems.size())
396 return nullptr;
397 return m_menuItems.at(position);
398}
399
401{
402 for (auto *item : m_menuItems) {
403 if (item && item->tag() == tag)
404 return item;
405 }
406 return nullptr;
407}
408
413
418
420{
421 std::vector<std::function<QOhosOptional<QNapi::Object>(QtOhos::JsState &)>> jsMenuItemsFactories;
422 for (auto *item : m_menuItems) {
423 auto *ohosItem = qobject_cast<QOhosStatusBarMenuItem *>(item);
424 jsMenuItemsFactories.push_back(
425 ohosItem != nullptr
426 ? ohosItem->makeJsStatusBarMenuItemFactory()
427 : [](QtOhos::JsState &) {
428 return QOhosOptional<QNapi::Object>();
429 });
430 }
431
432 return [jsMenuItemsFactories = std::move(jsMenuItemsFactories)](QtOhos::JsState &jsState) {
433 std::vector<QNapi::ValueWrapper> jsGroupMenusArray;
434
435 std::vector<QNapi::ValueWrapper> currentJsMenuItemsArray;
436
437 for (const auto &jsMenuItemFactory : jsMenuItemsFactories) {
438 QOhosOptional<QNapi::Object> optJsMenuItem = jsMenuItemFactory(jsState);
439 if (optJsMenuItem.hasValue()) {
440 currentJsMenuItemsArray.push_back(optJsMenuItem.value());
441 } else {
442 jsGroupMenusArray.push_back(QNapi::makeArray(jsState.env(), currentJsMenuItemsArray));
443 currentJsMenuItemsArray.clear();
444 }
445 }
446
447 jsGroupMenusArray.push_back(QNapi::makeArray(jsState.env(), currentJsMenuItemsArray));
448 currentJsMenuItemsArray.clear();
449
450 return QNapi::makeArray(jsState.env(), jsGroupMenusArray);
451 };
452}
453
455{
456 std::vector<std::function<QOhosOptional<QNapi::Object>(QtOhos::JsState &)>> jsSubMenuItemsFactories;
457 for (auto *item : m_menuItems) {
458 auto *ohosItem = qobject_cast<QOhosStatusBarMenuItem *>(item);
459 if (ohosItem != nullptr)
460 jsSubMenuItemsFactories.push_back(ohosItem->makeJsStatusBarSubMenuItemFactory());
461 }
462
463 return [jsSubMenuItemsFactories = std::move(jsSubMenuItemsFactories)](QtOhos::JsState &jsState) {
464 std::vector<QNapi::ValueWrapper> jsSubMenuItems;
465 for (const auto &jsSubMenuItemsFactory : jsSubMenuItemsFactories) {
466 QOhosOptional<QNapi::Object> optJsSubMenuItem = jsSubMenuItemsFactory(jsState);
467 if (optJsSubMenuItem.hasValue())
468 jsSubMenuItems.push_back(optJsSubMenuItem.value());
469 }
470 return QNapi::makeArray(jsState.env(), jsSubMenuItems);
471 };
472}
473
474QPlatformMenuItem *QOhosStatusBarMenuImpl::findItemByMenuCode(const std::string &menuCode) const
475{
476 for (auto *item : m_menuItems) {
477 auto *ohosItem = qobject_cast<QOhosStatusBarMenuItem *>(item);
478 if (ohosItem != nullptr && ohosItem->menuCode() == menuCode)
479 return item;
480 }
481 return nullptr;
482}
483
484void QOhosStatusBarMenuImpl::handleRightClickEvent(const std::string &menuCode)
485{
486 QPlatformMenuItem *item = findItemByMenuCode(menuCode);
487 if (item == nullptr) {
488 qOhosPrintfWarning("%s: Menu item with code '%s' not found", Q_FUNC_INFO, menuCode.c_str());
489 return;
490 }
491
492 QOhosStatusBarMenuItem *ohosItem = qobject_cast<QOhosStatusBarMenuItem *>(item);
493 if (ohosItem != nullptr)
494 Q_EMIT ohosItem->activated();
495}
496
497}
498
500
505
506QT_END_NAMESPACE
507
508#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::unique_ptr< QOhosStatusBarMenu > makeQOhosStatusBarMenu()