Skip to content

Commit 3cf9959

Browse files
horymuryhristoterezov
authored andcommitted
Make AOT window resizable
- preserve aspect ratio on resize - reset to default size on leave meeting
1 parent 3ced6f1 commit 3cf9959

File tree

3 files changed

+119
-21
lines changed

3 files changed

+119
-21
lines changed

alwaysontop/alwaysontop.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,17 @@ const {
44
ondblclick,
55
onload,
66
onbeforeunload,
7-
shouldImplementDrag
7+
shouldImplementDrag,
8+
getCurrentSize
89
} = window.alwaysOnTop;
910

11+
/**
12+
* Stores the initial size of AOT window before moving.
13+
* This is needed in order to keep the initial size during move on
14+
* monitors with a scaling applied
15+
*/
16+
let initialSize;
17+
1018
window.addEventListener('beforeunload', onbeforeunload);
1119

1220
window.addEventListener('dblclick', ondblclick);
@@ -25,6 +33,7 @@ api._getAlwaysOnTopResources().forEach(src => loadFile(src));
2533
function setupDraggable() {
2634
if (shouldImplementDrag) {
2735
window.addEventListener('mousedown', mouseDownEvent => {
36+
initialSize = getCurrentSize();
2837
pageX = mouseDownEvent.pageX;
2938
pageY = mouseDownEvent.pageY;
3039
window.addEventListener('mousemove', drag);
@@ -55,7 +64,7 @@ function drag(mouseMoveEvent) {
5564
mouseMoveEvent.stopPropagation();
5665
mouseMoveEvent.preventDefault();
5766
const { screenX, screenY } = mouseMoveEvent;
58-
move(screenX - pageX, screenY - pageY);
67+
move(screenX - pageX, screenY - pageY, initialSize);
5968
}
6069

6170
/**

alwaysontop/main.js

Lines changed: 70 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,33 @@
11
const os = require('os');
22
const electron = require('electron');
3+
const robot = require("robotjs");
34
const { BrowserWindow, ipcMain } = electron;
45
const { SIZE } = require('./constants');
56

7+
/**
8+
* The aspect ratio to preserve during AOT window resize
9+
* @type {number}
10+
*/
11+
const ASPECT_RATIO = 16 / 9;
12+
613
/**
714
* The coordinates(x and y) of the always on top window.
815
*
916
* @type {{x: number, y: number}}
1017
*/
1118
let position = {};
1219

20+
/**
21+
* Stores the current size of the AOT during the conference
22+
* @type {{width: number, height: number}}
23+
*/
24+
let size = Object.assign({}, SIZE);
25+
26+
/**
27+
* Keeps the old size of the window between resize handler calls
28+
*/
29+
let oldSize;
30+
1331
/**
1432
* Handles new-window events for the main process in order to customize the
1533
* BrowserWindow options of the always on top window. This handler will be
@@ -34,29 +52,56 @@ function onAlwaysOnTopWindow(
3452
const win = event.newGuest = new BrowserWindow(
3553
Object.assign(options, {
3654
backgroundColor: 'transparent',
37-
width: SIZE.width,
38-
height: SIZE.height,
3955
minWidth: SIZE.width,
4056
minHeight: SIZE.height,
41-
maxWidth: SIZE.width,
42-
maxHeight: SIZE.height,
4357
minimizable: false,
4458
maximizable: false,
45-
resizable: false,
59+
resizable: true,
4660
alwaysOnTop: true,
4761
fullscreen: false,
4862
fullscreenable: false,
4963
skipTaskbar: true,
5064
titleBarStyle: undefined,
5165
frame: false,
5266
show: false
53-
}, getPosition())
67+
}, getPosition(), getSize())
5468
);
5569
win.once('ready-to-show', () => {
5670
if (win && !win.isDestroyed()) {
5771
win.showInactive();
5872
}
5973
});
74+
75+
//for macOS we use the built-in setAspectRatio on resize, for other we use custom implementation
76+
if (os.type() === 'Darwin') {
77+
win.setAspectRatio(ASPECT_RATIO);
78+
} else {
79+
win.on('will-resize', (e, newBounds) => {
80+
oldSize = win.getSize();
81+
const mousePos = robot.getMousePos();
82+
const windowBottomRightPos = {
83+
x: newBounds.x + newBounds.width - 16,
84+
y: newBounds.y + newBounds.height - 16,
85+
};
86+
//prevent resize from bottom right corner as it is buggy.
87+
if (mousePos.x >= windowBottomRightPos.x && mousePos.y >= windowBottomRightPos.y) {
88+
e.preventDefault();
89+
}
90+
});
91+
win.on('resize', () => {
92+
let [width, height] = win.getSize();
93+
//we scale either width or height according to the other by checking which of the 2
94+
//changed the most since last resize.
95+
if (Math.abs(oldSize[0] - width) >= Math.abs(oldSize[1] - height)) {
96+
height = Math.round(width / ASPECT_RATIO);
97+
} else {
98+
width = Math.round(height * ASPECT_RATIO);
99+
}
100+
win.setSize(width, height);
101+
size.width = width;
102+
size.height = height;
103+
});
104+
}
60105
jitsiMeetWindow.webContents.send('jitsi-always-on-top', {
61106
type: 'event',
62107
data: {
@@ -126,7 +171,7 @@ function getPosition () {
126171
// only for windows. On Mac and Linux it is working as expected without
127172
// changing the coordinates.
128173
if (os.platform() === 'win32') {
129-
const windowRectangle = Object.assign({}, position, SIZE);
174+
const windowRectangle = Object.assign({}, position, size);
130175
const matchingScreen = Screen.getDisplayMatching(windowRectangle);
131176
if (matchingScreen) {
132177
return positionWindowWithinScreenBoundaries(
@@ -145,11 +190,24 @@ function getPosition () {
145190
} = Screen.getDisplayNearestPoint(Screen.getCursorScreenPoint()).workArea;
146191

147192
return {
148-
x: x + width - SIZE.width,
193+
x: x + width - size.width,
149194
y
150195
};
151196
}
152197

198+
/**
199+
* Gets the size to be set to the new AOT window.
200+
* This is used in order to preserve the size on close and open of AOT during the same meeting
201+
* @returns {{width: number, height: number}}
202+
*/
203+
function getSize () {
204+
if (typeof size.width === 'number' && typeof size.height === 'number') {
205+
return size;
206+
}
207+
208+
return SIZE;
209+
}
210+
153211
/**
154212
* Initializes the always on top functionality in the main electron process.
155213
*
@@ -166,6 +224,10 @@ module.exports = function setupAlwaysOnTopMain(jitsiMeetWindow) {
166224
y
167225
};
168226
}
227+
228+
if (type === 'event' && data.name === 'resetSize') {
229+
size = Object.assign({}, SIZE);
230+
}
169231
});
170232

171233
jitsiMeetWindow.webContents.on(

alwaysontop/render.js

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ class AlwaysOnTop extends EventEmitter {
9797
* @returns {HTMLElement|undefined} the large video.
9898
*/
9999
get _alwaysOnTopWindowVideo() {
100-
if(!this._alwaysOnTopWindow || !this._alwaysOnTopWindow.document) {
100+
if (!this._alwaysOnTopWindow || !this._alwaysOnTopWindow.document) {
101101
return;
102102
}
103103
return this._alwaysOnTopWindow.document.getElementById('video');
@@ -146,6 +146,20 @@ class AlwaysOnTop extends EventEmitter {
146146
}
147147
}
148148

149+
/**
150+
* Sends reset size command to the main process.
151+
* This is needed in order to reset AOT to the default size after leaving a conference
152+
* @private
153+
*/
154+
_sendResetSize() {
155+
ipcRenderer.send('jitsi-always-on-top', {
156+
type: 'event',
157+
data: {
158+
name: 'resetSize',
159+
}
160+
});
161+
}
162+
149163
/**
150164
* Handles videoConferenceJoined api event.
151165
*
@@ -177,6 +191,7 @@ class AlwaysOnTop extends EventEmitter {
177191
'close',
178192
this._closeAlwaysOnTopWindow
179193
);
194+
this._sendResetSize();
180195
this._closeAlwaysOnTopWindow();
181196
}
182197

@@ -211,7 +226,7 @@ class AlwaysOnTop extends EventEmitter {
211226
* @param {string} type - The type of the message.
212227
* @param {Object} data - The payload of the message.
213228
*/
214-
_onMessageReceived(event, { type, data = {}}) {
229+
_onMessageReceived(event, { type, data = {} }) {
215230
if (type === 'event' && data.name === 'new-window') {
216231
this._alwaysOnTopBrowserWindow
217232
= remote.BrowserWindow.fromId(data.id);
@@ -224,7 +239,7 @@ class AlwaysOnTop extends EventEmitter {
224239
* @returns {void}
225240
*/
226241
_openAlwaysOnTopWindow() {
227-
if(this._alwaysOnTopWindow) {
242+
if (this._alwaysOnTopWindow) {
228243
return;
229244
}
230245
ipcRenderer.on('jitsi-always-on-top', this._onMessageReceived);
@@ -234,7 +249,7 @@ class AlwaysOnTop extends EventEmitter {
234249
// cross-origin redirect can cause any set global variables to be blown
235250
// away.
236251
this._alwaysOnTopWindow = window.open('', 'AlwaysOnTop');
237-
if(!this._alwaysOnTopWindow) {
252+
if (!this._alwaysOnTopWindow) {
238253
return;
239254
}
240255
this._alwaysOnTopWindow.alwaysOnTop = {
@@ -265,15 +280,27 @@ class AlwaysOnTop extends EventEmitter {
265280
* @param x
266281
* @param y
267282
*/
268-
move: (x, y) => {
283+
move: (x, y, initialSize) => {
269284
if (this._alwaysOnTopBrowserWindow) {
270285
this._alwaysOnTopBrowserWindow.setBounds({
271286
x,
272287
y,
273-
width: SIZE.width,
274-
height: SIZE.height
288+
width: initialSize.width,
289+
height: initialSize.height
275290
});
276291
}
292+
},
293+
/**
294+
* Returns the current size of the AOT window
295+
* @returns {{width: number, height: number}}
296+
*/
297+
getCurrentSize: () => {
298+
if (this._alwaysOnTopBrowserWindow) {
299+
const [width, height] = this._alwaysOnTopBrowserWindow.getSize();
300+
return { width, height };
301+
}
302+
303+
return SIZE;
277304
}
278305
};
279306

@@ -298,7 +325,7 @@ class AlwaysOnTop extends EventEmitter {
298325
const scriptTag
299326
= this._alwaysOnTopWindow.document.createElement('script');
300327

301-
scriptTag.setAttribute('src',`file://${ jsPath }`);
328+
scriptTag.setAttribute('src', `file://${ jsPath }`);
302329
this._alwaysOnTopWindow.document.head.appendChild(scriptTag);
303330
};
304331
}
@@ -319,7 +346,7 @@ class AlwaysOnTop extends EventEmitter {
319346
};
320347
}
321348

322-
if(this._alwaysOnTopWindow) {
349+
if (this._alwaysOnTopWindow) {
323350
// we need to check the BrowserWindow reference here because
324351
// window.closed is not reliable due to Electron quirkiness
325352
if(this._alwaysOnTopBrowserWindow && !this._alwaysOnTopBrowserWindow.isDestroyed()) {
@@ -341,11 +368,11 @@ class AlwaysOnTop extends EventEmitter {
341368
* @returns {void}
342369
*/
343370
_updateLargeVideoSrc() {
344-
if(!this._alwaysOnTopWindowVideo) {
371+
if (!this._alwaysOnTopWindowVideo) {
345372
return;
346373
}
347374

348-
if(!this._jitsiMeetLargeVideo) {
375+
if (!this._jitsiMeetLargeVideo) {
349376
this._alwaysOnTopWindowVideo.style.display = 'none';
350377
this._alwaysOnTopWindowVideo.srcObject = null;
351378
} else {

0 commit comments

Comments
 (0)