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