rotor-framework 0.4.0 β 0.4.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/README.md +1 -1
- package/package.json +1 -1
- package/src/source/RotorFramework.bs +2 -2
- package/src/source/RotorFrameworkTask.bs +2 -2
- package/src/source/base/BaseWidget.bs +7 -3
- package/src/source/engine/builder/WidgetCreate.bs +12 -10
- package/src/source/engine/services/I18n.bs +3 -12
- package/src/source/plugins/FocusPlugin.bs +20 -13
package/README.md
CHANGED
|
@@ -96,7 +96,7 @@ You can find [π±](./docs/ai/readme.opt.yaml) symbols in all documentation page
|
|
|
96
96
|
|
|
97
97
|
## π Learn More
|
|
98
98
|
|
|
99
|
-

|
|
100
100
|
|
|
101
101
|
### Framework Core
|
|
102
102
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rotor-framework",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "Roku toolkit library providing a ViewBuilder, full UI lifecycle with focus handling and many core features, plus MVI-based state management.",
|
|
5
5
|
"author": "BalΓ‘zs MolnΓ‘r",
|
|
6
6
|
"license": "MIT",
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
' βββββββ ββ β ββ βββββββ βββββββββββββββββ βββββββββ ββββ βββββββββββ
|
|
5
5
|
' ββ βββββββ β βββββββ ββ ββ ββ ββββ ββββ βββββββββββββββββββ ββββ ββ
|
|
6
6
|
' Rotor Frameworkβ’
|
|
7
|
-
' Version 0.4.
|
|
7
|
+
' Version 0.4.2
|
|
8
8
|
' Β© 2025 BalΓ‘zs MolnΓ‘r β MIT License
|
|
9
9
|
' =========================================================================
|
|
10
10
|
|
|
@@ -85,7 +85,7 @@ namespace Rotor
|
|
|
85
85
|
class Framework
|
|
86
86
|
|
|
87
87
|
name = "Rotor Framework"
|
|
88
|
-
version = "0.4.
|
|
88
|
+
version = "0.4.2"
|
|
89
89
|
|
|
90
90
|
config = {
|
|
91
91
|
tasks: invalid, ' @array (optional)
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
' βββββββ ββ β ββ βββββββ βββββββββββββββββ βββββββββ ββββ βββββββββββ
|
|
5
5
|
' ββ βββββββ β βββββββ ββ ββ ββ ββββ ββββ βββββββββββββββββββ ββββ ββ
|
|
6
6
|
' Rotor Frameworkβ’
|
|
7
|
-
' Version 0.4.
|
|
7
|
+
' Version 0.4.2
|
|
8
8
|
' Β© 2025 BalΓ‘zs MolnΓ‘r β MIT License
|
|
9
9
|
' =========================================================================
|
|
10
10
|
|
|
@@ -70,7 +70,7 @@ namespace Rotor
|
|
|
70
70
|
class FrameworkTask
|
|
71
71
|
|
|
72
72
|
name = "Rotor Framework"
|
|
73
|
-
version = "0.4.
|
|
73
|
+
version = "0.4.2"
|
|
74
74
|
|
|
75
75
|
config = {
|
|
76
76
|
tasks: invalid, ' optional
|
|
@@ -72,9 +72,13 @@ namespace Rotor
|
|
|
72
72
|
|
|
73
73
|
' Framework Integration
|
|
74
74
|
getFrameworkInstance as Function ' Get framework instance
|
|
75
|
-
getDispatcher as Function
|
|
76
|
-
createDispatcher as Function
|
|
77
|
-
animator as Function
|
|
75
|
+
getDispatcher as Function ' Get dispatcher facade by ID
|
|
76
|
+
createDispatcher as Function ' Create dispatcher
|
|
77
|
+
animator as Function ' Get animator factory
|
|
78
|
+
|
|
79
|
+
' i18n Integration
|
|
80
|
+
getLocale as String ' Get locale e.g: "en_US"
|
|
81
|
+
isRTL as Boolean ' Indicates whether the current locale uses right-to-left text
|
|
78
82
|
|
|
79
83
|
' =============================================================
|
|
80
84
|
' PLUGIN METHODS
|
|
@@ -96,6 +96,18 @@ namespace Rotor.ViewBuilder
|
|
|
96
96
|
return m.getFrameworkInstance().builder.widgetTree.getSubtreeClone(searchPattern, keyPathList, m.parentHID)
|
|
97
97
|
end function
|
|
98
98
|
|
|
99
|
+
' Get isRTL flag
|
|
100
|
+
widget.isRtl = function() as boolean
|
|
101
|
+
i18nService = m.getFrameworkInstance().i18nService
|
|
102
|
+
return i18nService.getIsRtl()
|
|
103
|
+
end function
|
|
104
|
+
|
|
105
|
+
' Get locale string
|
|
106
|
+
widget.getLocale = function() as string
|
|
107
|
+
i18nService = m.getFrameworkInstance().i18nService
|
|
108
|
+
return i18nService.getLocale()
|
|
109
|
+
end function
|
|
110
|
+
|
|
99
111
|
' render - Renders widget updates (self, descendants, or children) *'
|
|
100
112
|
widget.render = sub(payloads as dynamic, params = {} as object)
|
|
101
113
|
for each payload in Rotor.Utils.ensureArray(payloads)
|
|
@@ -171,16 +183,6 @@ namespace Rotor.ViewBuilder
|
|
|
171
183
|
keysPaths = Rotor.Utils.isString(config.i18n?.path) ? config.i18n.path : Rotor.Utils.isArray(config.i18n?.paths) ? config.i18n.paths : invalid
|
|
172
184
|
widget.viewModelState.l10n = i18nService.getL10n(keysPaths)
|
|
173
185
|
|
|
174
|
-
' Include isRTL flag if requested
|
|
175
|
-
if config?.i18n?.includeIsRtl = true
|
|
176
|
-
widget.viewModelState.isRTL = i18nService.getIsRtl()
|
|
177
|
-
end if
|
|
178
|
-
|
|
179
|
-
' Include locale string if requested
|
|
180
|
-
if config?.i18n?.includeLocale = true
|
|
181
|
-
widget.viewModelState.locale = i18nService.getLocale()
|
|
182
|
-
end if
|
|
183
|
-
|
|
184
186
|
' Call lifecycle hook before template compilation
|
|
185
187
|
widget.onCreateView()
|
|
186
188
|
|
|
@@ -32,8 +32,6 @@ namespace Rotor.ViewBuilder
|
|
|
32
32
|
sub init(frameworkInstance as Rotor.Framework)
|
|
33
33
|
' Set current locale from framework device info
|
|
34
34
|
currentLocale = frameworkInstance.getInfo().device.currentLocale
|
|
35
|
-
' Store rootWidget reference
|
|
36
|
-
m.rootWidget = frameworkInstance.getRootWidget()
|
|
37
35
|
' Store current locale
|
|
38
36
|
m.setLocale(currentLocale)
|
|
39
37
|
end sub
|
|
@@ -46,9 +44,6 @@ namespace Rotor.ViewBuilder
|
|
|
46
44
|
sub setLocale(locale as string)
|
|
47
45
|
m.locale = locale
|
|
48
46
|
m.isRTL = m.detectRTL(locale)
|
|
49
|
-
' update root widget's locale and isRTL
|
|
50
|
-
m.rootWidget.viewModelState.locale = m.locale
|
|
51
|
-
m.rootWidget.viewModelState.isRTL = m.isRTL
|
|
52
47
|
end sub
|
|
53
48
|
|
|
54
49
|
' ---------------------------------------------------------------------
|
|
@@ -87,11 +82,9 @@ namespace Rotor.ViewBuilder
|
|
|
87
82
|
' @param {object} l10n - Localization data object
|
|
88
83
|
'
|
|
89
84
|
sub setL10n(l10n as object)
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
m.rootWidget.viewModelState.l10n = m.l10n
|
|
94
|
-
|
|
85
|
+
' Keep the object reference, but replace all keys
|
|
86
|
+
m.l10n.Clear()
|
|
87
|
+
m.l10n.append(l10n)
|
|
95
88
|
m.refreshCache()
|
|
96
89
|
end sub
|
|
97
90
|
|
|
@@ -102,7 +95,6 @@ namespace Rotor.ViewBuilder
|
|
|
102
95
|
'
|
|
103
96
|
sub extendL10n(l10n as object)
|
|
104
97
|
Rotor.Utils.deepExtendAA(m.l10n, l10n)
|
|
105
|
-
m.rootWidget.viewModelState.l10n = m.l10n
|
|
106
98
|
m.refreshCache()
|
|
107
99
|
end sub
|
|
108
100
|
|
|
@@ -110,7 +102,6 @@ namespace Rotor.ViewBuilder
|
|
|
110
102
|
' destroy - Cleans up cache and l10n data
|
|
111
103
|
'
|
|
112
104
|
sub destroy()
|
|
113
|
-
m.rootWidget = invalid
|
|
114
105
|
m.cache.Clear()
|
|
115
106
|
m.l10n.Clear()
|
|
116
107
|
end sub
|
|
@@ -972,32 +972,34 @@ namespace Rotor
|
|
|
972
972
|
|
|
973
973
|
refSegmentTop = focused.metrics.segments[Rotor.Const.Segment.TOP]
|
|
974
974
|
refSegmentRight = focused.metrics.segments[Rotor.Const.Segment.RIGHT]
|
|
975
|
+
refSegmentLeft = focused.metrics.segments[Rotor.Const.Segment.LEFT]
|
|
976
|
+
refSegmentBottom = focused.metrics.segments[Rotor.Const.Segment.BOTTOM]
|
|
975
977
|
referencePoint = { x: (refSegmentTop.x1 + refSegmentRight.x2) / 2, y: (refSegmentTop.y1 + refSegmentRight.y2) / 2 }
|
|
976
978
|
|
|
977
979
|
validators = {
|
|
978
980
|
|
|
979
|
-
"left": function(referencePoint as object, segments as object) as object
|
|
981
|
+
"left": function(referencePoint as object, segments as object, refSegmentLeft as object, refSegmentRight as object) as object
|
|
980
982
|
right = segments[Rotor.Const.Segment.RIGHT]
|
|
981
|
-
'
|
|
982
|
-
return right.
|
|
983
|
+
' Candidate's right edge must be strictly left of focused element's left edge
|
|
984
|
+
return right.x2 <= refSegmentLeft.x1 ? { isValid: true, segment: right } : { isValid: false }
|
|
983
985
|
end function,
|
|
984
986
|
|
|
985
|
-
"up": function(referencePoint as object, segments as object) as object
|
|
987
|
+
"up": function(referencePoint as object, segments as object, refSegmentTop as object, refSegmentBottom as object) as object
|
|
986
988
|
bottom = segments[Rotor.Const.Segment.BOTTOM]
|
|
987
|
-
'
|
|
988
|
-
return bottom.
|
|
989
|
+
' Candidate's bottom edge must be strictly above focused element's top edge
|
|
990
|
+
return bottom.y2 <= refSegmentTop.y1 ? { isValid: true, segment: bottom } : { isValid: false }
|
|
989
991
|
end function,
|
|
990
992
|
|
|
991
|
-
"right": function(referencePoint as object, segments as object) as object
|
|
993
|
+
"right": function(referencePoint as object, segments as object, refSegmentLeft as object, refSegmentRight as object) as object
|
|
992
994
|
left = segments[Rotor.Const.Segment.LEFT]
|
|
993
|
-
'
|
|
994
|
-
return left.x1 >=
|
|
995
|
+
' Candidate's left edge must be strictly right of focused element's right edge
|
|
996
|
+
return left.x1 >= refSegmentRight.x2 ? { isValid: true, segment: left } : { isValid: false }
|
|
995
997
|
end function,
|
|
996
998
|
|
|
997
|
-
"down": function(referencePoint as object, segments as object) as object
|
|
999
|
+
"down": function(referencePoint as object, segments as object, refSegmentTop as object, refSegmentBottom as object) as object
|
|
998
1000
|
top = segments[Rotor.Const.Segment.TOP]
|
|
999
|
-
'
|
|
1000
|
-
return top.y1 >=
|
|
1001
|
+
' Candidate's top edge must be strictly below focused element's bottom edge
|
|
1002
|
+
return top.y1 >= refSegmentBottom.y2 ? { isValid: true, segment: top } : { isValid: false }
|
|
1001
1003
|
end function
|
|
1002
1004
|
}
|
|
1003
1005
|
segments = {}
|
|
@@ -1006,7 +1008,12 @@ namespace Rotor
|
|
|
1006
1008
|
if HID <> focused.HID
|
|
1007
1009
|
focusItem = m.focusItemStack.get(HID)
|
|
1008
1010
|
focusItem.refreshBounding()
|
|
1009
|
-
|
|
1011
|
+
' Pass appropriate reference segments based on direction
|
|
1012
|
+
if direction = "left" or direction = "right"
|
|
1013
|
+
result = validator(referencePoint, focusItem.metrics.segments, refSegmentLeft, refSegmentRight)
|
|
1014
|
+
else ' up or down
|
|
1015
|
+
result = validator(referencePoint, focusItem.metrics.segments, refSegmentTop, refSegmentBottom)
|
|
1016
|
+
end if
|
|
1010
1017
|
if result.isValid
|
|
1011
1018
|
segments[HID] = result.segment
|
|
1012
1019
|
end if
|