macOS: Simplify helpers for flipping between quadrant I and IV

The different flip-functions have been replaced by a single function,
qt_mac_flip, with overloads for points and rects. This function is
primarily used to implement QCocoaScreen::map(To|From)Native, which
most clients of qt_flip* have been moved to. This makes it clearer
what kind of reference geometry we're flipping in relation to, and
simplifies call-sites.

Change-Id: I8a0862f94bb2c64a83a1c3168f984a195c0af6db
Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
This commit is contained in:
Tor Arne Vestbø 2017-11-07 19:25:13 +01:00
parent f8807b8220
commit b0c895fa7a
9 changed files with 85 additions and 126 deletions

View File

@ -40,6 +40,7 @@
#include "qcocoaaccessibility.h"
#include "qcocoahelpers.h"
#include "qcocoawindow.h"
#include "qcocoascreen.h"
#include "private/qaccessiblecache_p.h"
#include <QtAccessibilitySupport/private/qaccessiblebridgeutils_p.h>
#include <QtGui/qaccessible.h>
@ -298,9 +299,9 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of
// We're in the same top level element as our parent.
return [[self parentElement] accessibilityAttributeValue:NSAccessibilityTopLevelUIElementAttribute];
} else if ([attribute isEqualToString:NSAccessibilityPositionAttribute]) {
QPoint qtPosition = iface->rect().topLeft();
QSize qtSize = iface->rect().size();
return [NSValue valueWithPoint: NSMakePoint(qtPosition.x(), qt_mac_flipYCoordinate(qtPosition.y() + qtSize.height()))];
// The position in points of the element's lower-left corner in screen-relative coordinates
QPointF qtPosition = QRectF(iface->rect()).bottomLeft();
return [NSValue valueWithPoint:QCocoaScreen::mapToNative(qtPosition)];
} else if ([attribute isEqualToString:NSAccessibilitySizeAttribute]) {
QSize qtSize = iface->rect().size();
return [NSValue valueWithSize: NSMakeSize(qtSize.width(), qtSize.height())];
@ -430,7 +431,7 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of
if ([attribute isEqualToString: NSAccessibilityBoundsForRangeParameterizedAttribute]) {
NSRange range = [parameter rangeValue];
QRect firstRect = iface->textInterface()->characterRect(range.location);
QRect rect;
QRectF rect;
if (range.length > 0) {
NSUInteger position = range.location + range.length - 1;
if (position > range.location && iface->textInterface()->text(position, position + 1) == QStringLiteral("\n"))
@ -441,15 +442,14 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of
rect = firstRect;
rect.setWidth(1);
}
return [NSValue valueWithRect: NSMakeRect((CGFloat) rect.x(),(CGFloat) qt_mac_flipYCoordinate(rect.y() + rect.height()), rect.width(), rect.height())];
return [NSValue valueWithRect:QCocoaScreen::mapToNative(rect)];
}
if ([attribute isEqualToString: NSAccessibilityAttributedStringForRangeParameterizedAttribute]) {
NSRange range = [parameter rangeValue];
QString text = iface->textInterface()->text(range.location, range.location + range.length);
return [[NSAttributedString alloc] initWithString:text.toNSString()];
} else if ([attribute isEqualToString: NSAccessibilityRangeForPositionParameterizedAttribute]) {
NSPoint nsPoint = [parameter pointValue];
QPoint point(static_cast<int>(nsPoint.x), static_cast<int>(qt_mac_flipYCoordinate(nsPoint.y)));
QPoint point = QCocoaScreen::mapFromNative([parameter pointValue]).toPoint();
int offset = iface->textInterface()->offsetAtPoint(point);
return [NSValue valueWithRange:NSMakeRange(static_cast<NSUInteger>(offset), 1)];
} else if ([attribute isEqualToString: NSAccessibilityStyleRangeForIndexParameterizedAttribute]) {
@ -566,8 +566,8 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of
return NSAccessibilityUnignoredAncestor(self);
}
int y = qt_mac_flipYCoordinate(point.y);
QAccessibleInterface *childInterface = iface->childAt(point.x, y);
QPointF screenPoint = QCocoaScreen::mapFromNative(point);
QAccessibleInterface *childInterface = iface->childAt(screenPoint.x(), screenPoint.y());
// No child found, meaning we hit this element.
if (!childInterface || !childInterface->isValid())
return NSAccessibilityUnignoredAncestor(self);
@ -575,7 +575,7 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of
// find the deepest child at the point
QAccessibleInterface *childOfChildInterface = 0;
do {
childOfChildInterface = childInterface->childAt(point.x, y);
childOfChildInterface = childInterface->childAt(screenPoint.x(), screenPoint.y());
if (childOfChildInterface && childOfChildInterface->isValid())
childInterface = childOfChildInterface;
} while (childOfChildInterface && childOfChildInterface->isValid());

View File

@ -39,6 +39,7 @@
#include "qcocoacursor.h"
#include "qcocoawindow.h"
#include "qcocoascreen.h"
#include "qcocoahelpers.h"
#include <QtGui/private/qcoregraphics_p.h>
@ -70,7 +71,7 @@ void QCocoaCursor::changeCursor(QCursor *cursor, QWindow *window)
QPoint QCocoaCursor::pos() const
{
return qt_mac_flipPoint([NSEvent mouseLocation]).toPoint();
return QCocoaScreen::mapFromNative([NSEvent mouseLocation]).toPoint();
}
void QCocoaCursor::setPos(const QPoint &position)

View File

@ -85,13 +85,8 @@ QT_MANGLE_NAMESPACE(QNSView) *qnsview_cast(NSView *view);
void qt_mac_transformProccessToForegroundApplication();
QString qt_mac_applicationName();
int qt_mac_flipYCoordinate(int y);
qreal qt_mac_flipYCoordinate(qreal y);
QPointF qt_mac_flipPoint(const NSPoint &p);
NSPoint qt_mac_flipPoint(const QPoint &p);
NSPoint qt_mac_flipPoint(const QPointF &p);
NSRect qt_mac_flipRect(const QRect &rect);
QPointF qt_mac_flip(const QPointF &pos, const QRectF &reference);
QRectF qt_mac_flip(const QRectF &rect, const QRectF &reference);
Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum);

View File

@ -232,49 +232,36 @@ QString qt_mac_applicationName()
return appName;
}
int qt_mac_primaryScreenHeight()
// -------------------------------------------------------------------------
/*!
\fn QPointF qt_mac_flip(const QPointF &pos, const QRectF &reference)
\fn QRectF qt_mac_flip(const QRectF &rect, const QRectF &reference)
Flips the Y coordinate of the point/rect between quadrant I and IV.
The native coordinate system on macOS uses quadrant I, with origin
in bottom left, and Qt uses quadrant IV, with origin in top left.
By flipping the Y coordinate, we can map the point/rect between
the two coordinate systems.
The flip is always in relation to a reference rectangle, e.g.
the frame of the parent view, or the screen geometry. In the
latter case the specialized QCocoaScreen::mapFrom/To functions
should be used instead.
*/
QPointF qt_mac_flip(const QPointF &pos, const QRectF &reference)
{
QMacAutoReleasePool pool;
NSArray *screens = [NSScreen screens];
if ([screens count] > 0) {
// The first screen in the screens array is documented to
// have the (0,0) origin and is designated the primary screen.
NSRect screenFrame = [[screens objectAtIndex: 0] frame];
return screenFrame.size.height;
}
return 0;
return QPointF(pos.x(), reference.height() - pos.y());
}
int qt_mac_flipYCoordinate(int y)
QRectF qt_mac_flip(const QRectF &rect, const QRectF &reference)
{
return qt_mac_primaryScreenHeight() - y;
return QRectF(qt_mac_flip(rect.bottomLeft(), reference), rect.size());
}
qreal qt_mac_flipYCoordinate(qreal y)
{
return qt_mac_primaryScreenHeight() - y;
}
QPointF qt_mac_flipPoint(const NSPoint &p)
{
return QPointF(p.x, qt_mac_flipYCoordinate(p.y));
}
NSPoint qt_mac_flipPoint(const QPoint &p)
{
return NSMakePoint(p.x(), qt_mac_flipYCoordinate(p.y()));
}
NSPoint qt_mac_flipPoint(const QPointF &p)
{
return NSMakePoint(p.x(), qt_mac_flipYCoordinate(p.y()));
}
NSRect qt_mac_flipRect(const QRect &rect)
{
int flippedY = qt_mac_flipYCoordinate(rect.y() + rect.height());
return NSMakeRect(rect.x(), flippedY, rect.width(), rect.height());
}
// -------------------------------------------------------------------------
Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum)
{

View File

@ -77,16 +77,12 @@ public:
NSScreen *nativeScreen() const;
void updateGeometry();
QPointF mapToNative(const QPointF &pos) const { return flipCoordinate(pos); }
QRectF mapToNative(const QRectF &rect) const { return flipCoordinate(rect); }
QPointF mapFromNative(const QPointF &pos) const { return flipCoordinate(pos); }
QRectF mapFromNative(const QRectF &rect) const { return flipCoordinate(rect); }
static QCocoaScreen *primaryScreen();
private:
QPointF flipCoordinate(const QPointF &pos) const;
QRectF flipCoordinate(const QRectF &rect) const;
static CGPoint mapToNative(const QPointF &pos, QCocoaScreen *screen = QCocoaScreen::primaryScreen());
static CGRect mapToNative(const QRectF &rect, QCocoaScreen *screen = QCocoaScreen::primaryScreen());
static QPointF mapFromNative(CGPoint pos, QCocoaScreen *screen = QCocoaScreen::primaryScreen());
static QRectF mapFromNative(CGRect rect, QCocoaScreen *screen = QCocoaScreen::primaryScreen());
public:
int m_screenIndex;

View File

@ -75,34 +75,6 @@ NSScreen *QCocoaScreen::nativeScreen() const
return [screens objectAtIndex:m_screenIndex];
}
/*!
Flips the Y coordinate of the point between quadrant I and IV.
The native coordinate system on macOS uses quadrant I, with origin
in bottom left, and Qt uses quadrant IV, with origin in top left.
By flippig the Y coordinate, we can map the position between the
two coordinate systems.
*/
QPointF QCocoaScreen::flipCoordinate(const QPointF &pos) const
{
return QPointF(pos.x(), m_geometry.height() - pos.y());
}
/*!
Flips the Y coordinate of the rectangle between quadrant I and IV.
The native coordinate system on macOS uses quadrant I, with origin
in bottom left, and Qt uses quadrant IV, with origin in top left.
By flippig the Y coordinate, we can map the rectangle between the
two coordinate systems.
*/
QRectF QCocoaScreen::flipCoordinate(const QRectF &rect) const
{
return QRectF(flipCoordinate(rect.topLeft() + QPoint(0, rect.height())), rect.size());
}
static QString displayName(CGDirectDisplayID displayID)
{
QIOType<io_iterator_t> iterator;
@ -141,20 +113,10 @@ void QCocoaScreen::updateGeometry()
if (!nsScreen)
return;
// At this point the geometry is in native coordinates, but the size
// is correct, which we take advantage of next when we map the native
// coordinates to the Qt coordinate system.
m_geometry = QRectF::fromCGRect(NSRectToCGRect(nsScreen.frame)).toRect();
m_availableGeometry = QRectF::fromCGRect(NSRectToCGRect(nsScreen.visibleFrame)).toRect();
// The reference screen for the geometry is always the primary screen, but since
// we may be in the process of creating and registering the primary screen, we
// must special-case that and assign it direcly.
QCocoaScreen *primaryScreen = (nsScreen == [[NSScreen screens] firstObject]) ?
this : QCocoaScreen::primaryScreen();
m_geometry = primaryScreen->mapFromNative(m_geometry).toRect();
m_availableGeometry = primaryScreen->mapFromNative(m_availableGeometry).toRect();
// The reference screen for the geometry is always the primary screen
QRectF primaryScreenGeometry = QRectF::fromCGRect([[NSScreen screens] firstObject].frame);
m_geometry = qt_mac_flip(QRectF::fromCGRect(nsScreen.frame), primaryScreenGeometry).toRect();
m_availableGeometry = qt_mac_flip(QRectF::fromCGRect(nsScreen.visibleFrame), primaryScreenGeometry).toRect();
m_format = QImage::Format_RGB32;
m_depth = NSBitsPerPixelFromDepth([nsScreen depth]);
@ -197,7 +159,7 @@ QPlatformScreen::SubpixelAntialiasingType QCocoaScreen::subpixelAntialiasingType
QWindow *QCocoaScreen::topLevelAt(const QPoint &point) const
{
NSPoint screenPoint = qt_mac_flipPoint(point);
NSPoint screenPoint = mapToNative(point);
// Search (hit test) for the top-level window. [NSWidow windowNumberAtPoint:
// belowWindowWithWindowNumber] may return windows that are not interesting
@ -302,4 +264,28 @@ QCocoaScreen *QCocoaScreen::primaryScreen()
return static_cast<QCocoaScreen *>(QGuiApplication::primaryScreen()->handle());
}
CGPoint QCocoaScreen::mapToNative(const QPointF &pos, QCocoaScreen *screen)
{
Q_ASSERT(screen);
return qt_mac_flip(pos, screen->geometry()).toCGPoint();
}
CGRect QCocoaScreen::mapToNative(const QRectF &rect, QCocoaScreen *screen)
{
Q_ASSERT(screen);
return qt_mac_flip(rect, screen->geometry()).toCGRect();
}
QPointF QCocoaScreen::mapFromNative(CGPoint pos, QCocoaScreen *screen)
{
Q_ASSERT(screen);
return qt_mac_flip(QPointF::fromCGPoint(pos), screen->geometry());
}
QRectF QCocoaScreen::mapFromNative(CGRect rect, QCocoaScreen *screen)
{
Q_ASSERT(screen);
return qt_mac_flip(QRectF::fromCGRect(rect), screen->geometry());
}
QT_END_NAMESPACE

View File

@ -271,7 +271,7 @@ QRect QCocoaWindow::geometry() const
NSPoint windowPoint = [m_view convertPoint:NSMakePoint(0, 0) toView:nil];
NSRect screenRect = [[m_view window] convertRectToScreen:NSMakeRect(windowPoint.x, windowPoint.y, 1, 1)];
NSPoint screenPoint = screenRect.origin;
QPoint position = qt_mac_flipPoint(screenPoint).toPoint();
QPoint position = QCocoaScreen::mapFromNative(screenPoint).toPoint();
QSize size = QRectF::fromCGRect(NSRectToCGRect([m_view bounds])).toRect().size();
return QRect(position, size);
}
@ -294,7 +294,7 @@ void QCocoaWindow::setCocoaGeometry(const QRect &rect)
}
if (isContentView()) {
NSRect bounds = qt_mac_flipRect(rect);
NSRect bounds = QCocoaScreen::mapToNative(rect);
[m_view.window setFrame:[m_view.window frameRectForContentRect:bounds] display:YES animate:NO];
} else {
[m_view setFrame:NSMakeRect(rect.x(), rect.y(), rect.width(), rect.height())];
@ -387,7 +387,7 @@ void QCocoaWindow::setVisible(bool visible)
if (!(parentCocoaWindow && window()->transientParent()->isActive()) && window()->type() == Qt::Popup) {
removeMonitor();
monitor = [NSEvent addGlobalMonitorForEventsMatchingMask:NSLeftMouseDownMask|NSRightMouseDownMask|NSOtherMouseDownMask|NSMouseMovedMask handler:^(NSEvent *e) {
QPointF localPoint = qt_mac_flipPoint([NSEvent mouseLocation]);
QPointF localPoint = QCocoaScreen::mapFromNative([NSEvent mouseLocation]);
QWindowSystemInterface::handleMouseEvent(window(), window()->mapFromGlobal(localPoint.toPoint()), localPoint,
cocoaButton2QtButton([e buttonNumber]));
}];
@ -1100,7 +1100,7 @@ void QCocoaWindow::handleGeometryChange()
CGRect contentRect = [m_view.window contentRectForFrameRect:m_view.window.frame];
// The result above is in native screen coordinates, so remap to the Qt coordinate system
newGeometry = QCocoaScreen::primaryScreen()->mapFromNative(QRectF::fromCGRect(contentRect)).toRect();
newGeometry = QCocoaScreen::mapFromNative(contentRect).toRect();
} else {
// QNSView has isFlipped set, so no need to remap the geometry
newGeometry = QRectF::fromCGRect(m_view.frame).toRect();
@ -1362,7 +1362,7 @@ QCocoaNSWindow *QCocoaWindow::createNSWindow(bool shouldBePanel)
rect.translate(-targetScreen->geometry().topLeft());
QCocoaScreen *cocoaScreen = static_cast<QCocoaScreen *>(targetScreen->handle());
NSRect frame = NSRectFromCGRect(cocoaScreen->mapToNative(rect).toCGRect());
NSRect frame = QCocoaScreen::mapToNative(rect, cocoaScreen);
// Note: The macOS window manager has a bug, where if a screen is rotated, it will not allow
// a window to be created within the area of the screen that has a Y coordinate (I quadrant)

View File

@ -39,6 +39,7 @@
#include "qmultitouch_mac_p.h"
#include "qcocoahelpers.h"
#include "qcocoascreen.h"
#include <private/qtouchdevice_p.h>
QT_BEGIN_NAMESPACE
@ -83,7 +84,7 @@ void QCocoaTouch::updateTouchData(NSTouch *nstouch, NSTouchPhase phase)
if (_touchPoint.id == 0 && phase == NSTouchPhaseBegan) {
_trackpadReferencePos = qnpos;
_screenReferencePos = qt_mac_flipPoint([NSEvent mouseLocation]);
_screenReferencePos = QCocoaScreen::mapFromNative([NSEvent mouseLocation]);
}
QPointF screenPos = _screenReferencePos;

View File

@ -42,6 +42,7 @@
#include "qnsview.h"
#include "qcocoawindow.h"
#include "qcocoahelpers.h"
#include "qcocoascreen.h"
#include "qmultitouch_mac_p.h"
#include "qcocoadrag.h"
#include "qcocoainputcontext.h"
@ -436,8 +437,7 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet")
nsWindowPoint = windowRect.origin; // NSWindow coordinates
NSPoint nsViewPoint = [self convertPoint: nsWindowPoint fromView: nil]; // NSView/QWindow coordinates
*qtWindowPoint = QPointF(nsViewPoint.x, nsViewPoint.y); // NSView/QWindow coordinates
*qtScreenPoint = QPointF(mouseLocation.x, qt_mac_flipYCoordinate(mouseLocation.y)); // Qt screen coordinates
*qtScreenPoint = QCocoaScreen::mapFromNative(mouseLocation);
}
- (void)resetMouseButtons
@ -548,7 +548,7 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet")
NSPoint nsViewPoint = [self convertPoint: windowPoint fromView: nil];
QPoint qtWindowPoint = QPoint(nsViewPoint.x, titleBarHeight + nsViewPoint.y);
NSPoint screenPoint = [window convertRectToScreen:NSMakeRect(windowPoint.x, windowPoint.y, 0, 0)].origin;
QPoint qtScreenPoint = QPoint(screenPoint.x, qt_mac_flipYCoordinate(screenPoint.y));
QPoint qtScreenPoint = QCocoaScreen::mapFromNative(screenPoint).toPoint();
ulong timestamp = [theEvent timestamp] * 1000;
QWindowSystemInterface::handleFrameStrutMouseEvent(m_platformWindow->window(), timestamp, qtWindowPoint, qtScreenPoint, m_frameStrutButtons);
@ -640,7 +640,7 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet")
if (!popups->isEmpty()) {
// Check if the click is outside all popups.
bool inside = false;
QPointF qtScreenPoint = qt_mac_flipPoint([self screenMousePoint:theEvent]);
QPointF qtScreenPoint = QCocoaScreen::mapFromNative([self screenMousePoint:theEvent]);
for (QList<QCocoaWindow *>::const_iterator it = popups->begin(); it != popups->end(); ++it) {
if ((*it)->geometry().contains(qtScreenPoint.toPoint())) {
inside = true;
@ -1730,14 +1730,8 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent)
// The returned rect is always based on the internal cursor.
QRect mr = qApp->inputMethod()->cursorRectangle().toRect();
QPoint mp = m_platformWindow->window()->mapToGlobal(mr.bottomLeft());
NSRect rect;
rect.origin.x = mp.x();
rect.origin.y = qt_mac_flipYCoordinate(mp.y());
rect.size.width = mr.width();
rect.size.height = mr.height();
return rect;
mr.moveBottomLeft(m_platformWindow->window()->mapToGlobal(mr.bottomLeft()));
return QCocoaScreen::mapToNative(mr);
}
- (NSUInteger)characterIndexForPoint:(NSPoint)aPoint
@ -2022,8 +2016,7 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin
NSPoint windowPoint = [self.window convertRectFromScreen:NSMakeRect(screenPoint.x, screenPoint.y, 1, 1)].origin;
NSPoint nsViewPoint = [self convertPoint: windowPoint fromView: nil]; // NSView/QWindow coordinates
QPoint qtWindowPoint(nsViewPoint.x, nsViewPoint.y);
QPoint qtScreenPoint = QPoint(screenPoint.x, qt_mac_flipYCoordinate(screenPoint.y));
QPoint qtScreenPoint = QCocoaScreen::mapFromNative(screenPoint).toPoint();
QWindowSystemInterface::handleMouseEvent(target, mapWindowCoordinates(m_platformWindow->window(), target, qtWindowPoint), qtScreenPoint, m_buttons);
}