Reimplement offscreen window for more reliable WebContentsView visibility on load

This commit is contained in:
Max Goodhart
2025-07-04 18:58:46 -07:00
parent b9a97114a5
commit c0f9936d6b
2 changed files with 27 additions and 8 deletions

View File

@@ -138,7 +138,10 @@ export default class StreamWindow extends EventEmitter<StreamWindowEventMap> {
} }
createView() { createView() {
const { win } = this const {
win,
config: { width, height },
} = this
assert(win != null, 'Window must be initialized') assert(win != null, 'Window must be initialized')
const { backgroundColor } = this.config const { backgroundColor } = this.config
const view = new WebContentsView({ const view = new WebContentsView({
@@ -159,11 +162,19 @@ export default class StreamWindow extends EventEmitter<StreamWindowEventMap> {
ev.preventDefault() ev.preventDefault()
}) })
// Hidden window used for loading the BrowserView before it's positioned in the wall
const offscreenWin = new BrowserWindow({
width,
height,
show: false,
})
const actor = createActor(viewStateMachine, { const actor = createActor(viewStateMachine, {
input: { input: {
id: viewId, id: viewId,
view, view,
win, win,
offscreenWin,
}, },
}) })
@@ -277,9 +288,12 @@ export default class StreamWindow extends EventEmitter<StreamWindowEventMap> {
newViews.set(view.getSnapshot().context.id, view) newViews.set(view.getSnapshot().context.id, view)
} }
for (const view of unusedViews) { for (const view of unusedViews) {
const contentView = view.getSnapshot().context.view view.stop()
const { view: contentView, offscreenWin } = view.getSnapshot().context
offscreenWin.contentView.removeChildView(contentView)
win.contentView.removeChildView(contentView) win.contentView.removeChildView(contentView)
contentView.webContents.close() contentView.webContents.close()
offscreenWin.destroy()
} }
this.views = newViews this.views = newViews
this.emitState() this.emitState()

View File

@@ -21,11 +21,13 @@ const viewStateMachine = setup({
id: number id: number
view: WebContentsView view: WebContentsView
win: BrowserWindow win: BrowserWindow
offscreenWin: BrowserWindow
}, },
context: {} as { context: {} as {
id: number id: number
win: BrowserWindow win: BrowserWindow
offscreenWin: BrowserWindow
view: WebContentsView view: WebContentsView
pos: ViewPos | null pos: ViewPos | null
content: ViewContent | null content: ViewContent | null
@@ -83,20 +85,22 @@ const viewStateMachine = setup({
}, },
offscreenView: ({ context }) => { offscreenView: ({ context }) => {
const { view, win } = context const { view, win, offscreenWin } = context
win.contentView.addChildView(view, 0) // Insert below background (so hidden by background) win.contentView.removeChildView(view)
const { width, height } = win.getBounds() offscreenWin.contentView.addChildView(view)
const { width, height } = offscreenWin.getBounds()
view.setBounds({ x: 0, y: 0, width, height }) view.setBounds({ x: 0, y: 0, width, height })
}, },
positionView: ({ context }) => { positionView: ({ context }) => {
const { pos, view, win } = context const { pos, view, win, offscreenWin } = context
if (!pos) { if (!pos) {
return return
} }
win.contentView.addChildView(view, win.contentView.children.length - 2) // Insert below overlay but above background offscreenWin.contentView.removeChildView(view)
win.contentView.addChildView(view, win.contentView.children.length - 1) // Insert below overlay but above background
view.setBounds(pos) view.setBounds(pos)
}, },
}, },
@@ -148,10 +152,11 @@ const viewStateMachine = setup({
}).createMachine({ }).createMachine({
id: 'view', id: 'view',
initial: 'empty', initial: 'empty',
context: ({ input: { id, view, win } }) => ({ context: ({ input: { id, view, win, offscreenWin } }) => ({
id, id,
view, view,
win, win,
offscreenWin,
pos: null, pos: null,
content: null, content: null,
options: null, options: null,