@@ -14,12 +14,12 @@ import (
14
14
// It returns an unique value each time it is called.
15
15
func GenAutoID (id string ) ID {
16
16
idx , ok := Context .widgetIndex [id ]
17
- if ! ok {
18
- Context . widgetIndex [ id ] = 0
19
- return ID ( id )
17
+
18
+ if ok {
19
+ idx ++
20
20
}
21
21
22
- Context .widgetIndex [id ]++
22
+ Context .widgetIndex [id ] = idx
23
23
24
24
return ID (fmt .Sprintf ("%s##%d" , id , idx ))
25
25
}
@@ -56,6 +56,10 @@ type GIUContext struct {
56
56
// Indicate whether current application is running
57
57
isAlive bool
58
58
59
+ // when dirty is true, flushStates must be called before any GetState use
60
+ // when it is false, calling flushStates is noop
61
+ dirty bool
62
+
59
63
// States will used by custom widget to store data
60
64
state sync.Map
61
65
@@ -100,30 +104,32 @@ func (c *GIUContext) IO() *imgui.IO {
100
104
return imgui .CurrentIO ()
101
105
}
102
106
103
- // invalidAllState should be called before rendering.
104
- // it ensures all states are marked as invalid for that moment.
105
- func (c * GIUContext ) invalidAllState () {
106
- c .state .Range (func (_ , v any ) bool {
107
- if s , ok := v .(* state ); ok {
108
- c .m .Lock ()
109
- s .valid = false
110
- c .m .Unlock ()
111
- }
112
-
113
- return true
114
- })
107
+ // SetDirty permits MasterWindow defering setting dirty states after it's render().
108
+ func (c * GIUContext ) SetDirty () {
109
+ c .dirty = true
115
110
}
116
111
117
- // cleanState removes all states that were not marked as valid during rendering.
118
- // should be called after rendering.
119
- func (c * GIUContext ) cleanState () {
112
+ // cleanStates removes all states that were not marked as valid during rendering,
113
+ // then reset said flag before new usage
114
+ // should always be called before first Get/Set state use in renderloop
115
+ // since afterRender() and beforeRender() are not waranted to run (see glfw_window_refresh_callback)
116
+ // we call it at the very start of our render()
117
+ // but just in case something happened, we also use the "dirty" flag to enforce (or avoid) flushing
118
+ // on critical path.
119
+ func (c * GIUContext ) cleanStates () {
120
+ if ! c .dirty {
121
+ return
122
+ }
123
+
120
124
c .state .Range (func (k , v any ) bool {
121
125
if s , ok := v .(* state ); ok {
122
126
c .m .Lock ()
123
127
valid := s .valid
124
128
c .m .Unlock ()
125
129
126
- if ! valid {
130
+ if valid {
131
+ s .valid = false
132
+ } else {
127
133
c .state .Delete (k )
128
134
s .data .Dispose ()
129
135
}
@@ -132,8 +138,8 @@ func (c *GIUContext) cleanState() {
132
138
return true
133
139
})
134
140
135
- // Reset widgetIndex
136
141
c .widgetIndex = make (map [string ]int )
142
+ c .dirty = false
137
143
}
138
144
139
145
// Backend returns the imgui.backend used by the context.
@@ -143,16 +149,20 @@ func (c *GIUContext) Backend() backend.Backend[glfwbackend.GLFWWindowFlags] {
143
149
144
150
// SetState is a generic version of Context.SetState.
145
151
func SetState [T any , PT genericDisposable [T ]](c * GIUContext , id ID , data PT ) {
152
+ c .cleanStates ()
146
153
c .state .Store (id , & state {valid : true , data : data })
147
154
}
148
155
149
156
// SetState stores data in context by id.
150
157
func (c * GIUContext ) SetState (id ID , data Disposable ) {
158
+ c .cleanStates ()
151
159
c .state .Store (id , & state {valid : true , data : data })
152
160
}
153
161
154
162
// GetState is a generic version of Context.GetState.
155
163
func GetState [T any , PT genericDisposable [T ]](c * GIUContext , id ID ) PT {
164
+ c .cleanStates ()
165
+
156
166
if s , ok := c .load (id ); ok {
157
167
c .m .Lock ()
158
168
s .valid = true
@@ -169,6 +179,8 @@ func GetState[T any, PT genericDisposable[T]](c *GIUContext, id ID) PT {
169
179
170
180
// GetState returns previously stored state by id.
171
181
func (c * GIUContext ) GetState (id ID ) any {
182
+ c .cleanStates ()
183
+
172
184
if s , ok := c .load (id ); ok {
173
185
c .m .Lock ()
174
186
s .valid = true
0 commit comments