From ef3393bb03c78cabf1f84326d5d80e9cb373d604 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E7=BA=A2=E5=B2=A9?= Date: Sun, 1 Jan 2023 22:19:26 +0800 Subject: [PATCH] Fixed Chinese input and gtk3 issues for linux packaging --- cef/cef-application-config.go | 13 +++++- cef/cef-application.go | 8 ---- cef/cef-base-browser-window.go | 39 ++++++++-------- cef/cef-browser-config.go | 38 +++++++++++---- cef/cef-browser-view-component.go | 1 + cef/cef-browser-window.go | 30 +++++++++--- cef/cef-views-framework-browser-window.go | 56 ++++++++++++++++------- cef/cef-window-component.go | 1 + cef/window-info.go | 6 +-- example/browser-linux/linux.go | 9 ++++ 10 files changed, 135 insertions(+), 66 deletions(-) diff --git a/cef/cef-application-config.go b/cef/cef-application-config.go index 80ba8f5e..0edb121d 100644 --- a/cef/cef-application-config.go +++ b/cef/cef-application-config.go @@ -63,9 +63,18 @@ func NewApplicationConfig() *tCefApplicationConfig { m.SetDisableZygote(true) m.SetCheckCEFFiles(false) m.SetRemoteDebuggingPort(0) - m.SetExternalMessagePump(false) - m.SetMultiThreadedMessageLoop(true) m.SetChromeRuntime(false) + //linux >= 107.xxx 版本以后,默认启用的GTK3,106及以前版本默认支持GTK2但无法正常输入中文 + //顾强制使用GTK3方式,但又无法正常创建lcl组件到窗口中,该框架只对浏览器应用做封装 + //所以初衷以浏览器应用建设为目标 + if common.IsLinux() { + //linux平台默认设置为false,将启用 ViewsFrameworkBrowserWindow 窗口 + m.SetExternalMessagePump(false) + m.SetMultiThreadedMessageLoop(false) + } else { + m.SetExternalMessagePump(false) + m.SetMultiThreadedMessageLoop(true) + } return m } diff --git a/cef/cef-application.go b/cef/cef-application.go index 7923b89d..1da0bb60 100644 --- a/cef/cef-application.go +++ b/cef/cef-application.go @@ -32,14 +32,6 @@ func NewCEFApplication(cfg *tCefApplicationConfig) *TCEFApplication { cfg = NewApplicationConfig() } cfg.framework() - - //linux >= 107.xxx 版本以后,默认不支持GTK2,同时GTK2又无法正常输入中文 - //顾强制使用GTK3方式,但又无法正常创建lcl组件到窗口中,该框架只是对浏览器应用做封装 - //所以尽量以正常使用为基准 - if IsLinux() { - cfg.SetExternalMessagePump(false) - cfg.SetMultiThreadedMessageLoop(false) - } m := new(TCEFApplication) r1, _, _ := Proc(internale_CEFApplication_Create).Call(uintptr(unsafe.Pointer(cfg))) m.instance = unsafe.Pointer(r1) diff --git a/cef/cef-base-browser-window.go b/cef/cef-base-browser-window.go index 8c59cf67..eca66195 100644 --- a/cef/cef-base-browser-window.go +++ b/cef/cef-base-browser-window.go @@ -35,24 +35,23 @@ type IBaseWindow interface { //BaseWindow 是一个基于chromium 和 lcl 的窗口组件 type BaseWindow struct { - *lcl.TForm // - chromium IChromium // - windowParent ITCefWindow // - browserViewComponent *TCEFBrowserViewComponent // - windowComponent *TCEFWindowComponent // - 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 + *lcl.TForm // + chromium IChromium // + windowParent ITCefWindow // + vFrameBrowserWindow *ViewsFrameworkBrowserWindow //基于CEF views framework窗口 + windowInfo *TCefWindowInfo //基于LCL窗口信息 + 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 } //创建一个带有 chromium 窗口 @@ -67,6 +66,10 @@ func NewBrowserWindow(config *tCefChromiumConfig, defaultUrl string) *Window { return window } +func (m *BaseWindow) Id() int32 { + return m.windowId +} + func (m *BaseWindow) Show() { m.TForm.Show() } diff --git a/cef/cef-browser-config.go b/cef/cef-browser-config.go index 015a7834..327f5d95 100644 --- a/cef/cef-browser-config.go +++ b/cef/cef-browser-config.go @@ -12,16 +12,21 @@ import ( "github.com/energye/energy/common" ) +type viewsFrameBrowserWindowOnEventCallback func(event *BrowserEvent, window *ViewsFrameworkBrowserWindow) +type browserWindowOnEventCallback func(event *BrowserEvent, window *TCefWindowInfo) +type browserWindowAfterOnEventCallback func(window *TCefWindowInfo) + //创建主窗口指定的一些快捷配置属性 type browserConfig struct { - DefaultUrl string //默认URL地址 - Title string //窗口标题 - Icon string //窗口图标 - Width int32 //窗口宽 - Height int32 //窗口高 - chromiumConfig *tCefChromiumConfig //主窗体浏览器配置 - browserWindowOnEventCallback func(browserEvent *BrowserEvent, window *TCefWindowInfo) //主窗口初始化回调 - browserWindowAfterOnEventCallback func(window *TCefWindowInfo) //主窗口初始化之后回调 + DefaultUrl string //默认URL地址 + Title string //窗口标题 + Icon string //窗口图标 + Width int32 //窗口宽 + Height int32 //窗口高 + chromiumConfig *tCefChromiumConfig //主窗体浏览器配置 + viewsFrameBrowserWindowOnEventCallback viewsFrameBrowserWindowOnEventCallback //主窗口初始化回调 - 基于CEF views framework窗口 + browserWindowOnEventCallback browserWindowOnEventCallback //主窗口初始化回调 - 基于LCL窗口 + browserWindowAfterOnEventCallback browserWindowAfterOnEventCallback //主窗口初始化之后回调 } //设置chromium配置 @@ -31,13 +36,26 @@ func (m *browserConfig) SetChromiumConfig(chromiumConfig *tCefChromiumConfig) { } } -func (m *browserConfig) setBrowserWindowInitOnEvent(fn func(event *BrowserEvent, browserWindow *TCefWindowInfo)) { +//主窗口初始化回调 - 基于CEF views framework窗口 +// +//该回调函数和基于LCL窗口回调是互斥的,默认情况只有一个会被回调 +func (m *browserConfig) setViewsFrameBrowserWindowOnEventCallback(fn viewsFrameBrowserWindowOnEventCallback) { + if fn != nil && common.Args.IsMain() { + m.viewsFrameBrowserWindowOnEventCallback = fn + } +} + +//主窗口初始化回调 - 基于LCL窗口 +// +//该回调函数和基于CEF窗口回调是互斥的,默认情况只有一个会被回调 +func (m *browserConfig) setBrowserWindowInitOnEvent(fn browserWindowOnEventCallback) { if fn != nil && common.Args.IsMain() { m.browserWindowOnEventCallback = fn } } -func (m *browserConfig) setBrowserWindowInitAfterOnEvent(fn func(browserWindow *TCefWindowInfo)) { +//主窗口初始化回调 - 基于LCL窗口 +func (m *browserConfig) setBrowserWindowInitAfterOnEvent(fn browserWindowAfterOnEventCallback) { if fn != nil && common.Args.IsMain() { m.browserWindowAfterOnEventCallback = fn } diff --git a/cef/cef-browser-view-component.go b/cef/cef-browser-view-component.go index 5bb3b1ba..aad3ccb5 100644 --- a/cef/cef-browser-view-component.go +++ b/cef/cef-browser-view-component.go @@ -18,6 +18,7 @@ import ( ) type TCEFBrowserViewComponent struct { + lcl.IComponent instance unsafe.Pointer } diff --git a/cef/cef-browser-window.go b/cef/cef-browser-window.go index 932c6d9c..44407480 100644 --- a/cef/cef-browser-window.go +++ b/cef/cef-browser-window.go @@ -98,7 +98,7 @@ type browserWindow struct { // // 主进程启动成功之后,将创建主窗口 mainBrowserWindow 是一个默认的主窗口 // -// externalMessagePump和multiThreadedMessageLoop是false时将使用RunMessageLoop,这对linux v107.xxx之后版本支持的更好 +// externalMessagePump和multiThreadedMessageLoop是false时启用 ViewsFrameworkBrowserWindow 窗口 // // 在这里启动浏览器的主进程和子进程 func Run(cefApp *TCEFApplication) { @@ -179,24 +179,40 @@ func (m *browser) MainWindow() *TCefWindowInfo { return m.mainWindow } -// 主窗口和chromium初始化时回调 +// 基于CEF views framework窗口 - 主窗口和chromium初始化时回调 +// +// 当使用ViewsFramework创建窗口后,我们无法使用lcl创建组件到窗口中 +// +// ViewsFramework窗口主要解决在linux下gtk2和gtk3共存以及无法输入中文问题 +// +// ViewsFramework窗口 和 LCL窗口同时只能存在一种 +// +// event 浏览器事件 +// +// views framework window 窗口信息对象 +func (m *browser) SetViewFrameBrowserInit(fn viewsFrameBrowserWindowOnEventCallback) { + m.Config.setViewsFrameBrowserWindowOnEventCallback(fn) +} + +// 基于LCL窗口 - 主窗口和chromium初始化时回调 // // 在这里可以对主窗体事件监听和属性设置,和主窗口上的子组件创建 // // 如果想创建子窗口或带有browser的窗口最好在 SetBrowserInitAfter 回调函数中创建 // -// event 浏览器事件 -// mainBrowserWindow 窗口信息对象 -func (m *browser) SetBrowserInit(fn func(event *BrowserEvent, browserWindow *TCefWindowInfo)) { +// event 浏览器事件 +// +// browserWindow 窗口信息对象 +func (m *browser) SetBrowserInit(fn browserWindowOnEventCallback) { m.Config.setBrowserWindowInitOnEvent(fn) } -// 主窗体和chromium初始后回调 +// 基于LCL窗口 - 主窗体和chromium初始后回调 // // 在这里可以对主窗体属性设置、添加子窗口、带有browser的窗口和子组件创建 // // mainBrowserWindow 窗口信息对象 -func (m *browser) SetBrowserInitAfter(fn func(browserWindow *TCefWindowInfo)) { +func (m *browser) SetBrowserInitAfter(fn browserWindowAfterOnEventCallback) { m.Config.setBrowserWindowInitAfterOnEvent(fn) } diff --git a/cef/cef-views-framework-browser-window.go b/cef/cef-views-framework-browser-window.go index 853cc870..28c9d87c 100644 --- a/cef/cef-views-framework-browser-window.go +++ b/cef/cef-views-framework-browser-window.go @@ -10,17 +10,41 @@ package cef import ( "fmt" + "github.com/energye/energy/common" "github.com/energye/energy/consts" "github.com/energye/golcl/lcl" ) +//基于CEF views framework窗口 +// +//该窗口使用CEF内部实现,在linux下107.xx以后版本默认使用GTK3,但无法使用lcl组件集成到窗口中 +// +//当创建应用配置时 MultiThreadedMessageLoop 和 ExternalMessagePump 属性同时为false(linux系统默认强制false)时启用ViewsFramework窗口 type ViewsFrameworkBrowserWindow struct { + component lcl.IComponent // + windowComponent *TCEFWindowComponent // + browserViewComponent *TCEFBrowserViewComponent // +} + +func (m *ViewsFrameworkBrowserWindow) Component() lcl.IComponent { + return m.component +} + +func (m *ViewsFrameworkBrowserWindow) WindowComponent() *TCEFWindowComponent { + return m.windowComponent +} + +func (m *ViewsFrameworkBrowserWindow) BrowserViewComponent() *TCEFBrowserViewComponent { + return m.browserViewComponent } func (m *browserWindow) appContextInitialized(app *TCEFApplication) { + if !common.Args.IsMain() { + return + } app.SetOnContextInitialized(func() { - fmt.Println("OnContextInitialized()") - component := lcl.NewComponent(nil) + m.vFrameBrowserWindow = &ViewsFrameworkBrowserWindow{} + m.vFrameBrowserWindow.component = lcl.NewComponent(nil) if BrowserWindow.Config.chromiumConfig == nil { BrowserWindow.Config.chromiumConfig = NewChromiumConfig() BrowserWindow.Config.chromiumConfig.SetEnableMenu(true) @@ -28,43 +52,43 @@ func (m *browserWindow) appContextInitialized(app *TCEFApplication) { BrowserWindow.Config.chromiumConfig.SetEnableOpenUrlTab(true) BrowserWindow.Config.chromiumConfig.SetEnableWindowPopup(true) } - m.chromium = NewChromium(component, BrowserWindow.Config.chromiumConfig) + m.chromium = NewChromium(m.vFrameBrowserWindow.component, BrowserWindow.Config.chromiumConfig) m.chromium.SetEnableMultiBrowserMode(true) - m.browserViewComponent = NewBrowserViewComponent(component) - m.windowComponent = NewWindowComponent(component) + m.vFrameBrowserWindow.browserViewComponent = NewBrowserViewComponent(m.vFrameBrowserWindow.component) + m.vFrameBrowserWindow.windowComponent = NewWindowComponent(m.vFrameBrowserWindow.component) m.chromium.SetOnBeforePopup(func(sender lcl.IObject, browser *ICefBrowser, frame *ICefFrame, beforePopupInfo *BeforePopupInfo, client *ICefClient, noJavascriptAccess *bool) bool { fmt.Println("OnBeforePopup TargetUrl:", beforePopupInfo.TargetUrl) return false }) - m.windowComponent.SetOnWindowCreated(func(sender lcl.IObject, window *ICefWindow) { - if m.chromium.CreateBrowserByBrowserViewComponent(BrowserWindow.Config.DefaultUrl, m.browserViewComponent) { - m.windowComponent.AddChildView(m.browserViewComponent) - m.windowComponent.SetTitle(BrowserWindow.Config.Title) + m.vFrameBrowserWindow.windowComponent.SetOnWindowCreated(func(sender lcl.IObject, window *ICefWindow) { + if m.chromium.CreateBrowserByBrowserViewComponent(BrowserWindow.Config.DefaultUrl, m.vFrameBrowserWindow.browserViewComponent) { + m.vFrameBrowserWindow.windowComponent.AddChildView(m.vFrameBrowserWindow.browserViewComponent) + m.vFrameBrowserWindow.windowComponent.SetTitle(BrowserWindow.Config.Title) window.CenterWindow(NewCefSize(BrowserWindow.Config.Width, BrowserWindow.Config.Height)) - m.browserViewComponent.RequestFocus() + m.vFrameBrowserWindow.browserViewComponent.RequestFocus() if BrowserWindow.Config.Icon != "" { window.SetWindowAppIconFS(1, BrowserWindow.Config.Icon) } - if BrowserWindow.Config.browserWindowOnEventCallback != nil { + if BrowserWindow.Config.viewsFrameBrowserWindowOnEventCallback != nil { BrowserWindow.browserEvent.chromium = m.chromium - BrowserWindow.Config.browserWindowOnEventCallback(BrowserWindow.browserEvent, m.windowInfo) + BrowserWindow.Config.viewsFrameBrowserWindowOnEventCallback(BrowserWindow.browserEvent, m.vFrameBrowserWindow) } window.Show() } }) - m.windowComponent.SetOnCanClose(func(sender lcl.IObject, window *ICefWindow, aResult *bool) { + m.vFrameBrowserWindow.windowComponent.SetOnCanClose(func(sender lcl.IObject, window *ICefWindow, aResult *bool) { fmt.Println("OnCanClose") *aResult = true app.QuitMessageLoop() }) - m.windowComponent.SetOnGetInitialBounds(func(sender lcl.IObject, window *ICefWindow, aResult *TCefRect) { + m.vFrameBrowserWindow.windowComponent.SetOnGetInitialBounds(func(sender lcl.IObject, window *ICefWindow, aResult *TCefRect) { fmt.Println("OnGetInitialBounds") }) - m.windowComponent.SetOnGetInitialShowState(func(sender lcl.IObject, window *ICefWindow, aResult *consts.TCefShowState) { + m.vFrameBrowserWindow.windowComponent.SetOnGetInitialShowState(func(sender lcl.IObject, window *ICefWindow, aResult *consts.TCefShowState) { fmt.Println("OnGetInitialShowState", *aResult) }) - m.windowComponent.CreateTopLevelWindow() + m.vFrameBrowserWindow.windowComponent.CreateTopLevelWindow() }) } diff --git a/cef/cef-window-component.go b/cef/cef-window-component.go index a7b8d208..ab71b41a 100644 --- a/cef/cef-window-component.go +++ b/cef/cef-window-component.go @@ -20,6 +20,7 @@ import ( ) type TCEFWindowComponent struct { + lcl.IComponent instance unsafe.Pointer } diff --git a/cef/window-info.go b/cef/window-info.go index 82aa3180..ff3cc398 100644 --- a/cef/window-info.go +++ b/cef/window-info.go @@ -117,10 +117,6 @@ func (m *TCefWindowInfo) Maximize() { }) } -func (m *TCefWindowInfo) WindowId() int32 { - return m.Window.windowId -} - // 关闭窗口-在ui线程中执行 func (m *TCefWindowInfo) Close() { BrowserWindow.uiLock.Lock() @@ -131,7 +127,7 @@ func (m *TCefWindowInfo) Close() { return } if m.Window == nil { - logger.Error("关闭浏览器 Form 为空 WindowId:", m.WindowId()) + logger.Error("关闭浏览器 Window 为空") return } if common.IsDarwin() { diff --git a/example/browser-linux/linux.go b/example/browser-linux/linux.go index d5c49e19..4c9e6bed 100644 --- a/example/browser-linux/linux.go +++ b/example/browser-linux/linux.go @@ -21,7 +21,16 @@ func main() { //指定一个URL地址,或本地html文件目录 cef.BrowserWindow.Config.DefaultUrl = "http://localhost:22022/index.html" cef.BrowserWindow.Config.Icon = "resources/icon.png" + cef.BrowserWindow.SetViewFrameBrowserInit(func(event *cef.BrowserEvent, window *cef.ViewsFrameworkBrowserWindow) { + fmt.Println("cef.BrowserWindow.SetViewFrameBrowserInit", window) + fmt.Printf("%+v\n", window) + }) + cef.BrowserWindow.SetBrowserInit(func(event *cef.BrowserEvent, window *cef.TCefWindowInfo) { + fmt.Println("cef.BrowserWindow.SetBrowserInit", window) + fmt.Printf("%+v\n", window) + + }) //在主进程启动成功之后执行 //在这里启动内置http服务 //内置http服务需要使用 go:embed resources 内置资源到执行程序中