1/**************************************************************************** 2** 3** Copyright (C) 2016 The Qt Company Ltd. 4** Contact: https://www.qt.io/licensing/ 5** 6** This file is part of the plugins of the Qt Toolkit. 7** 8** $QT_BEGIN_LICENSE:LGPL$ 9** Commercial License Usage 10** Licensees holding valid commercial Qt licenses may use this file in 11** accordance with the commercial license agreement provided with the 12** Software or, alternatively, in accordance with the terms contained in 13** a written agreement between you and The Qt Company. For licensing terms 14** and conditions see https://www.qt.io/terms-conditions. For further 15** information use the contact form at https://www.qt.io/contact-us. 16** 17** GNU Lesser General Public License Usage 18** Alternatively, this file may be used under the terms of the GNU Lesser 19** General Public License version 3 as published by the Free Software 20** Foundation and appearing in the file LICENSE.LGPL3 included in the 21** packaging of this file. Please review the following information to 22** ensure the GNU Lesser General Public License version 3 requirements 23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. 24** 25** GNU General Public License Usage 26** Alternatively, this file may be used under the terms of the GNU 27** General Public License version 2.0 or (at your option) the GNU General 28** Public license version 3 or any later version approved by the KDE Free 29** Qt Foundation. The licenses are as published by the Free Software 30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 31** included in the packaging of this file. Please review the following 32** information to ensure the GNU General Public License requirements will 33** be met: https://www.gnu.org/licenses/gpl-2.0.html and 34** https://www.gnu.org/licenses/gpl-3.0.html. 35** 36** $QT_END_LICENSE$ 37** 38****************************************************************************/ 39 40#include <QtGui/qtguiglobal.h> 41 42#include "qnsview.h" 43#include "qcocoawindow.h" 44#include "qcocoahelpers.h" 45#include "qcocoascreen.h" 46#include "qmultitouch_mac_p.h" 47#include "qcocoadrag.h" 48#include "qcocoainputcontext.h" 49#include <qpa/qplatformintegration.h> 50 51#include <qpa/qwindowsysteminterface.h> 52#include <QtGui/QTextFormat> 53#include <QtCore/QDebug> 54#include <QtCore/QPointer> 55#include <QtCore/QSet> 56#include <QtCore/qsysinfo.h> 57#include <QtGui/QAccessible> 58#include <QtGui/QImage> 59#include <private/qguiapplication_p.h> 60#include <private/qcoregraphics_p.h> 61#include <private/qwindow_p.h> 62#include "qcocoabackingstore.h" 63#ifndef QT_NO_OPENGL 64#include "qcocoaglcontext.h" 65#endif 66#include "qcocoaintegration.h" 67 68// Private interface 69@interface QNSView () 70- (BOOL)isTransparentForUserInput; 71@property (assign) NSView* previousSuperview; 72@property (assign) NSWindow* previousWindow; 73@end 74 75@interface QNSView (Drawing) <CALayerDelegate> 76- (void)initDrawing; 77@end 78 79@interface QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper) : NSObject 80- (instancetype)initWithView:(QNSView *)theView; 81- (void)mouseMoved:(NSEvent *)theEvent; 82- (void)mouseEntered:(NSEvent *)theEvent; 83- (void)mouseExited:(NSEvent *)theEvent; 84- (void)cursorUpdate:(NSEvent *)theEvent; 85@end 86 87QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSViewMouseMoveHelper); 88 89@interface QNSView (Mouse) 90- (void)initMouse; 91- (NSPoint)screenMousePoint:(NSEvent *)theEvent; 92- (void)mouseMovedImpl:(NSEvent *)theEvent; 93- (void)mouseEnteredImpl:(NSEvent *)theEvent; 94- (void)mouseExitedImpl:(NSEvent *)theEvent; 95@end 96 97@interface QNSView (Touch) 98@end 99 100@interface QNSView (Tablet) 101- (bool)handleTabletEvent:(NSEvent *)theEvent; 102@end 103 104@interface QNSView (Gestures) 105@end 106 107@interface QNSView (Dragging) 108-(void)registerDragTypes; 109@end 110 111@interface QNSView (Keys) 112@end 113 114@interface QNSView (ComplexText) <NSTextInputClient> 115- (void)textInputContextKeyboardSelectionDidChangeNotification:(NSNotification *)textInputContextKeyboardSelectionDidChangeNotification; 116@end 117 118@implementation QNSView { 119 QPointer<QCocoaWindow> m_platformWindow; 120 Qt::MouseButtons m_buttons; 121 Qt::MouseButtons m_acceptedMouseDowns; 122 Qt::MouseButtons m_frameStrutButtons; 123 QString m_composingText; 124 QPointer<QObject> m_composingFocusObject; 125 bool m_sendKeyEvent; 126 bool m_dontOverrideCtrlLMB; 127 bool m_sendUpAsRightButton; 128 Qt::KeyboardModifiers m_currentWheelModifiers; 129 NSString *m_inputSource; 130 QNSViewMouseMoveHelper *m_mouseMoveHelper; 131 bool m_resendKeyEvent; 132 bool m_scrolling; 133 bool m_updatingDrag; 134 NSEvent *m_currentlyInterpretedKeyEvent; 135 QSet<quint32> m_acceptedKeyDowns; 136} 137 138- (instancetype)initWithCocoaWindow:(QCocoaWindow *)platformWindow 139{ 140 if ((self = [super initWithFrame:NSZeroRect])) { 141 m_platformWindow = platformWindow; 142 m_sendKeyEvent = false; 143 m_inputSource = nil; 144 m_resendKeyEvent = false; 145 m_updatingDrag = false; 146 m_currentlyInterpretedKeyEvent = nil; 147 148 self.focusRingType = NSFocusRingTypeNone; 149 150 self.previousSuperview = nil; 151 self.previousWindow = nil; 152 153 [self initDrawing]; 154 [self initMouse]; 155 [self registerDragTypes]; 156 157 [[NSNotificationCenter defaultCenter] addObserver:self 158 selector:@selector(textInputContextKeyboardSelectionDidChangeNotification:) 159 name:NSTextInputContextKeyboardSelectionDidChangeNotification 160 object:nil]; 161 } 162 return self; 163} 164 165- (void)dealloc 166{ 167 qCDebug(lcQpaWindow) << "Deallocating" << self; 168 169 [m_inputSource release]; 170 [[NSNotificationCenter defaultCenter] removeObserver:self]; 171 [m_mouseMoveHelper release]; 172 173 [super dealloc]; 174} 175 176- (NSString *)description 177{ 178 NSMutableString *description = [NSMutableString stringWithString:[super description]]; 179 180#ifndef QT_NO_DEBUG_STREAM 181 QString platformWindowDescription; 182 QDebug debug(&platformWindowDescription); 183 debug.nospace() << "; " << m_platformWindow << ">"; 184 185 NSRange lastCharacter = [description rangeOfComposedCharacterSequenceAtIndex:description.length - 1]; 186 [description replaceCharactersInRange:lastCharacter withString:platformWindowDescription.toNSString()]; 187#endif 188 189 return description; 190} 191 192// ----------------------------- Re-parenting --------------------------------- 193 194- (void)removeFromSuperview 195{ 196 QMacAutoReleasePool pool; 197 [super removeFromSuperview]; 198} 199 200- (void)viewWillMoveToSuperview:(NSView *)newSuperview 201{ 202 Q_ASSERT(!self.previousSuperview); 203 self.previousSuperview = self.superview; 204 205 if (newSuperview == self.superview) 206 qCDebug(lcQpaWindow) << "Re-ordering" << self << "inside" << self.superview; 207 else 208 qCDebug(lcQpaWindow) << "Re-parenting" << self << "from" << self.superview << "to" << newSuperview; 209} 210 211- (void)viewDidMoveToSuperview 212{ 213 auto cleanup = qScopeGuard([&] { self.previousSuperview = nil; }); 214 215 if (self.superview == self.previousSuperview) { 216 qCDebug(lcQpaWindow) << "Done re-ordering" << self << "new index:" 217 << [self.superview.subviews indexOfObject:self]; 218 return; 219 } 220 221 qCDebug(lcQpaWindow) << "Done re-parenting" << self << "into" << self.superview; 222 223 // Note: at this point the view's window property hasn't been updated to match the window 224 // of the new superview. We have to wait for viewDidMoveToWindow for that to be reflected. 225 226 if (!m_platformWindow) 227 return; 228 229 if (!m_platformWindow->isEmbedded()) 230 return; 231 232 if ([self superview]) { 233 QWindowSystemInterface::handleGeometryChange(m_platformWindow->window(), m_platformWindow->geometry()); 234 [self setNeedsDisplay:YES]; 235 QWindowSystemInterface::flushWindowSystemEvents(); 236 } 237} 238 239- (void)viewWillMoveToWindow:(NSWindow *)newWindow 240{ 241 Q_ASSERT(!self.previousWindow); 242 self.previousWindow = self.window; 243 244 // This callback is documented to be called also when a view is just moved between 245 // subviews in the same NSWindow, so we're not necessarily moving between NSWindows. 246 if (newWindow == self.window) 247 return; 248 249 qCDebug(lcQpaWindow) << "Moving" << self << "from" << self.window << "to" << newWindow; 250 251 // Note: at this point the superview has already been updated, so we know which view inside 252 // the new window the view will be a child of. 253} 254 255- (void)viewDidMoveToWindow 256{ 257 auto cleanup = qScopeGuard([&] { self.previousWindow = nil; }); 258 259 // This callback is documented to be called also when a view is just moved between 260 // subviews in the same NSWindow, so we're not necessarily moving between NSWindows. 261 if (self.window == self.previousWindow) 262 return; 263 264 qCDebug(lcQpaWindow) << "Done moving" << self << "to" << self.window; 265} 266 267// ---------------------------------------------------------------------------- 268 269- (QWindow *)topLevelWindow 270{ 271 if (!m_platformWindow) 272 return nullptr; 273 274 QWindow *focusWindow = m_platformWindow->window(); 275 276 // For widgets we need to do a bit of trickery as the window 277 // to activate is the window of the top-level widget. 278 if (qstrcmp(focusWindow->metaObject()->className(), "QWidgetWindow") == 0) { 279 while (focusWindow->parent()) { 280 focusWindow = focusWindow->parent(); 281 } 282 } 283 284 return focusWindow; 285} 286 287- (void)viewDidHide 288{ 289 if (!m_platformWindow->isExposed()) 290 return; 291 292 m_platformWindow->handleExposeEvent(QRegion()); 293 294 // Note: setNeedsDisplay is automatically called for 295 // viewDidUnhide so no reason to override it here. 296} 297 298- (BOOL)isTransparentForUserInput 299{ 300 return m_platformWindow->window() && 301 m_platformWindow->window()->flags() & Qt::WindowTransparentForInput; 302} 303 304- (BOOL)becomeFirstResponder 305{ 306 if (!m_platformWindow) 307 return NO; 308 if ([self isTransparentForUserInput]) 309 return NO; 310 if (!m_platformWindow->windowIsPopupType()) 311 QWindowSystemInterface::handleWindowActivated([self topLevelWindow]); 312 return YES; 313} 314 315- (BOOL)acceptsFirstResponder 316{ 317 if (!m_platformWindow) 318 return NO; 319 if (m_platformWindow->shouldRefuseKeyWindowAndFirstResponder()) 320 return NO; 321 if ([self isTransparentForUserInput]) 322 return NO; 323 if ((m_platformWindow->window()->flags() & Qt::ToolTip) == Qt::ToolTip) 324 return NO; 325 return YES; 326} 327 328- (NSView *)hitTest:(NSPoint)aPoint 329{ 330 NSView *candidate = [super hitTest:aPoint]; 331 if (candidate == self) { 332 if ([self isTransparentForUserInput]) 333 return nil; 334 } 335 return candidate; 336} 337 338- (void)convertFromScreen:(NSPoint)mouseLocation toWindowPoint:(QPointF *)qtWindowPoint andScreenPoint:(QPointF *)qtScreenPoint 339{ 340 // Calculate the mouse position in the QWindow and Qt screen coordinate system, 341 // starting from coordinates in the NSWindow coordinate system. 342 // 343 // This involves translating according to the window location on screen, 344 // as well as inverting the y coordinate due to the origin change. 345 // 346 // Coordinate system overview, outer to innermost: 347 // 348 // Name Origin 349 // 350 // OS X screen bottom-left 351 // Qt screen top-left 352 // NSWindow bottom-left 353 // NSView/QWindow top-left 354 // 355 // NSView and QWindow are equal coordinate systems: the QWindow covers the 356 // entire NSView, and we've set the NSView's isFlipped property to true. 357 358 NSWindow *window = [self window]; 359 NSPoint nsWindowPoint; 360 NSRect windowRect = [window convertRectFromScreen:NSMakeRect(mouseLocation.x, mouseLocation.y, 1, 1)]; 361 nsWindowPoint = windowRect.origin; // NSWindow coordinates 362 NSPoint nsViewPoint = [self convertPoint: nsWindowPoint fromView: nil]; // NSView/QWindow coordinates 363 *qtWindowPoint = QPointF(nsViewPoint.x, nsViewPoint.y); // NSView/QWindow coordinates 364 *qtScreenPoint = QCocoaScreen::mapFromNative(mouseLocation); 365} 366 367@end 368 369#include "qnsview_drawing.mm" 370#include "qnsview_mouse.mm" 371#include "qnsview_touch.mm" 372#include "qnsview_gestures.mm" 373#include "qnsview_tablet.mm" 374#include "qnsview_dragging.mm" 375#include "qnsview_keys.mm" 376#include "qnsview_complextext.mm" 377#include "qnsview_menus.mm" 378#ifndef QT_NO_ACCESSIBILITY 379#include "qnsview_accessibility.mm" 380#endif 381 382// ----------------------------------------------------- 383 384@implementation QNSView (QtExtras) 385 386- (QCocoaWindow*)platformWindow 387{ 388 return m_platformWindow.data();; 389} 390 391@end 392