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

mobalazs / rotor-framework / 20686038680

04 Jan 2026 02:00AM UTC coverage: 85.538% (+0.05%) from 85.488%
20686038680

push

github

mobalazs
feat(FocusPlugin): add setGroupLastFocusedHID method to manage last focused widget ID

24 of 24 new or added lines in 1 file covered. (100.0%)

19 existing lines in 2 files now uncovered.

2011 of 2351 relevant lines covered (85.54%)

1.18 hits per line

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

93.67
/src/source/engine/builder/WidgetCreate.bs
1
namespace Rotor.ViewBuilder
2

3
    ' =====================================================================
4
    ' createWidget - Creates and initializes a widget instance. Core widget factory function that handles the complete widget creation lifecycle: 1. Widget tree registration 2. Decorator method injection 3. ViewModel initialization (if applicable) 4. SceneGraph node creation 5. Plugin registration and lifecycle hooks
5
    '
6
    ' @param {object} postProcessBuffer - Buffer for deferred plugin/lifecycle operations
7
    ' @param {object} config - Widget configuration object
8
    ' @param {string} parentHID - Parent's Hierarchical ID (default: "0" for root)
9
    ' @returns {object} Widget metadata { HID, children, parentHID, id }
10
    ' =====================================================================
11
    function createWidget(postProcessBuffer as object, config as object, parentHID = "0" as string) as object
12

13
        ' Extract and remove ViewModel class from config
14
        ViewModelClass = config.viewModel
1✔
15
        config.delete("viewModel")
1✔
16

17
        ' =============================================================
18
        ' WIDGET TREE REGISTRATION
19
        ' =============================================================
20

21
        ' Register widget in tree and get HID
22
        widget = m.frameworkInstance.builder.widgetTree.add({
1✔
23
            id: config.id,
24
            parentHID: parentHID
25
        }, ViewModelClass)
26
        HID = widget.HID
1✔
27

28
        ' =============================================================
29
        ' WIDGET DECORATOR METHODS
30
        ' =============================================================
31
        ' These methods provide widget API for tree navigation,
32
        ' rendering, state management, and framework integration
33

34
        ' getFrameworkInstance - Returns framework instance *'
35
        widget.getFrameworkInstance = function() as object
1✔
36
            return GetGlobalAA().rotor_framework_helper.frameworkInstance
37
        end function
38

39
        ' refresh - Refreshes specific widget properties by key paths *'
40
        widget.refresh = sub(featureKeyPaths as object)
1✔
41
            iterateKeyPaths = Rotor.Utils.ensureArray(featureKeyPaths)
42
            refreshObject = {}
43
            for each keyPath in iterateKeyPaths
44
                refreshObject.append(Rotor.Utils.getCloneByKeyPath(m, keyPath))
45
            end for
46
            m.render(refreshObject)
47
        end sub
48

49
        ' getWidget - Finds widget by search pattern from this widget's context *'
50
        widget.getWidget = function(searchPattern as string) as object
1✔
51
            return m.getFrameworkInstance().builder.widgetTree.get(searchPattern, m.HID)
52
        end function
53

54
        ' getSiblingWidget - Gets sibling widget by ID *'
55
        widget.getSiblingWidget = function(id as string) as object
1✔
56
            return m.parent.children[id]
57
        end function
58

59
        ' getViewModel - Returns owning ViewModel (self if ViewModel, or parent VM) *'
60
        widget.getViewModel = function() as object
1✔
61
            if m.isViewModel = true
62
                return m
63
            else
64
                return m.getFrameworkInstance().builder.widgetTree.getByHID(m.vmHID)
65
            end if
66
        end function
67

68
        ' getParentViewModel - Returns parent's ViewModel *'
69
        widget.getParentViewModel = function() as object
1✔
70
            return m.getViewModel().parent.getViewModel()
71
        end function
72

73
        ' getRootWidget - Returns root widget (HID "0") *'
74
        widget.getRootWidget = function() as object
1✔
75
            return m.getFrameworkInstance().builder.widgetTree.getByHID("0")
76
        end function
77

78
        ' findWidgets - Finds multiple widgets by search pattern *'
79
        widget.findWidgets = function(searchPattern as string) as object
1✔
80
            return m.getFrameworkInstance().builder.widgetTree.find(searchPattern, m.HID)
81
        end function
82

83
        ' getChildrenWidgets - Gets child widgets with optional pattern matching *'
84
        widget.getChildrenWidgets = function(matchingPattern = "" as string) as object
1✔
85
            return m.getFrameworkInstance().builder.widgetTree.getChildrenWidgets(m, matchingPattern)
86
        end function
87

88
        ' getSubtreeClone - Creates a clone of widget subtree *'
89
        widget.getSubtreeClone = function(searchPattern = "" as string, configIncludeFilter = [] as object) as object
1✔
90
            if searchPattern = "" then searchPattern = m.HID
91
            return m.getFrameworkInstance().builder.widgetTree.getSubtreeClone(searchPattern, configIncludeFilter, m.parentHID)
92
        end function
93

94
       ' Get i18n service
95
        widget.i18n = function() as object
1✔
96
            return m.getFrameworkInstance().i18nService
97
        end function
98

99
        ' render - Renders widget updates (self, descendants, or children) *'
100
        widget.render = sub(payloads as dynamic, params = {} as object)
1✔
101
            for each payload in Rotor.Utils.ensureArray(payloads)
102
                if payload.DoesExist("id") = false
103
                    ' Self update
104
                    payload.id = m.id
105
                    payload.HID = m.HID
106
                else if payload.id <> m.id
107
                    ' Update descendants starting from this widget
108
                    payload.parentHID = m.HID
109
                else
110
                    ' Update descendants starting from parent widget
111
                    payload.parentHID = m.parentHID
112
                end if
113
            end for
114
            if Rotor.Utils.isValid(params.callback) then params.callbackScope = m
115
            m.getFrameworkInstance().builder.render(payloads, params)
116
        end sub
117

118
        ' erase - Removes widget(s) from tree *'
119
        widget.erase = sub(payloads = invalid as dynamic, shouldSkipNodePool = false as boolean)
1✔
120
            if payloads = invalid
121
                ' Self destroy
122
                payloads = m.HID
123
                parentHID = m.parentHID
124
            else
125
                ' Children destroy
126
                parentHID = m.HID
127
            end if
128
            m.getFrameworkInstance().builder.erase(payloads, shouldSkipNodePool, parentHID)
129
        end sub
130

131
        ' getDispatcher - Gets dispatcher facade by ID *'
132
        widget.getDispatcher = function(dispatcherId as string) as object
1✔
133
            return m.getFrameworkInstance().dispatcherProvider.getFacade(dispatcherId, m)
134
        end function
135

136
        ' Dispatch shortcut - Dispatches an event via specific dispatcher'
137
        widget.dispatchTo = sub(dispatcherId as string, dispatchObject as object)
1✔
138
            dispatcherFaced = m.getDispatcher(dispatcherId)
139
            dispatcherFaced.dispatch(dispatchObject)
140
        end sub
141

142
        ' Listen shortcut - Listen to specific dispatcher'
143
        widget.getStateFrom = function(dispatcherId as string, mapStateToProps = invalid as dynamic)
1✔
144
            dispatcherFaced = m.getDispatcher(dispatcherId)
145
            return dispatcherFaced.getState(mapStateToProps)
146
        end function
147

148
        ' animator - Gets animator factory by ID'
149
        widget.animator = function(animatorId) as object
1✔
150
            return m.getFrameworkInstance().animatorProvider.getFactory(animatorId, m)
151
        end function
152

153
        ' TTS - Text-to-Speech service interface'
154
        widget.tts = function() as Rotor.ViewBuilder.TtsService
1✔
155
            return m.getFrameworkInstance().ttsService
156
        end function
157

158
        ' =============================================================
159
        ' VIEWMODEL INITIALIZATION
160
        ' =============================================================
161

162
        if widget.isViewModel = true
2✔
163

164
            ' Merge props from config
165
            if Rotor.Utils.isAssociativeArray(config.props)
3✔
166
                Rotor.Utils.deepExtendAA(widget.props, config.props)
1✔
167
            end if
168

169
            ' Merge viewModelState from config
170
            if Rotor.Utils.isAssociativeArray(config.viewModelState)
3✔
171
                Rotor.Utils.deepExtendAA(widget.viewModelState, config.viewModelState)
1✔
172
            end if
173

174
            ' Set l10n reference in viewModelState
175
            i18nService = widget.getFrameworkInstance().i18nService
1✔
176
            widget.viewModelState.l10n = i18nService.getL10n()
1✔
177

178
            ' Call lifecycle hook before template compilation
179
            widget.onCreateView()
1✔
180

181
            ' Generate template
182
            template = widget.template()
1✔
183

184
            ' Optional template post-processing hook
185
            if Rotor.Utils.isFunction(widget.onTemplateCreated)
3✔
186
                widget.onTemplateCreated(template)
1✔
187
            end if
188

189
            ' Merge template into config
190
            if template <> invalid and template.Count() > 0
3✔
191
                templateChildren = template.children
1✔
192
                template.delete("children")
1✔
193
                config = Rotor.Utils.deepExtendAA(template, config)
1✔
194
                config.children = templateChildren
1✔
195
            end if
196

197
        end if
198

199
        ' =============================================================
200
        ' LIFECYCLE HOOK REGISTRATION
201
        ' =============================================================
202

203
        ' Register custom lifecycle hooks from config
204
        if Rotor.Utils.isFunction(config.onMountWidget)
2✔
UNCOV
205
            widget.onMountWidget = config.onMountWidget
×
206
        end if
207

208
        if Rotor.Utils.isFunction(config.onUpdateWidget)
2✔
209
            widget.onUpdateWidget = config.onUpdateWidget
1✔
210
        end if
211

212
        if Rotor.Utils.isFunction(config.onDestroyWidget)
2✔
UNCOV
213
            widget.onDestroyWidget = config.onDestroyWidget
×
214
        end if
215

216
        ' Queue onRenderSettled callback
217
        if Rotor.Utils.isFunction(config.onRenderSettled) = true
2✔
218
            m.frameworkInstance.builder.callbackQueue.push({
×
219
                callback: config.onRenderSettled,
220
                callbackScope: widget
221
            })
UNCOV
222
            config.delete("onRenderSettled")
×
223
        end if
224

225
        ' =============================================================
226
        ' SCENEGRAPH NODE CREATION
227
        ' =============================================================
228

229
        ' Get children for processing
230
        if config.children = invalid then config.children = {}
1✔
231
        children = config.children
1✔
232

233
        ' Create SceneGraph node from node pool
3✔
234
        nodeType = Rotor.Utils.isString(config.nodeType) ? config.nodeType : "Group"
1✔
235
        widget.node = m.frameworkInstance.builder.nodePool.acquireNode(nodeType)
1✔
236
        widget.nodeType = nodeType
1✔
237
        widget.markedToAppend = true
1✔
238

239
        ' Debug: Set node ID for scene graph inspector
240
        #if debug
4✔
241
            Rotor.Utils.setCustomFields(widget.node, {
1✔
242
                id: `${widget.id}-${widget.HID}`
243
            }, true, false)
244
        #end if
245

246
        ' =============================================================
247
        ' PLUGIN INTEGRATION
248
        ' =============================================================
249

250
        ' Process plugins and inject addon methods
251
        widgetAddon = {}
1✔
252
        pluginKeyList = m.frameworkInstance.builder.pluginAdapter.pluginKeyList
1✔
253
        pluginKeyList.ResetIndex()
1✔
254
        pluginKey = pluginKeyList.GetIndex()
1✔
255

256
        while pluginKey <> invalid
1✔
257

258
            if config.doesExist(pluginKey)
2✔
259

260
                ' Register plugin lifecycle hooks (beforeMount, afterMounted)
261
                for each LifeCycleHookType in [Rotor.Const.LifeCycleHookType.BEFORE_MOUNT, Rotor.Const.LifeCycleHookType.AFTER_MOUNTED]
1✔
262
                    if m.frameworkInstance.builder.pluginAdapter.pluginHooks[LifeCycleHookType].DoesExist(pluginKey)
2✔
263
                        widget[pluginKey] = config[pluginKey]
1✔
264
                        postProcessBuffer.add({
1✔
265
                            isPlugin: true,
266
                            widget: widget,
267
                            hookType: LifeCycleHookType,
268
                            pluginKey: pluginKey
269
                        })
270
                    end if
271
                end for
272

273
                ' Inject plugin widget methods directly to widget
274
                plugin = m.frameworkInstance.builder.pluginAdapter.getPlugin(pluginKey)
1✔
275
                if Rotor.Utils.isValid(plugin["widgetMethods"])
2✔
276
                    ' Inject methods directly to widget
277
                    widgetAddon.append(plugin["widgetMethods"])
1✔
278
                end if
279

280
            end if
281

282
            pluginKey = pluginKeyList.GetIndex()
1✔
283
        end while
284

285
        ' Append all plugin addon methods to widget
286
        if widgetAddon.Count() > 0
2✔
287
            widget.append(widgetAddon)
1✔
288
        end if
289

290
        ' =============================================================
291
        ' POST-PROCESS QUEUE
292
        ' =============================================================
293

294
        ' Queue child append operation
295
        appendChildProcess = {
1✔
296
            hookType: Rotor.Const.LifeCycleHookType.APPEND_CHILD,
297
            widget: widget
298
        }
299
        if config.zIndex <> invalid
2✔
UNCOV
300
            appendChildProcess.zIndex = config.zIndex
×
301
        end if
302
        postProcessBuffer.add(appendChildProcess)
1✔
303

304
        ' Queue mounted lifecycle hook
305
        if Rotor.Utils.isFunction(widget.onMountWidget) or widget?.isViewModel = true
2✔
306
            postProcessBuffer.add({
1✔
307
                hookType: Rotor.Const.LifeCycleHookType.MOUNTED,
308
                widget: widget
309
            })
310
        end if
311

312
        ' Return widget metadata for tree processing
313
        return {
1✔
314
            HID: HID,
315
            children: children,
316
            parentHID: parentHID,
317
            id: widget.id
318
        }
319

320
    end function
321

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

© 2026 Coveralls, Inc