• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mobalazs / rotor-framework / 20916448569

12 Jan 2026 10:44AM UTC coverage: 86.364% (+0.01%) from 86.352%
20916448569

push

github

web-flow
fix(PostProcessor): reindex node child by widget zIndex (#19)

7 of 8 new or added lines in 1 file covered. (87.5%)

2033 of 2354 relevant lines covered (86.36%)

1.19 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.21
/src/source/engine/builder/PostProcessor.bs
1
' ===== POST PROCESSOR =====
2
namespace Rotor.ViewBuilder
3

4
    ' =====================================================================
5
    ' postProcessBuffer - Manages lifecycle hook buffers with priority-based execution ordering. Maintains separate buffers for root and default priority processes, organizing hooks by lifecycle type for sequential execution.
6
    ' =====================================================================
7
    class postProcessBuffer
8

9
        frameworkInstance as Rotor.Framework
10

11
        buffers = invalid
12

13
        hookTypeIterationUsageFlags = invalid
14

15
        orderedLifeCycleHookType = invalid
16

17
        orderedHookPriorities = invalid
18

19
        ' ---------------------------------------------------------------------
20
        ' new - Initializes the instance
21
        '
22
        ' @description Initializes buffer structures and lifecycle hook ordering
23
        ' ---------------------------------------------------------------------
24
        sub new()
25
            ' initialize ordered hook collections
26
            m.buffers = {}
1✔
27
            m.hookTypeIterationUsageFlags = {}
1✔
28

29
            m.orderedLifeCycleHookType = []
1✔
30
            m.orderedLifeCycleHookType.push(Rotor.Const.LifeCycleHookType.APPEND_CHILD)
1✔
31
            m.orderedLifeCycleHookType.push(Rotor.Const.LifeCycleHookType.REINDEX_CHILD)
1✔
32
            m.orderedLifeCycleHookType.push(Rotor.Const.LifeCycleHookType.BEFORE_MOUNT)
1✔
33
            m.orderedLifeCycleHookType.push(Rotor.Const.LifeCycleHookType.MOUNTED)
1✔
34
            m.orderedLifeCycleHookType.push(Rotor.Const.LifeCycleHookType.AFTER_MOUNTED)
1✔
35
            m.orderedLifeCycleHookType.push(Rotor.Const.LifeCycleHookType.BEFORE_UPDATE)
1✔
36
            m.orderedLifeCycleHookType.push(Rotor.Const.LifeCycleHookType.UPDATED)
1✔
37
            m.orderedLifeCycleHookType.push(Rotor.Const.LifeCycleHookType.VIEWMODEL_STATE_UPDATE)
1✔
38
            m.orderedLifeCycleHookType.push(Rotor.Const.LifeCycleHookType.AFTER_UPDATED)
1✔
39
            m.orderedLifeCycleHookType.push(Rotor.Const.LifeCycleHookType.BEFORE_DESTROY)
1✔
40
            m.orderedLifeCycleHookType.push(Rotor.Const.LifeCycleHookType.REMOVE_CHILD)
1✔
41
            m.orderedLifeCycleHookType.push(Rotor.Const.LifeCycleHookType.DELETE_WIDGET)
1✔
42

43
            m.orderedHookPriorities = []
1✔
44
            m.orderedHookPriorities.push(Rotor.Const.HookPriorityTypes.DEFAULT)
1✔
45
            m.orderedHookPriorities.push(Rotor.Const.HookPriorityTypes.ROOT)
1✔
46

47
            ' initialize buffers for prioritized (root) and default (default) items
48
            m.createLifeCycleBuffers()
1✔
49
        end sub
50

51
        ' ---------------------------------------------------------------------
52
        ' createLifeCycleBuffers - Creates buffer structures for all priority types
53
        ' ---------------------------------------------------------------------
54
        sub createLifeCycleBuffers()
55
            for each priority in m.orderedHookPriorities
1✔
56
                m.buffers[priority] = m.createBuffers()
1✔
57
            end for
58
        end sub
59

60
        ' ---------------------------------------------------------------------
61
        '
62

63
        ' clearLifeCycleBuffers - Clears all lifecycle buffers and iteration flags
64
        '
65
        ' ---------------------------------------------------------------------
66
        sub clearLifeCycleBuffers()
67
            for each priority in m.orderedHookPriorities
1✔
68
                m.clearBuffers(m.buffers[priority])
1✔
69
            end for
70
            ' clear iteration cache
71
            for each hookType in m.hookTypeIterationUsageFlags
1✔
72
                m.hookTypeIterationUsageFlags[hookType] = false
1✔
73
            end for
74
        end sub
75

76
        ' ---------------------------------------------------------------------
77
        ' createBuffers - Creates buffers for all lifecycle hook types
78
        '
79

80
        ' @returns {object} Buffer object with roList for each hook type
81
        ' ---------------------------------------------------------------------
82
        function createBuffers() as object
83
            buffer = {}
1✔
84
            for each hookType in m.orderedLifeCycleHookType
1✔
85
                buffer[hookType] = CreateObject("roList")
1✔
86
                m.hookTypeIterationUsageFlags[hookType] = false
1✔
87
            end for
88
            return buffer
1✔
89
        end function
90

91
        ' ---------------------------------------------------------------------
92
        ' clearBuffers - Clears all buffers in a priority container
93
        '
94

95
        ' @param {object} priorityContainer - Container with lifecycle hook buffers
96
        ' ---------------------------------------------------------------------
97
        sub clearBuffers(priorityContainer)
98
            for each hookType in m.orderedLifeCycleHookType
1✔
99
                priorityContainer[hookType].Clear()
1✔
100
            end for
101
        end sub
102

103
        ' ---------------------------------------------------------------------
104
        ' add - Adds processes to appropriate buffers based on priority and type
105
        '
106

107
        ' @param {object} newProcesses - Process or array of processes to add
108
        ' @param {boolean} isHandledRootChild - Whether this is a root child (default: false)
109
        ' ---------------------------------------------------------------------
110
        sub add (newProcesses as object, isHandledRootChild = false as boolean)
111

112
            processesToAdd = Rotor.Utils.ensureArray(newProcesses)
1✔
113

114
            for each process in processesToAdd
1✔
115
                m.hookTypeIterationUsageFlags[process.hookType] = true
1✔
116

117
                widget = process.widget
1✔
118
                isProcessRoot = m.frameworkInstance.builder.widgetTree.isBranchOfAppend(widget) or m.frameworkInstance.builder.widgetTree.isBranchOfRemove(widget)
1✔
119
                isPluginProcess = process?.isPlugin = true
3✔
120
                priorityType = isProcessRoot = true and not isPluginProcess ? Rotor.Const.HookPriorityTypes.ROOT : Rotor.Const.HookPriorityTypes.DEFAULT
1✔
121

122
                if isPluginProcess
2✔
123
                    m.buffers[priorityType][process.hookType].AddTail(process)
1✔
124
                else
3✔
125
                    if process.hookType = Rotor.Const.LifeCycleHookType.APPEND_CHILD or process.hookType = Rotor.Const.LifeCycleHookType.REINDEX_CHILD
2✔
126
                        m.buffers[priorityType][process.hookType].AddTail(process)
1✔
127
                    else
3✔
128
                        m.buffers[priorityType][process.hookType].AddHead(process)
1✔
129
                    end if
130
                end if
131

132
            end for
133
        end sub
134

135
        ' ---------------------------------------------------------------------
136
        ' init - Initializes the buffer with framework instance reference
137
        '
138

139
        ' @param {Rotor.Framework} frameworkInstance - Framework instance reference
140
        ' ---------------------------------------------------------------------
141
        sub init(frameworkInstance as Rotor.Framework)
142
            m.frameworkInstance = frameworkInstance
1✔
143
        end sub
144

145
        ' ---------------------------------------------------------------------
146
        ' destroy - Cleans up framework instance reference
147
        ' ---------------------------------------------------------------------
148
        sub destroy()
149
            m.frameworkInstance = invalid
1✔
150
        end sub
151
    end class
152

153
    ' =====================================================================
154
    ' PostProcessor - Executes lifecycle hooks and plugin processes in priority order. Handles widget lifecycle operations like mount, update, destroy, and manages SceneGraph node operations.
155
    ' =====================================================================
156
    class PostProcessor
157

158
        frameworkInstance as Rotor.Framework
159

160
        ' ---------------------------------------------------------------------
161
        '
162

163
        ' run - Executes all processes from the buffer in priority and hook type order
164
        '
165
        ' @param {object} postProcessBuffer - Buffer containing processes to execute
166
        ' ---------------------------------------------------------------------
167
        sub run(postProcessBuffer as object)
168

169
            for each priorityType in postProcessBuffer.orderedHookPriorities
1✔
170
                for each hookType in postProcessBuffer.orderedLifeCycleHookType
1✔
171
                    if postProcessBuffer.hookTypeIterationUsageFlags[hookType] = true
3✔
172
                        processList = postProcessBuffer.buffers[priorityType][hookType]
1✔
173
                        processList.ResetIndex()
1✔
174
                        process = processList.GetIndex()
1✔
175
                        while process <> invalid
1✔
176
                            m.executeGeneralProcess(process)
1✔
177
                            process = processList.GetIndex()
1✔
178
                        end while
179
                    end if
180
                end for
181
            end for
182

183
        end sub
184

185
        ' ---------------------------------------------------------------------
186
        ' executeGeneralProcess - Executes a single process based on its hook type
187
        '
188

189
        ' @param {object} process - Process object with hookType and widget
190
        ' ---------------------------------------------------------------------
191
        sub executeGeneralProcess(process as object)
192
            widget = process.widget
1✔
193

194
            if process?.isPlugin = true
2✔
195
                pluginHook = m.frameworkInstance.builder.pluginAdapter.pluginHooks[process.hookType][process.pluginKey] ' array of handlers
1✔
196
                if process.hookType = Rotor.Const.LifeCycleHookType.BEFORE_UPDATE or process.hookType = Rotor.Const.LifeCycleHookType.AFTER_UPDATED
2✔
197
                    m.runPluginHookHandler(process.hookType, pluginHook, widget, process.newValue, process.oldValue)
1✔
198
                else
3✔
199
                    m.runPluginHookHandler(process.hookType, pluginHook, widget)
1✔
200
                end if
201

202
            else if process.hookType = Rotor.Const.LifeCycleHookType.APPEND_CHILD
2✔
203
                m.appendNodeAtZIndex(process)
1✔
204
                widget.Delete("markedToAppend")
1✔
205

206
            else if process.hookType = Rotor.Const.LifeCycleHookType.REINDEX_CHILD
2✔
207
                parentNode = widget.parent.node
1✔
208
                parentNode.removeChild(widget.node)
1✔
209
                m.appendNodeAtZIndex(process)
1✔
210

211
            else if process.hookType = Rotor.Const.LifeCycleHookType.REMOVE_CHILD
2✔
212
                parentNode = widget.parent.node
1✔
213
                parentNode.removeChild(widget.node)
1✔
214
                if process?.shouldSkipNodePool = invalid or process.shouldSkipNodePool = false
2✔
215
                    widget.node = m.frameworkInstance.builder.nodePool.releaseNodeBranch(widget.node)
1✔
216
                end if
217
                widget.node = invalid
1✔
218

219
            else if process.hookType = Rotor.Const.LifeCycleHookType.MOUNTED
2✔
220
                if widget.onMountWidget <> invalid
2✔
221
                    widget.onMountWidget()
1✔
222
                end if
223
                if widget?.isViewModel = true
3✔
224
                    widget.onMountView()
1✔
225
                end if
226

227
            else if process.hookType = Rotor.Const.LifeCycleHookType.UPDATED
2✔
228
                widget.onUpdateWidget()
1✔
229

230
            else if process.hookType = Rotor.Const.LifeCycleHookType.VIEWMODEL_STATE_UPDATE
2✔
231
                widget.setProps(process.props)
1✔
232

233
            else if process.hookType = Rotor.Const.LifeCycleHookType.DELETE_WIDGET
3✔
234
                if widget.onDestroyWidget <> invalid
2✔
235
                    widget.onDestroyWidget()
1✔
236
                end if
237
                if widget?.isViewModel = true
2✔
238
                    widget.onDestroyView()
1✔
239
                end if
240
                m.frameworkInstance.builder.widgetTree.remove(widget.HID)
1✔
241

242
            end if
243
        end sub
244

245
        ' ---------------------------------------------------------------------
246
        ' runPluginHookHandler - Executes plugin hook handler with appropriate parameters
247
        '
248

249
        ' @param {string} LifeCycleHookTypeType - Lifecycle hook type identifier
250
        ' @param {object} pluginHook - Plugin hook configuration
251
        ' @param {object} widget - Widget instance (default: invalid)
252
        ' @param {dynamic} newValue - New value for update hooks (default: invalid)
253
        ' @param {dynamic} oldValue - Old value for update hooks (default: invalid)
254
        ' ---------------------------------------------------------------------
255
        sub runPluginHookHandler(LifeCycleHookTypeType as string, pluginHook as object, widget = invalid as object, newValue = invalid as dynamic, oldValue = invalid as dynamic)
256
            pluginKey = pluginHook.pluginKey
1✔
257
            scope = m.frameworkInstance.builder.pluginAdapter.getPlugin(pluginKey)
1✔
258
            if scope.isEnabled
3✔
259
                if LifeCycleHookTypeType = Rotor.Const.LifeCycleHookType.BEFORE_UPDATE
2✔
260
                    pluginHook.handlerFn(scope, widget, newValue, oldValue)
1✔
261
                else
3✔
262
                    pluginHook.handlerFn(scope, widget)
1✔
263
                end if
264
            end if
265
        end sub
266

267
        ' ---------------------------------------------------------------------
268
        ' appendNodeAtZIndex - Appends a node to its parent at the correct z-index position
269
        '
270

271
        ' @param {object} process - Process containing widget and zIndex information
272
        ' ---------------------------------------------------------------------
273
        sub appendNodeAtZIndex (process as object)
274

275
            widget = process.widget
1✔
276
            zIndex = process.zIndex
1✔
277
            node = widget.node
1✔
278
            parentNode = widget.parent.node
1✔
279

280
            if zIndex = invalid
3✔
281

282
                parentNode.appendChild(node)
1✔
283

284
            else
285

286
                ' respect zIndex when inserted
3✔
287
                rootChildCount = parentNode.getChildCount()
1✔
288
                siblingNodes = parentNode.getChildren(rootChildCount, 0)
1✔
289
                inserted = false
1✔
290
                index = rootChildCount - 1
1✔
291

292
                while index >= 0 and inserted = false
1✔
293
                    siblingNode = siblingNodes[index]
1✔
294
                    ' Find matching widget by node reference
295
                    matchingWidget = invalid
1✔
296
                    for each childId in widget.parent.children
1✔
297
                        child = widget.parent.children[childId]
1✔
298
                        if child.node <> invalid and child.node.isSameNode(siblingNode)
3✔
299
                            matchingWidget = child
1✔
300
                            exit for
301
                        end if
302
                    end for
303
                    if Rotor.Utils.isInteger(matchingWidget?.zIndex) and matchingWidget?.zIndex <= zIndex
2✔
NEW
304
                        inserted = true
×
305
                    else
3✔
306
                        index--
1✔
307
                    end if
308
                end while
309
                index++
1✔
310
                parentNode.insertChild(node, index) ' insert
1✔
311
                widget.zIndex = zIndex ' store
1✔
312

313
            end if
314
        end sub
315

316
        ' ---------------------------------------------------------------------
317
        ' init - Initializes the post processor with framework instance reference
318
        '
319

320
        ' @param {Rotor.Framework} frameworkInstance - Framework instance reference
321
        ' ---------------------------------------------------------------------
322
        sub init(frameworkInstance as Rotor.Framework)
323
            m.frameworkInstance = frameworkInstance
1✔
324
        end sub
325

326
        ' ---------------------------------------------------------------------
327
        ' destroy - Cleans up framework instance reference
328
        '
329
        ' ---------------------------------------------------------------------
330
        sub destroy()
331
            m.frameworkInstance = invalid
1✔
332
        end sub
333

334
    end class
335

336
end namespace
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc