154 lines
3.6 KiB
JavaScript
154 lines
3.6 KiB
JavaScript
const { EventEmitter } = require('events')
|
|
const { WebContentsView } = require('electron')
|
|
|
|
const toolbarHeight = 64
|
|
|
|
class Tab {
|
|
constructor(parentWindow, wcvOpts = {}) {
|
|
this.invalidateLayout = this.invalidateLayout.bind(this)
|
|
|
|
// Delete undefined properties which cause WebContentsView constructor to
|
|
// throw. This should probably be fixed in Electron upstream.
|
|
if (wcvOpts.hasOwnProperty('webContents') && !wcvOpts.webContents) delete wcvOpts.webContents
|
|
if (wcvOpts.hasOwnProperty('webPreferences') && !wcvOpts.webPreferences)
|
|
delete wcvOpts.webPreferences
|
|
|
|
this.view = new WebContentsView(wcvOpts)
|
|
this.id = this.view.webContents.id
|
|
this.window = parentWindow
|
|
this.webContents = this.view.webContents
|
|
this.window.contentView.addChildView(this.view)
|
|
}
|
|
|
|
destroy() {
|
|
if (this.destroyed) return
|
|
|
|
this.destroyed = true
|
|
|
|
this.hide()
|
|
|
|
this.window.contentView.removeChildView(this.view)
|
|
this.window = undefined
|
|
|
|
if (!this.webContents.isDestroyed()) {
|
|
if (this.webContents.isDevToolsOpened()) {
|
|
this.webContents.closeDevTools()
|
|
}
|
|
|
|
// TODO: why is this no longer called?
|
|
this.webContents.emit('destroyed')
|
|
|
|
this.webContents.destroy()
|
|
}
|
|
|
|
this.webContents = undefined
|
|
this.view = undefined
|
|
}
|
|
|
|
loadURL(url) {
|
|
return this.view.webContents.loadURL(url)
|
|
}
|
|
|
|
show() {
|
|
this.invalidateLayout()
|
|
this.startResizeListener()
|
|
this.view.setVisible(true)
|
|
}
|
|
|
|
hide() {
|
|
this.stopResizeListener()
|
|
this.view.setVisible(false)
|
|
}
|
|
|
|
reload() {
|
|
this.view.webContents.reload()
|
|
}
|
|
|
|
invalidateLayout() {
|
|
const [width, height] = this.window.getSize()
|
|
const padding = 4
|
|
this.view.setBounds({
|
|
x: padding,
|
|
y: toolbarHeight,
|
|
width: width - padding * 2,
|
|
height: height - toolbarHeight - padding,
|
|
})
|
|
this.view.setBorderRadius(8)
|
|
}
|
|
|
|
// Replacement for BrowserView.setAutoResize. This could probably be better...
|
|
startResizeListener() {
|
|
this.stopResizeListener()
|
|
this.window.on('resize', this.invalidateLayout)
|
|
}
|
|
stopResizeListener() {
|
|
this.window.off('resize', this.invalidateLayout)
|
|
}
|
|
}
|
|
|
|
class Tabs extends EventEmitter {
|
|
tabList = []
|
|
selected = null
|
|
|
|
constructor(browserWindow) {
|
|
super()
|
|
this.window = browserWindow
|
|
}
|
|
|
|
destroy() {
|
|
this.tabList.forEach((tab) => tab.destroy())
|
|
this.tabList = []
|
|
|
|
this.selected = undefined
|
|
|
|
if (this.window) {
|
|
this.window.destroy()
|
|
this.window = undefined
|
|
}
|
|
}
|
|
|
|
get(tabId) {
|
|
return this.tabList.find((tab) => tab.id === tabId)
|
|
}
|
|
|
|
create(webContentsViewOptions) {
|
|
const tab = new Tab(this.window, webContentsViewOptions)
|
|
this.tabList.push(tab)
|
|
if (!this.selected) this.selected = tab
|
|
tab.show() // must be attached to window
|
|
this.emit('tab-created', tab)
|
|
this.select(tab.id)
|
|
return tab
|
|
}
|
|
|
|
remove(tabId) {
|
|
const tabIndex = this.tabList.findIndex((tab) => tab.id === tabId)
|
|
if (tabIndex < 0) {
|
|
throw new Error(`Tabs.remove: unable to find tab.id = ${tabId}`)
|
|
}
|
|
const tab = this.tabList[tabIndex]
|
|
this.tabList.splice(tabIndex, 1)
|
|
tab.destroy()
|
|
if (this.selected === tab) {
|
|
this.selected = undefined
|
|
const nextTab = this.tabList[tabIndex] || this.tabList[tabIndex - 1]
|
|
if (nextTab) this.select(nextTab.id)
|
|
}
|
|
this.emit('tab-destroyed', tab)
|
|
if (this.tabList.length === 0) {
|
|
this.destroy()
|
|
}
|
|
}
|
|
|
|
select(tabId) {
|
|
const tab = this.get(tabId)
|
|
if (!tab) return
|
|
if (this.selected) this.selected.hide()
|
|
tab.show()
|
|
this.selected = tab
|
|
this.emit('tab-selected', tab)
|
|
}
|
|
}
|
|
|
|
exports.Tabs = Tabs
|