Подтвердить что ты не робот

Mac OS X: преобразование координат NSView и глобальных экранных координат

У меня есть следующая ситуация в моей настройке нескольких мониторов:

OSX coordinate conversions

В этом примере я хочу расположить окно точно в координатах, обозначенных желтой стрелкой. Тем не менее, все, что у меня есть, - это координаты NSView, который является подвидным содержимым ContentView NSWindow, который охватывает весь (больший, верхний) дополнительный монитор.

Здесь определяется глобальное пространство координат:

  • {0,0} - координата верхнего левого угла экрана ноутбука. (Зеленый)
  • {- 296, -1080} - координата верхнего левого угла моего второго экрана (черный)
  • {0, 800} - координата нижнего левого угла (здесь нет стрелки)

Таким образом, y растет вниз с зеленой стрелки, уменьшаясь вверх от зеленой стрелки.

Проблема:

Как преобразовать точку, обозначенную желтой стрелкой ({100,100}, NSView внутри NSWindow внутри этого NSScreen) в эту глобальную систему координат. ( Примечание: в NSView система координат имеет {0,0} в левом нижнем углу, увеличиваясь вверх.)

Я верю, что правильный ответ - {-196, -980}, но зачем код для этого преобразования для любого окна на любом экране?

Я потратил слишком много времени на эту проблему, поэтому любая помощь очень ценится.

(Не уверен, если это уместно, но на нижнем экране отображается разрешение сетчатки.)

4b9b3361

Ответ 1

Mac OS использует разные системы координат в разных местах. Представления могут определять, имеют ли они направленную вверх или вниз ось Y (isFlipped). Происхождение окна выражается в "координатах экрана" с восходящей осью y. Экраны организованы с использованием глобальной системы координат, где y направлен вниз.

Лучше не пытаться самостоятельно преобразовывать все координатные пространства, но пусть ответственные объекты выполняют работу:

NSView *yellowView; // your view that contains the point with the yellow arrow.
NSPoint yellowPoint = { 100, 100 };

NSPoint pointInWindow = [yellowView convertPoint:yellowPoint toView:nil];
NSPoint pointOnScreen = [[yellowView window] convertRectToScreen:(CGRect){.origin=pointInWindow}];

NSWindow *newWindow = [[NSWindow alloc] initWithContentRect:(CGRect){ pointOnScreen, {32, 32}} styleMask: ...];

Ответ 2

Версия принятого ответа Swift (4.0) в основном одинакова:

let yellowView: NSView // your view that contains the point with the yellow arrow.
let yellowPoint = NSPoint(x: 100, y: 100)

let pointInWindow = yellowView.convert(yellowPoint, to: nil)
let pointOnScreen = yellowView.window?.convertToScreen(NSRect(origin: pointInWindow, size: .zero)).origin ?? .zero

let contentRect = NSRect(origin: pointOnScreen, size: NSSize(width: 32, height: 32))
let newWindow = NSWindow(contentRect: contentRect, styleMask: ...)

Ниже приведен альтернативный способ сделать это:

let someView: NSView // Some existing view
var rect: NSRect

rect = NSRect(x: 100, y: 100, width: 0, height: 0)
rect = someView.convert(rect, to: nil)
rect = someView.window?.convertToScreen(rect) ?? rect
rect.size = NSSize(width: 32, height: 32)
let newWindow = NSWindow(contentRect: rect, styleMask: ...)

Последний способ просто устанавливает исправление раньше времени. Здесь пошаговая игра для тех, кто любит прохождение:

1. Создайте прямоугольник. Инициализируйте прямоугольник нулевого размера в нужном месте в системе координат вида.

let someView: NSView // Some existing view
var rect = NSRect(x: 100, y: 100, width: 0, height: 0)

2. Преобразовать из окна в окно. Преобразовать прямоугольник из системы координат вида в систему координат окна, указав nil для адресата view.

rect = someView.convert(rect, to: nil)

3. Преобразование из окна в экран.. Затем преобразуйте прямоугольник из системы координат окна в систему координат экрана.

Обратите внимание, что someView.window может быть nil, поэтому мы используем необязательную цепочку (т.е. ? в window?) и возвращаемся к исходному значению rect, если это случай. Это, вероятно, не обязательно, но это хорошая привычка вступать.

rect = someView.window?.convertToScreen(rect) ?? rect

4. Задайте размер прямоугольника. Обновите прямоугольник нужного размера в новом окне.

rect.size = NSSize(width: 32, height: 32)

5. Создайте окно. Инициализируйте новое окно с преобразованным прямоугольником.

let newWindow = NSWindow(contentRect: rect, styleMask: ...)