SDA-4049 Taking screenshot - hiding fullscreen window (#1690)

This commit is contained in:
Salah Benmoussati 2023-02-08 11:33:48 +01:00 committed by GitHub
parent 20f0d92152
commit 3fbe9a9e63
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 202 additions and 82 deletions

2
package-lock.json generated
View File

@ -36499,4 +36499,4 @@
}
}
}
}
}

View File

@ -4,6 +4,7 @@ import {
clipboard,
dialog,
ipcMain,
NativeImage,
nativeImage,
WebContents,
} from 'electron';
@ -161,6 +162,7 @@ class ScreenSnippet {
await this.execCmd(this.captureUtil, this.captureUtilArgs);
if (windowHandler.isMana) {
winStore.restoreWindows(hideOnCapture);
logger.info(
'screen-snippet-handler: Attempting to extract image dimensions from: ' +
this.outputFilePath,
@ -177,7 +179,6 @@ class ScreenSnippet {
if (dimensions.width === 0 && dimensions.height === 0) {
logger.info('screen-snippet-handler: no screen capture picture');
winStore.restoreWindowsOnCapturing(hideOnCapture);
return;
}
@ -356,7 +357,7 @@ class ScreenSnippet {
'screen-snippet-handler: Snippet uploaded correctly, sending payload to SFE',
);
webContents.send('screen-snippet-data', payload);
winStore.focusWindowsSnippingFinished(hideOnCapture);
winStore.restoreWindows(hideOnCapture);
await this.verifyAndUpdateAlwaysOnTop();
} catch (error) {
await this.verifyAndUpdateAlwaysOnTop();
@ -432,6 +433,9 @@ class ScreenSnippet {
clipboard: string;
},
) => {
if (isMac) {
windowHandler.closeSnippingToolWindow();
}
const filePath = path.join(
app.getPath('downloads'),
'symphonyImage-' + Date.now() + '.png',
@ -439,46 +443,11 @@ class ScreenSnippet {
const [, data] = saveAsData.clipboard.split(',');
const buffer = Buffer.from(data, 'base64');
const img = nativeImage.createFromBuffer(buffer);
const dialogResult = await dialog
.showSaveDialog(BrowserWindow.getFocusedWindow() as BrowserWindow, {
title: 'Select place to store your file',
defaultPath: filePath,
// defaultPath: path.join(__dirname, '../assets/'),
buttonLabel: 'Save',
// Restricting the user to only Text Files.
filters: [
{
name: 'Image file',
extensions: ['png'],
},
],
properties: [],
})
.then((file) => {
// Stating whether dialog operation was cancelled or not.
if (!file.canceled && file.filePath) {
// Creating and Writing to the sample.txt file
fs.writeFile(file.filePath.toString(), img.toPNG(), (err) => {
if (err) {
throw logger.error(
`screen-snippet-handler: cannot save file, failed with error: ${err}!`,
);
}
logger.info(`screen-snippet-handler: modal save opened!`);
});
}
return file;
})
.catch((err) => {
logger.error(
`screen-snippet-handler: cannot save file, failed with error: ${err}!`,
);
return undefined;
});
const dialogResult = await this.saveFile(
filePath,
img,
BrowserWindow.getFocusedWindow(),
);
if (dialogResult?.filePath) {
windowHandler.closeSnippingToolWindow();
}
@ -496,7 +465,6 @@ class ScreenSnippet {
const windowObj = winStore.getWindowStore();
const currentWindowName = (currentWindowObj as ICustomBrowserWindow)
?.winName;
if (windowObj.windows.length < 1) {
const allWindows = BrowserWindow.getAllWindows();
let windowsArr: IWindowState[] = [];
@ -531,12 +499,67 @@ class ScreenSnippet {
} else {
windowsArr = windowsArr.concat(mainArr);
}
winStore.setWindowStore({
windows: windowsArr,
});
}
};
/**
* Save image in a given location
* @param filePath where the image should be stored
* @param img the image
* @param parent parent window to attach save dialog
*/
private saveFile = (
filePath: string,
img: NativeImage,
parentWindow: BrowserWindow | null,
) => {
const saveOptions = {
title: 'Select place to store your file',
defaultPath: filePath,
// defaultPath: path.join(__dirname, '../assets/'),
buttonLabel: 'Save',
// Restricting the user to only Text Files.
filters: [
{
name: 'Image file',
extensions: ['png'],
},
],
properties: [],
};
const saveDialog =
!isMac && parentWindow
? dialog.showSaveDialog(parentWindow, saveOptions)
: dialog.showSaveDialog(saveOptions);
return saveDialog
.then((file) => {
// Stating whether dialog operation was cancelled or not.
if (!file.canceled && file.filePath) {
// Creating and Writing to the sample.txt file
fs.writeFile(file.filePath.toString(), img.toPNG(), (err) => {
if (err) {
throw logger.error(
`screen-snippet-handler: cannot save file, failed with error: ${err}!`,
);
}
logger.info(`screen-snippet-handler: modal save opened!`);
});
}
return file;
})
.catch((err) => {
logger.error(
`screen-snippet-handler: cannot save file, failed with error: ${err}!`,
);
return undefined;
});
};
}
const screenSnippet = new ScreenSnippet();

View File

@ -1,4 +1,6 @@
import { BrowserWindow } from 'electron';
import { isMac, isWindowsOS } from '../../common/env';
import { ICustomBrowserWindow, windowHandler } from '../window-handler';
import { getWindowByName } from '../window-utils';
export interface IWindowObject {
@ -9,9 +11,11 @@ export interface IWindowState {
id: string;
minimized?: boolean;
focused?: boolean;
isFullScreen?: boolean;
}
export class WindowStore {
public windowsRestored: boolean = true;
private windowVariable: IWindowObject = {
windows: [],
};
@ -35,47 +39,112 @@ export class WindowStore {
public hideWindowsOnCapturing = (hideOnCapture?: boolean) => {
if (hideOnCapture) {
this.windowsRestored = false;
const currentWindows = BrowserWindow.getAllWindows();
currentWindows.forEach((currentWindow) => {
currentWindow?.hide();
const isFullScreen = currentWindow.isFullScreen();
if (isFullScreen) {
this.hideFullscreenWindow(currentWindow);
} else {
currentWindow?.hide();
}
});
}
};
public focusWindowsSnippingFinished = (hideOnCapture?: boolean) => {
public restoreWindows = (hideOnCapture?: boolean) => {
if (hideOnCapture) {
const currentWindows = this.getWindowStore();
const currentWindow = currentWindows.windows.find(
const storedWindows = this.getWindowStore();
let currentWindow = storedWindows.windows.find(
(currentWindow) => currentWindow.focused,
);
if (currentWindow) {
if (!currentWindow.minimized) {
getWindowByName(currentWindow.id || '')?.show();
}
if (currentWindow.focused) {
getWindowByName(currentWindow.id || '')?.focus();
}
if (!currentWindow) {
// In case there is no window focused, we automatically focus on the main one.
currentWindow = storedWindows.windows.find(
(currentWindow) => currentWindow.id === 'main',
);
currentWindow!.focused = true;
}
}
};
public restoreWindowsOnCapturing = (hideOnCapture?: boolean) => {
if (hideOnCapture) {
const currentWindows = this.getWindowStore();
currentWindows.windows.forEach((currentWindow) => {
if (!currentWindow.minimized) {
getWindowByName(currentWindow.id || '')?.show();
}
let focusedWindowToRestore: ICustomBrowserWindow | undefined;
if (currentWindow.focused) {
getWindowByName(currentWindow.id || '')?.focus();
const fullscreenedWindows: IWindowState[] = [];
// Restoring all windows except focused one
storedWindows.windows.forEach((currentWindow) => {
if (currentWindow) {
const window: ICustomBrowserWindow | undefined = getWindowByName(
currentWindow.id || '',
) as ICustomBrowserWindow;
if (window) {
if (currentWindow.isFullScreen) {
fullscreenedWindows.push(currentWindow);
// Window should be shown before putting it in fullscreen on Windows
if (isWindowsOS) {
window.show();
}
} else if (!currentWindow.minimized && !currentWindow.focused) {
window.showInactive();
}
if (currentWindow.focused) {
focusedWindowToRestore = window;
}
}
}
});
// First item in array should be the focused window
fullscreenedWindows.sort((x: IWindowState, y: IWindowState) =>
x.focused === y.focused ? 0 : x.focused ? -1 : 1,
);
this.putWindowInFullScreenAndFocus(
fullscreenedWindows,
focusedWindowToRestore,
);
// Store reset
this.destroyWindowStore();
}
};
private hideFullscreenWindow = (window: BrowserWindow) => {
window.once('leave-full-screen', () => {
if (isMac) {
window.hide();
} else {
setTimeout(() => {
window.hide();
}, 0);
}
});
window.setFullScreen(false);
};
/**
* Restores windows that are in fullscreen and focus on the right window
* On macOS, windows in fullscreen need to be restore one by one
* @param windowsNames
*/
private putWindowInFullScreenAndFocus(
windows: IWindowState[],
windowToFocus?: BrowserWindow,
) {
if (windows.length) {
const windowDetails = windows[windows.length - 1];
const window: ICustomBrowserWindow | undefined = getWindowByName(
windowDetails.id || '',
) as ICustomBrowserWindow;
window.once('enter-full-screen', () => {
windows.pop();
this.putWindowInFullScreenAndFocus(windows, windowToFocus);
});
window.setFullScreen(true);
} else {
if (windowToFocus) {
windowToFocus?.show();
windowHandler.moveSnippingToolWindow(windowToFocus);
}
this.windowsRestored = true;
}
}
}

View File

@ -1057,11 +1057,18 @@ export class WindowHandler {
}
/**
* Move window to the same screen as main window
* Move window to the same screen as main window or provided parent window
*/
public moveWindow(windowToMove: BrowserWindow, fixedYPosition?: number) {
public moveWindow(
windowToMove: BrowserWindow,
fixedYPosition?: number,
parentWindow?: BrowserWindow,
) {
if (this.mainWindow && windowExists(this.mainWindow)) {
const display = screen.getDisplayMatching(this.mainWindow.getBounds());
let display = screen.getDisplayMatching(this.mainWindow.getBounds());
if (parentWindow && windowExists(parentWindow)) {
display = screen.getDisplayMatching(parentWindow.getBounds());
}
logger.info(
'window-handler: moveWindow, display: ' +
@ -1238,7 +1245,7 @@ export class WindowHandler {
this.hideOnCapture = !!hideOnCapture;
logger.info(
'window-handler, createSnippingToolWindow: Receiving snippet props: ' +
'window-handler: createSnippingToolWindow: Receiving snippet props: ' +
JSON.stringify({
snipImage,
snipDimensions,
@ -1247,7 +1254,7 @@ export class WindowHandler {
const allDisplays = screen.getAllDisplays();
logger.info(
'window-handler, createSnippingToolWindow: User has these displays: ' +
'window-handler: createSnippingToolWindow: User has these displays: ' +
JSON.stringify(allDisplays),
);
@ -1263,7 +1270,10 @@ export class WindowHandler {
const BUTTON_BAR_BOTTOM_HEIGHT = 72;
const BUTTON_BARS_HEIGHT = BUTTON_BAR_TOP_HEIGHT + BUTTON_BAR_BOTTOM_HEIGHT;
const display = screen.getDisplayMatching(this.mainWindow.getBounds());
const focusedWindow = BrowserWindow.getFocusedWindow();
const display = screen.getDisplayMatching(
focusedWindow ? focusedWindow.getBounds() : this.mainWindow.getBounds(),
);
const workAreaSize = display.workAreaSize;
// Snipping tool height shouldn't be greater than min of screen width and height (screen orientation can be portrait or landscape)
const minSize = Math.min(workAreaSize.width, workAreaSize.height);
@ -1277,7 +1287,7 @@ export class WindowHandler {
width: Math.floor(snipDimensions.width / scaleFactor),
};
logger.info(
'window-handler, createSnippingToolWindow: Image will open with scaled dimensions: ' +
'window-handler: createSnippingToolWindow: Image will open with scaled dimensions: ' +
JSON.stringify(scaledImageDimensions),
);
@ -1314,12 +1324,12 @@ export class WindowHandler {
}
this.currentWindow = currentWindow || '';
const parentWindow = getWindowByName(this.currentWindow);
const opts: ICustomBrowserWindowConstructorOpts = this.getWindowOpts(
{
width: toolWidth,
height: toolHeight,
parent: getWindowByName(this.currentWindow),
modal: true,
modal: isWindowsOS,
alwaysOnTop: this.hideOnCapture,
resizable: false,
fullscreenable: false,
@ -1337,8 +1347,16 @@ export class WindowHandler {
opts.alwaysOnTop = true;
}
const areWindowsRestoredPostHide =
(winStore.windowsRestored && this.hideOnCapture) || !this.hideOnCapture;
if (isWindowsOS || (isMac && areWindowsRestoredPostHide)) {
opts.parent = parentWindow;
opts.modal = true;
}
this.snippingToolWindow = createComponentWindow('snipping-tool', opts);
this.moveWindow(this.snippingToolWindow);
this.moveWindow(this.snippingToolWindow, undefined, parentWindow);
this.snippingToolWindow.setVisibleOnAllWorkspaces(true);
this.snippingToolWindow.webContents.once('did-finish-load', async () => {
@ -1391,12 +1409,11 @@ export class WindowHandler {
'snipping-tool-data',
snippingToolInfo,
);
winStore.restoreWindowsOnCapturing(this.hideOnCapture);
}
});
this.snippingToolWindow.once('close', () => {
logger.info(
'window-handler, createSnippingToolWindow: Closing snipping window, attempting to delete temp snip image',
'window-handler: createSnippingToolWindow: Closing snipping window, attempting to delete temp snip image',
);
ipcMain.removeAllListeners(ScreenShotAnnotation.COPY_TO_CLIPBOARD);
ipcMain.removeAllListeners(ScreenShotAnnotation.SAVE_AS);
@ -1404,7 +1421,6 @@ export class WindowHandler {
this.deleteFile(snipImage);
this.removeWindow(opts.winKey);
this.screenPickerWindow = null;
winStore.focusWindowsSnippingFinished(this.hideOnCapture);
});
}
@ -2215,6 +2231,18 @@ export class WindowHandler {
exportLogs();
}
/**
* Moves snipping tool to the right place after restoring all hidden windows
* @param focusedWindow Focused window where snipping tool window should be moved
*/
public moveSnippingToolWindow(focusedWindow: BrowserWindow): void {
if (this.snippingToolWindow && !this.snippingToolWindow.isDestroyed()) {
this.snippingToolWindow.setAlwaysOnTop(true);
this.snippingToolWindow.setParentWindow(focusedWindow);
this.moveWindow(this.snippingToolWindow, undefined, focusedWindow);
}
}
/**
* Listens for app load timeouts and reloads if required
*/