Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qcocoahelpers.mm
Go to the documentation of this file.
1// Copyright (C) 2016 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
4#include <AppKit/AppKit.h>
5
6#include <qpa/qplatformtheme.h>
7
8#include "qcocoahelpers.h"
9#include "qnsview.h"
10
11#include <QtCore>
12#include <QtGui>
13#include <qpa/qplatformscreen.h>
14#include <private/qguiapplication_p.h>
15#include <private/qwindow_p.h>
16#include <QtGui/private/qcoregraphics_p.h>
17
18#include <algorithm>
19
21
22Q_LOGGING_CATEGORY(lcQpaWindow, "qt.qpa.window");
23Q_LOGGING_CATEGORY(lcQpaDrawing, "qt.qpa.drawing");
24Q_LOGGING_CATEGORY(lcQpaMouse, "qt.qpa.input.mouse", QtCriticalMsg);
25Q_LOGGING_CATEGORY(lcQpaKeys, "qt.qpa.input.keys", QtCriticalMsg);
26Q_LOGGING_CATEGORY(lcQpaInputMethods, "qt.qpa.input.methods")
27Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen", QtCriticalMsg);
28Q_LOGGING_CATEGORY(lcQpaApplication, "qt.qpa.application");
29Q_LOGGING_CATEGORY(lcQpaClipboard, "qt.qpa.clipboard")
30Q_LOGGING_CATEGORY(lcInputDevices, "qt.qpa.input.devices")
31Q_LOGGING_CATEGORY(lcQpaDialogs, "qt.qpa.dialogs")
32Q_LOGGING_CATEGORY(lcQpaMenus, "qt.qpa.menus")
33
34//
35// Conversion Functions
36//
37
39{
41 for (NSString *string in array)
42 result << QString::fromNSString(string);
43 return result;
44}
45
46NSMutableArray<NSString *> *qt_mac_QStringListToNSMutableArray(const QStringList &list)
47{
48 NSMutableArray<NSString *> *result = [NSMutableArray<NSString *> arrayWithCapacity:list.size()];
49 for (const QString &string : list)
50 [result addObject:string.toNSString()];
51 return result;
52}
53
55{
56 NSDragOperation mac_code;
58 bool Qt2Mac;
59};
60
62 { NSDragOperationLink, Qt::LinkAction, true },
63 { NSDragOperationMove, Qt::MoveAction, true },
64 { NSDragOperationDelete, Qt::MoveAction, true },
65 { NSDragOperationCopy, Qt::CopyAction, true },
66 { NSDragOperationGeneric, Qt::CopyAction, false },
67 { NSDragOperationEvery, Qt::ActionMask, false },
68 { NSDragOperationNone, Qt::IgnoreAction, false }
69};
70
71NSDragOperation qt_mac_mapDropAction(Qt::DropAction action)
72{
73 for (int i=0; dnd_enums[i].qt_code; i++) {
74 if (dnd_enums[i].Qt2Mac && (action & dnd_enums[i].qt_code)) {
75 return dnd_enums[i].mac_code;
76 }
77 }
78 return NSDragOperationNone;
79}
80
81NSDragOperation qt_mac_mapDropActions(Qt::DropActions actions)
82{
83 NSDragOperation nsActions = NSDragOperationNone;
84 for (int i=0; dnd_enums[i].qt_code; i++) {
85 if (dnd_enums[i].Qt2Mac && (actions & dnd_enums[i].qt_code))
86 nsActions |= dnd_enums[i].mac_code;
87 }
88 return nsActions;
89}
90
91Qt::DropAction qt_mac_mapNSDragOperation(NSDragOperation nsActions)
92{
94 for (int i=0; dnd_enums[i].mac_code; i++) {
95 if (nsActions & dnd_enums[i].mac_code)
96 return dnd_enums[i].qt_code;
97 }
98 return action;
99}
100
101Qt::DropActions qt_mac_mapNSDragOperations(NSDragOperation nsActions)
102{
103 Qt::DropActions actions = Qt::IgnoreAction;
104
105 for (int i=0; dnd_enums[i].mac_code; i++) {
106 if (dnd_enums[i].mac_code == NSDragOperationEvery)
107 continue;
108
109 if (nsActions & dnd_enums[i].mac_code)
110 actions |= dnd_enums[i].qt_code;
111 }
112 return actions;
113}
114
132{
133 return qt_objc_cast<QNSView *>(view);
134}
135
136//
137// Misc
138//
139
140// Sets the activation policy for this process to NSApplicationActivationPolicyRegular,
141// unless either LSUIElement or LSBackgroundOnly is set in the Info.plist.
143{
144 bool forceTransform = true;
145 CFTypeRef value = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(),
146 CFSTR("LSUIElement"));
147 if (value) {
148 CFTypeID valueType = CFGetTypeID(value);
149 // Officially it's supposed to be a string, a boolean makes sense, so we'll check.
150 // A number less so, but OK.
151 if (valueType == CFStringGetTypeID())
152 forceTransform = !(QString::fromCFString(static_cast<CFStringRef>(value)).toInt());
153 else if (valueType == CFBooleanGetTypeID())
154 forceTransform = !CFBooleanGetValue(static_cast<CFBooleanRef>(value));
155 else if (valueType == CFNumberGetTypeID()) {
156 int valueAsInt;
157 CFNumberGetValue(static_cast<CFNumberRef>(value), kCFNumberIntType, &valueAsInt);
158 forceTransform = !valueAsInt;
159 }
160 }
161
162 if (forceTransform) {
163 value = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(),
164 CFSTR("LSBackgroundOnly"));
165 if (value) {
166 CFTypeID valueType = CFGetTypeID(value);
167 if (valueType == CFBooleanGetTypeID())
168 forceTransform = !CFBooleanGetValue(static_cast<CFBooleanRef>(value));
169 else if (valueType == CFStringGetTypeID())
170 forceTransform = !(QString::fromCFString(static_cast<CFStringRef>(value)).toInt());
171 else if (valueType == CFNumberGetTypeID()) {
172 int valueAsInt;
173 CFNumberGetValue(static_cast<CFNumberRef>(value), kCFNumberIntType, &valueAsInt);
174 forceTransform = !valueAsInt;
175 }
176 }
177 }
178
179 if (forceTransform) {
180 [[NSApplication sharedApplication] setActivationPolicy:NSApplicationActivationPolicyRegular];
181 }
182}
183
185{
186 QString appName;
187 CFTypeRef string = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(), CFSTR("CFBundleName"));
188 if (string)
189 appName = QString::fromCFString(static_cast<CFStringRef>(string));
190
191 if (appName.isEmpty()) {
192 QString arg0 = QGuiApplicationPrivate::instance()->appName();
193 if (arg0.contains("/")) {
194 QStringList parts = arg0.split(u'/');
195 appName = parts.at(parts.count() - 1);
196 } else {
197 appName = arg0;
198 }
199 }
200 return appName;
201}
202
203// -------------------------------------------------------------------------
204
223{
224 return QPointF(pos.x(), reference.height() - pos.y());
225}
226
228{
229 return QRectF(qt_mac_flip(rect.bottomLeft(), reference), rect.size());
230}
231
232// -------------------------------------------------------------------------
233
245{
246 if (buttonNum >= 0 && buttonNum <= 31)
247 return Qt::MouseButton(1 << buttonNum);
248 return Qt::NoButton;
249}
250
265{
267 return Qt::NoButton;
268
269 switch (event.type) {
270 case NSEventTypeRightMouseUp:
271 case NSEventTypeRightMouseDown:
272 return Qt::RightButton;
273
274 default:
275 break;
276 }
277
278 return cocoaButton2QtButton(event.buttonNumber);
279}
280
287{
288 switch (event.type) {
289 case NSEventTypeLeftMouseDown:
290 case NSEventTypeRightMouseDown:
291 case NSEventTypeOtherMouseDown:
293
294 case NSEventTypeLeftMouseUp:
295 case NSEventTypeRightMouseUp:
296 case NSEventTypeOtherMouseUp:
298
299 case NSEventTypeLeftMouseDragged:
300 case NSEventTypeRightMouseDragged:
301 case NSEventTypeOtherMouseDragged:
302 return QEvent::MouseMove;
303
304 case NSEventTypeMouseMoved:
305 return QEvent::MouseMove;
306
307 default:
308 break;
309 }
310
311 return QEvent::None;
312}
313
319Qt::MouseButtons cocoaMouseButtons2QtMouseButtons(NSInteger pressedMouseButtons)
320{
321 return static_cast<Qt::MouseButton>(pressedMouseButtons & Qt::MouseButtonMask);
322}
323
330{
331 return cocoaMouseButtons2QtMouseButtons(NSEvent.pressedMouseButtons);
332}
333
338
339NSString *qt_mac_AppKitString(NSString *table, NSString *key)
340{
341 static const NSBundle *appKit = [NSBundle bundleForClass:NSApplication.class];
342 if (!appKit)
343 return key;
344
345 return [appKit localizedStringForKey:key value:nil table:table];
346}
347
349
359@implementation QNSPanelContentsWrapper {
360 NSButton *_okButton;
361 NSButton *_cancelButton;
364}
365
366@synthesize okButton = _okButton;
367@synthesize cancelButton = _cancelButton;
368@synthesize panelContents = _panelContents;
369@synthesize panelContentsMargins = _panelContentsMargins;
370
371- (instancetype)initWithPanelDelegate:(id<QNSPanelDelegate>)panelDelegate
372{
373 if ((self = [super initWithFrame:NSZeroRect])) {
374 // create OK and Cancel buttons and add these as subviews
375 _okButton = [self createButtonWithTitle:QPlatformDialogHelper::Ok];
376 _okButton.action = @selector(onOkClicked);
377 _okButton.target = panelDelegate;
378 _cancelButton = [self createButtonWithTitle:QPlatformDialogHelper::Cancel];
379 _cancelButton.action = @selector(onCancelClicked);
380 _cancelButton.target = panelDelegate;
381
382 _panelContents = nil;
383
384 _panelContentsMargins = NSEdgeInsetsMake(0, 0, 0, 0);
385 }
386
387 return self;
388}
389
390- (void)dealloc
391{
392 [_okButton release];
393 _okButton = nil;
394 [_cancelButton release];
395 _cancelButton = nil;
396
397 _panelContents = nil;
398
399 [super dealloc];
400}
401
402- (NSButton *)createButtonWithTitle:(QPlatformDialogHelper::StandardButton)type
403{
404 NSButton *button = [[NSButton alloc] initWithFrame:NSZeroRect];
405 button.buttonType = NSButtonTypeMomentaryLight;
406 button.bezelStyle = NSBezelStyleRounded;
407 const QString &cleanTitle =
409 // FIXME: Not obvious, from Cocoa's documentation, that QString::toNSString() makes a deep copy
410 button.title = (NSString *)cleanTitle.toCFString();
411 ((NSButtonCell *)button.cell).font =
412 [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSControlSizeRegular]];
413 [self addSubview:button];
414 return button;
415}
416
417- (void)layout
418{
419 static const CGFloat ButtonMinWidth = 78.0; // 84.0 for Carbon
420 static const CGFloat ButtonMinHeight = 32.0;
421 static const CGFloat ButtonSpacing = 0.0;
422 static const CGFloat ButtonTopMargin = 0.0;
423 static const CGFloat ButtonBottomMargin = 7.0;
424 static const CGFloat ButtonSideMargin = 9.0;
425
426 NSSize frameSize = self.frame.size;
427
428 [self.okButton sizeToFit];
429 NSSize okSizeHint = self.okButton.frame.size;
430
431 [self.cancelButton sizeToFit];
432 NSSize cancelSizeHint = self.cancelButton.frame.size;
433
434 const CGFloat buttonWidth = qMin(qMax(ButtonMinWidth,
435 qMax(okSizeHint.width, cancelSizeHint.width)),
436 CGFloat((frameSize.width - 2.0 * ButtonSideMargin - ButtonSpacing) * 0.5));
437 const CGFloat buttonHeight = qMax(ButtonMinHeight,
438 qMax(okSizeHint.height, cancelSizeHint.height));
439
440 NSRect okRect = { { frameSize.width - ButtonSideMargin - buttonWidth,
441 ButtonBottomMargin },
442 { buttonWidth, buttonHeight } };
443 self.okButton.frame = okRect;
444 self.okButton.needsDisplay = YES;
445
446 NSRect cancelRect = { { okRect.origin.x - ButtonSpacing - buttonWidth,
447 ButtonBottomMargin },
448 { buttonWidth, buttonHeight } };
449 self.cancelButton.frame = cancelRect;
450 self.cancelButton.needsDisplay = YES;
451
452 // The third view should be the original panel contents. Cache it.
453 if (!self.panelContents)
454 for (NSView *view in self.subviews)
455 if (view != self.okButton && view != self.cancelButton) {
457 break;
458 }
459
460 const CGFloat buttonBoxHeight = ButtonBottomMargin + buttonHeight + ButtonTopMargin;
461 const NSRect panelContentsFrame = NSMakeRect(
462 self.panelContentsMargins.left,
463 buttonBoxHeight + self.panelContentsMargins.bottom,
464 frameSize.width - (self.panelContentsMargins.left + self.panelContentsMargins.right),
465 frameSize.height - buttonBoxHeight - (self.panelContentsMargins.top + self.panelContentsMargins.bottom));
466 self.panelContents.frame = panelContentsFrame;
467 self.panelContents.needsDisplay = YES;
468
469 self.needsDisplay = YES;
470 [super layout];
471}
472
473@end // QNSPanelContentsWrapper
474
475// -------------------------------------------------------------------------
476
477InputMethodQueryResult queryInputMethod(QObject *object, Qt::InputMethodQueries queries)
478{
479 if (object) {
480 QInputMethodQueryEvent queryEvent(queries | Qt::ImEnabled);
481 if (QCoreApplication::sendEvent(object, &queryEvent)) {
482 if (queryEvent.value(Qt::ImEnabled).toBool()) {
483 InputMethodQueryResult result;
484 static QMetaEnum queryEnum = QMetaEnum::fromType<Qt::InputMethodQuery>();
485 for (int i = 0; i < queryEnum.keyCount(); ++i) {
486 auto query = Qt::InputMethodQuery(queryEnum.value(i));
487 if (queries & query)
488 result.insert(query, queryEvent.value(query));
489 }
490 return result;
491 }
492 }
493 }
494 return {};
495}
496
497// -------------------------------------------------------------------------
498
500{
501 if (range.location == NSNotFound) {
502 QDebugStateSaver saver(debug);
503 debug.nospace() << "{NSNotFound, " << range.length << "}";
504 } else {
505 debug << NSStringFromRange(range);
506 }
507 return debug;
508}
509
511{
512 debug << NSStringFromSelector(selector);
513 return debug;
514}
static bool sendEvent(QObject *receiver, QEvent *event)
Sends event event directly to receiver receiver, using the notify() function.
\inmodule QtCore
\inmodule QtCore
Type
This enum type defines the valid event types in Qt.
Definition qcoreevent.h:51
@ MouseMove
Definition qcoreevent.h:63
@ MouseButtonPress
Definition qcoreevent.h:60
@ MouseButtonRelease
Definition qcoreevent.h:61
static QGuiApplicationPrivate * instance()
static QPlatformTheme * platformTheme()
The QInputMethodQueryEvent class provides an event sent by the input context to input objects.
Definition qevent.h:679
qsizetype size() const noexcept
Definition qlist.h:397
\inmodule QtCore
\inmodule QtCore
Definition qobject.h:103
The QPlatformDialogHelper class allows for platform-specific customization of dialogs.
static QString removeMnemonics(const QString &original)
\inmodule QtCore\reentrant
Definition qpoint.h:217
\inmodule QtCore\reentrant
Definition qrect.h:484
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QString left(qsizetype n) const &
Definition qstring.h:363
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
QString right(qsizetype n) const &
Definition qstring.h:375
QFont font
the font currently set for the widget
Definition qwidget.h:133
QPushButton * button
[2]
rect
[4]
Combined button and popup list for selecting options.
InputMethodQuery
@ ImEnabled
MouseButton
Definition qnamespace.h:56
@ RightButton
Definition qnamespace.h:59
@ MouseButtonMask
Definition qnamespace.h:93
@ NoButton
Definition qnamespace.h:57
DropAction
@ CopyAction
@ ActionMask
@ IgnoreAction
@ MoveAction
@ LinkAction
QString self
Definition language.cpp:58
float CGFloat
Qt::MouseButtons cocoaMouseButtons2QtMouseButtons(NSInteger pressedMouseButtons)
Returns the Qt::MouseButtons that corresponds to an NSEvent.pressedMouseButtons.
NSString * qt_mac_AppKitString(NSString *table, NSString *key)
QNSView * qnsview_cast(NSView *view)
Returns the view cast to a QNSview if possible.
NSView * _panelContents
NSButton * _cancelButton
QEvent::Type cocoaEvent2QtMouseEvent(NSEvent *event)
Returns the QEvent::Type that corresponds to an NSEvent.type.
Qt::DropActions qt_mac_mapNSDragOperations(NSDragOperation nsActions)
QString qt_mac_applicationName()
QPointF qt_mac_flip(const QPointF &pos, const QRectF &reference)
Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum)
Returns the Qt::Button that corresponds to an NSEvent.buttonNumber.
QStringList qt_mac_NSArrayToQStringList(NSArray< NSString * > *array)
QDebug operator<<(QDebug debug, const NSRange &range)
void qt_mac_transformProccessToForegroundApplication()
NSDragOperation qt_mac_mapDropActions(Qt::DropActions actions)
static dndenum_mapper dnd_enums[]
Qt::DropAction qt_mac_mapNSDragOperation(NSDragOperation nsActions)
QString qt_mac_removeAmpersandEscapes(QString s)
NSEdgeInsets _panelContentsMargins
InputMethodQueryResult queryInputMethod(QObject *object, Qt::InputMethodQueries queries)
NSDragOperation qt_mac_mapDropAction(Qt::DropAction action)
NSMutableArray< NSString * > * qt_mac_QStringListToNSMutableArray(const QStringList &list)
Qt::MouseButtons currentlyPressedMouseButtons()
Returns the Qt::MouseButtons that corresponds to an NSEvent.pressedMouseButtons.
long NSInteger
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
EGLDeviceEXT * devices
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
@ QtCriticalMsg
Definition qlogging.h:32
#define Q_LOGGING_CATEGORY(name,...)
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
GLuint64 key
GLsizei range
GLint reference
GLenum type
struct _cl_event * event
GLdouble s
[6]
Definition qopenglext.h:235
GLenum query
GLenum array
GLuint in
GLuint64EXT * result
[6]
GLenum GLenum GLenum input
GLenum GLenum GLsizei void * table
static constexpr QSize frameSize(const T &frame)
QScreen * screen
[1]
Definition main.cpp:29
QList< int > list
[14]
QFileSelector selector
[1]
QVBoxLayout * layout
QQuickView * view
[0]
NSDragOperation mac_code
Qt::DropAction qt_code