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