Path: blob/main/replay/backend/overlays/BaseOverlay.ts
1030 views
import { BrowserView, BrowserWindow } from 'electron';1import IRectangle from '~shared/interfaces/IRectangle';2import Application from '~backend/Application';3import generateContextMenu from '~backend/menus/generateContextMenu';4import Window from '../models/Window';5import Rectangle = Electron.Rectangle;67interface IOptions {8name: string;9bounds?: IRectangle;10calcBounds?: (bounds: IRectangle) => IRectangle;11customHide?: boolean;12webPreferences?: Electron.WebPreferences;13onWindowBoundsUpdate?: () => void;14maxHeight?: number;15}1617export default class BaseOverlay {18public name: string;19public browserWindow: BrowserWindow;20public browserView: BrowserView;21public visible = false;2223public maxHeight = 500;24protected lastHeight = 0;25protected hasNewHeight = true;2627private readonly calcBounds: (bounds: IRectangle) => IRectangle;28private isReady: Promise<void>;2930public constructor(options: IOptions) {31const { name, bounds, calcBounds, webPreferences, maxHeight } = options;32this.browserView = new BrowserView({33webPreferences: {34nodeIntegration: true,35contextIsolation: false,36enableRemoteModule: true,37...webPreferences,38},39});4041this.browserView.setAutoResize({42height: true,43width: true,44});4546this.calcBounds = calcBounds;47this.name = name;48this.maxHeight = maxHeight ?? 500;4950if (bounds) {51this.browserView.setBounds({52x: bounds.x ?? 0,53y: bounds.y ?? 0,54height: bounds.height ?? this.maxHeight,55width: bounds.width,56});57}58this.isReady = this.load();59}6061public get webContents() {62return this.browserView.webContents;63}6465public get id() {66return this.webContents.id;67}6869public show(70browserWindow: BrowserWindow,71options: { focus?: boolean; rect?: IRectangle },72...args: any[]73) {74const { focus = true, rect } = options;75this.browserWindow = browserWindow;7677this.webContents.send('will-show', ...args);78if (this.visible) {79this.rearrange(rect);80return;81}8283// remove first so we can add back on top84browserWindow.addBrowserView(this.browserView);85this.visible = true;86this.rearrange(rect);87if (focus) {88this.webContents.focus();89}90}9192public send(channel: string, ...args: any[]) {93this.webContents.send(channel, ...args);94}9596public hide() {97if (!this.browserWindow || this.browserWindow.isDestroyed()) return;98if (!this.visible) return;99100this.browserWindow.removeBrowserView(this.browserView);101102this.visible = false;103}104105public destroy() {106this.browserView = null;107}108109protected getHeight(): Promise<number> {110return this.webContents.executeJavaScript(`document.querySelector('.Page').offsetHeight`);111}112113protected async adjustHeight() {114const height = await this.getHeight();115116this.hasNewHeight = height !== this.lastHeight;117this.lastHeight = height;118}119120protected rearrange(rect: IRectangle = {}) {121if (!this.visible) return;122123// put on top124this.browserWindow.addBrowserView(this.browserView);125const newRect = roundifyRectangle(this.calcBounds ? this.calcBounds(rect) : rect);126const current = this.browserView.getBounds();127if (128current.height === newRect.height &&129current.width === newRect.width &&130current.x === newRect.x &&131current.y === newRect.y132) {133return;134}135136this.browserView.setBounds(newRect as Rectangle);137}138139private async maximize() {140const bounds = await Window.current.getAvailableBounds();141// inset 10%142const inset = Math.round(bounds.width * 0.1);143bounds.width -= inset;144bounds.x += Math.round(inset / 2);145bounds.y += 50;146bounds.height -= 50 + 28;147this.browserView.setBounds(bounds);148}149150private async load() {151await this.webContents.loadURL(Application.instance.getPageUrl(this.name));152this.webContents.on('ipc-message', (e, message) => {153if (message === 'resize-height') {154this.adjustHeight();155}156if (message === 'zoomin-overlay') {157this.maximize();158}159if (message === 'zoomout-overlay') {160this.rearrange(this.browserView.getBounds());161}162});163164// resize the BrowserView's height when the toolbar height changes165await this.webContents.executeJavaScript(`166const {ipcRenderer} = require('electron');167const resizeObserver = new ResizeObserver(() => {168ipcRenderer.send('resize-height');169});170const elem = document.querySelector('.Page');171resizeObserver.observe(elem);172`);173174if (process.env.NODE_ENV === 'development') {175this.webContents.on('context-menu', (e, params) => {176generateContextMenu(params, this.webContents).popup();177});178}179}180}181182export const roundifyRectangle = (rect: IRectangle): IRectangle => {183const newRect: any = { ...rect };184Object.keys(newRect).forEach(key => {185if (!Number.isNaN(newRect[key])) newRect[key] = Math.round(newRect[key]);186});187return newRect;188};189190191