Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions ios/RNMagicScript.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
/* Begin PBXBuildFile section */
3DEF99B5FD3ABA197D66C4F6 /* Pods_RNMagicScriptHostApplication.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D90B77C03B61C2B979129A93 /* Pods_RNMagicScriptHostApplication.framework */; };
3FDF269A199C97F94F40505D /* Pods_RNMagicScriptTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E9A28791FDC1824D1E2A2F18 /* Pods_RNMagicScriptTests.framework */; };
44050D9325873EBF0004F8FB /* FocusIntercepting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44050D9225873EBF0004F8FB /* FocusIntercepting.swift */; };
44050D9625873EC70004F8FB /* FocusIntercepting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44050D9225873EBF0004F8FB /* FocusIntercepting.swift */; };
4415977C23C8868C008EDD3C /* SCNNinePatchSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4415977B23C8868C008EDD3C /* SCNNinePatchSpec.swift */; };
4415DA3023A146F00096072B /* SCNNode+Enumerate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4415DA2F23A146F00096072B /* SCNNode+Enumerate.swift */; };
4415DA3123A146F00096072B /* SCNNode+Enumerate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4415DA2F23A146F00096072B /* SCNNode+Enumerate.swift */; };
Expand Down Expand Up @@ -638,6 +640,7 @@
/* Begin PBXFileReference section */
134814201AA4EA6300B7C361 /* libRNMagicScript.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNMagicScript.a; sourceTree = BUILT_PRODUCTS_DIR; };
1E12ED9B0BE34039931966BA /* Pods-RNMagicScriptTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RNMagicScriptTests.release.xcconfig"; path = "Target Support Files/Pods-RNMagicScriptTests/Pods-RNMagicScriptTests.release.xcconfig"; sourceTree = "<group>"; };
44050D9225873EBF0004F8FB /* FocusIntercepting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FocusIntercepting.swift; sourceTree = "<group>"; };
4415977B23C8868C008EDD3C /* SCNNinePatchSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SCNNinePatchSpec.swift; sourceTree = "<group>"; };
4415DA2F23A146F00096072B /* SCNNode+Enumerate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SCNNode+Enumerate.swift"; sourceTree = "<group>"; };
441EA5A12347797A00EDD033 /* SystemIcon+Names.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SystemIcon+Names.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1433,6 +1436,7 @@
C46014B023C7356F002A63D4 /* PlaneDection */,
44AFAC25232B9BCF005AC8EF /* Bundle.swift */,
44AFAC19232B9BCF005AC8EF /* FileDownloader.swift */,
44050D9225873EBF0004F8FB /* FocusIntercepting.swift */,
44AFAC1A232B9BCF005AC8EF /* ImageAssets.swift */,
C4C3D7482355D3E500ECEEDA /* TapSimulating.swift */,
C419CF922359A7B2000094C3 /* NodeConfiguration.swift */,
Expand Down Expand Up @@ -2394,6 +2398,7 @@
C4633E23246DDDE200944F14 /* OperationMode.swift in Sources */,
44AFAD33232BCAE3005AC8EF /* NodesManager.swift in Sources */,
44A59E89233DF884006A15D0 /* MultiLineTextAccessoryView.swift in Sources */,
44050D9625873EC70004F8FB /* FocusIntercepting.swift in Sources */,
C4B73CB4238BAB610080D3F3 /* TransformNodeContainer.swift in Sources */,
44890E1A2413EFB500903629 /* PrismOutlineNode.swift in Sources */,
44AFAD1B232BCAE3005AC8EF /* UiLineNode.swift in Sources */,
Expand Down Expand Up @@ -2828,6 +2833,7 @@
C428C0A8237D560500771FBC /* UiDialogNode.swift in Sources */,
44301CC223AC165C001A1766 /* GroupContainer.swift in Sources */,
44890E192413EFB500903629 /* PrismOutlineNode.swift in Sources */,
44050D9325873EBF0004F8FB /* FocusIntercepting.swift in Sources */,
44739B992398034300EC31A0 /* NumberFormatter+Extension.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
38 changes: 19 additions & 19 deletions ios/RNMagicScript/bridge/ARComponentManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ - (void)registerNode:(TransformNode *)node nodeId:(NSString *)nodeId {

// MARK: - UiScrollViewNode event handlers
RCT_EXPORT_METHOD(addOnScrollChangedEventHandler:(NSString *)nodeId) {
TransformNode *node = [NodesManager.instance findNodeWithId:nodeId];
BaseNode *node = [NodesManager.instance findNodeWithId:nodeId];
if (node && [node isKindOfClass:[UiScrollViewNode class]]) {
((UiScrollViewNode *)node).onScrollChanged = ^(UiNode *sender, CGFloat value) {
ARLog(@"scrollView changed: %@", @(value));
Expand All @@ -378,7 +378,7 @@ - (void)registerNode:(TransformNode *)node nodeId:(NSString *)nodeId {

// MARK: - UiTextEditNode event handlers
RCT_EXPORT_METHOD(addOnTextChangedEventHandler:(NSString *)nodeId) {
TransformNode *node = [NodesManager.instance findNodeWithId:nodeId];
BaseNode *node = [NodesManager.instance findNodeWithId:nodeId];
if (node && [node isKindOfClass:[UiTextEditNode class]]) {
((UiTextEditNode *)node).onTextChanged = ^(UiNode *sender, NSString *text) {
ARLog(@"textEdit changed: %@", text);
Expand All @@ -389,7 +389,7 @@ - (void)registerNode:(TransformNode *)node nodeId:(NSString *)nodeId {

// MARK: - UiToggleNode event handlers
RCT_EXPORT_METHOD(addOnToggleChangedEventHandler:(NSString *)nodeId) {
TransformNode *node = [NodesManager.instance findNodeWithId:nodeId];
BaseNode *node = [NodesManager.instance findNodeWithId:nodeId];
if (node && [node isKindOfClass:[UiToggleNode class]]) {
((UiToggleNode *)node).onChanged = ^(UiNode *sender, BOOL on) {
ARLog(@"toggle onChanged: %@", on ? @"on" : @"off");
Expand All @@ -400,7 +400,7 @@ - (void)registerNode:(TransformNode *)node nodeId:(NSString *)nodeId {

// MARK: - UiVideoNode event handlers
RCT_EXPORT_METHOD(addOnVideoPreparedEventHandler:(NSString *)nodeId) {
TransformNode *node = [NodesManager.instance findNodeWithId:nodeId];
BaseNode *node = [NodesManager.instance findNodeWithId:nodeId];
if (node && [node isKindOfClass:[UiVideoNode class]]) {
((UiVideoNode *)node).onVideoPrepared = ^(UiVideoNode *sender, NSString *videoURL) {
ARLog(@"video onPrepared");
Expand All @@ -411,7 +411,7 @@ - (void)registerNode:(TransformNode *)node nodeId:(NSString *)nodeId {

// MARK: - UiDropdownListNode event handlers
RCT_EXPORT_METHOD(addOnSelectionChangedEventHandler:(NSString *)nodeId) {
TransformNode *node = [NodesManager.instance findNodeWithId:nodeId];
BaseNode *node = [NodesManager.instance findNodeWithId:nodeId];
if (node && [node isKindOfClass:[UiDropdownListNode class]]) {
((UiDropdownListNode *)node).onSelectionChanged = ^(UiDropdownListNode *sender, NSArray<UiDropdownListItemNode *> *selectedItems) {
ARLog(@"DropdownList item selected");
Expand All @@ -422,7 +422,7 @@ - (void)registerNode:(TransformNode *)node nodeId:(NSString *)nodeId {

// MARK: - UiSliderNode event handlers
RCT_EXPORT_METHOD(addOnSliderChangedEventHandler:(NSString *)nodeId) {
TransformNode *node = [NodesManager.instance findNodeWithId:nodeId];
BaseNode *node = [NodesManager.instance findNodeWithId:nodeId];
if (node && [node isKindOfClass:[UiSliderNode class]]) {
((UiSliderNode *)node).onSliderChanged = ^(UiSliderNode *sender, CGFloat value) {
ARLog(@"slider changed: %@", @(value));
Expand All @@ -433,7 +433,7 @@ - (void)registerNode:(TransformNode *)node nodeId:(NSString *)nodeId {

// MARK: - UiDatePickerNode event handlers
RCT_EXPORT_METHOD(addOnDateChangedEventHandler:(NSString *)nodeId) {
TransformNode *node = [NodesManager.instance findNodeWithId:nodeId];
BaseNode *node = [NodesManager.instance findNodeWithId:nodeId];
if (node && [node isKindOfClass:[UiDatePickerNode class]]) {
((UiDatePickerNode *)node).onDateChanged = ^(UiDatePickerNode *sender, NSString *value) {
ARLog(@"datePicker changed: %@", value);
Expand All @@ -443,7 +443,7 @@ - (void)registerNode:(TransformNode *)node nodeId:(NSString *)nodeId {
}

RCT_EXPORT_METHOD(addOnDateConfirmedEventHandler:(NSString *)nodeId) {
TransformNode *node = [NodesManager.instance findNodeWithId:nodeId];
BaseNode *node = [NodesManager.instance findNodeWithId:nodeId];
if (node && [node isKindOfClass:[UiDatePickerNode class]]) {
((UiDatePickerNode *)node).onDateConfirmed = ^(UiDatePickerNode *sender, NSString *value) {
ARLog(@"datePicker confirmed: %@", value);
Expand All @@ -454,7 +454,7 @@ - (void)registerNode:(TransformNode *)node nodeId:(NSString *)nodeId {

// MARK: - UiTimePickerNode event handlers
RCT_EXPORT_METHOD(addOnTimeChangedEventHandler:(NSString *)nodeId) {
TransformNode *node = [NodesManager.instance findNodeWithId:nodeId];
BaseNode *node = [NodesManager.instance findNodeWithId:nodeId];
if (node && [node isKindOfClass:[UiTimePickerNode class]]) {
((UiTimePickerNode *)node).onTimeChanged = ^(UiTimePickerNode *sender, NSString *value) {
ARLog(@"timePicker changed: %@", value);
Expand All @@ -464,7 +464,7 @@ - (void)registerNode:(TransformNode *)node nodeId:(NSString *)nodeId {
}

RCT_EXPORT_METHOD(addOnTimeConfirmedEventHandler:(NSString *)nodeId) {
TransformNode *node = [NodesManager.instance findNodeWithId:nodeId];
BaseNode *node = [NodesManager.instance findNodeWithId:nodeId];
if (node && [node isKindOfClass:[UiTimePickerNode class]]) {
((UiTimePickerNode *)node).onTimeConfirmed = ^(UiTimePickerNode *sender, NSString *value) {
ARLog(@"timePicker confirmed: %@", value);
Expand All @@ -475,7 +475,7 @@ - (void)registerNode:(TransformNode *)node nodeId:(NSString *)nodeId {

// MARK: - UiColorPickerNode event handlers
RCT_EXPORT_METHOD(addOnColorChangedEventHandler:(NSString *)nodeId) {
TransformNode *node = [NodesManager.instance findNodeWithId:nodeId];
BaseNode *node = [NodesManager.instance findNodeWithId:nodeId];
if (node && [node isKindOfClass:[UiColorPickerNode class]]) {
((UiColorPickerNode *)node).onColorChanged = ^(UiColorPickerNode *sender, NSArray<NSNumber *> *value) {
ARLog(@"colorPicker changed: %@", value);
Expand All @@ -485,7 +485,7 @@ - (void)registerNode:(TransformNode *)node nodeId:(NSString *)nodeId {
}

RCT_EXPORT_METHOD(addOnColorConfirmedEventHandler:(NSString *)nodeId) {
TransformNode *node = [NodesManager.instance findNodeWithId:nodeId];
BaseNode *node = [NodesManager.instance findNodeWithId:nodeId];
if (node && [node isKindOfClass:[UiColorPickerNode class]]) {
((UiColorPickerNode *)node).onColorConfirmed = ^(UiColorPickerNode *sender, NSArray<NSNumber *> *value) {
ARLog(@"colorPicker confirmed: %@", value);
Expand All @@ -495,7 +495,7 @@ - (void)registerNode:(TransformNode *)node nodeId:(NSString *)nodeId {
}

RCT_EXPORT_METHOD(addOnColorCanceledEventHandler:(NSString *)nodeId) {
TransformNode *node = [NodesManager.instance findNodeWithId:nodeId];
BaseNode *node = [NodesManager.instance findNodeWithId:nodeId];
if (node && [node isKindOfClass:[UiColorPickerNode class]]) {
((UiColorPickerNode *)node).onColorCanceled = ^(UiColorPickerNode *sender, NSArray<NSNumber *> *value) {
ARLog(@"colorPicker canceled: %@");
Expand All @@ -506,7 +506,7 @@ - (void)registerNode:(TransformNode *)node nodeId:(NSString *)nodeId {

// MARK: - UiDialogNode event handlers
RCT_EXPORT_METHOD(addOnDialogConfirmedEventHandler:(NSString *)nodeId) {
TransformNode *node = [NodesManager.instance findNodeWithId:nodeId];
BaseNode *node = [NodesManager.instance findNodeWithId:nodeId];
if (node && [node isKindOfClass:[UiDialogNode class]]) {
((UiDialogNode *)node).onDialogConfirmed = ^(UiDialogNode *sender) {
ARLog(@"dialogNode confirmed: %@");
Expand All @@ -516,7 +516,7 @@ - (void)registerNode:(TransformNode *)node nodeId:(NSString *)nodeId {
}

RCT_EXPORT_METHOD(addOnDialogCanceledEventHandler:(NSString *)nodeId) {
TransformNode *node = [NodesManager.instance findNodeWithId:nodeId];
BaseNode *node = [NodesManager.instance findNodeWithId:nodeId];
if (node && [node isKindOfClass:[UiDialogNode class]]) {
((UiDialogNode *)node).onDialogCanceled = ^(UiDialogNode *sender) {
ARLog(@"dialogNode canceled: %@");
Expand All @@ -526,7 +526,7 @@ - (void)registerNode:(TransformNode *)node nodeId:(NSString *)nodeId {
}

RCT_EXPORT_METHOD(addOnDialogTimeExpiredEventHandler:(NSString *)nodeId) {
TransformNode *node = [NodesManager.instance findNodeWithId:nodeId];
BaseNode *node = [NodesManager.instance findNodeWithId:nodeId];
if (node && [node isKindOfClass:[UiDialogNode class]]) {
((UiDialogNode *)node).onDialogTimeExpired = ^(UiDialogNode *sender) {
ARLog(@"dialogNode timeExpired: %@");
Expand All @@ -537,7 +537,7 @@ - (void)registerNode:(TransformNode *)node nodeId:(NSString *)nodeId {

// MARK: - UiCircleConfirmationNode event handlers
RCT_EXPORT_METHOD(addOnConfirmationCompletedEventHandler:(NSString *)nodeId) {
TransformNode *node = [NodesManager.instance findNodeWithId:nodeId];
BaseNode *node = [NodesManager.instance findNodeWithId:nodeId];
if (node && [node isKindOfClass:[UiCircleConfirmationNode class]]) {
((UiCircleConfirmationNode *)node).onConfirmationCompleted = ^(UiCircleConfirmationNode *sender) {
ARLog(@"circleConfirmationNode completed: %@");
Expand All @@ -547,7 +547,7 @@ - (void)registerNode:(TransformNode *)node nodeId:(NSString *)nodeId {
}

RCT_EXPORT_METHOD(addOnConfirmationUpdatedEventHandler:(NSString *)nodeId) {
TransformNode *node = [NodesManager.instance findNodeWithId:nodeId];
BaseNode *node = [NodesManager.instance findNodeWithId:nodeId];
if (node && [node isKindOfClass:[UiCircleConfirmationNode class]]) {
((UiCircleConfirmationNode *)node).onConfirmationUpdated = ^(UiCircleConfirmationNode *sender, CGFloat value) {
ARLog(@"circleConfirmationNode completed: %@");
Expand All @@ -557,7 +557,7 @@ - (void)registerNode:(TransformNode *)node nodeId:(NSString *)nodeId {
}

RCT_EXPORT_METHOD(addOnConfirmationCanceledEventHandler:(NSString *)nodeId) {
TransformNode *node = [NodesManager.instance findNodeWithId:nodeId];
BaseNode *node = [NodesManager.instance findNodeWithId:nodeId];
if (node && [node isKindOfClass:[UiCircleConfirmationNode class]]) {
((UiCircleConfirmationNode *)node).onConfirmationCanceled = ^(UiCircleConfirmationNode *sender) {
ARLog(@"circleConfirmationNode canceled: %@");
Expand Down
51 changes: 35 additions & 16 deletions ios/RNMagicScript/components/UiNodeSelector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,24 +31,22 @@ class UiNodeSelector: NodeSelecting {
}

func hitTest(ray: Ray) -> HitTestResult? {
let topNodes: [BaseNode] = rootNode.childNodes.filter { $0 is BaseNode }.map { $0 as! BaseNode }
var hitResults: [HitTestResult] = []

for node in topNodes {
if let hitResult = node.hitTest(ray: ray) {
hitResults.append(hitResult)
}
// Check if any component has intercepted focus (e.g. DropdownListNode)
let focusInterceptedNodes = collectFocusInterceptedNodes()
let focusInterceptedHitResults: [HitTestResult] = focusInterceptedNodes.compactMap { $0.hitTest(ray: ray) }
if let hitResult = getClosestHitResult(focusInterceptedHitResults, ray: ray) {
return hitResult
}

hitResults.sort { (hitResult1, hitResult2) -> Bool in
let worldPosition1 = hitResult1.node.convertPosition(hitResult1.point, to: nil)
let worldPosition2 = hitResult2.node.convertPosition(hitResult2.point, to: nil)
let dist1 = (worldPosition1 - ray.begin).lengthSq()
let dist2 = (worldPosition2 - ray.begin).lengthSq()
return dist1 < dist2

// If there is no hitResults for focus-intercepted nodes, we still want to return any focus-intercepted node
if let firstNode = focusInterceptedNodes.first {
return (node: firstNode, point: .zero)
}

return hitResults.first

// Check hit test in a standard way
let topNodes: [BaseNode] = rootNode.childNodes.compactMap { $0 as? BaseNode }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will this code ever but hit? I think it will only be hit if focusInterceptedNodes is empty.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's the goal of this code:

  • if there is any component which has intercepted the focus (ex. dropdown List is showing its list), then return that component (so that it can handle the input: either select list item or dismiss the list)
  • else do standard hitTest

let hitResults: [HitTestResult] = topNodes.compactMap { $0.hitTest(ray: ray) }
return getClosestHitResult(hitResults, ray: ray)
}

func draggingHitTest(ray: Ray) -> Dragging? {
Expand All @@ -65,4 +63,25 @@ class UiNodeSelector: NodeSelecting {

return node as? Dragging
}

func collectFocusInterceptedNodes() -> [BaseNode] {
var result: [BaseNode] = []
rootNode.enumerateBaseNodes { node in
if let focusedNode = node as? FocusIntercepting, focusedNode.isFocusIntercepted {
result.append(node)
}
}

return result
}

private func getClosestHitResult(_ hitResults: [HitTestResult], ray: Ray) -> HitTestResult? {
return hitResults.sorted { (hitResult1, hitResult2) -> Bool in
let worldPosition1 = hitResult1.node.convertPosition(hitResult1.point, to: nil)
let worldPosition2 = hitResult2.node.convertPosition(hitResult2.point, to: nil)
let dist1 = (worldPosition1 - ray.begin).lengthSq()
let dist2 = (worldPosition2 - ray.begin).lengthSq()
return dist1 < dist2
}.first
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -344,3 +344,5 @@ extension UiDropdownListNode: DropdownListItemTapHandling {
}

extension UiDropdownListNode: TapSimulating { }

extension UiDropdownListNode: FocusIntercepting { }
25 changes: 25 additions & 0 deletions ios/RNMagicScript/components/Utils/FocusIntercepting.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// Copyright (c) 2019-2020 Magic Leap, Inc. All Rights Reserved
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import Foundation

protocol FocusIntercepting {
var isFocusIntercepted: Bool { get }
}

extension FocusIntercepting where Self: UiNode {
var isFocusIntercepted: Bool { return hasFocus }
}
Loading