From 3a93c088138a96a3c287f65ee98bfbdaf329b528 Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Sun, 14 Nov 2021 22:40:37 +1100 Subject: [PATCH] [linux] basic windowing --- v2/go.mod | 2 + v2/go.sum | 2 + .../frontend/desktop/linux/frontend.go | 32 ++-- v2/internal/frontend/desktop/linux/window.go | 179 +++++++++++++----- v2/pkg/options/linux/linux.go | 7 + v2/pkg/options/options.go | 2 + 6 files changed, 162 insertions(+), 62 deletions(-) create mode 100644 v2/pkg/options/linux/linux.go diff --git a/v2/go.mod b/v2/go.mod index 164632f8..7de24228 100644 --- a/v2/go.mod +++ b/v2/go.mod @@ -49,6 +49,8 @@ require ( nhooyr.io/websocket v1.8.6 ) +require github.com/gotk3/gotk3 v0.6.1 + require ( github.com/Microsoft/go-winio v0.4.16 // indirect github.com/andybalholm/brotli v1.0.2 // indirect diff --git a/v2/go.sum b/v2/go.sum index 8dd91f46..7ee93614 100644 --- a/v2/go.sum +++ b/v2/go.sum @@ -82,6 +82,8 @@ github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gotk3/gotk3 v0.6.1 h1:GJ400a0ecEEWrzjBvzBzH+pB/esEMIGdB9zPSmBdoeo= +github.com/gotk3/gotk3 v0.6.1/go.mod h1:/hqFpkNa9T3JgNAE2fLvCdov7c5bw//FHNZrZ3Uv9/Q= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/jackmordaunt/icns v1.0.0 h1:RYSxplerf/l/DUd09AHtITwckkv/mqjVv4DjYdPmAMQ= diff --git a/v2/internal/frontend/desktop/linux/frontend.go b/v2/internal/frontend/desktop/linux/frontend.go index 9e912e30..4f8100fd 100644 --- a/v2/internal/frontend/desktop/linux/frontend.go +++ b/v2/internal/frontend/desktop/linux/frontend.go @@ -7,6 +7,7 @@ import ( "context" "encoding/json" "log" + "strconv" "text/template" "github.com/wailsapp/wails/v2/internal/binding" @@ -14,6 +15,8 @@ import ( "github.com/wailsapp/wails/v2/internal/frontend/assetserver" "github.com/wailsapp/wails/v2/internal/logger" "github.com/wailsapp/wails/v2/pkg/options" + + "github.com/gotk3/gotk3/gtk" ) type Frontend struct { @@ -82,6 +85,9 @@ func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger. } result.assets = assets + // Initialise GTK + gtk.Init(nil) + return result } @@ -101,14 +107,14 @@ func (f *Frontend) Run(ctx context.Context) error { f.debug = _debug.(bool) } - f.WindowCenter() + //f.WindowCenter() //f.setupChromium() // - //mainWindow.OnSize().Bind(func(arg *winc.Event) { + //gtkWindow.OnSize().Bind(func(arg *winc.Event) { // f.chromium.Resize() //}) // - //mainWindow.OnClose().Bind(func(arg *winc.Event) { + //gtkWindow.OnClose().Bind(func(arg *winc.Event) { // if f.frontendOptions.HideWindowOnClose { // f.WindowHide() // } else { @@ -177,13 +183,13 @@ func (f *Frontend) WindowMaximise() { f.mainWindow.Maximise() } func (f *Frontend) WindowUnmaximise() { - f.mainWindow.Restore() + f.mainWindow.UnMaximise() } func (f *Frontend) WindowMinimise() { f.mainWindow.Minimise() } func (f *Frontend) WindowUnminimise() { - f.mainWindow.Restore() + f.mainWindow.UnMinimise() } func (f *Frontend) WindowSetMinSize(width int, height int) { @@ -202,7 +208,7 @@ func (f *Frontend) WindowSetRGBA(col *options.RGBA) { return } // - //f.mainWindow.Dispatch(func() { + //f.gtkWindow.Dispatch(func() { // controller := f.chromium.GetController() // controller2 := controller.GetICoreWebView2Controller2() // @@ -240,10 +246,10 @@ func (f *Frontend) Quit() { // chromium.WebResourceRequestedCallback = f.processRequest // chromium.NavigationCompletedCallback = f.navigationCompleted // chromium.AcceleratorKeyCallback = func(vkey uint) bool { -// w32.PostMessage(f.mainWindow.Handle(), w32.WM_KEYDOWN, uintptr(vkey), 0) +// w32.PostMessage(f.gtkWindow.Handle(), w32.WM_KEYDOWN, uintptr(vkey), 0) // return false // } -// chromium.Embed(f.mainWindow.Handle()) +// chromium.Embed(f.gtkWindow.Handle()) // chromium.Resize() // settings, err := chromium.GetSettings() // if err != nil { @@ -363,21 +369,19 @@ func (f *Frontend) processMessage(message string) { } func (f *Frontend) Callback(message string) { - //f.mainWindow.Dispatch(func() { - // f.chromium.Eval(`window.wails.Callback(` + strconv.Quote(message) + `);`) - //}) + f.ExecJS(`window.wails.Callback(` + strconv.Quote(message) + `);`) } func (f *Frontend) startDrag() error { //if !w32.ReleaseCapture() { // return fmt.Errorf("unable to release mouse capture") //} - //w32.SendMessage(f.mainWindow.Handle(), w32.WM_NCLBUTTONDOWN, w32.HTCAPTION, 0) + //w32.SendMessage(f.gtkWindow.Handle(), w32.WM_NCLBUTTONDOWN, w32.HTCAPTION, 0) return nil } func (f *Frontend) ExecJS(js string) { - //f.mainWindow.Dispatch(func() { + //f.gtkWindow.Dispatch(func() { // f.chromium.Eval(js) //}) } @@ -392,6 +396,6 @@ func (f *Frontend) ExecJS(js string) { // return // } // -// f.mainWindow.Show() +// f.gtkWindow.Show() // //} diff --git a/v2/internal/frontend/desktop/linux/window.go b/v2/internal/frontend/desktop/linux/window.go index 39b02943..3376080b 100644 --- a/v2/internal/frontend/desktop/linux/window.go +++ b/v2/internal/frontend/desktop/linux/window.go @@ -4,8 +4,14 @@ package linux import ( + "github.com/gotk3/gotk3/gdk" + "github.com/gotk3/gotk3/glib" + "github.com/gotk3/gotk3/gtk" "github.com/wailsapp/wails/v2/pkg/menu" "github.com/wailsapp/wails/v2/pkg/options" + "github.com/wailsapp/wails/v2/pkg/options/linux" + "log" + "math" "sync" ) @@ -13,12 +19,59 @@ type Window struct { frontendOptions *options.App applicationMenu *menu.Menu m sync.Mutex + application *gtk.Application + gtkWindow *gtk.ApplicationWindow + //dispatchq []func() } func NewWindow(options *options.App) *Window { result := new(Window) result.frontendOptions = options + + var linuxOptions linux.Options + if options.Linux != nil { + linuxOptions = *options.Linux + } + appID := linuxOptions.AppID + if appID == "" { + appID = "io.wails" + } + + println("AppID =", appID) + + application, err := gtk.ApplicationNew(appID, glib.APPLICATION_FLAGS_NONE) + if err != nil { + log.Fatal("Could not create application:", err) + } + + result.application = application + + application.Connect("activate", func() { + + window, err := gtk.ApplicationWindowNew(application) + if err != nil { + log.Fatal("Could not create application window:", err) + } + window.Connect("delete-event", func() { + if options.HideWindowOnClose { + result.gtkWindow.Hide() + return + } + result.gtkWindow.Close() + }) + result.gtkWindow = window + window.SetTitle(options.Title) + window.SetDecorated(!options.Frameless) + window.SetDefaultSize(600, 300) + window.SetResizable(!options.DisableResize) + window.SetKeepAbove(options.AlwaysOnTop) + window.SetPosition(gtk.WIN_POS_CENTER) + if !options.StartHidden { + window.ShowAll() + } + }) + //result.SetIsForm(true) // //var exStyle int @@ -84,79 +137,109 @@ func NewWindow(options *options.App) *Window { } func (w *Window) Run() { - + w.application.Run(nil) } func (w *Window) Dispatch(f func()) { - //w.m.Lock() - //w.dispatchq = append(w.dispatchq, f) - //w.m.Unlock() - //w32.PostMainThreadMessage(w32.WM_APP, 0, 0) + glib.IdleAdd(f) } func (w *Window) Fullscreen() { - -} - -func (w *Window) Close() { - -} - -func (w *Window) Center() { - -} - -func (w *Window) SetPos(x int, y int) { - -} - -func (w *Window) Pos() (int, int) { - return 0, 0 -} - -func (w *Window) SetSize(width int, height int) { - -} - -func (w *Window) Size() (int, int) { - return 0, 0 - -} - -func (w *Window) SetText(title string) { - -} - -func (w *Window) SetMaxSize(maxWidth int, maxHeight int) { - -} - -func (w *Window) SetMinSize(minWidth int, minHeight int) { - + w.gtkWindow.Fullscreen() } func (w *Window) UnFullscreen() { + w.gtkWindow.Unfullscreen() +} +func (w *Window) Close() { + w.application.Quit() +} + +func (w *Window) Center() { + w.gtkWindow.SetPosition(gtk.WIN_POS_CENTER) +} + +func (w *Window) SetPos(x int, y int) { + display, err := w.gtkWindow.GetDisplay() + if err != nil { + w.gtkWindow.Move(x, y) + return + } + window, err := w.gtkWindow.GetWindow() + if err != nil { + w.gtkWindow.Move(x, y) + return + } + + monitor, err := display.GetMonitorAtWindow(window) + if err != nil { + w.gtkWindow.Move(x, y) + return + } + + geom := monitor.GetGeometry() + w.gtkWindow.Move(geom.GetX()+x, geom.GetY()+y) +} + +func (w *Window) Pos() (int, int) { + return w.gtkWindow.GetPosition() +} + +func (w *Window) SetSize(width int, height int) { + w.gtkWindow.SetDefaultSize(width, height) +} + +func (w *Window) Size() (int, int) { + return w.gtkWindow.GetSize() +} + +func (w *Window) SetText(title string) { + w.gtkWindow.SetTitle(title) +} + +func (w *Window) SetMaxSize(maxWidth int, maxHeight int) { + var geom gdk.Geometry + if maxWidth == 0 { + maxWidth = math.MaxInt + } + if maxHeight == 0 { + maxHeight = math.MaxInt + } + geom.SetMaxWidth(maxWidth) + geom.SetMaxHeight(maxHeight) + w.gtkWindow.SetGeometryHints(w.gtkWindow, geom, gdk.HINT_MAX_SIZE) +} + +func (w *Window) SetMinSize(minWidth int, minHeight int) { + var geom gdk.Geometry + geom.SetMinWidth(minWidth) + geom.SetMinHeight(minHeight) + w.gtkWindow.SetGeometryHints(w.gtkWindow, geom, gdk.HINT_MIN_SIZE) } func (w *Window) Show() { - + w.gtkWindow.ShowAll() } func (w *Window) Hide() { - + w.gtkWindow.Hide() } func (w *Window) Maximise() { - + w.gtkWindow.Maximize() } -func (w *Window) Restore() { - +func (w *Window) UnMaximise() { + w.gtkWindow.Unmaximize() } func (w *Window) Minimise() { + w.gtkWindow.Iconify() +} +func (w *Window) UnMinimise() { + w.gtkWindow.Present() } func (w *Window) IsFullScreen() bool { diff --git a/v2/pkg/options/linux/linux.go b/v2/pkg/options/linux/linux.go new file mode 100644 index 00000000..a3b044e2 --- /dev/null +++ b/v2/pkg/options/linux/linux.go @@ -0,0 +1,7 @@ +package linux + +// Options specific to Linux builds +type Options struct { + // AppID is the gtk application id string. Defaults to a random uuid. + AppID string +} diff --git a/v2/pkg/options/options.go b/v2/pkg/options/options.go index ce9a03a0..f6c78710 100644 --- a/v2/pkg/options/options.go +++ b/v2/pkg/options/options.go @@ -3,6 +3,7 @@ package options import ( "context" "embed" + "github.com/wailsapp/wails/v2/pkg/options/linux" "log" "runtime" @@ -44,6 +45,7 @@ type App struct { //TrayMenus []*menu.TrayMenu Windows *windows.Options Mac *mac.Options + Linux *linux.Options } type RGBA struct {