Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enforce a aspect ratio when resizing for Windows #8036

Closed
szymonc opened this issue Nov 21, 2016 · 20 comments · Fixed by #26941
Closed

Enforce a aspect ratio when resizing for Windows #8036

szymonc opened this issue Nov 21, 2016 · 20 comments · Fixed by #26941

Comments

@szymonc
Copy link

szymonc commented Nov 21, 2016

As in topic. API currently offers fixed ratio only for Mac but not for Windows

@Cleod9
Copy link

Cleod9 commented Mar 17, 2017

Possible workaround/implementation? (16:9 aspect ratio example)

// 'win' as the BrowserWindow instance
win.on('resize', function () {
  setTimeout(function () {
    var size = win.getSize();
    win.setSize(size[0], parseInt(size[0] * 9 / 16));
  }, 0);
});

@szymonc
Copy link
Author

szymonc commented Mar 17, 2017

@Cleod9 did you try this one? We have tried something similar but it causes blinking of window and strange behaviour as it interferes with native resize events. It worked well but just for one edge

@Cleod9
Copy link

Cleod9 commented Mar 17, 2017

@szymonc Yes, I did try it and it works for my purposes on Windows 7. Doesn't look pretty while resizing but not a deal-breaker for me. Also I had to account for the top/bottom windows borders using (size[0] + 60) * 9 / 16 (which may not translate to all versions of windows, need to confirm)

@darkguy2008
Copy link

darkguy2008 commented Mar 1, 2018

Almost 1 year and I can't believe this issue hasn't been fixed yet for Windows. @Cleod9's suggestion works, but it flickers and it looks ugly as heck.

@darkguy2008
Copy link

darkguy2008 commented Mar 1, 2018

After 1 year of nobody watching this... here's my fix. It works and it doesn't flicker as much (it still does, but it's hardly noticeable), somebody please PR this :)

750 = Original height
1200 = Original width

Taken from: https://stackoverflow.com/questions/48043469/how-to-lock-aspect-ratio-while-resizing-the-window

let oldSize;
setInterval(() => {
	oldSize = this._win.getSize();
}, 10);
this._win.on('resize', () => {
	let size = this._win.getSize();
	let widthChanged = oldSize[0] != size[0];
	var ratioY2X = 750 / 1200;
	if (widthChanged)
		this._win.setSize(size[0], parseInt((size[0] * ratioY2X).toString());
	else
		this._win.setSize(parseInt((size[1] / ratioY2X).toString()), size[1]);
});

@bluntspoon
Copy link

Would be nice to see win.setAspectRatio added for Windows? Any plans for this?

@HaulinOats
Copy link

HaulinOats commented Sep 12, 2018

This solution is based on the one proposed by @Cleod9 but with a 'debounce' type function worked well for me, just a little jarring that the height can jump when you're finally done resizing. Not perfect but an alternate solution that feels a little smoother

var resizeTimeout;
mainWindow.on('resize', (e)=>{
    clearTimeout(resizeTimeout);
    resizeTimeout = setTimeout(function(){
        var size = mainWindow.getSize();
        mainWindow.setSize(size[0], parseInt(size[0] * 9 / 16));
    }, 100);
});

@Jacobboogiebear
Copy link

Based upon the upper answers, I made a npm package for this reason!
https://www.npmjs.com/package/electron-aspectratio

@HaulinOats
Copy link

@Jacobboogiebear Cool, I'll give this a try in the next couple days and report back!

@HaulinOats
Copy link

@Jacobboogiebear Do you know if there's a way to prevent 'resize' from triggering when just moving the window? I don't want to stop the ability to move the window, but for some reason, Electron triggers a resize on move. It's bizarre.

@Jacobboogiebear
Copy link

@HaulinOats I don't know how to deal with that but I will put some time in to study it and report back

@HaulinOats
Copy link

HaulinOats commented Jul 5, 2019

@Jacobboogiebear Turns out doing a window move actually triggers a resize, by default, in Electron. I opened a separate thread about this but it hasn't be resolved or addressed yet.

However, I found this solution on another thread that prevents the weird incremental resize glitch when moving the window (stays exact same size) while also removing the timeout I put, making the resize process a little bit faster. Basically, a window move attempts a resize by an increment of one pixel, so comparing the previous tmpSize values to the current size values, and ensuring it's smaller than 2 pixels, prevents a resize on window move.

     var tmpSize = [0,0];

     mainWindow.on('resize', (e)=>{
        var size = mainWindow.getSize()
        if( Math.abs(size[0]-tmpSize[0]) > 2 || Math.abs(size[1]-tmpSize[1]) > 2){
          mainWindow.setSize(size[0], parseInt(size[0] * 9.4 / 16))
        }
        tmpSize = size;
    });

@Jip-Hop
Copy link

Jip-Hop commented Jan 4, 2020

I'm using this approach to enforce 1:1 ratio on Windows. It works quite nicely for the right and bottom sides. The other two are a bit weird :P

mainWindow.on("will-resize", maintainAspectRatio);

function maintainAspectRatio(event, newBounds){
  const win = event.sender;
  if(newBounds.width !== newBounds.height){
    event.preventDefault();

    const currentSize = win.getSize();
    const widthChanged = currentSize[0] !== newBounds.width;
    if(widthChanged){
      win.setSize(newBounds.width, newBounds.width);
    } else {
      win.setSize(newBounds.height, newBounds.height);
    }
  }
}

@Jip-Hop
Copy link

Jip-Hop commented Jan 4, 2020

This one works better for resizing from the top and left sides. Still not perfect because the window may start to move when resizing from those sides.

mainWindow.on("will-resize", maintainAspectRatio);
function maintainAspectRatio(event, newBounds) {
  if (newBounds.width !== newBounds.height) {
    event.preventDefault();
    
    const win = event.sender;
    const currentSize = win.getSize();
    const widthChanged = currentSize[0] !== newBounds.width;
    if (widthChanged) {
      newBounds.height = newBounds.width;
    } else {
      newBounds.width = newBounds.height;
    }
    win.setBounds(newBounds);
  }
}

@jaehayoo
Copy link

@nornagon @codebytere @MadfishDT I hope #18306 will patch and merge to master.

@jaehayoo
Copy link

jaehayoo commented Apr 7, 2020

@nornagon I think you have solution since you commented #18306. Do you have any plans?

@asc0910
Copy link

asc0910 commented Jun 24, 2020

After spend some time I found this solution.
It works perfect on windows preserving aspect ratio.

enum Directions {
  LEFT = 1,
  RIGHT = 2,
  TOP = 3,
  LEFTTOP = 4,
  RIGHTTOP = 5,
  BOTTOM = 6,
  BOTTOMLEFT = 7,
  BOTTOMRIGHT = 8
}

const aspectRatio = 16 / 9;
const extraWidth = 16;
const extraHeight = 48;

function getHeight(aspectRatio: number, width: number): number {
  return Math.max(
    MIN_HEIGHT,
    Math.floor((width - extraWidth) / aspectRatio + extraHeight)
  );
}

function getWidth(aspectRatio: number, height: number): number {
  return Math.max(
    MIN_WIDTH,
    Math.floor((height - extraHeight) * aspectRatio + extraWidth)
  );
}


if (process.platform === 'win32') {
  let resizeDirection: Directions;

  window.hookWindowMessage(0x0214, (wParam: Buffer) => {
    resizeDirection = wParam.readUIntBE(0, 1);
  });

  window?.on('will-resize', (event, newBounds) => {
    
    if (aspectRatio && window) {
      event.preventDefault();
      let temp_width;
      let temp_height;
      const toBounds = { ...newBounds };
      switch (resizeDirection) {
        case Directions.LEFT:
        case Directions.RIGHT:
          toBounds.height = getHeight(
            aspectRatio,
            newBounds.width
          );
          break;
        case Directions.TOP:
        case Directions.BOTTOM:
          toBounds.width = getWidth(aspectRatio, newBounds.height);
          break;
        case Directions.BOTTOMLEFT:
        case Directions.BOTTOMRIGHT:
        case Directions.LEFTTOP:
        case Directions.RIGHTTOP:
          toBounds.width = getWidth(aspectRatio, newBounds.height);
          temp_width = newBounds.width;
          temp_height = getHeight(aspectRatio, temp_width);
          if (
            temp_width * temp_height >
            toBounds.width * toBounds.height
          ) {
            toBounds.width = temp_width;
            toBounds.height = temp_height;
          }
          break;
        default:
      }
      switch (resizeDirection) {
        case Directions.BOTTOMLEFT:
          toBounds.x = newBounds.x + newBounds.width - toBounds.width;
          break;
        case Directions.LEFTTOP:
          toBounds.x = newBounds.x + newBounds.width - toBounds.width;
          toBounds.y = newBounds.y + newBounds.height - toBounds.height;
          break;
        case Directions.RIGHTTOP:
          toBounds.y = newBounds.y + newBounds.height - toBounds.height;
          break;
        default:
      }
      window.setBounds(toBounds);
    }
  });
}
  

@jaehayoo
Copy link

@asc0910 Great and Awesome!!!

@swimauger
Copy link

swimauger commented Jun 25, 2020

Plenty of great answers in here, thought I would give my two cents anyway. You could use this RatioWindow wrapper of BrowserWindow instead of BrowserWindow to maintain aspect ratio.

RatioWindow.js

const { BrowserWindow } = require('electron');

function __handleWillResize(event, newSize) {
    event.preventDefault();
    event.sender.setSize(newSize.width, parseInt(newSize.width * this.height / this.width));
}

class RatioWindow extends BrowserWindow {
    constructor(options) {
        super(options);
        this.on('will-resize', __handleWillResize.bind(options));
    }
}

module.exports = RatioWindow;

app.js

const { app } = require('electron');
const RatioWindow = require('./RatioWindow');

app.whenReady().then(function() {
    let win = new RatioWindow({
        width: 1280,
        height: 720,
        webPreferences: {
            nodeIntegration: true
        }
    });

    win.loadFile(/* Some File */);
});

@erickzhao erickzhao removed their assignment Jun 25, 2020
@antonfisher
Copy link

I've combined @asc0910 and @swimauger approaches (thanks!), added support for different screen scales on Windows (100-125-150%), and published as a drop-in replacement module for BrowserWindow: https://github.com/antonfisher/electron-aspect-ratio-browser-window. The most reliable workaround I've found so far for the issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.