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
qnsview.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 <QtGui/qtguiglobal.h>
5
6#include <AppKit/AppKit.h>
7#include <MetalKit/MetalKit.h>
8#include <UniformTypeIdentifiers/UTCoreTypes.h>
9
10#include "qnsview.h"
11#include "qcocoawindow.h"
12#include "qcocoahelpers.h"
13#include "qcocoascreen.h"
14#include "qmultitouch_mac_p.h"
15#include "qcocoadrag.h"
16#include "qcocoainputcontext.h"
17#include <qpa/qplatformintegration.h>
18
19#include <qpa/qwindowsysteminterface.h>
20#include <QtGui/QTextFormat>
21#include <QtCore/QDebug>
22#include <QtCore/QPointer>
23#include <QtCore/QSet>
24#include <QtCore/private/qcore_mac_p.h>
25#include <QtGui/QAccessible>
26#include <QtGui/QImage>
27#include <private/qguiapplication_p.h>
28#include <private/qcoregraphics_p.h>
29#include <private/qwindow_p.h>
30#include <private/qpointingdevice_p.h>
31#include <private/qhighdpiscaling_p.h>
32#include "qcocoabackingstore.h"
33#ifndef QT_NO_OPENGL
34#include "qcocoaglcontext.h"
35#endif
36#include "qcocoaintegration.h"
37#include <QtGui/private/qmacmimeregistry_p.h>
38#include <QtGui/private/qmetallayer_p.h>
39
40#include <QuartzCore/CATransaction.h>
41
42@interface QNSView (Drawing) <CALayerDelegate>
44@end
45
47- (instancetype)initWithView:(QNSView *)theView;
48- (void)mouseMoved:(NSEvent *)theEvent;
49- (void)mouseEntered:(NSEvent *)theEvent;
50- (void)mouseExited:(NSEvent *)theEvent;
51- (void)cursorUpdate:(NSEvent *)theEvent;
52@end
53
55
56@interface QNSView (Mouse)
57- (void)initMouse;
58- (NSPoint)screenMousePoint:(NSEvent *)theEvent;
59- (void)mouseMovedImpl:(NSEvent *)theEvent;
60- (void)mouseEnteredImpl:(NSEvent *)theEvent;
61- (void)mouseExitedImpl:(NSEvent *)theEvent;
62@end
63
64@interface QNSView (Touch)
65@end
66
67@interface QNSView (Tablet)
68- (bool)handleTabletEvent:(NSEvent *)theEvent;
69@end
70
71@interface QNSView (Gestures)
72@end
73
74@interface QNSView (Dragging)
75-(void)registerDragTypes;
76@end
77
78@interface QNSView (Keys)
79@end
80
81@interface QNSView (ComplexText) <NSTextInputClient>
82@property (readonly) QObject* focusObject;
83@end
84
86- (instancetype)initWithView:(QNSView *)theView;
87@end
89
90// Private interface
91@interface QNSView ()
92- (BOOL)isTransparentForUserInput;
93@property (assign) NSView* previousSuperview;
94@property (assign) NSWindow* previousWindow;
95@property (retain) QNSViewMenuHelper* menuHelper;
96@property (nonatomic, retain) NSColorSpace *colorSpace;
97@end
98
99@implementation QNSView {
100 QPointer<QCocoaWindow> m_platformWindow;
101
102 // Mouse
104 Qt::MouseButtons m_buttons;
105 Qt::MouseButtons m_acceptedMouseDowns;
106 Qt::MouseButtons m_frameStrutButtons;
107 Qt::KeyboardModifiers m_currentWheelModifiers;
112
113 // Keys
118 QSet<quint32> m_acceptedKeyDowns;
119
120 // Text
122 QPointer<QObject> m_composingFocusObject;
123 NSDraggingContext m_lastSeenContext;
124}
125
126@synthesize colorSpace = m_colorSpace;
127
128- (instancetype)initWithCocoaWindow:(QCocoaWindow *)platformWindow
129{
130 if ((self = [super initWithFrame:NSZeroRect])) {
131 m_platformWindow = platformWindow;
132
133 // NSViews are by default visible, but QWindows are not.
134 // We should ideally pick up the actual QWindow state here,
135 // but QWindowPrivate::setVisible() expects to control the
136 // order of events tightly, so we need to wait for a call
137 // to QCocoaWindow::setVisible().
138 self.hidden = YES;
139
140 self.focusRingType = NSFocusRingTypeNone;
141
142 self.previousSuperview = nil;
143 self.previousWindow = nil;
144
145 [self initDrawing];
146 [self initMouse];
147 [self registerDragTypes];
148
149 m_updatingDrag = false;
150
151 m_lastKeyDead = false;
152 m_sendKeyEvent = false;
154 m_lastSeenContext = NSDraggingContextWithinApplication;
155
156 self.menuHelper = [[[QNSViewMenuHelper alloc] initWithView:self] autorelease];
157 }
158 return self;
159}
160
161- (void)dealloc
162{
163 qCDebug(lcQpaWindow) << "Deallocating" << self;
164
165 [[NSNotificationCenter defaultCenter] removeObserver:self];
166 [m_mouseMoveHelper release];
167
168 [super dealloc];
169}
170
171- (NSString *)description
172{
173 NSMutableString *description = [NSMutableString stringWithString:[super description]];
174
175#ifndef QT_NO_DEBUG_STREAM
176 QString platformWindowDescription;
177 QDebug debug(&platformWindowDescription);
178 debug.nospace() << "; " << m_platformWindow << ">";
179
180 NSRange lastCharacter = [description rangeOfComposedCharacterSequenceAtIndex:description.length - 1];
181 [description replaceCharactersInRange:lastCharacter withString:platformWindowDescription.toNSString()];
182#endif
183
184 return description;
185}
186
187// ----------------------------- Re-parenting ---------------------------------
188
189- (void)removeFromSuperview
190{
192 [super removeFromSuperview];
193}
194
195- (void)viewWillMoveToSuperview:(NSView *)newSuperview
196{
197 Q_ASSERT(!self.previousSuperview);
198 self.previousSuperview = self.superview;
199
200 if (newSuperview == self.superview)
201 qCDebug(lcQpaWindow) << "Re-ordering" << self << "inside" << self.superview;
202 else
203 qCDebug(lcQpaWindow) << "Re-parenting" << self << "from" << self.superview << "to" << newSuperview;
204}
205
206- (void)viewDidMoveToSuperview
207{
208 auto cleanup = qScopeGuard([&] { self.previousSuperview = nil; });
209
210 if (self.superview == self.previousSuperview) {
211 qCDebug(lcQpaWindow) << "Done re-ordering" << self << "new index:"
212 << [self.superview.subviews indexOfObject:self];
213 return;
214 }
215
216 qCDebug(lcQpaWindow) << "Done re-parenting" << self << "into" << self.superview;
217
218 // Note: at this point the view's window property hasn't been updated to match the window
219 // of the new superview. We have to wait for viewDidMoveToWindow for that to be reflected.
220
221 if (!m_platformWindow)
222 return;
223
224 if (!m_platformWindow->isEmbedded())
225 return;
226
227 if ([self superview]) {
228 QWindowSystemInterface::handleGeometryChange(m_platformWindow->window(), m_platformWindow->geometry());
229 [self setNeedsDisplay:YES];
231 }
232}
233
234- (void)viewWillMoveToWindow:(NSWindow *)newWindow
235{
236 Q_ASSERT(!self.previousWindow);
237 self.previousWindow = self.window;
238
239 // This callback is documented to be called also when a view is just moved between
240 // subviews in the same NSWindow, so we're not necessarily moving between NSWindows.
241 if (newWindow == self.window)
242 return;
243
244 qCDebug(lcQpaWindow) << "Moving" << self << "from" << self.window << "to" << newWindow;
245
246 // Note: at this point the superview has already been updated, so we know which view inside
247 // the new window the view will be a child of.
248}
249
250- (void)viewDidMoveToWindow
251{
252 auto cleanup = qScopeGuard([&] { self.previousWindow = nil; });
253
254 // This callback is documented to be called also when a view is just moved between
255 // subviews in the same NSWindow, so we're not necessarily moving between NSWindows.
256 if (self.window == self.previousWindow)
257 return;
258
259 qCDebug(lcQpaWindow) << "Done moving" << self << "to" << self.window;
260}
261
262// ----------------------------------------------------------------------------
263
264- (QWindow *)topLevelWindow
265{
266 if (!m_platformWindow)
267 return nullptr;
268
269 QWindow *focusWindow = m_platformWindow->window();
270
271 // For widgets we need to do a bit of trickery as the window
272 // to activate is the window of the top-level widget.
273 if (qstrcmp(focusWindow->metaObject()->className(), "QWidgetWindow") == 0) {
274 while (focusWindow->parent()) {
275 focusWindow = focusWindow->parent();
276 }
277 }
278
279 return focusWindow;
280}
281
282/*
283 Invoked when the view is hidden, either directly,
284 or in response to an ancestor being hidden.
285*/
286- (void)viewDidHide
287{
288 qCDebug(lcQpaWindow) << "Did hide" << self;
289
290 if (!m_platformWindow->isExposed())
291 return;
292
293 m_platformWindow->handleExposeEvent(QRegion());
294}
295
296/*
297 Invoked when the view is unhidden, either directly,
298 or in response to an ancestor being unhidden.
299*/
300- (void)viewDidUnhide
301{
302 qCDebug(lcQpaWindow) << "Did unhide" << self;
303
304 [self setNeedsDisplay:YES];
305}
306
307- (BOOL)isTransparentForUserInput
308{
309 return m_platformWindow->window() &&
310 m_platformWindow->window()->flags() & Qt::WindowTransparentForInput;
311}
312
313- (BOOL)becomeFirstResponder
314{
315 if (!m_platformWindow)
316 return NO;
317 if ([self isTransparentForUserInput])
318 return NO;
319
320 if (!m_platformWindow->windowIsPopupType()
321 && (!self.window.canBecomeKeyWindow || self.window.keyWindow)) {
322 // Calling handleWindowActivated for a QWindow has two effects: first, it
323 // will set the QWindow (and all other QWindows in the same hierarchy)
324 // as Active. Being Active means that the window should appear active from
325 // a style perspective (according to QWindow::isActive()). The second
326 // effect is that it will set QQuiApplication::focusWindow() to point to
327 // the QWindow. The latter means that the QWindow should have keyboard
328 // focus. But those two are not necessarily the same; A tool window could e.g be
329 // rendered as Active while the parent window, which is also Active, has
330 // input focus. But we currently don't distinguish between that cleanly in Qt.
331 // Since we don't want a QWindow to be rendered as Active when the NSWindow
332 // it belongs to is not key, we skip calling handleWindowActivated when
333 // that is the case. Instead, we wait for the window to become key, and handle
334 // QWindow activation from QCocoaWindow::windowDidBecomeKey instead. The only
335 // exception is if the window can never become key, in which case we naturally
336 // cannot wait for that to happen.
337 QWindowSystemInterface::handleFocusWindowChanged<QWindowSystemInterface::SynchronousDelivery>(
338 [self topLevelWindow], Qt::ActiveWindowFocusReason);
339 }
340
341 return YES;
342}
343
344- (BOOL)acceptsFirstResponder
345{
346 if (!m_platformWindow)
347 return NO;
348 if (m_platformWindow->shouldRefuseKeyWindowAndFirstResponder())
349 return NO;
350 if ([self isTransparentForUserInput])
351 return NO;
352 if ((m_platformWindow->window()->flags() & Qt::ToolTip) == Qt::ToolTip)
353 return NO;
354 return YES;
355}
356
357- (NSView *)hitTest:(NSPoint)aPoint
358{
359 NSView *candidate = [super hitTest:aPoint];
360 if (candidate == self) {
361 if ([self isTransparentForUserInput])
362 return nil;
363 }
364 return candidate;
365}
366
367- (void)convertFromScreen:(NSPoint)mouseLocation toWindowPoint:(QPointF *)qtWindowPoint andScreenPoint:(QPointF *)qtScreenPoint
368{
369 // Calculate the mouse position in the QWindow and Qt screen coordinate system,
370 // starting from coordinates in the NSWindow coordinate system.
371 //
372 // This involves translating according to the window location on screen,
373 // as well as inverting the y coordinate due to the origin change.
374 //
375 // Coordinate system overview, outer to innermost:
376 //
377 // Name Origin
378 //
379 // OS X screen bottom-left
380 // Qt screen top-left
381 // NSWindow bottom-left
382 // NSView/QWindow top-left
383 //
384 // NSView and QWindow are equal coordinate systems: the QWindow covers the
385 // entire NSView, and we've set the NSView's isFlipped property to true.
386
387 NSWindow *window = [self window];
388 NSPoint nsWindowPoint;
389 NSRect windowRect = [window convertRectFromScreen:NSMakeRect(mouseLocation.x, mouseLocation.y, 1, 1)];
390 nsWindowPoint = windowRect.origin; // NSWindow coordinates
391 NSPoint nsViewPoint = [self convertPoint: nsWindowPoint fromView: nil]; // NSView/QWindow coordinates
392 *qtWindowPoint = QPointF(nsViewPoint.x, nsViewPoint.y); // NSView/QWindow coordinates
393 *qtScreenPoint = QCocoaScreen::mapFromNative(mouseLocation);
394}
395
396@end
397
398#include "qnsview_drawing.mm"
399#include "qnsview_mouse.mm"
400#include "qnsview_touch.mm"
401#include "qnsview_gestures.mm"
402#include "qnsview_tablet.mm"
403#include "qnsview_dragging.mm"
404#include "qnsview_keys.mm"
405#include "qnsview_complextext.mm"
406#include "qnsview_menus.mm"
407#if QT_CONFIG(accessibility)
409#endif
410
411// -----------------------------------------------------
412
413@implementation QNSView (QtExtras)
414
415- (QCocoaWindow*)platformWindow
416{
417 return m_platformWindow.data();;
418}
419
420@end
static QPointF mapFromNative(CGPoint pos, QCocoaScreen *screen=QCocoaScreen::primaryScreen())
\inmodule QtCore
Native interface for QPlatformWindow on \macos. \inmodule QtGui.
\inmodule QtCore
Definition qobject.h:103
\inmodule QtCore\reentrant
Definition qpoint.h:217
The QRegion class specifies a clip region for a painter.
Definition qregion.h:27
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static bool flushWindowSystemEvents(QEventLoop::ProcessEventsFlags flags=QEventLoop::AllEvents)
Make Qt Gui process all events on the event queue immediately.
static void handleGeometryChange(QWindow *window, const QRect &newRect)
\inmodule QtGui
Definition qwindow.h:63
@ ToolTip
Definition qnamespace.h:213
@ WindowTransparentForInput
Definition qnamespace.h:234
@ ActiveWindowFocusReason
QString self
Definition language.cpp:58
Q_CORE_EXPORT int qstrcmp(const char *str1, const char *str2)
#define QT_NAMESPACE_ALIAS_OBJC_CLASS(__KLASS__)
Definition qcore_mac_p.h:58
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
#define qCDebug(category,...)
bool m_sendKeyEvent
Definition qnsview.mm:115
bool m_updatingDrag
Definition qnsview.mm:111
NSEvent * m_currentlyInterpretedKeyEvent
Definition qnsview.mm:117
bool m_sendUpAsRightButton
Definition qnsview.mm:109
Qt::MouseButtons m_buttons
Definition qnsview.mm:104
bool m_dontOverrideCtrlLMB
Definition qnsview.mm:108
bool m_lastKeyDead
Definition qnsview.mm:114
QPointer< QObject > m_composingFocusObject
Definition qnsview.mm:122
bool m_scrolling
Definition qnsview.mm:110
QNSViewMouseMoveHelper * m_mouseMoveHelper
Definition qnsview.mm:103
QSet< quint32 > m_acceptedKeyDowns
Definition qnsview.mm:118
bool m_sendKeyEventWithoutText
Definition qnsview.mm:116
Qt::MouseButtons m_acceptedMouseDowns
Definition qnsview.mm:105
Qt::MouseButtons m_frameStrutButtons
Definition qnsview.mm:106
Qt::KeyboardModifiers m_currentWheelModifiers
Definition qnsview.mm:107
NSDraggingContext m_lastSeenContext
Definition qnsview.mm:123
QString m_composingText
Definition qnsview.mm:121
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
Definition qscopeguard.h:60
aWidget window() -> setWindowTitle("New Window Title")
[2]