rotor-framework 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +21 -0
- package/README.md +120 -0
- package/package.json +59 -0
- package/src/source/RotorFramework.bs +654 -0
- package/src/source/RotorFrameworkTask.bs +278 -0
- package/src/source/base/BaseModel.bs +52 -0
- package/src/source/base/BasePlugin.bs +48 -0
- package/src/source/base/BaseReducer.bs +184 -0
- package/src/source/base/BaseStack.bs +92 -0
- package/src/source/base/BaseViewModel.bs +124 -0
- package/src/source/base/BaseWidget.bs +104 -0
- package/src/source/base/DispatcherCreator.bs +193 -0
- package/src/source/base/DispatcherExternal.bs +260 -0
- package/src/source/base/ListenerForDispatchers.bs +246 -0
- package/src/source/engine/Constants.bs +74 -0
- package/src/source/engine/animator/Animator.bs +334 -0
- package/src/source/engine/builder/Builder.bs +213 -0
- package/src/source/engine/builder/NodePool.bs +236 -0
- package/src/source/engine/builder/PluginAdapter.bs +139 -0
- package/src/source/engine/builder/PostProcessor.bs +331 -0
- package/src/source/engine/builder/Processor.bs +156 -0
- package/src/source/engine/builder/Tree.bs +278 -0
- package/src/source/engine/builder/TreeBase.bs +313 -0
- package/src/source/engine/builder/WidgetCreate.bs +322 -0
- package/src/source/engine/builder/WidgetRemove.bs +72 -0
- package/src/source/engine/builder/WidgetUpdate.bs +113 -0
- package/src/source/engine/providers/Dispatcher.bs +72 -0
- package/src/source/engine/providers/DispatcherProvider.bs +95 -0
- package/src/source/engine/services/I18n.bs +169 -0
- package/src/source/libs/animate/Animate.bs +753 -0
- package/src/source/libs/animate/LICENSE.txt +21 -0
- package/src/source/plugins/DispatcherProviderPlugin.bs +127 -0
- package/src/source/plugins/FieldsPlugin.bs +180 -0
- package/src/source/plugins/FocusPlugin.bs +1522 -0
- package/src/source/plugins/FontStylePlugin.bs +159 -0
- package/src/source/plugins/ObserverPlugin.bs +548 -0
- package/src/source/utils/ArrayUtils.bs +495 -0
- package/src/source/utils/GeneralUtils.bs +181 -0
- package/src/source/utils/NodeUtils.bs +180 -0
|
@@ -0,0 +1,322 @@
|
|
|
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
|
|
15
|
+
config.delete("viewModel")
|
|
16
|
+
|
|
17
|
+
' =============================================================
|
|
18
|
+
' WIDGET TREE REGISTRATION
|
|
19
|
+
' =============================================================
|
|
20
|
+
|
|
21
|
+
' Register widget in tree and get HID
|
|
22
|
+
widget = m.frameworkInstance.builder.widgetTree.add({
|
|
23
|
+
id: config.id,
|
|
24
|
+
parentHID: parentHID
|
|
25
|
+
}, ViewModelClass)
|
|
26
|
+
HID = widget.HID
|
|
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
|
|
36
|
+
return GetGlobalAA().rotor_framework_helper.frameworkInstance
|
|
37
|
+
end function
|
|
38
|
+
|
|
39
|
+
' getInfo - Returns framework info *'
|
|
40
|
+
widget.getInfo = function() as object
|
|
41
|
+
return m.getFrameworkInstance().getInfo()
|
|
42
|
+
end function
|
|
43
|
+
|
|
44
|
+
' refresh - Refreshes specific widget properties by key paths *'
|
|
45
|
+
widget.refresh = sub(featureKeyPaths as object)
|
|
46
|
+
iterateKeyPaths = Rotor.Utils.ensureArray(featureKeyPaths)
|
|
47
|
+
refreshObject = {}
|
|
48
|
+
for each keyPath in iterateKeyPaths
|
|
49
|
+
refreshObject.append(Rotor.Utils.getCloneByKeyPath(m, keyPath))
|
|
50
|
+
end for
|
|
51
|
+
m.render(refreshObject)
|
|
52
|
+
end sub
|
|
53
|
+
|
|
54
|
+
' getWidget - Finds widget by search pattern from this widget's context *'
|
|
55
|
+
widget.getWidget = function(searchPattern as string) as object
|
|
56
|
+
return m.getFrameworkInstance().builder.widgetTree.get(searchPattern, m.HID)
|
|
57
|
+
end function
|
|
58
|
+
|
|
59
|
+
' getSiblingWidget - Gets sibling widget by ID *'
|
|
60
|
+
widget.getSiblingWidget = function(id as string) as object
|
|
61
|
+
return m.parent.children[id]
|
|
62
|
+
end function
|
|
63
|
+
|
|
64
|
+
' getViewModel - Returns owning ViewModel (self if ViewModel, or parent VM) *'
|
|
65
|
+
widget.getViewModel = function() as object
|
|
66
|
+
if m.isViewModel = true
|
|
67
|
+
return m
|
|
68
|
+
else
|
|
69
|
+
return m.getFrameworkInstance().builder.widgetTree.getByHID(m.vmHID)
|
|
70
|
+
end if
|
|
71
|
+
end function
|
|
72
|
+
|
|
73
|
+
' getParentViewModel - Returns parent's ViewModel *'
|
|
74
|
+
widget.getParentViewModel = function() as object
|
|
75
|
+
return m.getViewModel().parent.getViewModel()
|
|
76
|
+
end function
|
|
77
|
+
|
|
78
|
+
' getRootWidget - Returns root widget (HID "0") *'
|
|
79
|
+
widget.getRootWidget = function() as object
|
|
80
|
+
return m.getFrameworkInstance().builder.widgetTree.getByHID("0")
|
|
81
|
+
end function
|
|
82
|
+
|
|
83
|
+
' findWidgets - Finds multiple widgets by search pattern *'
|
|
84
|
+
widget.findWidgets = function(searchPattern as string) as object
|
|
85
|
+
return m.getFrameworkInstance().builder.widgetTree.find(searchPattern, m.HID)
|
|
86
|
+
end function
|
|
87
|
+
|
|
88
|
+
' getChildrenWidgets - Gets child widgets with optional pattern matching *'
|
|
89
|
+
widget.getChildrenWidgets = function(matchingPattern = "" as string) as object
|
|
90
|
+
return m.getFrameworkInstance().builder.widgetTree.getChildrenWidgets(m, matchingPattern)
|
|
91
|
+
end function
|
|
92
|
+
|
|
93
|
+
' getSubtreeClone - Creates a clone of widget subtree *'
|
|
94
|
+
widget.getSubtreeClone = function(searchPattern = "" as string, keyPathList = [] as object) as object
|
|
95
|
+
if searchPattern = "" then searchPattern = m.HID
|
|
96
|
+
return m.getFrameworkInstance().builder.widgetTree.getSubtreeClone(searchPattern, keyPathList, m.parentHID)
|
|
97
|
+
end function
|
|
98
|
+
|
|
99
|
+
' render - Renders widget updates (self, descendants, or children) *'
|
|
100
|
+
widget.render = sub(payloads as dynamic, params = {} as object)
|
|
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)
|
|
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
|
|
133
|
+
return m.getFrameworkInstance().dispatcherProvider.getFacade(dispatcherId, m)
|
|
134
|
+
end function
|
|
135
|
+
|
|
136
|
+
' animator - Gets animator factory by ID *'
|
|
137
|
+
widget.animator = function(animatorId) as object
|
|
138
|
+
return m.getFrameworkInstance().animatorProvider.getFactory(animatorId, m)
|
|
139
|
+
end function
|
|
140
|
+
|
|
141
|
+
' =============================================================
|
|
142
|
+
' VIEWMODEL INITIALIZATION
|
|
143
|
+
' =============================================================
|
|
144
|
+
|
|
145
|
+
if widget.isViewModel = true
|
|
146
|
+
|
|
147
|
+
' Merge props from config
|
|
148
|
+
if Rotor.Utils.isAssociativeArray(config.props)
|
|
149
|
+
Rotor.Utils.deepExtendAA(widget.props, config.props)
|
|
150
|
+
end if
|
|
151
|
+
|
|
152
|
+
' Merge viewModelState from config
|
|
153
|
+
if Rotor.Utils.isAssociativeArray(config.viewModelState)
|
|
154
|
+
Rotor.Utils.deepExtendAA(widget.viewModelState, config.viewModelState)
|
|
155
|
+
end if
|
|
156
|
+
|
|
157
|
+
' Setup i18n (l10n)
|
|
158
|
+
i18nService = widget.getFrameworkInstance().i18nService
|
|
159
|
+
keysPaths = Rotor.Utils.isString(config.i18n?.path) ? config.i18n.path : Rotor.Utils.isArray(config.i18n?.paths) ? config.i18n.paths : invalid
|
|
160
|
+
widget.viewModelState.l10n = i18nService.getL10n(keysPaths)
|
|
161
|
+
|
|
162
|
+
' Include isRTL flag if requested
|
|
163
|
+
if config?.i18n?.includeIsRtl = true
|
|
164
|
+
widget.viewModelState.isRTL = i18nService.getIsRtl()
|
|
165
|
+
end if
|
|
166
|
+
|
|
167
|
+
' Include locale string if requested
|
|
168
|
+
if config?.i18n?.includeLocale = true
|
|
169
|
+
widget.viewModelState.locale = i18nService.getLocale()
|
|
170
|
+
end if
|
|
171
|
+
|
|
172
|
+
' Call lifecycle hook before template compilation
|
|
173
|
+
widget.onCreateView()
|
|
174
|
+
|
|
175
|
+
' Generate template
|
|
176
|
+
template = widget.template()
|
|
177
|
+
|
|
178
|
+
' Optional template post-processing hook
|
|
179
|
+
if Rotor.Utils.isFunction(widget.onTemplateCreated)
|
|
180
|
+
widget.onTemplateCreated(template)
|
|
181
|
+
end if
|
|
182
|
+
|
|
183
|
+
' Merge template into config
|
|
184
|
+
if template <> invalid and template.Count() > 0
|
|
185
|
+
templateChildren = template.children
|
|
186
|
+
template.delete("children")
|
|
187
|
+
config = Rotor.Utils.deepExtendAA(config, template)
|
|
188
|
+
config.children = templateChildren
|
|
189
|
+
end if
|
|
190
|
+
|
|
191
|
+
end if
|
|
192
|
+
|
|
193
|
+
' =============================================================
|
|
194
|
+
' LIFECYCLE HOOK REGISTRATION
|
|
195
|
+
' =============================================================
|
|
196
|
+
|
|
197
|
+
' Register custom lifecycle hooks from config
|
|
198
|
+
if Rotor.Utils.isFunction(config.onMountWidget)
|
|
199
|
+
widget.onMountWidget = config.onMountWidget
|
|
200
|
+
end if
|
|
201
|
+
|
|
202
|
+
if Rotor.Utils.isFunction(config.onUpdateWidget)
|
|
203
|
+
widget.onUpdateWidget = config.onUpdateWidget
|
|
204
|
+
end if
|
|
205
|
+
|
|
206
|
+
if Rotor.Utils.isFunction(config.onDestroyWidget)
|
|
207
|
+
widget.onDestroyWidget = config.onDestroyWidget
|
|
208
|
+
end if
|
|
209
|
+
|
|
210
|
+
' Queue onRenderSettled callback
|
|
211
|
+
if Rotor.Utils.isFunction(config.onRenderSettled) = true
|
|
212
|
+
m.frameworkInstance.builder.callbackQueue.push({
|
|
213
|
+
callback: config.onRenderSettled,
|
|
214
|
+
callbackScope: widget
|
|
215
|
+
})
|
|
216
|
+
config.delete("onRenderSettled")
|
|
217
|
+
end if
|
|
218
|
+
|
|
219
|
+
' =============================================================
|
|
220
|
+
' SCENEGRAPH NODE CREATION
|
|
221
|
+
' =============================================================
|
|
222
|
+
|
|
223
|
+
' Get children for processing
|
|
224
|
+
if config.children = invalid then config.children = {}
|
|
225
|
+
children = config.children
|
|
226
|
+
|
|
227
|
+
' Create SceneGraph node from node pool
|
|
228
|
+
nodeType = Rotor.Utils.isString(config.nodeType) ? config.nodeType : "Group"
|
|
229
|
+
widget.node = m.frameworkInstance.builder.nodePool.acquireNode(nodeType)
|
|
230
|
+
widget.nodeType = nodeType
|
|
231
|
+
widget.markedToAppend = true
|
|
232
|
+
|
|
233
|
+
' Debug: Set node ID for scene graph inspector
|
|
234
|
+
#if debug
|
|
235
|
+
Rotor.Utils.setCustomFields(widget.node, {
|
|
236
|
+
id: `${widget.id}-${widget.HID}`
|
|
237
|
+
}, true, false)
|
|
238
|
+
#end if
|
|
239
|
+
|
|
240
|
+
' =============================================================
|
|
241
|
+
' PLUGIN INTEGRATION
|
|
242
|
+
' =============================================================
|
|
243
|
+
|
|
244
|
+
' Process plugins and inject addon methods
|
|
245
|
+
widgetAddon = {}
|
|
246
|
+
pluginKeyList = m.frameworkInstance.builder.pluginAdapter.pluginKeyList
|
|
247
|
+
pluginKeyList.ResetIndex()
|
|
248
|
+
pluginKey = pluginKeyList.GetIndex()
|
|
249
|
+
|
|
250
|
+
while pluginKey <> invalid
|
|
251
|
+
|
|
252
|
+
if config.doesExist(pluginKey)
|
|
253
|
+
|
|
254
|
+
' Register plugin lifecycle hooks (beforeMount, afterMounted)
|
|
255
|
+
for each LifeCycleHookType in [Rotor.Const.LifeCycleHookType.BEFORE_MOUNT, Rotor.Const.LifeCycleHookType.AFTER_MOUNTED]
|
|
256
|
+
if m.frameworkInstance.builder.pluginAdapter.pluginHooks[LifeCycleHookType].DoesExist(pluginKey)
|
|
257
|
+
widget[pluginKey] = config[pluginKey]
|
|
258
|
+
postProcessBuffer.add({
|
|
259
|
+
isPlugin: true,
|
|
260
|
+
widget: widget,
|
|
261
|
+
hookType: LifeCycleHookType,
|
|
262
|
+
pluginKey: pluginKey
|
|
263
|
+
})
|
|
264
|
+
end if
|
|
265
|
+
end for
|
|
266
|
+
|
|
267
|
+
' Inject plugin widget methods
|
|
268
|
+
plugin = m.frameworkInstance.plugins[pluginKey]
|
|
269
|
+
if Rotor.Utils.isValid(plugin["widgetMethods"])
|
|
270
|
+
if widgetAddon.plugins = invalid then widgetAddon.plugins = {}
|
|
271
|
+
if widgetAddon.plugins[pluginKey] = invalid then widgetAddon.plugins[pluginKey] = {
|
|
272
|
+
pluginKey: pluginKey,
|
|
273
|
+
id: widget.id,
|
|
274
|
+
HID: widget.HID,
|
|
275
|
+
parentHID: widget.parentHID
|
|
276
|
+
}
|
|
277
|
+
widgetAddon.plugins[pluginKey].append(plugin["widgetMethods"])
|
|
278
|
+
end if
|
|
279
|
+
|
|
280
|
+
end if
|
|
281
|
+
|
|
282
|
+
pluginKey = pluginKeyList.GetIndex()
|
|
283
|
+
end while
|
|
284
|
+
|
|
285
|
+
' Append all plugin addon methods to widget
|
|
286
|
+
if widgetAddon.Count() > 0
|
|
287
|
+
widget.append(widgetAddon)
|
|
288
|
+
end if
|
|
289
|
+
|
|
290
|
+
' =============================================================
|
|
291
|
+
' POST-PROCESS QUEUE
|
|
292
|
+
' =============================================================
|
|
293
|
+
|
|
294
|
+
' Queue child append operation
|
|
295
|
+
appendChildProcess = {
|
|
296
|
+
hookType: Rotor.Const.LifeCycleHookType.APPEND_CHILD,
|
|
297
|
+
widget: widget
|
|
298
|
+
}
|
|
299
|
+
if config.zIndex <> invalid
|
|
300
|
+
appendChildProcess.zIndex = config.zIndex
|
|
301
|
+
end if
|
|
302
|
+
postProcessBuffer.add(appendChildProcess)
|
|
303
|
+
|
|
304
|
+
' Queue mounted lifecycle hook
|
|
305
|
+
if Rotor.Utils.isFunction(widget.onMountWidget) or widget?.isViewModel = true
|
|
306
|
+
postProcessBuffer.add({
|
|
307
|
+
hookType: Rotor.Const.LifeCycleHookType.MOUNTED,
|
|
308
|
+
widget: widget
|
|
309
|
+
})
|
|
310
|
+
end if
|
|
311
|
+
|
|
312
|
+
' Return widget metadata for tree processing
|
|
313
|
+
return {
|
|
314
|
+
HID: HID,
|
|
315
|
+
children: children,
|
|
316
|
+
parentHID: parentHID,
|
|
317
|
+
id: widget.id
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
end function
|
|
321
|
+
|
|
322
|
+
end namespace
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
namespace Rotor.ViewBuilder
|
|
2
|
+
|
|
3
|
+
' =====================================================================
|
|
4
|
+
' removeWidget - Removes a widget and its children from the widget tree. Handles plugin beforeDestroy hooks, widget destruction, and SceneGraph node removal. Recursively marks children for removal.
|
|
5
|
+
'
|
|
6
|
+
' @param {object} postProcessBuffer - Buffer for deferred plugin/lifecycle operations
|
|
7
|
+
' @param {object} widget - Widget instance to remove
|
|
8
|
+
' @param {object} config - Removal configuration with markedToRemove and shouldSkipNodePool flags
|
|
9
|
+
' @returns {object} Object with children array and HID for recursive processing
|
|
10
|
+
' =====================================================================
|
|
11
|
+
function removeWidget(postProcessBuffer as object, widget as object, config as object) as object
|
|
12
|
+
HID = widget.HID
|
|
13
|
+
|
|
14
|
+
widget.markedToRemove = config.markedToRemove
|
|
15
|
+
|
|
16
|
+
' plugin :: beforeDestroy
|
|
17
|
+
pluginKeyList = m.frameworkInstance.builder.pluginAdapter.pluginKeyList
|
|
18
|
+
pluginKeyList.ResetIndex()
|
|
19
|
+
pluginKey = pluginKeyList.GetIndex()
|
|
20
|
+
while pluginKey <> invalid
|
|
21
|
+
|
|
22
|
+
if widget.doesExist(pluginKey)
|
|
23
|
+
LifeCycleHookType = Rotor.Const.LifeCycleHookType.BEFORE_DESTROY
|
|
24
|
+
if m.frameworkInstance.builder.pluginAdapter.pluginHooks[LifeCycleHookType].DoesExist(pluginKey)
|
|
25
|
+
postProcessBuffer.add({
|
|
26
|
+
isPlugin: true,
|
|
27
|
+
' isView: widget?.isView ?? false,
|
|
28
|
+
widget: widget,
|
|
29
|
+
hookType: LifeCycleHookType,
|
|
30
|
+
pluginKey: pluginKey
|
|
31
|
+
})
|
|
32
|
+
end if
|
|
33
|
+
end if
|
|
34
|
+
|
|
35
|
+
pluginKey = pluginKeyList.GetIndex()
|
|
36
|
+
end while
|
|
37
|
+
|
|
38
|
+
postProcessBuffer.add({
|
|
39
|
+
hookType: Rotor.Const.LifeCycleHookType.DELETE_WIDGET,
|
|
40
|
+
widget: widget
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
isBranchOfRemove = m.frameworkInstance.builder.widgetTree.isBranchOfRemove(widget)
|
|
44
|
+
if isBranchOfRemove
|
|
45
|
+
postProcessBuffer.add({
|
|
46
|
+
hookType: Rotor.Const.LifeCycleHookType.REMOVE_CHILD,
|
|
47
|
+
shouldSkipNodePool: config.shouldSkipNodePool,
|
|
48
|
+
widget: widget
|
|
49
|
+
|
|
50
|
+
})
|
|
51
|
+
end if
|
|
52
|
+
|
|
53
|
+
childrenToRemove = []
|
|
54
|
+
for each id in widget.children
|
|
55
|
+
if not widget.children[id].doesExist("markedToRemove")
|
|
56
|
+
childrenToRemove.push({
|
|
57
|
+
markedToRemove: true,
|
|
58
|
+
id: id,
|
|
59
|
+
HID: widget.children[id].HID,
|
|
60
|
+
parentHID: widget.children[id].parentHID
|
|
61
|
+
})
|
|
62
|
+
end if
|
|
63
|
+
end for
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
children: childrenToRemove,
|
|
67
|
+
HID: HID
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
end function
|
|
71
|
+
|
|
72
|
+
end namespace
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
namespace Rotor.ViewBuilder
|
|
2
|
+
|
|
3
|
+
' =====================================================================
|
|
4
|
+
' updateWidget - Updates an existing widget with new configuration. Handles props updates, viewModelState changes, plugin lifecycle hooks, z-index changes, and custom lifecycle callbacks.
|
|
5
|
+
'
|
|
6
|
+
' @param {object} postProcessBuffer - Buffer for deferred plugin/lifecycle operations
|
|
7
|
+
' @param {object} widget - Existing widget instance to update
|
|
8
|
+
' @param {object} newConfig - New configuration to apply
|
|
9
|
+
' @returns {object} Object with children and HID for further processing
|
|
10
|
+
' =====================================================================
|
|
11
|
+
function updateWidget(postProcessBuffer as object, widget as object, newConfig as object) as object
|
|
12
|
+
HID = widget.HID
|
|
13
|
+
|
|
14
|
+
isWidgetChanged = false
|
|
15
|
+
|
|
16
|
+
if widget?.isViewModel = true and newConfig.DoesExist("props")
|
|
17
|
+
newConfig.delete("viewModel") ' In case app update with the same template.
|
|
18
|
+
postProcessBuffer.add({
|
|
19
|
+
hookType: Rotor.Const.LifeCycleHookType.VIEWMODEL_STATE_UPDATE,
|
|
20
|
+
widget: widget,
|
|
21
|
+
props: newConfig.props
|
|
22
|
+
})
|
|
23
|
+
end if
|
|
24
|
+
|
|
25
|
+
if newConfig.DoesExist("viewModelState")
|
|
26
|
+
Rotor.Utils.deepExtendAA(widget.viewModelState, newConfig.viewModelState)
|
|
27
|
+
isWidgetChanged = true
|
|
28
|
+
end if
|
|
29
|
+
|
|
30
|
+
' Method decorators during update
|
|
31
|
+
|
|
32
|
+
pluginKeyList = m.frameworkInstance.builder.pluginAdapter.pluginKeyList
|
|
33
|
+
pluginKeyList.ResetIndex()
|
|
34
|
+
pluginKey = pluginKeyList.GetIndex()
|
|
35
|
+
while pluginKey <> invalid
|
|
36
|
+
|
|
37
|
+
if newConfig.doesExist(pluginKey)
|
|
38
|
+
for each LifeCycleHookType in [Rotor.Const.LifeCycleHookType.BEFORE_UPDATE, Rotor.Const.LifeCycleHookType.AFTER_UPDATED]
|
|
39
|
+
if m.frameworkInstance.builder.pluginAdapter.pluginHooks[LifeCycleHookType].DoesExist(pluginKey)
|
|
40
|
+
|
|
41
|
+
isWidgetChanged = true
|
|
42
|
+
|
|
43
|
+
process = {
|
|
44
|
+
isPlugin: true,
|
|
45
|
+
widget: widget,
|
|
46
|
+
hookType: LifeCycleHookType,
|
|
47
|
+
' isView: widget?.isView ?? false,
|
|
48
|
+
pluginKey: pluginKey
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if LifeCycleHookType = Rotor.Const.LifeCycleHookType.BEFORE_UPDATE or LifeCycleHookType = Rotor.Const.LifeCycleHookType.AFTER_UPDATED
|
|
52
|
+
newValue = newConfig[pluginKey]
|
|
53
|
+
oldValue = widget[pluginKey]
|
|
54
|
+
' Change is depend on plugin ;)
|
|
55
|
+
process.append({
|
|
56
|
+
oldValue: oldValue,
|
|
57
|
+
newValue: newValue
|
|
58
|
+
})
|
|
59
|
+
end if
|
|
60
|
+
|
|
61
|
+
postProcessBuffer.add(process)
|
|
62
|
+
|
|
63
|
+
end if
|
|
64
|
+
end for
|
|
65
|
+
end if
|
|
66
|
+
|
|
67
|
+
pluginKey = pluginKeyList.GetIndex()
|
|
68
|
+
end while
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
if Rotor.Utils.isInteger(newConfig.zIndex)
|
|
72
|
+
postProcessBuffer.add({
|
|
73
|
+
hookType: Rotor.Const.LifeCycleHookType.REINDEX_CHILD,
|
|
74
|
+
widget: widget,
|
|
75
|
+
zIndex: newConfig.zIndex
|
|
76
|
+
})
|
|
77
|
+
end if
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
if Rotor.Utils.isFunction(newConfig.onUpdateWidget)
|
|
81
|
+
isWidgetChanged = true
|
|
82
|
+
widget.onUpdateWidget = newConfig.onUpdateWidget
|
|
83
|
+
end if
|
|
84
|
+
|
|
85
|
+
if Rotor.Utils.isFunction(newConfig.onDestroyWidget)
|
|
86
|
+
widget.onDestroyWidget = newConfig.onDestroyWidget
|
|
87
|
+
end if
|
|
88
|
+
|
|
89
|
+
if Rotor.Utils.isFunction(newConfig.onRenderSettled) = true
|
|
90
|
+
m.frameworkInstance.builder.callbackQueue.push({
|
|
91
|
+
callback: newConfig.onRenderSettled,
|
|
92
|
+
callbackScope: widget
|
|
93
|
+
})
|
|
94
|
+
newConfig.delete("onRenderSettled")
|
|
95
|
+
end if
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
if isWidgetChanged = true and Rotor.Utils.isFunction(widget.onUpdateWidget)
|
|
99
|
+
postProcessBuffer.add({
|
|
100
|
+
hookType: Rotor.Const.LifeCycleHookType.UPDATED,
|
|
101
|
+
widget: widget
|
|
102
|
+
})
|
|
103
|
+
end if
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
children: newConfig.children,
|
|
107
|
+
HID: HID
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
end function
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
end namespace
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
|
|
2
|
+
namespace Rotor
|
|
3
|
+
|
|
4
|
+
' =====================================================================
|
|
5
|
+
' Dispatcher - Dispatcher Facade that provides a simplified interface for state management
|
|
6
|
+
'
|
|
7
|
+
' Acts as a proxy to the underlying dispatcher instance, managing intents,
|
|
8
|
+
' listeners, and state access. The facade pattern allows for clean separation
|
|
9
|
+
' between the dispatcher implementation and its consumers, enabling multiple
|
|
10
|
+
' listeners to subscribe to the same dispatcher with different scopes.
|
|
11
|
+
' =====================================================================
|
|
12
|
+
class Dispatcher
|
|
13
|
+
|
|
14
|
+
dispatcherId as string
|
|
15
|
+
listenerId as string
|
|
16
|
+
listenerScope as object
|
|
17
|
+
dispatcherInstance as object
|
|
18
|
+
|
|
19
|
+
sub new(dispatcherInstance as dynamic, dispatcherId as string, listenerId = "" as string, listenerScope = invalid as object)
|
|
20
|
+
m.dispatcherId = dispatcherId
|
|
21
|
+
m.listenerScope = listenerScope
|
|
22
|
+
m.listenerId = listenerId
|
|
23
|
+
m.dispatcherInstance = dispatcherInstance
|
|
24
|
+
end sub
|
|
25
|
+
|
|
26
|
+
' ---------------------------------------------------------------------
|
|
27
|
+
' dispatch - Dispatches an intent to trigger state changes through the reducer
|
|
28
|
+
'
|
|
29
|
+
' @param {Intent} intent - The intent object containing action type and payload
|
|
30
|
+
'
|
|
31
|
+
sub dispatch(intent)
|
|
32
|
+
m.dispatcherInstance.dispatch(intent)
|
|
33
|
+
end sub
|
|
34
|
+
|
|
35
|
+
' ---------------------------------------------------------------------
|
|
36
|
+
' addListener - Adds a listener to subscribe to state changes from this dispatcher
|
|
37
|
+
'
|
|
38
|
+
' @param {Dynamic} listenerConfig - Configuration object for the listener (can be a function or object with mapStateToProps)
|
|
39
|
+
'
|
|
40
|
+
sub addListener(listenerConfig as dynamic)
|
|
41
|
+
m.dispatcherInstance.addListener(listenerConfig, m.listenerId, m.listenerScope)
|
|
42
|
+
end sub
|
|
43
|
+
|
|
44
|
+
' ---------------------------------------------------------------------
|
|
45
|
+
' removeAllListenersByListenerId - Removes all listeners associated with this dispatcher's listener ID
|
|
46
|
+
'
|
|
47
|
+
sub removeAllListenersByListenerId()
|
|
48
|
+
m.dispatcherInstance.removeAllListenersByListenerId(m.listenerId)
|
|
49
|
+
end sub
|
|
50
|
+
|
|
51
|
+
' ---------------------------------------------------------------------
|
|
52
|
+
' getState - Retrieves the current state from the dispatcher, optionally mapped through a selector function
|
|
53
|
+
'
|
|
54
|
+
' @param {Dynamic} mapStateToProps - Optional function to map/select specific parts of the state
|
|
55
|
+
' @returns {Object} The current state object (mapped if selector provided)
|
|
56
|
+
'
|
|
57
|
+
function getState(mapStateToProps = invalid as Dynamic) as object
|
|
58
|
+
return m.dispatcherInstance.getState(mapStateToProps, m.listenerScope)
|
|
59
|
+
end function
|
|
60
|
+
|
|
61
|
+
' ---------------------------------------------------------------------
|
|
62
|
+
' destroy - Cleans up the dispatcher facade by removing all listeners and clearing references
|
|
63
|
+
'
|
|
64
|
+
sub destroy()
|
|
65
|
+
m.removeAllListenersByListenerId()
|
|
66
|
+
m.listenerScope = invalid
|
|
67
|
+
m.dispatcherInstance = invalid
|
|
68
|
+
end sub
|
|
69
|
+
|
|
70
|
+
end class
|
|
71
|
+
|
|
72
|
+
end namespace
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
|
|
2
|
+
namespace Rotor
|
|
3
|
+
|
|
4
|
+
' =====================================================================
|
|
5
|
+
' DispatcherProvider - Manages a collection of dispatcher instances and provides facade access
|
|
6
|
+
'
|
|
7
|
+
' Manages a collection of dispatcher instances and provides facade access
|
|
8
|
+
' to dispatchers. Extends BaseStack to store and organize dispatchers by ID.
|
|
9
|
+
' Handles both internal and external dispatcher registration/deregistration.
|
|
10
|
+
' =====================================================================
|
|
11
|
+
class DispatcherProvider extends Rotor.BaseStack
|
|
12
|
+
|
|
13
|
+
threadType as string
|
|
14
|
+
|
|
15
|
+
' ---------------------------------------------------------------------
|
|
16
|
+
' new - Initializes the dispatcher provider with thread type
|
|
17
|
+
'
|
|
18
|
+
' @param {string} threadType - Type of thread this provider operates on (render/task)
|
|
19
|
+
'
|
|
20
|
+
sub new(threadType as string)
|
|
21
|
+
super()
|
|
22
|
+
m.threadType = threadType
|
|
23
|
+
end sub
|
|
24
|
+
|
|
25
|
+
' ---------------------------------------------------------------------
|
|
26
|
+
' getFacade - Returns a dispatcher facade for the requested dispatcher ID
|
|
27
|
+
'
|
|
28
|
+
' @param {string} dispatcherId - The unique identifier for the dispatcher
|
|
29
|
+
' @param {object} listenerScope - Optional scope for the listener (default: invalid)
|
|
30
|
+
' @returns {object} Dispatcher facade instance
|
|
31
|
+
' @throws {object} Error if dispatcher ID is not registered (debug mode only)
|
|
32
|
+
'
|
|
33
|
+
function getFacade(dispatcherId as string, listenerScope = invalid as object) as object
|
|
34
|
+
listenerId = Rotor.Utils.getUUIDHex(32)
|
|
35
|
+
|
|
36
|
+
dispatcherTaskNode = m.stack.LookupCI(dispatcherId)
|
|
37
|
+
|
|
38
|
+
#if debug
|
|
39
|
+
if dispatcherTaskNode = invalid
|
|
40
|
+
throw { message: `[DISPATCHER_PROVIDER][ERROR] Dispatcher id:${dispatcherId} is not registered.` }
|
|
41
|
+
end if
|
|
42
|
+
#end if
|
|
43
|
+
|
|
44
|
+
dispatcherFacadeInstance = new Rotor.Dispatcher(dispatcherTaskNode, dispatcherId, listenerId, listenerScope)
|
|
45
|
+
|
|
46
|
+
return dispatcherFacadeInstance
|
|
47
|
+
end function
|
|
48
|
+
|
|
49
|
+
' ---------------------------------------------------------------------
|
|
50
|
+
' registerDispatcher - Registers a dispatcher instance with the provider
|
|
51
|
+
'
|
|
52
|
+
' @param {object} dispatcherInstance - The dispatcher instance to register
|
|
53
|
+
' @param {string} id - The unique identifier for the dispatcher
|
|
54
|
+
'
|
|
55
|
+
override sub registerDispatcher(dispatcherInstance, id as string)
|
|
56
|
+
|
|
57
|
+
m.set(id, dispatcherInstance)
|
|
58
|
+
|
|
59
|
+
end sub
|
|
60
|
+
|
|
61
|
+
' ---------------------------------------------------------------------
|
|
62
|
+
' registerExternalDispatchers - Registers external dispatchers from another thread
|
|
63
|
+
'
|
|
64
|
+
' @param {dynamic} dispatcherIds - Single ID string or array of dispatcher IDs
|
|
65
|
+
' @param {object} taskNode - The task node for cross-thread communication
|
|
66
|
+
'
|
|
67
|
+
sub registerExternalDispatchers(dispatcherIds as dynamic, taskNode as object)
|
|
68
|
+
dispatcherIds = Rotor.Utils.ensureArray(dispatcherIds)
|
|
69
|
+
for each dispatcherId in dispatcherIds
|
|
70
|
+
dispatcherInstance = new Rotor.DispatcherExternal(dispatcherId, taskNode, m.threadType)
|
|
71
|
+
m.set(dispatcherId, dispatcherInstance)
|
|
72
|
+
end for
|
|
73
|
+
end sub
|
|
74
|
+
|
|
75
|
+
' ---------------------------------------------------------------------
|
|
76
|
+
' deregisterDispatcher - Removes a dispatcher from the provider
|
|
77
|
+
'
|
|
78
|
+
' @param {dynamic} dispatcherId - The ID of the dispatcher to remove
|
|
79
|
+
'
|
|
80
|
+
sub deregisterDispatcher(dispatcherId as dynamic)
|
|
81
|
+
m.remove(dispatcherId)
|
|
82
|
+
end sub
|
|
83
|
+
|
|
84
|
+
' ---------------------------------------------------------------------
|
|
85
|
+
' destroy - Destroys all registered dispatchers and cleans up resources
|
|
86
|
+
'
|
|
87
|
+
sub destroy()
|
|
88
|
+
dispatchers = m.getAll()
|
|
89
|
+
for each dispatcherId in dispatchers
|
|
90
|
+
dispatchers[dispatcherId].destroy() ' Note that destroy will deregister dispatcher automatically
|
|
91
|
+
end for
|
|
92
|
+
end sub
|
|
93
|
+
|
|
94
|
+
end class
|
|
95
|
+
end namespace
|