rotor-framework 0.7.5 β†’ 0.7.6

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.5-blue?label=Documents%20TAG)
99
+ ![Version](https://img.shields.io/badge/version-v0.7.6-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.5",
3
+ "version": "0.7.6",
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.5
7
+ ' Version 0.7.6
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.5"
89
+ version = "0.7.6"
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.5
7
+ ' Version 0.7.6
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.5"
73
+ version = "0.7.6"
74
74
 
75
75
  config = {
76
76
  tasks: invalid, ' optional
@@ -797,17 +797,12 @@ namespace Rotor
797
797
  if group = invalid then return ""
798
798
 
799
799
  ' Get fallback identifier for this group
800
- newHID = group.getFallbackIdentifier()
800
+ ' (enableSpatialEnter groups return the spatially closest member here)
801
+ newHID = group.getFallbackIdentifier(m.globalFocusHID)
801
802
 
802
803
  ' Check if we found a FocusItem
803
804
  if m.focusItemStack.has(newHID)
804
- ' Apply spatial enter feature if enabled
805
- if group.enableSpatialEnter = true and direction <> ""
806
- focused = m.focusItemStack.get(m.globalFocusHID)
807
- newSpatialHID = m.spatialNavigation(focused, direction, group.getGroupMembersHIDs())
808
- if newSpatialHID <> "" then newHID = newSpatialHID
809
- end if
810
-
805
+ ' noop β€” direct focusItem resolved
811
806
  else if newHID <> ""
812
807
  ' Try to find as group first, then deep search
813
808
  newHID = m.capturingFocus_recursively(newHID, direction, group.HID)
@@ -819,7 +814,8 @@ namespace Rotor
819
814
  end if
820
815
 
821
816
  ' Prevent capturing by fallback in the same group where original focus was
822
- if newHID <> "" and m.globalFocusHID <> ""
817
+ ' Skip this guard for enableSpatialEnter groups (spatial enter explicitly targets a sibling group's member)
818
+ if not group.enableSpatialEnter and newHID <> "" and m.globalFocusHID <> ""
823
819
  currentAncestors = m.findAncestorGroups(m.globalFocusHID)
824
820
  newAncestors = m.findAncestorGroups(newHID)
825
821
  if currentAncestors.Count() > 0 and newAncestors.Count() > 0
@@ -923,7 +919,6 @@ namespace Rotor
923
919
  end if
924
920
  ' Prevent any navigation if it is disabled
925
921
  #if debug
926
- if m.enableFocusNavigation = false and press = true then print "[PLUGIN][FOCUS][INFO] Focus navigation is disabled. Call enableFocusNavigation(true) to make it enabled"
927
922
  #end if
928
923
  if m.enableFocusNavigation = false then return m.parseOnKeyEventResult(key, false, false)
929
924
  ' Execute action according to key press
@@ -1307,7 +1302,6 @@ namespace Rotor
1307
1302
  trackDescendantFocus as boolean
1308
1303
  focusItemsRef as object
1309
1304
  groupsRef as object
1310
-
1311
1305
  isFocusItem = false
1312
1306
  isGroup = true
1313
1307
 
@@ -1368,34 +1362,66 @@ namespace Rotor
1368
1362
  end if
1369
1363
  end function
1370
1364
 
1371
- function getFallbackIdentifier() as string
1365
+ function getFallbackIdentifier(globalFocusHID = "" as string) as string
1372
1366
  HID = ""
1373
- if m.lastFocusedHID <> ""
1367
+ ' enableSpatialEnter takes priority over lastFocusedHID
1368
+ ' (lastFocusedHID may be stale from slot recycling)
1369
+ if not m.enableSpatialEnter and m.lastFocusedHID <> ""
1374
1370
  return m.lastFocusedHID
1375
- else
1376
- if Rotor.Utils.isFunction(m.defaultFocusId)
1377
- defaultFocusId = Rotor.Utils.callbackScoped(m.defaultFocusId, m.widget) ?? ""
1378
- else
1379
- defaultFocusId = m.defaultFocusId
1380
- end if
1381
-
1382
- if defaultFocusId <> ""
1383
- focusItemsHIDlist = m.getGroupMembersHIDs()
1384
- if focusItemsHIDlist.Count() > 0
1371
+ end if
1385
1372
 
1386
- ' Try find valid HID in focusItems by node id
1387
- focusItemHID = m.findHIDinFocusItemsByNodeId(defaultFocusId, focusItemsHIDlist)
1388
- if focusItemHID <> ""
1389
- HID = focusItemHID
1373
+ ' enableSpatialEnter: return the spatially closest member to the current focus
1374
+ if m.enableSpatialEnter = true and globalFocusHID <> ""
1375
+ prevFocused = m.focusItemsRef.get(globalFocusHID)
1376
+ if prevFocused <> invalid
1377
+ prevFocused.refreshBounding()
1378
+ refPoint = prevFocused.metrics.middlePoint
1379
+ members = m.getGroupMembersHIDs()
1380
+ minDist = 2147483647
1381
+ closestHID = ""
1382
+ for each memberHID in members
1383
+ if memberHID <> globalFocusHID
1384
+ member = m.focusItemsRef.get(memberHID)
1385
+ if member <> invalid
1386
+ member.refreshBounding()
1387
+ dx = member.metrics.middlePoint.x - refPoint.x
1388
+ dy = member.metrics.middlePoint.y - refPoint.y
1389
+ dist = dx * dx + dy * dy
1390
+ if dist < minDist
1391
+ minDist = dist
1392
+ closestHID = memberHID
1393
+ end if
1394
+ end if
1390
1395
  end if
1396
+ end for
1397
+ if closestHID <> ""
1398
+ return closestHID
1399
+ end if
1400
+ end if
1401
+ end if
1391
1402
 
1392
- else
1403
+ ' Default: use defaultFocusId expression
1404
+ if Rotor.Utils.isFunction(m.defaultFocusId)
1405
+ defaultFocusId = Rotor.Utils.callbackScoped(m.defaultFocusId, m.widget) ?? ""
1406
+ else
1407
+ defaultFocusId = m.defaultFocusId
1408
+ end if
1393
1409
 
1394
- return defaultFocusId
1410
+ if defaultFocusId <> ""
1411
+ focusItemsHIDlist = m.getGroupMembersHIDs()
1412
+ if focusItemsHIDlist.Count() > 0
1395
1413
 
1414
+ ' Try find valid HID in focusItems by node id
1415
+ focusItemHID = m.findHIDinFocusItemsByNodeId(defaultFocusId, focusItemsHIDlist)
1416
+ if focusItemHID <> ""
1417
+ HID = focusItemHID
1396
1418
  end if
1397
- end if
1398
1419
 
1420
+ else
1421
+
1422
+ return defaultFocusId
1423
+
1424
+ end if
1399
1425
  end if
1400
1426
 
1401
1427
  return HID