From 1bd51a1025695757c6d631481b4a9ee425ea39a2 Mon Sep 17 00:00:00 2001 From: gucio321 Date: Tue, 17 Dec 2024 16:13:55 +0100 Subject: [PATCH 1/3] update cimgui-go --- MasterWindow.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MasterWindow.go b/MasterWindow.go index bd43c138..42ad4334 100644 --- a/MasterWindow.go +++ b/MasterWindow.go @@ -342,8 +342,8 @@ func (w *MasterWindow) SetSize(x, y int) { // Mac OS X: Selecting Quit from the application menu will trigger the close // callback for all windows. func (w *MasterWindow) SetCloseCallback(cb func() bool) { - w.backend.SetCloseCallback(func(b backend.Backend[glfwbackend.GLFWWindowFlags]) { - b.SetShouldClose(cb()) + w.backend.SetCloseCallback(func() { + w.backend.SetShouldClose(cb()) }) } From 4c6d6bd71fd45ef0b9984540283ce78a6fb23112 Mon Sep 17 00:00:00 2001 From: gucio321 Date: Wed, 8 Jan 2025 16:13:51 +0100 Subject: [PATCH 2/3] backend: add abstraction from cimgui-go backend impl allows to use different backend either from cimgui-go or manually ijmplemented. The idea is to use same giu code (especially MasterWindowFlags) in abstraction from used backend. --- Backend.go | 64 ++++++++++++++++++++++++++++++++++++++++++ Context.go | 8 ++---- MasterWindow.go | 74 +++++++++++++++++++++---------------------------- go.mod | 2 +- go.sum | 2 ++ 5 files changed, 101 insertions(+), 49 deletions(-) create mode 100644 Backend.go diff --git a/Backend.go b/Backend.go new file mode 100644 index 00000000..95968f79 --- /dev/null +++ b/Backend.go @@ -0,0 +1,64 @@ +package giu + +import ( + "github.com/AllenDang/cimgui-go/backend" + "github.com/AllenDang/cimgui-go/backend/glfwbackend" +) + +// sometimes we need to tell what we mean ;-) +// good example is: +// FlagsNotResizable in giu, +// but cimgui-go has only FlagsResizable. So SetFlags(Resizable, 0) +type flagValue[T ~int] struct { + flag T + value int +} + +// GIUBackend is an abstraction layer between cimgui-go's Backends. +type GIUBackend backend.Backend[MasterWindowFlags] + +var _ GIUBackend = &GLFWBackend{} + +// GLFWBackend is an implementation of glfbackend.GLFWBackend cimgui-go backend with respect to +// giu's MasterWIndowFlags. +type GLFWBackend struct { + *glfwbackend.GLFWBackend +} + +func NewGLFWBackend() *GLFWBackend { + return &GLFWBackend{ + GLFWBackend: glfwbackend.NewGLFWBackend(), + } +} + +func (b *GLFWBackend) SetInputMode(mode MasterWindowFlags, value MasterWindowFlags) { + flag := b.parseFlag(mode) + b.GLFWBackend.SetInputMode(flag.flag, glfwbackend.GLFWWindowFlags(flag.value)) +} + +func (b *GLFWBackend) SetSwapInterval(interval MasterWindowFlags) error { + intervalV := b.parseFlag(interval).flag + b.GLFWBackend.SetSwapInterval(intervalV) + return nil +} + +func (b *GLFWBackend) SetWindowFlags(flags MasterWindowFlags, _ int) { + flag := b.parseFlag(flags) + b.GLFWBackend.SetWindowFlags(flag.flag, flag.value) +} + +func (b *GLFWBackend) parseFlag(m MasterWindowFlags) flagValue[glfwbackend.GLFWWindowFlags] { + data := map[MasterWindowFlags]flagValue[glfwbackend.GLFWWindowFlags]{ + MasterWindowFlagsNotResizable: {glfwbackend.GLFWWindowFlagsResizable, 0}, + MasterWindowFlagsMaximized: {glfwbackend.GLFWWindowFlagsMaximized, 1}, + MasterWindowFlagsFloating: {glfwbackend.GLFWWindowFlagsFloating, 1}, + MasterWindowFlagsFrameless: {glfwbackend.GLFWWindowFlagsDecorated, 0}, + MasterWindowFlagsTransparent: {glfwbackend.GLFWWindowFlagsTransparent, 1}, + MasterWindowFlagsHidden: {glfwbackend.GLFWWindowFlagsVisible, 0}, + } + + d, ok := data[m] + Assert(ok, "GLFWBackend", "parseFlag", "Unknown MasterWindowFlags") + + return d +} diff --git a/Context.go b/Context.go index 94930fa2..b482181a 100644 --- a/Context.go +++ b/Context.go @@ -4,8 +4,6 @@ import ( "fmt" "sync" - "github.com/AllenDang/cimgui-go/backend" - "github.com/AllenDang/cimgui-go/backend/glfwbackend" "github.com/AllenDang/cimgui-go/imgui" "gopkg.in/eapache/queue.v1" ) @@ -51,7 +49,7 @@ type state struct { // //nolint:revive // I WANT TO CALL THIS GIUContext! type GIUContext struct { - backend backend.Backend[glfwbackend.GLFWWindowFlags] + backend GIUBackend isRunning bool @@ -79,7 +77,7 @@ type GIUContext struct { } // CreateContext creates a new giu context. -func CreateContext(b backend.Backend[glfwbackend.GLFWWindowFlags]) *GIUContext { +func CreateContext(b GIUBackend) *GIUContext { result := GIUContext{ cssStylesheet: make(cssStylesheet), backend: b, @@ -148,7 +146,7 @@ func (c *GIUContext) cleanStates() { } // Backend returns the imgui.backend used by the context. -func (c *GIUContext) Backend() backend.Backend[glfwbackend.GLFWWindowFlags] { +func (c *GIUContext) Backend() GIUBackend { return c.backend } diff --git a/MasterWindow.go b/MasterWindow.go index 42ad4334..bbe9d4cb 100644 --- a/MasterWindow.go +++ b/MasterWindow.go @@ -36,25 +36,6 @@ const ( ) // parseAndApply converts MasterWindowFlags to appropriate glfwbackend.GLFWWindowFlags. -func (m MasterWindowFlags) parseAndApply(b backend.Backend[glfwbackend.GLFWWindowFlags]) { - data := map[MasterWindowFlags]struct { - f glfwbackend.GLFWWindowFlags - value int // value isn't always true (sometimes false). Also WindowHint takes int not bool - }{ - MasterWindowFlagsNotResizable: {glfwbackend.GLFWWindowFlagsResizable, 0}, - MasterWindowFlagsMaximized: {glfwbackend.GLFWWindowFlagsMaximized, 1}, - MasterWindowFlagsFloating: {glfwbackend.GLFWWindowFlagsFloating, 1}, - MasterWindowFlagsFrameless: {glfwbackend.GLFWWindowFlagsDecorated, 0}, - MasterWindowFlagsTransparent: {glfwbackend.GLFWWindowFlagsTransparent, 1}, - MasterWindowFlagsHidden: {glfwbackend.GLFWWindowFlagsVisible, 0}, - } - - for flag, d := range data { - if m&flag != 0 { - b.SetWindowFlags(d.f, d.value) - } - } -} // TODO(gucio321) implement this in cimgui-go // DontCare could be used as an argument to (*MasterWindow).SetSizeLimits. @@ -63,7 +44,9 @@ func (m MasterWindowFlags) parseAndApply(b backend.Backend[glfwbackend.GLFWWindo // MasterWindow represents a glfw master window // It is a base for a windows (see Window.go). type MasterWindow struct { - backend backend.Backend[glfwbackend.GLFWWindowFlags] + // generally Context should be used instead but as I don't like global + // variables, I prefer to keep a pointer here and refer it as possible. + ctx *GIUContext width int height int @@ -96,7 +79,7 @@ func NewMasterWindow(title string, width, height int, flags MasterWindowFlags) * // Disable imgui.ini io.SetIniFilename("") - currentBackend, err := backend.CreateBackend(glfwbackend.NewGLFWBackend()) + currentBackend, err := backend.CreateBackend(NewGLFWBackend()) if err != nil && !errors.Is(err, backend.CExposerError) { panic(err) } @@ -111,18 +94,23 @@ func NewMasterWindow(title string, width, height int, flags MasterWindowFlags) * title: title, io: io, context: imGuiContext, - backend: currentBackend, + ctx: Context, } currentBackend.SetBeforeRenderHook(mw.beforeRender) currentBackend.SetAfterRenderHook(mw.afterRender) currentBackend.SetBeforeDestroyContextHook(mw.beforeDestroy) - flags.parseAndApply(currentBackend) + for f := MasterWindowFlagsNotResizable; f <= MasterWindowFlagsHidden; f <<= 1 { + if f&flags != 0 { + currentBackend.SetWindowFlags(f, 0) // 0 because it is not used anyway (flag values are determined by giu + } + } + currentBackend.CreateWindow(title, width, height) mw.SetInputHandler(newInputHandler()) - mw.backend.SetSizeChangeCallback(mw.sizeChange) + mw.ctx.backend.SetSizeChangeCallback(mw.sizeChange) mw.SetBgColor(colornames.Black) @@ -279,8 +267,8 @@ func (w *MasterWindow) Run(loopFunc func()) { // GetSize return size of master window. func (w *MasterWindow) GetSize() (width, height int) { - if w.backend != nil { - w, h := w.backend.DisplaySize() + if w.ctx.backend != nil { + w, h := w.ctx.backend.DisplaySize() return int(w), int(h) } @@ -299,20 +287,20 @@ func (w *MasterWindow) SetBgColor(bgColor color.Color) { W: float32(a) / mask, } - w.backend.SetBgColor(w.clearColor) + w.ctx.backend.SetBgColor(w.clearColor) } // SetTargetFPS sets target FPS of master window. // Default for GLFW is 30. func (w *MasterWindow) SetTargetFPS(fps uint) { - w.backend.SetTargetFPS(fps) + w.ctx.backend.SetTargetFPS(fps) } // GetPos return position of master window. func (w *MasterWindow) GetPos() (x, y int) { var xResult, yResult int32 - if w.backend != nil { - xResult, yResult = w.backend.GetWindowPos() + if w.ctx.backend != nil { + xResult, yResult = w.ctx.backend.GetWindowPos() } return int(xResult), int(yResult) @@ -320,15 +308,15 @@ func (w *MasterWindow) GetPos() (x, y int) { // SetPos sets position of master window. func (w *MasterWindow) SetPos(x, y int) { - if w.backend != nil { - w.backend.SetWindowPos(x, y) + if w.ctx.backend != nil { + w.ctx.backend.SetWindowPos(x, y) } } // SetSize sets size of master window. func (w *MasterWindow) SetSize(x, y int) { - if w.backend != nil { - w.backend.SetWindowSize(x, y) + if w.ctx.backend != nil { + w.ctx.backend.SetWindowSize(x, y) } } @@ -342,14 +330,14 @@ func (w *MasterWindow) SetSize(x, y int) { // Mac OS X: Selecting Quit from the application menu will trigger the close // callback for all windows. func (w *MasterWindow) SetCloseCallback(cb func() bool) { - w.backend.SetCloseCallback(func() { - w.backend.SetShouldClose(cb()) + w.ctx.backend.SetCloseCallback(func() { + w.ctx.backend.SetShouldClose(cb()) }) } // SetDropCallback sets callback when file was dropped into the window. func (w *MasterWindow) SetDropCallback(cb func([]string)) { - w.backend.SetDropCallback(cb) + w.ctx.backend.SetDropCallback(cb) } // RegisterKeyboardShortcuts registers a global - master window - keyboard shortcuts. @@ -379,7 +367,7 @@ func (w *MasterWindow) RegisterKeyboardShortcuts(s ...WindowShortcut) *MasterWin // The desired image sizes varies depending on platform and system settings. The selected // images will be rescaled as needed. Good sizes include 16x16, 32x32 and 48x48. func (w *MasterWindow) SetIcon(icons ...image.Image) { - w.backend.SetIcons(icons...) + w.ctx.backend.SetIcons(icons...) } // SetSizeLimits sets the size limits of the client area of the specified window. @@ -389,22 +377,22 @@ func (w *MasterWindow) SetIcon(icons ...image.Image) { // To specify only a minimum size or only a maximum one, set the other pair to giu.DontCare. // To disable size limits for a window, set them all to giu.DontCare. func (w *MasterWindow) SetSizeLimits(minw, minh, maxw, maxh int) { - w.backend.SetWindowSizeLimits(minw, minh, maxw, maxh) + w.ctx.backend.SetWindowSizeLimits(minw, minh, maxw, maxh) } // SetTitle updates master window's title. func (w *MasterWindow) SetTitle(title string) { - w.backend.SetWindowTitle(title) + w.ctx.backend.SetWindowTitle(title) } // Close will safely close the master window. func (w *MasterWindow) Close() { - w.SetShouldClose(true) + w.ctx.backend.SetShouldClose(true) } // SetShouldClose sets whether master window should be closed. func (w *MasterWindow) SetShouldClose(v bool) { - w.backend.SetShouldClose(v) + w.ctx.backend.SetShouldClose(v) } // SetInputHandler allows to change default input handler. @@ -412,7 +400,7 @@ func (w *MasterWindow) SetShouldClose(v bool) { func (w *MasterWindow) SetInputHandler(handler InputHandler) { Context.InputHandler = handler - w.backend.SetKeyCallback(func(key, _, action, modifier int) { + w.ctx.backend.SetKeyCallback(func(key, _, action, modifier int) { k, m, a := keyFromGLFWKey(glfwbackend.GLFWKey(key)), Modifier(modifier), Action(action) handler.Handle(k, m, a) diff --git a/go.mod b/go.mod index 5aa756ac..02e50d5c 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/AllenDang/giu go 1.23.3 require ( - github.com/AllenDang/cimgui-go v1.2.0 + github.com/AllenDang/cimgui-go v1.2.1-0.20241217145553-3c6fe4a1233f github.com/AllenDang/go-findfont v0.0.0-20200702051237-9f180485aeb8 github.com/faiface/mainthread v0.0.0-20171120011319-8b78f0a41ae3 github.com/gucio321/glm-go v0.0.0-20241029220517-e1b5a3e011c8 diff --git a/go.sum b/go.sum index e75a87fb..c28b89f5 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/AllenDang/cimgui-go v1.2.0 h1:xlsBNlGW2n4X6WYi0B84iOoAYWQT+iJFKWM2iZvpzNI= github.com/AllenDang/cimgui-go v1.2.0/go.mod h1:KT0QhbfG00LVdgN/eOGhnrSSG8lMfdBvYmZJCBgp2JM= +github.com/AllenDang/cimgui-go v1.2.1-0.20241217145553-3c6fe4a1233f h1:66vaSucgotA2Y1yEVQre+JFq2WQHTswQLhq8BVd2v48= +github.com/AllenDang/cimgui-go v1.2.1-0.20241217145553-3c6fe4a1233f/go.mod h1:1i5vebTbRcCAlui+AUn3A3U0V2tu/qImW0NoLr+/Fek= github.com/AllenDang/go-findfont v0.0.0-20200702051237-9f180485aeb8 h1:dKZMqib/yUDoCFigmz2agG8geZ/e3iRq304/KJXqKyw= github.com/AllenDang/go-findfont v0.0.0-20200702051237-9f180485aeb8/go.mod h1:b4uuDd0s6KRIPa84cEEchdQ9ICh7K0OryZHbSzMca9k= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= From b753ede40858938158405ea3867cdce496eb0f61 Mon Sep 17 00:00:00 2001 From: gucio321 Date: Wed, 8 Jan 2025 16:57:35 +0100 Subject: [PATCH 3/3] linting --- Backend.go | 17 ++++++++++++++--- MasterWindow.go | 1 + 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/Backend.go b/Backend.go index 95968f79..c880bcff 100644 --- a/Backend.go +++ b/Backend.go @@ -1,6 +1,8 @@ package giu import ( + "fmt" + "github.com/AllenDang/cimgui-go/backend" "github.com/AllenDang/cimgui-go/backend/glfwbackend" ) @@ -8,13 +10,15 @@ import ( // sometimes we need to tell what we mean ;-) // good example is: // FlagsNotResizable in giu, -// but cimgui-go has only FlagsResizable. So SetFlags(Resizable, 0) +// but cimgui-go has only FlagsResizable. So SetFlags(Resizable, 0). type flagValue[T ~int] struct { flag T value int } // GIUBackend is an abstraction layer between cimgui-go's Backends. +// +//nolint:revive // this name is OK type GIUBackend backend.Backend[MasterWindowFlags] var _ GIUBackend = &GLFWBackend{} @@ -25,23 +29,30 @@ type GLFWBackend struct { *glfwbackend.GLFWBackend } +// NewGLFWBackend creates a new instance of GLFWBackend. func NewGLFWBackend() *GLFWBackend { return &GLFWBackend{ GLFWBackend: glfwbackend.NewGLFWBackend(), } } -func (b *GLFWBackend) SetInputMode(mode MasterWindowFlags, value MasterWindowFlags) { +// SetInputMode implements backend.Backend interface. +func (b *GLFWBackend) SetInputMode(mode, _ MasterWindowFlags) { flag := b.parseFlag(mode) b.GLFWBackend.SetInputMode(flag.flag, glfwbackend.GLFWWindowFlags(flag.value)) } +// SetSwapInterval implements backend.Backend interface. func (b *GLFWBackend) SetSwapInterval(interval MasterWindowFlags) error { intervalV := b.parseFlag(interval).flag - b.GLFWBackend.SetSwapInterval(intervalV) + if err := b.GLFWBackend.SetSwapInterval(intervalV); err != nil { + return fmt.Errorf("giu.GLFWBackend got error while SwapInterval: %w", err) + } + return nil } +// SetWindowFlags implements backend.Backend interface. func (b *GLFWBackend) SetWindowFlags(flags MasterWindowFlags, _ int) { flag := b.parseFlag(flags) b.GLFWBackend.SetWindowFlags(flag.flag, flag.value) diff --git a/MasterWindow.go b/MasterWindow.go index bbe9d4cb..0d9974b2 100644 --- a/MasterWindow.go +++ b/MasterWindow.go @@ -100,6 +100,7 @@ func NewMasterWindow(title string, width, height int, flags MasterWindowFlags) * currentBackend.SetBeforeRenderHook(mw.beforeRender) currentBackend.SetAfterRenderHook(mw.afterRender) currentBackend.SetBeforeDestroyContextHook(mw.beforeDestroy) + for f := MasterWindowFlagsNotResizable; f <= MasterWindowFlagsHidden; f <<= 1 { if f&flags != 0 { currentBackend.SetWindowFlags(f, 0) // 0 because it is not used anyway (flag values are determined by giu