rodrigocfd / windigo

Windows API and GUI in idiomatic Go.
https://pkg.go.dev/github.com/rodrigocfd/windigo
MIT License
405 stars 22 forks source link

"Cannot add event handling after the window is created" error when trying to add button to visible window #19

Closed Hoto-Cocoa closed 1 year ago

Hoto-Cocoa commented 1 year ago

I was trying to add button when some situation. However, It makes panic always.

Here is reproducible code:

package main

import (
    "fmt"
    "runtime"

    "github.com/rodrigocfd/windigo/ui"
    "github.com/rodrigocfd/windigo/win"

    "internal/modules/utils"
)

func main() {
    defer utils.PanicHandler()

    runtime.LockOSThread()

    window := ui.NewWindowMain(
        ui.WindowMainOpts().
            Title("App"),
    )

    button := ui.NewButton(
        window,
        ui.ButtonOpts().
            Position(win.POINT{X: 100, Y: 100}),
    )

    button.On().BnClicked(func() {
        fmt.Println("Clicked")

        window.Hwnd().EnumChildWindows(func(childHwnd win.HWND) bool {
            childHwnd.DestroyWindow()

            button := ui.NewButton(
                window,
                ui.ButtonOpts().
                    Position(win.POINT{X: 100, Y: 100}),
            )

            button.On().BnClicked(func() {
                fmt.Println("Clicked")
            })

            return true
        })
    })

    window.RunAsMain()
}

When I click the button, It panic. I tried to omit WS_VISIBLE but not works. Also I tried dummy window to create button, and changing parent using SetParent, but not works.

I don't know where to I should ask this question. Please let me know If this issue not attended...

Thanks.

rodrigocfd commented 1 year ago

Dynamic events are not allowed, this is intentional.

In the early days of Windigo, some programs I wrote used to crash in random situations, with no apparent cause. At first I thought it was something related to the goroutine address containment – something going wrong with address translation from one heap to another with different address spaces.

It took me months to realize what was really happening: when adding an event during an event, you are essentially modifying the same array you're reading from, which is a classic cause of memory access concurrency. Since then, dynamic events are strictly forbidden, they can be added only before the window is launched by RunAsMain().

Thinking the problem from another perspective: the event handling boils down to a big switch statement, which is a static thing. You can't add new switch clauses during runtime.

Possible solution: your code is destroying and creating controls at runtime; the way to respond to those button clicks is giving them IDs and then handling the WM_COMMAND message, where you can see the ID and act accordingly.

Hoto-Cocoa commented 1 year ago

Thanks for the explain! I will try that.