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 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
- ![Version](https://img.shields.io/badge/version-v0.4.0-blue?label=Documents%20TAG)
99
+ ![Version](https://img.shields.io/badge/version-v0.4.2-blue?label=Documents%20TAG)
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.0",
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.0
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.0"
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.0
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.0"
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 ' Get dispatcher facade by ID
76
- createDispatcher as Function ' Create dispatcher
77
- animator as Function ' Get animator factory
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
- m.l10n = l10n
91
-
92
- ' Update root widget's l10n reference
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
- ' stop
982
- return right.x1 <= referencePoint.x ? { isValid: true, segment: right } : { isValid: false }
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
- ' stop
988
- return bottom.y1 <= referencePoint.y ? { isValid: true, segment: bottom } : { isValid: false }
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
- ' stop
994
- return left.x1 >= referencePoint.x ? { isValid: true, segment: left } : { isValid: false }
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
- ' stop
1000
- return top.y1 >= referencePoint.y ? { isValid: true, segment: top } : { isValid: false }
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
- result = validator(referencePoint, focusItem.metrics.segments)
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