6#include <qguiapplication.h>
7#include <qpa/qplatformtheme.h>
28@interface QUIMenuController : UIResponder
31@implementation QUIMenuController {
32 QIOSMenuItemList m_visibleMenuItems;
35- (instancetype)initWithVisibleMenuItems:(
const QIOSMenuItemList &)visibleMenuItems
37 if (self = [super init]) {
38 [self setVisibleMenuItems:visibleMenuItems];
39 [[NSNotificationCenter defaultCenter]
41 selector:@selector(menuClosed)
42 name:UIMenuControllerDidHideMenuNotification object:nil];
50 [[NSNotificationCenter defaultCenter]
52 name:UIMenuControllerDidHideMenuNotification object:nil];
56- (
void)setVisibleMenuItems:(
const QIOSMenuItemList &)visibleMenuItems
58 m_visibleMenuItems = visibleMenuItems;
59 NSMutableArray<UIMenuItem *> *menuItemArray = [NSMutableArray<UIMenuItem *> arrayWithCapacity:m_visibleMenuItems.size()];
65 for (
int i = 0; i < m_visibleMenuItems.count(); ++i) {
66 QIOSMenuItem *item = m_visibleMenuItems.at(i);
67 SEL sel = NSSelectorFromString([NSString stringWithFormat:@
"%@%i:", kSelectorPrefix, i]);
68 [menuItemArray addObject:[[[UIMenuItem alloc] initWithTitle:item->m_text.toNSString() action:sel] autorelease]];
70 [UIMenuController sharedMenuController].menuItems = menuItemArray;
71 if ([UIMenuController sharedMenuController].menuVisible)
72 [[UIMenuController sharedMenuController] setMenuVisible:YES animated:NO];
77 QIOSMenu::currentMenu()->dismiss();
80- (
id)targetForAction:(SEL)action withSender:(id)sender
83 BOOL containsPrefix = ([NSStringFromSelector(action) rangeOfString:kSelectorPrefix].location != NSNotFound);
84 return containsPrefix ? self : 0;
87- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
92 return [super methodSignatureForSelector:@selector(methodSignatureForSelector:)];
95- (
void)forwardInvocation:(NSInvocation *)invocation
99 NSString *selector = NSStringFromSelector(invocation.selector);
100 NSRange range = NSMakeRange(kSelectorPrefix.length, selector.length - kSelectorPrefix.length - 1);
101 NSInteger selectedIndex = [[selector substringWithRange:range] integerValue];
102 QIOSMenu::currentMenu()->handleItemSelected(m_visibleMenuItems.at(selectedIndex));
109@interface QUIPickerView : UIPickerView <UIPickerViewDelegate, UIPickerViewDataSource>
111@property(retain) UIToolbar *toolbar;
115@implementation QUIPickerView {
116 QIOSMenuItemList m_visibleMenuItems;
117 QPointer<QObject> m_focusObjectWithPickerView;
118 NSInteger m_selectedRow;
121- (instancetype)initWithVisibleMenuItems:(
const QIOSMenuItemList &)visibleMenuItems selectItem:(
const QIOSMenuItem *)selectItem
123 if (self = [super init]) {
124 [self setVisibleMenuItems:visibleMenuItems selectItem:selectItem];
126 self.autoresizingMask = UIViewAutoresizingFlexibleWidth;
127 self.toolbar = [[[UIToolbar alloc] init] autorelease];
128 self.toolbar.frame.size = [self.toolbar sizeThatFits:self.bounds.size];
129 self.toolbar.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
131 UIBarButtonItem *spaceButton = [[[UIBarButtonItem alloc]
132 initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
133 target:self action:@selector(closeMenu)] autorelease];
134 UIBarButtonItem *cancelButton = [[[UIBarButtonItem alloc]
135 initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
136 target:self action:@selector(cancelMenu)] autorelease];
137 UIBarButtonItem *doneButton = [[[UIBarButtonItem alloc]
138 initWithBarButtonSystemItem:UIBarButtonSystemItemDone
139 target:self action:@selector(closeMenu)] autorelease];
140 [self.toolbar setItems:@[cancelButton, spaceButton, doneButton]];
142 [self setDelegate:self];
143 [self setDataSource:self];
144 [self selectRow:m_selectedRow inComponent:0 animated:
false];
145 [self listenForKeyboardWillHideNotification:YES];
151- (
void)setVisibleMenuItems:(
const QIOSMenuItemList &)visibleMenuItems selectItem:(
const QIOSMenuItem *)selectItem
153 m_visibleMenuItems = visibleMenuItems;
154 m_selectedRow = visibleMenuItems.indexOf(
const_cast<QIOSMenuItem *>(selectItem));
155 if (m_selectedRow == -1)
157 [self reloadAllComponents];
160- (
void)listenForKeyboardWillHideNotification:(BOOL)listen
163 [[NSNotificationCenter defaultCenter]
165 selector:@selector(cancelMenu)
166 name:@
"UIKeyboardWillHideNotification" object:nil];
168 [[NSNotificationCenter defaultCenter]
170 name:@
"UIKeyboardWillHideNotification" object:nil];
176 [self listenForKeyboardWillHideNotification:NO];
181- (
NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
183 Q_UNUSED(pickerView);
185 return m_visibleMenuItems.at(row)->m_text.toNSString();
188- (
NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
190 Q_UNUSED(pickerView);
194- (
NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
196 Q_UNUSED(pickerView);
198 return m_visibleMenuItems.length();
201- (
void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
203 Q_UNUSED(pickerView);
210 if (!m_visibleMenuItems.isEmpty())
211 QIOSMenu::currentMenu()->handleItemSelected(m_visibleMenuItems.at(m_selectedRow));
213 QIOSMenu::currentMenu()->dismiss();
218 QIOSMenu::currentMenu()->dismiss();
225QIOSMenuItem::QIOSMenuItem()
226 : QPlatformMenuItem()
229 , m_role(MenuRole(0))
238 m_text = QPlatformTheme::removeMnemonics(text);
243 m_menu =
static_cast<QIOSMenu *>(menu);
261#ifndef QT_NO_SHORTCUT
264 m_shortcut = sequence;
296 m_menuItems.append(
static_cast<
QIOSMenuItem *>(menuItem));
298 int index = m_menuItems.indexOf(
static_cast<
QIOSMenuItem *>(before)) + 1;
299 m_menuItems.insert(index,
static_cast<
QIOSMenuItem *>(menuItem));
301 if (m_currentMenu ==
this)
302 syncMenuItem(menuItem);
307 m_menuItems.removeOne(
static_cast<
QIOSMenuItem *>(menuItem));
308 if (m_currentMenu ==
this)
309 syncMenuItem(menuItem);
314 if (m_currentMenu !=
this)
317 switch (m_effectiveMenuType) {
319 [m_menuController setVisibleMenuItems:filterFirstResponderActions(visibleMenuItems())];
322 [m_pickerView setVisibleMenuItems:visibleMenuItems() selectItem:m_targetItem];
342void QIOSMenu::setMenuType(QPlatformMenu::MenuType type)
349 emit menuItem->activated();
352 if (
QIOSMenu *menu = menuItem->m_menu) {
353 menu->setMenuType(m_effectiveMenuType);
354 menu->showPopup(m_parentWindow, m_targetRect, 0);
358void QIOSMenu::showPopup(
const QWindow *parentWindow,
const QRect &targetRect,
const QPlatformMenuItem *item)
360 if (m_currentMenu ==
this || !parentWindow)
365 m_parentWindow =
const_cast<QWindow *>(parentWindow);
366 m_targetRect = targetRect;
369 if (!m_parentWindow->isActive())
370 m_parentWindow->requestActivate();
372 if (m_currentMenu && m_currentMenu !=
this)
375 m_currentMenu =
this;
376 m_effectiveMenuType = m_menuType;
377 connect(
qGuiApp, &QGuiApplication::focusObjectChanged,
this, &QIOSMenu::dismiss);
379 switch (m_effectiveMenuType) {
381 toggleShowUsingUIMenuController(
true);
384 toggleShowUsingUIPickerView(
true);
393 if (m_currentMenu !=
this)
398 disconnect(
qGuiApp, &QGuiApplication::focusObjectChanged,
this, &QIOSMenu::dismiss);
400 switch (m_effectiveMenuType) {
402 toggleShowUsingUIMenuController(
false);
405 toggleShowUsingUIPickerView(
false);
409 m_currentMenu =
nullptr;
413void QIOSMenu::toggleShowUsingUIMenuController(
bool show)
416 Q_ASSERT(!m_menuController);
417 m_menuController = [[QUIMenuController alloc] initWithVisibleMenuItems:filterFirstResponderActions(visibleMenuItems())];
419 connect(
qGuiApp->inputMethod(), &QInputMethod::keyboardRectangleChanged,
this, &QIOSMenu::repositionMenu);
421 disconnect(
qGuiApp->inputMethod(), &QInputMethod::keyboardRectangleChanged,
this, &QIOSMenu::repositionMenu);
423 Q_ASSERT(m_menuController);
424 [[UIMenuController sharedMenuController] setMenuVisible:NO animated:YES];
425 [m_menuController release];
426 m_menuController =
nullptr;
430void QIOSMenu::toggleShowUsingUIPickerView(
bool show)
432 static QObject *focusObjectWithPickerView =
nullptr;
435 Q_ASSERT(!m_pickerView);
436 m_pickerView = [[QUIPickerView alloc] initWithVisibleMenuItems:visibleMenuItems() selectItem:m_targetItem];
438 Q_ASSERT(!focusObjectWithPickerView);
439 focusObjectWithPickerView =
qApp->focusWindow()->focusObject();
440 focusObjectWithPickerView->installEventFilter(
this);
441 qApp->inputMethod()->update(Qt::ImEnabled | Qt::ImPlatformData);
443 Q_ASSERT(focusObjectWithPickerView);
444 focusObjectWithPickerView->removeEventFilter(
this);
445 focusObjectWithPickerView =
nullptr;
447 Q_ASSERT(m_pickerView);
448 [m_pickerView listenForKeyboardWillHideNotification:NO];
449 [m_pickerView release];
450 m_pickerView =
nullptr;
452 qApp->inputMethod()->update(Qt::ImEnabled | Qt::ImPlatformData);
458 if (event->type() == QEvent::InputMethodQuery) {
459 QInputMethodQueryEvent *queryEvent =
static_cast<QInputMethodQueryEvent *>(event);
460 if (queryEvent->queries() & Qt::ImPlatformData) {
462 obj->event(queryEvent);
464 QVariantMap imPlatformData = queryEvent->value(Qt::ImPlatformData).toMap();
465 imPlatformData.insert(kImePlatformDataInputView, QVariant::fromValue(
static_cast<
void *>(m_pickerView)));
466 imPlatformData.insert(kImePlatformDataInputAccessoryView, QVariant::fromValue(
static_cast<
void *>(m_pickerView.toolbar)));
468 queryEvent->setValue(Qt::ImPlatformData, imPlatformData);
469 queryEvent->setValue(Qt::ImEnabled,
true);
475 return QObject::eventFilter(obj, event);
481 visibleMenuItems.reserve(m_menuItems.size());
482 std::copy_if(m_menuItems.begin(), m_menuItems.end(),
std::back_inserter(visibleMenuItems),
484 return visibleMenuItems;
495 UIResponder *responder = [UIResponder qt_currentFirstResponder];
497 for (
int i = 0; i < menuItems.count(); ++i) {
499#ifndef QT_NO_SHORTCUT
500 QKeySequence shortcut = menuItem->m_shortcut;
501 if ((shortcut == QKeySequence::Cut && [responder canPerformAction:@selector(cut:) withSender:nil])
502 || (shortcut == QKeySequence::Copy && [responder canPerformAction:@selector(copy:) withSender:nil])
503 || (shortcut == QKeySequence::Paste && [responder canPerformAction:@selector(paste:) withSender:nil])
504 || (shortcut == QKeySequence::Delete && [responder canPerformAction:@selector(
delete:) withSender:nil])
505 || (shortcut == QKeySequence::SelectAll && [responder canPerformAction:@selector(selectAll:) withSender:nil])
506 || (shortcut == QKeySequence::Undo && [responder canPerformAction:@selector(undo) withSender:nil])
507 || (shortcut == QKeySequence::Redo && [responder canPerformAction:@selector(redo) withSender:nil])
508 || (shortcut == QKeySequence::Bold && [responder canPerformAction:@selector(toggleBoldface:) withSender:nil])
509 || (shortcut == QKeySequence::Italic && [responder canPerformAction:@selector(toggleItalics:) withSender:nil])
510 || (shortcut == QKeySequence::Underline && [responder canPerformAction:@selector(toggleUnderline:) withSender:nil])) {
514 filteredMenuItems.append(menuItem);
516 return filteredMenuItems;
521 switch (m_effectiveMenuType) {
523 UIView *view =
reinterpret_cast<UIView *>(m_parentWindow->winId());
524 [[UIMenuController sharedMenuController] setTargetRect:m_targetRect.toCGRect() inView:view];
525 [[UIMenuController sharedMenuController] setMenuVisible:YES animated:YES];
534 if (position < 0 || position >= m_menuItems.size())
536 return m_menuItems.at(position);
541 for (
int i = 0; i < m_menuItems.size(); ++i) {
542 QPlatformMenuItem *item = m_menuItems.at(i);
543 if (item->tag() == tag)
Q_FORWARD_DECLARE_OBJC_CLASS(NSString)
const char kImePlatformDataHideShortcutsBar[]