4
votes

enter image description here

I've got an app that programmatically moves its window around the user's screen. My problem:

  1. User has two physical monitors
  2. User starts app in primary monitor
  3. App moves window, causing more of it to "overflow" into the secondary monitor than is on the primary monitor
  4. This causes the app's window to entirely jump to that secondary monitor and disappear from the primary monitor.

I would like for the window to stay in the primary monitor unless I want it to go to the secondary monitor, or some defined point (e.g. the center) goes into the secondary monitor, or it would even be ok if the window were split and shown on both. Is there any way to prevent this jump from happening as soon the window intersects more with the secondary monitor?

To reiterate, I'm moving the window programmatically and am using Electron on macOS.

Also, this doesn't appear to happen when I move window manually, but I think this is because it's not using its percentage rule but going on whether or not the point of the mouse has entered the secondary monitor.

Also, I'm open to any kind of solution, including C/C++ or Swift.

EDIT: Here's how I'm currently moving the window:

win.setPosition(x, y);

where win is an instance of Electron's BrowserWindow.

The desired behavior is to move the window anywhere on a display. Currently, if some of the window goes off the edge of the current display enough, it jumps to another display. This is because Apple's default behavior it to automatically move a window to the display with which it overlaps the most and hide it on all other displays. It is worth noting that this is not the behavior when dragging a window manually, but just when moving it programmatically.

3
Would you show code of how do you move window programmatically? This would be the place of fix, I suppose.Asperi
Just added the code snippet.sr3
What is the version of Electron are you using ?CR7
The version is 6.0.5sr3
> This is because Apple's default behavior it to automatically move a window to the display with which it overlaps the most and hide it on all other displays. Does anyone know as a user how to disable this behavior as a user?Andrew Farrell

3 Answers

0
votes

I don't know how you are moving this window, or how you want the window to act when it hits the edge, but I will do my best to give you some solutions.

My first thought was for you to create the bounds of the screen when the program starts. So maybe use GetSystemMetrics(), screen.getPrimaryDisplay(), or perhaps this pseudo-code helps:

var canvas = GetWindowSize()
var canvasWidth = canvas.width
var canvasHeight = canvas.height
if myWindow.x  + mywindow.width > canvasWidth
//if you want it to bounce off the edge
then myWindow.direction = oppositeDirection 
//if you want it to wrap to the other side of the screen
then myWindow.X -= canvasWidth

You should check out this code of a Bouncing DVD Logo. Or look at my Asteroids game to see how ship and asteroids move in the screen.

Furthermore, you can use the distance formula to have the window react to the edge when the center gets close. (Although, this would also require you to get the screen bounds x and y.)

Hope that helps!

EDIT: I think if you were to somehow use the entire screen, (both monitors) your window wouldn't even recognize that there is an edge. That being said, I still don't know what your code actually does.

0
votes

It's not working when you do win.setPosition(x, y); because of mac OS behaviour.
(I also tried with electron quick start project https://github.com/electron/electron-quick-start, even with the same electron version 6.0.5)

What I did is to programmatically simulate a mouse drag of your app to the right of the screen. To do it you can use robotjs. You will not see the dragging effect, which is pretty close to do a setPosition(x, y)
When you install it you could have an issue when starting you app. Then you have to rebuild rebotJS for your version of electron. https://github.com/octalmage/robotjs/wiki/Electron

So what you will try to do programmatically is

enter image description here

Working code

      // get your app bounds and screen bounds
      const windowBounds = mainWindow.getBounds()                         
      const displayBounds = electron.screen.getPrimaryDisplay().bounds   

      setTimeout(() => {                                                  
        // 1. move mouse upon the top left corner your app
        robot.moveMouse(windowBounds.x + 100, windowBounds.y + 5);        
        // 2. mouse left key, 'down'
        robot.mouseToggle("down", "left");                               
        // 3. drag window to (100, 100) from default 
        robot.dragMouse(displayBounds.width, 0);                         
        // 4. mouse left key toggle 'up'
        robot.mouseToggle("up", "left");                                 
      }, 100);

Example in main.js: https://playcode.io/electron

I use those version:

  "devDependencies": {
    "electron": "^6.0.5"
  },
  "dependencies": {
    "robotjs": "^0.6.0"
  }

and my versoon of nodejs is v10.20.0

0
votes

Maybe it's not quite what you asked for, but here's a thought: why not resize the window instead of letting it "overflow" to other screens?

When you predict that window will go out bounds of current display, just reduce its size.

It can be done by something along this lines:

const winWidth = 800;
const winHeight = 600;

const SAFETY_MARGINS = 10;

let mainWindow;

// setPosition //////////////////////////////////////////////////////////////////////////

function setPosition(x, y) {
  const displayBounds = screen.getPrimaryDisplay().bounds
  const fixedX = Math.min(
    displayBounds.x + displayBounds.width,
    Math.max(x, displayBounds.x)
  );
  const fixedY = Math.min(
    displayBounds.y + displayBounds.height,
    Math.max(y, displayBounds.y)
  );
  mainWindow.setPosition(fixedX, fixedY);

  const fixedWidth = Math.max(SAFETY_MARGINS, Math.min(winWidth, displayBounds.width + displayBounds.x - x, winWidth - displayBounds.x + x));
  const fixedHeight = Math.max(SAFETY_MARGINS, Math.min(winHeight, displayBounds.height + displayBounds.y - y, winHeight - displayBounds.y + y));
  mainWindow.setSize(fixedWidth, fixedHeight);
}

This version of setPosition will always resize your window so that it won't go beyond display bounds.

You can modify/extend it to allow window to go out of bounds just a bit and to scroll window's content (if needed).

I believe that with some tweaks, for most users, resizing and moving the window just slightly off the screen would look more-or-less like actually moving the window off the screen.

Here's a demo moving the window randomly using this code: https://github.com/yoava/so-61092503

(I didn't test in with dual screens as I'm only with my laptop now.)