kasper / phoenix

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

3 Monitor setup - Move window to specific monitor #312

Closed ABC-LG closed 1 year ago

ABC-LG commented 1 year ago

I have a setup with 3 monitors and want to be able to move a window to a specific screen using a key combination.

I know how to move my window to the next screen and how to fetch the UUID identifier for all screens, but I have no clue on how to achieve this.

Send window to the next screen:

Key.on('n', ['ctrl', 'shift'], () => {
    const window = Window.focused();
    const nextScreenFrame = frameOfNextScreen(window);
    if (!window || !nextScreenFrame) {
        return;
    }
    if (windowFitsInFrame(window, nextScreenFrame)) {

        centerWindowInFrame(window, nextScreenFrame);
    } else {
        maximizeWindowInFrame(window, nextScreenFrame);
    }
});

function frameOfNextScreen(window) {
    if (!window || !window.screen().next()) {
        return;
    }
    return window.screen().next().flippedVisibleFrame();
}

Thanks in advance for any help.

kasper commented 1 year ago

Hey! Perhaps use Screen.all() and find the screen instance you are interested on with the UUID from the provided array? The UUIDs stay constant, so you can define them static based on your display setup.

mafredri commented 1 year ago

Something like this could also work:

const orderedScreens = Screen.all().sort((a, b) => a.frame().x - b.frame().x);

It should give you your screens in horizontal order so you can place windows via moveToScreen(orderedScreens[1]).

thetomcraig commented 1 year ago

Hi there, I am also struggling with this same issue. Is moveToScreen() a function defined somewhere? I don't see it when searching the repo.

kasper commented 1 year ago

@thetomcraig Hi, there is no moveToScreen(…) function in the core API. You need to use the setFrame(…) or setTopLeft(…) to move a window to a specific screen by using the screen’s coordinates. Hope that helps! https://kasper.github.io/phoenix/api/window

thetomcraig commented 1 year ago

Okay this makes sense, the missing piece though is getting those coordinates for a desired screen based on it's UUID. I don't see a way to do this with the Screen API, afaik.

The method I can think of now would be to do a loop that calls .next() repeatedly on an iterator until it finds the one with the identifier property matching the desired screen's UUID, then get's it's frame, then sets that frame for the current window. Something like this pseudo code:

  const desiredUUID = '37D8832A-2D66-02CA-B9F7-8F30A301B230'

  const window = Window.focused ();
  const oldScreen = window.screen();

  let desiredScreen = undefined;
  let screenIterator = oldScreen.next();
  while(!oldScreen.isEqual(newScreen)) {
    if (screenIterator.identifier() === desiredUUID) {
       desiredScreen = screenIterator;
       break;
    }
    screenIterator = screenIterator.next();
  }

if (desiredScreen !== undefined) {
  const ratio = frameRatio(
     oldScreen.flippedVisibleFrame(), 
     desiredScreen.flippedVisibleFrame(),
  )
  window.setFrame(ratio(window.frame()));
}

I think this should work but it seems pretty inefficient. Am I missing something here?

kasper commented 1 year ago

@thetomcraig How about something like this?

const foundScreen = Screen.all().find(screen => screen.identifier() === desiredUUID);
thetomcraig commented 1 year ago

YES this is perfect! (As you can tell I'm not a js developer, lol). I was able to achieve what I wanted with this, it may prove useful to others:

const windowIds = [
                    "37D8832A-2D66-02CA-B9F7-8F30A301B230",
                    "95458444-A649-45C1-94AA-7D8880A7144F",
                    "AA4129F8-E94C-455C-A2CE-F3C7DF605E8E",
                    "B2085621-B801-4448-8FB9-30E7D08C1419",
                    "3913815D-56F0-4FE7-90F7-6CB91892A81D"
                   ];

function frameRatio(a, b){
  const widthRatio = b.width / a.width;
  const heightRatio = b.height / a.height;

  return ({width, height, x, y}) => {
    width = Math.round(width * widthRatio);
    height = Math.round(height * heightRatio);
    x = Math.round(b.x + (x - a.x) * widthRatio);
    y = Math.round(b.y + (y - a.y) * heightRatio);

    return {width, height, x, y};
  };
};

function moveToDisplayWithUUID(uuid) {
  const window = Window.focused ();
  if ( !window ) return;

  const oldScreen = window.screen(); 

  const newScreen = Screen.all().find(screen => screen.identifier() === uuid)

  if(oldScreen.isEqual(newScreen) || newScreen === undefined) {
     return; 
  }

  const ratio = frameRatio(
     oldScreen.flippedVisibleFrame(), 
     newScreen.flippedVisibleFrame(),
  )

  window.setFrame(ratio(window.frame()));
}

setKeyHandler ('1', HYPER, () => {
  moveToDisplayWithUUID(windowIds[0])
});
...
other functions here for other indices

Thanks @kasper this is great.

kasper commented 1 year ago

@thetomcraig Awesome! 😁