kasper / phoenix

A lightweight macOS window and app manager scriptable with JavaScript
https://kasper.github.io/phoenix/
Other
4.36k stars 128 forks source link

`Window.focused().topLeft()` returns `Point` relative to `Screen` with menu bar #298

Closed ptrkstr closed 2 years ago

ptrkstr commented 2 years ago

Steps:

  1. Configure display to the following:

image

  1. Configure .phoenix.js script to the following:
Key.on('q', ['ctrl', 'option'], () => {
  const frame = Window.focused().topLeft();
  Phoenix.notify(`${frame.x} ${frame.y}`)
})
  1. Drag a window to the top left of the menu bar screen

image

  1. Press ctrl+option+q and observe notification:
image

✅ 0, 38

  1. Drag a window to the top left of the top screen

image

  1. Press ctrl+option+q and observe notification:
image

❌ -434, -1415

Expected:

0, 38

Observed:

-434, -1415

Notes:

If the menu bar is moved to the top screen, the correct values are observed when pressing ctrl+option+q when a window is in the top screen.

ptrkstr commented 2 years ago

I noticed that topLeft uses NSAccessibilityPositionAttribute which doesn't provide much documentation.

The position in points of the element's lower-left corner in screen-relative coordinates (NSValue).

However I believe it's the same as kAXPositionAttribute:

The global screen coordinates of the top-left corner of this accessibility object. Note that the coordinates 0,0 represent the top-left corner of the screen that displays the menu bar. All accessibility objects that have a screen position (in other words, are visible on the screen) should include this attribute.

If this is the case @kasper, does Phoenix currently offer a way to convert the Window.focused coordinates to Screen.main coordinates?

ptrkstr commented 2 years ago

Closing the issue as I believe this is how it's meant to behave.

kasper commented 2 years ago

@ptrkstr Hello! So as you figured it out, all the screens together combine a massive coordinate system, depending on your display configuration. Therefore you need to calculate the positions based on the origin.

ptrkstr commented 2 years ago

Thanks for the great software @kasper , would you consider signing up for github sponsors to make it easier for people to sponsor it?


I come across this as I wanted to define the following keys (in combination with ctrl + option):

q,w,e
a,s,d
z,x,c

as:

top left,     top,          top right
left,         maximise,     right
bottom left,  bottom,       bottom right

relative to the screen you're on.

For anyone else that comes across this, here's the config:

function frame() {
  return Window.focused().screen().flippedFrame();
}

// top left
Key.on('q', ['ctrl', 'option'], () => {
  Window.focused().setFrame({ x: frame().x, y: frame().y, width: frame().width / 2, height: frame().height / 2 });
})

// top
Key.on('w', ['ctrl', 'option'], () => {
  Window.focused().setFrame({ x: frame().x, y: frame().y, width: frame().width, height: frame().height / 2 });
})

// top right
Key.on('e', ['ctrl', 'option'], () => {
  Window.focused().setFrame({ x: frame().x + (frame().width / 2), y: frame().y, width: frame().width / 2, height: frame().height / 2 });
})

// left
Key.on('a', ['ctrl', 'option'], () => {
  setLeft(Window.focused());
})

// maximise
Key.on('s', ['ctrl', 'option'], () => {
  Window.focused().maximise();
})

// right
Key.on('d', ['ctrl', 'option'], () => {
  setRight(Window.focused());
})

// bottom left
Key.on('z', ['ctrl', 'option'], () => {
  Window.focused().setFrame({ x: frame().x, y: frame().y + (frame().height / 2), width: frame().width / 2, height: frame().height / 2 });
})

// bottom
Key.on('x', ['ctrl', 'option'], () => {
  Window.focused().setFrame({ x: frame().x, y: frame().y + (frame().height / 2), width: frame().width, height: frame().height / 2 });
})

// bottom right
Key.on('c', ['ctrl', 'option'], () => {
  Window.focused().setFrame({ x: frame().x + (frame().width / 2), y: frame().y + (frame().height / 2), width: frame().width / 2, height: frame().height / 2 });
})

function setLeft(window) {
  window.setFrame({ x: frame().x, y: frame().y, width: frame().width / 2, height: frame().height });
}

function setRight(window) {
  window.setFrame({ x: frame().x + (frame().width / 2), y: frame().y, width: frame().width / 2, height: frame().height });
}

// split last 2 windows vertically
Key.on('v', ['ctrl', 'option'], () => {
  const windows = Window.recent();
  if (windows.length < 2) {
    return;
  }

  setLeft(windows[0]);
  setRight(windows[1]);
})
kasper commented 2 years ago

@ptrkstr Very simple and nice! 😄 I haven’t signed up for GitHub sponsorship yet, I would need to check what the tax implications look like. Thanks for the thought! ❤️