energy/cef/cef-base-browser-window.go

526 lines
15 KiB
Go
Raw Normal View History

2022-10-04 13:21:05 +08:00
//----------------------------------------
//
// Copyright © yanghy. All Rights Reserved.
//
2022-10-04 16:38:43 +08:00
// Licensed under GNU General Public License v3.0
2022-10-04 13:21:05 +08:00
//
//----------------------------------------
package cef
import (
2022-10-04 22:34:57 +08:00
. "github.com/energye/energy/commons"
. "github.com/energye/energy/consts"
"github.com/energye/energy/ipc"
"github.com/energye/energy/logger"
2022-10-04 13:21:05 +08:00
"github.com/energye/golcl/lcl"
"github.com/energye/golcl/lcl/api"
"github.com/energye/golcl/lcl/types"
)
type IBaseWindow interface {
lcl.IWinControl
FormCreate()
WindowParent() ITCefWindow
Chromium() IChromium
ChromiumCreate(config *tCefChromiumConfig, defaultUrl string)
registerEvent()
registerDefaultEvent()
}
2022-10-05 16:38:43 +08:00
//BaseWindow 是一个基于chromium 和 lcl 的窗口组件
2022-10-04 13:21:05 +08:00
type BaseWindow struct {
*lcl.TForm //
chromium *TCEFChromium //
windowParent ITCefWindow //
windowInfo *TCefWindowInfo //窗口信息
windowId int32 //
windowType WINDOW_TYPE //0:browser 1:devTools 2:viewSource 默认:0
isClosing bool //
canClose bool //
onResize []TNotifyEvent //
onActivate []TNotifyEvent //
onShow []TNotifyEvent //
onClose []TCloseEvent //
onCloseQuery []TCloseQueryEvent //
onActivateAfter lcl.TNotifyEvent //
isFormCreate bool //是否创建完成 WindowForm
isChromiumCreate bool //是否创建完成 Chromium
isMainWindow bool //是否为主窗口
}
2022-10-05 16:38:43 +08:00
// 自定义窗口组件
2022-10-04 13:21:05 +08:00
type Window struct {
BaseWindow
defaultUrl string
config *tCefChromiumConfig
}
//创建一个新window窗口
func NewWindow() *Window {
var window = &Window{}
//window.TForm = lcl.NewForm(owner)
lcl.Application.CreateForm(&window)
window.ParentDoubleBuffered()
window.FormCreate()
window.SetNotInTaskBar()
window.defaultWindowEvent()
return window
}
2022-10-05 16:38:43 +08:00
//创建一个带有 chromium 窗口
2022-10-04 13:21:05 +08:00
//
//该窗口默认不具备默认事件处理能力, 通过 EnableDefaultEvent 函数注册事件处理
func NewBrowserWindow(config *tCefChromiumConfig, defaultUrl string) *Window {
var window = NewWindow()
window.ChromiumCreate(config, defaultUrl)
window.putChromiumWindowInfo()
//BeforeBrowser是一个必须的默认事件在浏览器创建时窗口序号会根据browserId生成
window.Chromium().SetOnBeforeBrowser(func(sender lcl.IObject, browser *ICefBrowser, frame *ICefFrame) bool { return false })
return window
}
//返回完整的chromium对象
func (m *Window) Chromium() IChromium {
return m.chromium
}
//启用默认关闭事件行为-该窗口将被关闭
func (m *Window) EnableDefaultClose() {
m.defaultWindowCloseEvent()
m.registerDefaultChromiumCloseEvent()
}
//启用所有默认事件行为
func (m *Window) EnableAllDefaultEvent() {
m.defaultWindowCloseEvent()
m.defaultChromiumEvent()
}
func (m *BaseWindow) Show() {
m.TForm.Show()
}
func (m *BaseWindow) Hide() {
m.TForm.Hide()
}
func (m *BaseWindow) Visible() bool {
return m.TForm.Visible()
}
func (m *BaseWindow) SetVisible(value bool) {
m.TForm.SetVisible(value)
}
func (m *BaseWindow) WindowInfo() *TCefWindowInfo {
return m.windowInfo
}
func (m *BaseWindow) SetDefaultInTaskBar() {
m.TForm.SetShowInTaskBar(types.StDefault)
}
func (m *BaseWindow) SetShowInTaskBar() {
m.TForm.SetShowInTaskBar(types.StAlways)
}
func (m *BaseWindow) SetNotInTaskBar() {
m.TForm.SetShowInTaskBar(types.StNever)
}
func (m *BaseWindow) WindowParent() ITCefWindow {
return m.windowParent
}
//返回窗口关闭状态
func (m *BaseWindow) IsClosing() bool {
return m.isClosing
}
// 窗口类型
//
//0:browser 1:devTools 2:viewSource 默认:0
func (m *BaseWindow) SetWindowType(windowType WINDOW_TYPE) {
m.windowType = windowType
}
// 创建窗口浏览器组件
//
// 不带有默认事件的chromium
func (m *BaseWindow) ChromiumCreate(config *tCefChromiumConfig, defaultUrl string) {
if m.isChromiumCreate {
return
}
m.isChromiumCreate = true
m.windowId = BrowserWindow.GetNextWindowNum()
if config == nil {
config = NewChromiumConfig()
config.SetEnableMenu(true)
config.SetEnableDevTools(true)
config.SetEnableOpenUrlTab(true)
config.SetEnableWindowPopup(true)
}
m.chromium = NewChromium(m, config)
m.chromium.SetEnableMultiBrowserMode(true)
if defaultUrl != "" {
m.chromium.SetDefaultURL(defaultUrl)
}
//windowParent
m.windowParent = NewCEFWindow(m)
m.windowParent.SetParent(m)
m.windowParent.HandleAllocated()
m.windowParent.SetAlign(types.AlClient)
m.windowParent.SetAnchors(types.NewSet(types.AkTop, types.AkLeft, types.AkRight, types.AkBottom))
m.windowParent.SetChromium(m.chromium, 0)
m.windowParent.SetOnEnter(func(sender lcl.IObject) {
if m.isClosing {
return
}
m.chromium.Initialized()
m.chromium.FrameIsFocused()
m.chromium.SetFocus(true)
})
m.windowParent.SetOnExit(func(sender lcl.IObject) {
if m.isClosing {
return
}
m.chromium.SendCaptureLostEvent()
})
}
func (m *BaseWindow) putChromiumWindowInfo() {
m.windowInfo = &TCefWindowInfo{
Window: m,
Browser: nil,
Frames: make(map[int64]*ICefFrame),
WindowProperty: NewWindowProperty(),
auxTools: &auxTools{},
}
BrowserWindow.putWindowInfo(m.windowId, m.windowInfo)
}
//默认的chromium事件
func (m *BaseWindow) defaultChromiumEvent() {
if m.windowType != WT_DEV_TOOLS && m.windowType != WT_VIEW_SOURCE {
Proc("CEF_AddGoForm").Call(uintptr(m.windowId), m.Instance())
m.registerDefaultEvent()
m.registerDefaultChromiumCloseEvent()
}
}
// 创建窗口
//
// 不带有默认事件的窗口
func (m *BaseWindow) FormCreate() {
if m.isFormCreate {
return
}
m.isFormCreate = true
m.SetPosition(types.PoDesktopCenter)
}
//默认窗口活动/关闭处理事件
func (m *BaseWindow) defaultWindowEvent() {
if m.windowType != WT_DEV_TOOLS {
m.SetOnResize(m.resize)
m.SetOnActivate(m.activate)
}
m.SetOnShow(m.show)
}
//默认的窗口关闭事件
func (m *BaseWindow) defaultWindowCloseEvent() {
m.SetOnClose(m.close)
m.SetOnCloseQuery(m.closeQuery)
}
// 添加OnResize事件,不会覆盖默认事件返回值false继续执行默认事件, true跳过默认事件
func (m *BaseWindow) AddOnResize(fn TNotifyEvent) {
m.onResize = append(m.onResize, fn)
}
// 添加OnActivate事件,不会覆盖默认事件返回值false继续执行默认事件, true跳过默认事件
func (m *BaseWindow) AddOnActivate(fn TNotifyEvent) {
m.onActivate = append(m.onActivate, fn)
}
// 添加OnShow事件,不会覆盖默认事件返回值false继续执行默认事件, true跳过默认事件
func (m *BaseWindow) AddOnShow(fn TNotifyEvent) {
m.onShow = append(m.onShow, fn)
}
// 添加OnClose事件,不会覆盖默认事件返回值false继续执行默认事件, true跳过默认事件
func (m *BaseWindow) AddOnClose(fn TCloseEvent) {
m.onClose = append(m.onClose, fn)
}
// 添加OnCloseQuery事件,不会覆盖默认事件返回值false继续执行默认事件, true跳过默认事件
func (m *BaseWindow) AddOnCloseQuery(fn TCloseQueryEvent) {
m.onCloseQuery = append(m.onCloseQuery, fn)
}
//每次激活窗口之后执行一次
func (m *BaseWindow) SetOnActivateAfter(fn lcl.TNotifyEvent) {
m.onActivateAfter = fn
}
func (m *BaseWindow) close(sender lcl.IObject, action *types.TCloseAction) {
var ret bool
if m.onClose != nil {
for _, fn := range m.onClose {
if fn(sender, action) {
ret = true
}
}
}
if !ret {
*action = types.CaFree
}
}
func (m *BaseWindow) closeQuery(sender lcl.IObject, close *bool) {
var ret bool
if m.onCloseQuery != nil {
for _, fn := range m.onCloseQuery {
if fn(sender, close) {
ret = true
}
}
}
if !ret {
*close = m.canClose
if m.isClosing {
return
}
m.isClosing = true
m.Hide()
m.chromium.CloseBrowser(true)
}
}
func (m *BaseWindow) show(sender lcl.IObject) {
var ret bool
if m.onShow != nil {
for _, fn := range m.onShow {
if fn(sender) {
ret = true
}
}
}
if !ret {
if m.windowParent != nil {
QueueAsyncCall(func(id int) {
m.windowParent.UpdateSize()
})
}
}
}
func (m *BaseWindow) resize(sender lcl.IObject) {
var ret bool
if m.onResize != nil {
for _, fn := range m.onResize {
if fn(sender) {
ret = true
}
}
}
if !ret {
if m.isClosing {
return
}
if m.chromium != nil {
m.chromium.NotifyMoveOrResizeStarted()
}
if m.windowParent != nil {
m.windowParent.UpdateSize()
}
}
}
func (m *BaseWindow) activate(sender lcl.IObject) {
var ret bool
if m.onActivate != nil {
for _, fn := range m.onActivate {
if fn(sender) {
ret = true
}
}
}
if !ret {
if m.isClosing {
return
}
if m.chromium != nil {
if !m.chromium.Initialized() {
m.chromium.CreateBrowser(m.windowParent)
}
}
}
if m.onActivateAfter != nil {
m.onActivateAfter(sender)
}
}
// 默认事件注册 部分事件允许被覆盖
func (m *BaseWindow) registerDefaultEvent() {
var bwEvent = BrowserWindow.browserEvent
m.chromium.SetOnBeforePopup(func(sender lcl.IObject, browser *ICefBrowser, frame *ICefFrame, beforePopupInfo *BeforePopupInfo, windowInfo *TCefWindowInfo, noJavascriptAccess *bool) bool {
if !api.DBoolToGoBool(BrowserWindow.Config.chromiumConfig.enableWindowPopup) {
return true
}
if bwEvent.onBeforePopup != nil {
return bwEvent.onBeforePopup(sender, browser, frame, beforePopupInfo, windowInfo, noJavascriptAccess)
}
return false
})
2022-10-04 22:34:57 +08:00
m.chromium.SetOnProcessMessageReceived(func(sender lcl.IObject, browser *ICefBrowser, frame *ICefFrame, sourceProcess CefProcessId, message *ipc.ICefProcessMessage) bool {
2022-10-04 13:21:05 +08:00
if bwEvent.onProcessMessageReceived != nil {
return bwEvent.onProcessMessageReceived(sender, browser, frame, sourceProcess, message)
}
return false
})
m.chromium.SetOnFrameCreated(func(sender lcl.IObject, browser *ICefBrowser, frame *ICefFrame) {
QueueAsyncCall(func(id int) {
BrowserWindow.putBrowserFrame(browser, frame)
})
2022-10-04 13:21:05 +08:00
if bwEvent.onFrameCreated != nil {
bwEvent.onFrameCreated(sender, browser, frame)
}
})
m.chromium.SetOnFrameDetached(func(sender lcl.IObject, browser *ICefBrowser, frame *ICefFrame) {
if bwEvent.onFrameDetached != nil {
bwEvent.onFrameDetached(sender, browser, frame)
}
})
m.chromium.SetOnAfterCreated(func(sender lcl.IObject, browser *ICefBrowser) {
if bwEvent.onAfterCreated != nil {
bwEvent.onAfterCreated(sender, browser)
}
})
//事件可以被覆盖
m.chromium.SetOnBeforeDownload(func(sender lcl.IObject, browser *ICefBrowser, beforeDownloadItem *DownloadItem, suggestedName string, callback *ICefBeforeDownloadCallback) {
if bwEvent.onBeforeDownload != nil {
bwEvent.onBeforeDownload(sender, browser, beforeDownloadItem, suggestedName, callback)
} else {
callback.Cont(ExePath+Separator+suggestedName, true)
}
})
//默认自定义快捷键
defaultAcceleratorCustom()
//事件可以被覆盖
m.chromium.SetOnKeyEvent(func(sender lcl.IObject, browser *ICefBrowser, event *TCefKeyEvent, result *bool) {
if api.DBoolToGoBool(BrowserWindow.Config.chromiumConfig.enableDevTools) {
if winInfo := BrowserWindow.GetWindowInfo(browser.Identifier()); winInfo != nil {
if winInfo.Window.windowType == WT_DEV_TOOLS || winInfo.Window.windowType == WT_VIEW_SOURCE {
return
}
}
if event.WindowsKeyCode == VkF12 && event.Kind == KEYEVENT_RAW_KEYDOWN {
browser.ShowDevTools()
*result = true
} else if event.WindowsKeyCode == VkF12 && event.Kind == KEYEVENT_KEYUP {
*result = true
}
}
if KeyAccelerator.accelerator(browser, event, result) {
return
}
if bwEvent.onKeyEvent != nil {
bwEvent.onKeyEvent(sender, browser, event, result)
}
})
m.chromium.SetOnBeforeBrowser(func(sender lcl.IObject, browser *ICefBrowser, frame *ICefFrame) bool {
if bwEvent.onBeforeBrowser != nil {
return bwEvent.onBeforeBrowser(sender, browser, frame)
}
return false
})
m.chromium.SetOnTitleChange(func(sender lcl.IObject, browser *ICefBrowser, title string) {
updateBrowserDevTools(browser, title)
updateBrowserViewSource(browser, title)
if bwEvent.onTitleChange != nil {
bwEvent.onTitleChange(sender, browser, title)
}
})
m.chromium.SetOnBeforeContextMenu(func(sender lcl.IObject, browser *ICefBrowser, frame *ICefFrame, params *ICefContextMenuParams, model *ICefMenuModel) {
chromiumOnBeforeContextMenu(sender, browser, frame, params, model)
if bwEvent.onBeforeContextMenu != nil {
bwEvent.onBeforeContextMenu(sender, browser, frame, params, model)
}
})
m.chromium.SetOnContextMenuCommand(func(sender lcl.IObject, browser *ICefBrowser, frame *ICefFrame, params *ICefContextMenuParams, commandId int32, eventFlags uint32, result *bool) {
chromiumOnContextMenuCommand(sender, browser, frame, params, commandId, eventFlags, result)
if bwEvent.onContextMenuCommand != nil {
bwEvent.onContextMenuCommand(sender, browser, frame, params, commandId, eventFlags, result)
}
})
m.chromium.SetOnLoadingStateChange(func(sender lcl.IObject, browser *ICefBrowser, isLoading, canGoBack, canGoForward bool) {
if bwEvent.onLoadingStateChange != nil {
bwEvent.onLoadingStateChange(sender, browser, isLoading, canGoBack, canGoForward)
}
})
}
//默认的chromium关闭事件
func (m *BaseWindow) registerDefaultChromiumCloseEvent() {
var bwEvent = BrowserWindow.browserEvent
m.chromium.SetOnClose(func(sender lcl.IObject, browser *ICefBrowser, aAction *TCefCloseBrowsesAction) {
BrowserWindow.uiLock.Lock()
defer BrowserWindow.uiLock.Unlock()
if IsDarwin() { //macos x
*aAction = CbaDelay
} else { // window linux
*aAction = CbaClose
}
if winInfo := BrowserWindow.GetWindowInfo(browser.Identifier()); winInfo != nil {
if winInfo.Window.windowParent != nil {
if IsDarwin() { //macos x
winInfo.Window.windowParent.DestroyChildWindow()
} else { // window linux
QueueAsyncCall(func(id int) { //主进程执行
winInfo.Window.windowParent.Free()
})
}
}
}
if bwEvent.onClose != nil {
bwEvent.onClose(sender, browser, aAction)
}
})
m.chromium.SetOnBeforeClose(func(sender lcl.IObject, browser *ICefBrowser) {
BrowserWindow.uiLock.Lock()
defer BrowserWindow.uiLock.Unlock()
if winInfo := BrowserWindow.GetWindowInfo(browser.Identifier()); winInfo != nil {
window := winInfo.Window
window.canClose = true
var tempClose = func() {
defer func() {
if err := recover(); err != nil {
2022-10-05 20:43:25 +08:00
logger.Error("OnBeforeClose Error:", err)
2022-10-04 13:21:05 +08:00
}
}()
if window.windowInfo.auxTools.viewSourceWindow != nil {
window.windowInfo.auxTools.viewSourceWindow = nil
}
if window.windowInfo != nil && window.windowInfo.auxTools.devToolsWindow != nil {
window.windowInfo.auxTools.devToolsWindow.Close()
}
BrowserWindow.removeWindowInfo(winInfo.Window.windowId)
winInfo.Window.Close()
}
if IsWindows() {
tempClose()
} else {
QueueAsyncCall(func(id int) {
tempClose()
})
}
}
if bwEvent.onBeforeClose != nil {
bwEvent.onBeforeClose(sender, browser)
}
})
}