Qt 6.x
The Qt SDK
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()) {
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
335{
337}
338
340
350@implementation QNSPanelContentsWrapper {
351 NSButton *_okButton;
352 NSButton *_cancelButton;
355}
356
357@synthesize okButton = _okButton;
358@synthesize cancelButton = _cancelButton;
359@synthesize panelContents = _panelContents;
360@synthesize panelContentsMargins = _panelContentsMargins;
361
362- (instancetype)initWithPanelDelegate:(id<QNSPanelDelegate>)panelDelegate
363{
364 if ((self = [super initWithFrame:NSZeroRect])) {
365 // create OK and Cancel buttons and add these as subviews
366 _okButton = [self createButtonWithTitle:QPlatformDialogHelper::Ok];
367 _okButton.action = @selector(onOkClicked);
368 _okButton.target = panelDelegate;
369 _cancelButton = [self createButtonWithTitle:QPlatformDialogHelper::Cancel];
370 _cancelButton.action = @selector(onCancelClicked);
371 _cancelButton.target = panelDelegate;
372
373 _panelContents = nil;
374
375 _panelContentsMargins = NSEdgeInsetsMake(0, 0, 0, 0);
376 }
377
378 return self;
379}
380
381- (void)dealloc
382{
383 [_okButton release];
384 _okButton = nil;
385 [_cancelButton release];
386 _cancelButton = nil;
387
388 _panelContents = nil;
389
390 [super dealloc];
391}
392
393- (NSButton *)createButtonWithTitle:(QPlatformDialogHelper::StandardButton)type
394{
395 NSButton *button = [[NSButton alloc] initWithFrame:NSZeroRect];
396 button.buttonType = NSButtonTypeMomentaryLight;
397 button.bezelStyle = NSBezelStyleRounded;
398 const QString &cleanTitle =
400 // FIXME: Not obvious, from Cocoa's documentation, that QString::toNSString() makes a deep copy
401 button.title = (NSString *)cleanTitle.toCFString();
402 ((NSButtonCell *)button.cell).font =
403 [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSControlSizeRegular]];
404 [self addSubview:button];
405 return button;
406}
407
408- (void)layout
409{
410 static const CGFloat ButtonMinWidth = 78.0; // 84.0 for Carbon
411 static const CGFloat ButtonMinHeight = 32.0;
412 static const CGFloat ButtonSpacing = 0.0;
413 static const CGFloat ButtonTopMargin = 0.0;
414 static const CGFloat ButtonBottomMargin = 7.0;
415 static const CGFloat ButtonSideMargin = 9.0;
416
417 NSSize frameSize = self.frame.size;
418
419 [self.okButton sizeToFit];
420 NSSize okSizeHint = self.okButton.frame.size;
421
422 [self.cancelButton sizeToFit];
423 NSSize cancelSizeHint = self.cancelButton.frame.size;
424
425 const CGFloat buttonWidth = qMin(qMax(ButtonMinWidth,
426 qMax(okSizeHint.width, cancelSizeHint.width)),
427 CGFloat((frameSize.width - 2.0 * ButtonSideMargin - ButtonSpacing) * 0.5));
428 const CGFloat buttonHeight = qMax(ButtonMinHeight,
429 qMax(okSizeHint.height, cancelSizeHint.height));
430
431 NSRect okRect = { { frameSize.width - ButtonSideMargin - buttonWidth,
432 ButtonBottomMargin },
433 { buttonWidth, buttonHeight } };
434 self.okButton.frame = okRect;
435 self.okButton.needsDisplay = YES;
436
437 NSRect cancelRect = { { okRect.origin.x - ButtonSpacing - buttonWidth,
438 ButtonBottomMargin },
439 { buttonWidth, buttonHeight } };
440 self.cancelButton.frame = cancelRect;
441 self.cancelButton.needsDisplay = YES;
442
443 // The third view should be the original panel contents. Cache it.
444 if (!self.panelContents)
445 for (NSView *view in self.subviews)
446 if (view != self.okButton && view != self.cancelButton) {
448 break;
449 }
450
451 const CGFloat buttonBoxHeight = ButtonBottomMargin + buttonHeight + ButtonTopMargin;
452 const NSRect panelContentsFrame = NSMakeRect(
453 self.panelContentsMargins.left,
454 buttonBoxHeight + self.panelContentsMargins.bottom,
455 frameSize.width - (self.panelContentsMargins.left + self.panelContentsMargins.right),
456 frameSize.height - buttonBoxHeight - (self.panelContentsMargins.top + self.panelContentsMargins.bottom));
457 self.panelContents.frame = panelContentsFrame;
458 self.panelContents.needsDisplay = YES;
459
460 self.needsDisplay = YES;
461 [super layout];
462}
463
464@end // QNSPanelContentsWrapper
465
466// -------------------------------------------------------------------------
467
468InputMethodQueryResult queryInputMethod(QObject *object, Qt::InputMethodQueries queries)
469{
470 if (object) {
471 QInputMethodQueryEvent queryEvent(queries | Qt::ImEnabled);
472 if (QCoreApplication::sendEvent(object, &queryEvent)) {
473 if (queryEvent.value(Qt::ImEnabled).toBool()) {
474 InputMethodQueryResult result;
475 static QMetaEnum queryEnum = QMetaEnum::fromType<Qt::InputMethodQuery>();
476 for (int i = 0; i < queryEnum.keyCount(); ++i) {
477 auto query = Qt::InputMethodQuery(queryEnum.value(i));
478 if (queries & query)
479 result.insert(query, queryEvent.value(query));
480 }
481 return result;
482 }
483 }
484 }
485 return {};
486}
487
488// -------------------------------------------------------------------------
489
491{
492 if (range.location == NSNotFound) {
493 QDebugStateSaver saver(debug);
494 debug.nospace() << "{NSNotFound, " << range.length << "}";
495 } else {
496 debug << NSStringFromRange(range);
497 }
498 return debug;
499}
500
502{
503 debug << NSStringFromSelector(selector);
504 return debug;
505}
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:678
QVariant value(Qt::InputMethodQuery query) const
Returns value of the property query.
Definition qevent.cpp:2378
qsizetype size() const noexcept
Definition qlist.h:386
\inmodule QtCore
int value(int index) const
Returns the value with the given index; or returns -1 if there is no such value.
int keyCount() const
Returns the number of keys.
\inmodule QtCore
Definition qobject.h:90
The QPlatformDialogHelper class allows for platform-specific customization of dialogs.
static QString removeMnemonics(const QString &original)
\inmodule QtCore\reentrant
Definition qpoint.h:214
\inmodule QtCore\reentrant
Definition qrect.h:483
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:132
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:129
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
QString right(qsizetype n) const
Returns a substring that contains the n rightmost characters of the string.
Definition qstring.cpp:5180
QStringList split(const QString &sep, Qt::SplitBehavior behavior=Qt::KeepEmptyParts, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Splits the string into substrings wherever sep occurs, and returns the list of those strings.
Definition qstring.cpp:7956
qsizetype size() const
Returns the number of characters in this string.
Definition qstring.h:182
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
bool contains(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.h:1217
QString left(qsizetype n) const
Returns a substring that contains the n leftmost characters of the string.
Definition qstring.cpp:5161
QString trimmed() const &
Definition qstring.h:380
bool toBool() const
Returns the variant as a bool if the variant has userType() Bool.
QPushButton * button
[2]
rect
[4]
Combined button and popup list for selecting options.
InputMethodQuery
@ ImEnabled
MouseButton
Definition qnamespace.h:55
@ RightButton
Definition qnamespace.h:58
@ MouseButtonMask
Definition qnamespace.h:92
@ NoButton
Definition qnamespace.h:56
DropAction
@ CopyAction
@ ActionMask
@ IgnoreAction
@ MoveAction
@ LinkAction
QString self
Definition language.cpp:57
float CGFloat
Qt::MouseButtons cocoaMouseButtons2QtMouseButtons(NSInteger pressedMouseButtons)
Returns the Qt::MouseButtons that corresponds to an NSEvent.pressedMouseButtons.
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
GLsizei range
GLint reference
GLenum type
struct _cl_event * event
GLenum query
GLenum array
GLuint in
GLuint64EXT * result
[6]
GLdouble s
[6]
Definition qopenglext.h:235
GLenum GLenum GLenum input
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