rotor-framework 0.7.3 β†’ 0.7.5

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.7.3-blue?label=Documents%20TAG)
99
+ ![Version](https://img.shields.io/badge/version-v0.7.5-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.7.3",
3
+ "version": "0.7.5",
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": "Apache-2.0",
@@ -4,7 +4,7 @@
4
4
  ' β–β–›β–€β–šβ––β–β–Œ β–β–Œ β–ˆ β–β–Œ β–β–Œβ–β–›β–€β–šβ–– β–β–›β–€β–€β–˜β–β–›β–€β–šβ––β–β–›β–€β–œβ–Œβ–β–Œ β–β–Œβ–β–›β–€β–€β–˜β–β–Œ β–β–Œβ–β–Œ β–β–Œβ–β–›β–€β–šβ––β–β–›β–šβ––
5
5
  ' β–β–Œ β–β–Œβ–β–šβ–„β–žβ–˜ β–ˆ β–β–šβ–„β–žβ–˜β–β–Œ β–β–Œ β–β–Œ β–β–Œ β–β–Œβ–β–Œ β–β–Œβ–β–Œ β–β–Œβ–β–™β–„β–„β––β–β–™β–ˆβ–Ÿβ–Œβ–β–šβ–„β–žβ–˜β–β–Œ β–β–Œβ–β–Œ β–β–Œ
6
6
  ' Rotor Frameworkβ„’
7
- ' Version 0.7.3
7
+ ' Version 0.7.5
8
8
  ' Β© 2025 BalΓ‘zs MolnΓ‘r β€” Apache License 2.0
9
9
  ' =========================================================================
10
10
 
@@ -86,7 +86,7 @@ namespace Rotor
86
86
  class Framework
87
87
 
88
88
  name = "Rotor Framework"
89
- version = "0.7.3"
89
+ version = "0.7.5"
90
90
 
91
91
  config = {
92
92
  tasks: invalid, ' @array (optional)
@@ -4,7 +4,7 @@
4
4
  ' β–β–›β–€β–šβ––β–β–Œ β–β–Œ β–ˆ β–β–Œ β–β–Œβ–β–›β–€β–šβ–– β–β–›β–€β–€β–˜β–β–›β–€β–šβ––β–β–›β–€β–œβ–Œβ–β–Œ β–β–Œβ–β–›β–€β–€β–˜β–β–Œ β–β–Œβ–β–Œ β–β–Œβ–β–›β–€β–šβ––β–β–›β–šβ––
5
5
  ' β–β–Œ β–β–Œβ–β–šβ–„β–žβ–˜ β–ˆ β–β–šβ–„β–žβ–˜β–β–Œ β–β–Œ β–β–Œ β–β–Œ β–β–Œβ–β–Œ β–β–Œβ–β–Œ β–β–Œβ–β–™β–„β–„β––β–β–™β–ˆβ–Ÿβ–Œβ–β–šβ–„β–žβ–˜β–β–Œ β–β–Œβ–β–Œ β–β–Œ
6
6
  ' Rotor Frameworkβ„’
7
- ' Version 0.7.3
7
+ ' Version 0.7.5
8
8
  ' Β© 2025 BalΓ‘zs MolnΓ‘r β€” Apache License 2.0
9
9
  ' =========================================================================
10
10
 
@@ -70,7 +70,7 @@ namespace Rotor
70
70
  class FrameworkTask
71
71
 
72
72
  name = "Rotor Framework"
73
- version = "0.7.3"
73
+ version = "0.7.5"
74
74
 
75
75
  config = {
76
76
  tasks: invalid, ' optional
@@ -24,6 +24,7 @@ namespace Rotor
24
24
  ' =============================================================
25
25
 
26
26
  id as string ' Widget identifier
27
+ HID as string ' Hierarchical unique ID
27
28
  children as object ' Child widgets collection
28
29
 
29
30
  ' =============================================================
@@ -100,7 +101,6 @@ namespace Rotor
100
101
  ' PRIVATE PROPERTIES (Engine Use Only)
101
102
  ' =============================================================
102
103
 
103
- private HID as string ' Hierarchical ID (e.g., "0.header.logo")
104
104
  private vmHID as string ' Owning ViewModel's HID
105
105
  private isRootChild as boolean ' True if direct child of root
106
106
  private childrenHIDhash as object ' Hash of child HIDs for fast lookup
@@ -290,10 +290,18 @@ namespace Rotor.ViewBuilder
290
290
  index = rootChildCount - 1
291
291
 
292
292
  while index >= 0 and inserted = false
293
- siblingNodeHID = siblingNodes[index].HID
294
- siblingWidget = siblingNodeHID <> invalid ? m.frameworkInstance.builder.widgetTree.get(siblingNodeHID) : invalid
295
- if Rotor.Utils.isInteger(siblingWidget?.zIndex) and siblingWidget?.zIndex <= zIndex
296
- inserted = true ' loop
293
+ siblingNode = siblingNodes[index]
294
+ ' Find matching widget by node reference
295
+ matchingWidget = invalid
296
+ for each childId in widget.parent.children
297
+ child = widget.parent.children[childId]
298
+ if child.node <> invalid and child.node.isSameNode(siblingNode)
299
+ matchingWidget = child
300
+ exit for
301
+ end if
302
+ end for
303
+ if Rotor.Utils.isInteger(matchingWidget?.zIndex) and matchingWidget?.zIndex <= zIndex
304
+ inserted = true
297
305
  else
298
306
  index--
299
307
  end if
@@ -325,7 +333,4 @@ namespace Rotor.ViewBuilder
325
333
 
326
334
  end class
327
335
 
328
-
329
-
330
-
331
336
  end namespace
@@ -121,6 +121,13 @@ namespace Rotor
121
121
  ' 2. group.defaultFocusId [CONFIGURED]
122
122
  ' 3. Deep search (if defaultFocusId not found immediately)
123
123
  '
124
+ ' RULE #11b: Deep Focus Tracking (trackDescendantFocus: true)
125
+ ' When a FocusGroup has trackDescendantFocus: true, it stores lastFocusedHID
126
+ ' for ANY descendant FocusItem (not just direct children).
127
+ ' - Immediate parent ALWAYS stores lastFocusedHID (default behavior)
128
+ ' - Ancestor groups with trackDescendantFocus: true ALSO store it
129
+ ' - Enables "deep focus memory" - returning to deeply nested items
130
+ '
124
131
  ' RULE #12: DefaultFocusId Targets
125
132
  ' - FocusItem node ID β†’ Focus goes directly to it
126
133
  ' - Group node ID β†’ Capturing continues on that group
@@ -636,6 +643,7 @@ namespace Rotor
636
643
  ' Record the last focused item within its parent group for potential future use (e.g., returning focus)
637
644
  lastFocusChainingGroups = m.findAncestorGroups(m.globalFocusHID)
638
645
  if lastFocusChainingGroups.Count() > 0
646
+ ' Always set on immediate parent (index 0)
639
647
  parentGroupHID = lastFocusChainingGroups[0]
640
648
  if parentGroupHID <> invalid and parentGroupHID <> ""
641
649
  group = m.groupStack.get(parentGroupHID)
@@ -652,9 +660,20 @@ namespace Rotor
652
660
  for each groupHID in focusChainGroups
653
661
  allAffectedGroups.unshift(groupHID) ' Add in reverse order (highest ancestor first)
654
662
  end for
655
- for each groupHID in lastFocusChainingGroups
663
+ for i = 0 to lastFocusChainingGroups.Count() - 1
664
+ groupHID = lastFocusChainingGroups[i]
665
+
666
+ ' Add to allAffectedGroups if not present
656
667
  if -1 = Rotor.Utils.findInArray(allAffectedGroups, groupHID)
657
- allAffectedGroups.unshift(groupHID) ' Add in reverse order if not already present
668
+ allAffectedGroups.unshift(groupHID)
669
+ end if
670
+
671
+ ' Deep remember (skip index 0 - immediate parent is handled separately)
672
+ if i > 0 and lastFocused <> invalid
673
+ ancestorGroup = m.groupStack.get(groupHID)
674
+ if ancestorGroup <> invalid and ancestorGroup.trackDescendantFocus = true
675
+ ancestorGroup.setLastFocusedHID(m.globalFocusHID)
676
+ end if
658
677
  end if
659
678
  end for
660
679
 
@@ -1279,11 +1298,13 @@ namespace Rotor
1279
1298
  m.defaultFocusId = config.defaultFocusId ?? ""
1280
1299
  m.lastFocusedHID = config.lastFocusedHID ?? ""
1281
1300
  m.enableSpatialEnter = config.enableSpatialEnter ?? false
1301
+ m.trackDescendantFocus = config.trackDescendantFocus ?? false
1282
1302
  end sub
1283
1303
 
1284
1304
  defaultFocusId as string
1285
1305
  lastFocusedHID as string
1286
1306
  enableSpatialEnter as boolean
1307
+ trackDescendantFocus as boolean
1287
1308
  focusItemsRef as object
1288
1309
  groupsRef as object
1289
1310