skip to content
Andrei Calazans

TV Apps — Handling Focus Styling In C++ For Performance Gains With React Native You.i

/ 4 min read

  1. Why
  2. The Current Scenario
  3. After Effects and Timelines
  4. Styling In JavaScript
  5. Styling in C++
  6. Conclusion
  7. Further
  8. Demo Code
  9. Learn more

Why

If you are building a TV app on very low-end devices like Fire TV stick and Roku sticks. And you want to animate or change the styling of a React Native item when receiving and losing focus, you might notice a lag if compared to user input. The following solution can fix this lag.

The Current Scenario

User input is handled first in C++, then the events for onFocus and onBlur are dispatched for the JavaScript callbacks to be handle them. This results in an inevitable lag. Thus, if you are applying a visual feedback in React when the item receives focus or blur, it will happen after the actual input was handled.

User input explanation architecture

Initially, the difference between when the input is handled in C++ and the dispatch of the events to JavaScript is very little. However, since the JavaScript executor is running in a separate thread, it can be busy when the events are dispatched, thus causing the lag to increase.

After Effects and Timelines

Since responding to events in C++ will result in a faster response, You.I Engine has a few default timelines in some AE widgets/components which have their timelines handled in C++. For instance, when you make use of the After Effects CYIPushButtonView, it has FocusIn and FocusOut timelines which you do not have to connect to in JavaScript for them to be played, they are automatically handled by the Engine in C++ when the events occur.

There are some cases where using AE is not desired by the team. In these cases you can explore the following alternative to animate your button in response to Focus In and Focus out.

Styling In JavaScript

What many decide to do is have a local state for focus, and when the button’s onFocus and onBlur update that state to change the styling or trigger an animation. As was stated before this results in a lag.

Styling in C++

Instead, we can add Signal listeners to the CYISceneView’s GainedFocus and LostFocus .

The following button will have no onFocus visual feedback.

class Button extends Component {
  render() {
    return (
      <TouchableWithoutFeedback onPress={this.props.onPress}>
        <View>
        <Text>Title</Text>
        <Text>Description will go here</Text>
        </View>
      </TouchableWithoutFeedback>

    )
  }
}

If we wanted to add visual feedback natively to it, we can write a Native Module achieve this.

ButtonFocusManager.h

ifndef ButtonFocusManager_h
#define ButtonFocusManager_h
#include <youireact/NativeModule.h>
#include <view/YiDecoratedView.h>

class YI_RN_MODULE(ButtonFocusManager)
{
public:
    ButtonFocusManager();

    YI_RN_EXPORT_NAME(ButtonFocusManager);

   YI_RN_EXPORT_METHOD(setItem)(uint64_t tag);
};

#endif /* ButtonFocusManager_h */

ButttonFocusManager.cpp

#include "ButtonFocusManager.h"

#include <youireact/NativeModuleRegistry.h>
#include <scenetree/YiSceneManager.h>

#include <youireact/IBridge.h>
#include <youireact/ShadowTree.h>

using namespace folly;
using namespace std;

const CYIString TAG = "ButtonFocusManager";

YI_RN_INSTANTIATE_MODULE(ButtonFocusManager);
YI_RN_REGISTER_MODULE(ButtonFocusManager);

ButtonFocusManager::ButtonFocusManager(){};

YI_RN_DEFINE_EXPORT_METHOD(ButtonFocusManager, setItem)(uint64_t tag)
{
    // ShadowRegistry contains all of the items available in the ShadowTree (similar to the virtual DOM).
    auto &shadowRegistry = GetBridge().GetShadowTree().GetShadowRegistry();
    auto pComponent = shadowRegistry.Get(tag);
    YI_ASSERT(pComponent, TAG, "Shadow view with tag %" PRIu64 " not found in ShadowRegistry.", tag);

    // For every React Native component we have a corresponding Widget (counterpart) in the Engine.
    auto pCounterpart = pComponent->GetCounterpart();
    YI_ASSERT(pCounterpart, TAG, "Shadow view with tag %" PRIu64 " doesn't have a counterpart.", tag);

    // Almost all counterparts extend CYISceneNode, CYISceneView, and CYIDecoratedView, for styling get the CYIDecoratedView. (You can get more types like CYIPushButtonView if your item corresponds to one)
    CYIDecoratedView * pSceneView = dynamic_cast<CYIDecoratedView *>(pCounterpart);
    YI_ASSERT(pSceneView, TAG, "Error -- Could not cast tag to CYISceneView, tag: %" PRIu64 "", tag);

    // The available Signal events to connect can be found in the documentation: https://developer.youi.tv/API_Docs/5.15/core/html/classCYISceneView.html#a930ea87ae241ad890fc04234f5ae2d64
    pSceneView->GainedFocus.Connect([pSceneView](){
				// You can set any styles here. See the documentation of CYIDecoratedView.
        pSceneView->SetBorderThickness(10.0f);
        pSceneView->SetBorderColor(CYIColor::FromString("red"));
    });

    pSceneView->LostFocus.Connect([pSceneView](){
        pSceneView->SetBorderColor(CYIColor::Named().Transparent);
    });
}

Result:

Gif sample of module working

Conclusion

The advantage of You.i Engine over pure React Native solution is the shared codebase not only in JavaScript but also C++ — this is by default without any extra configuration. The C++ layer allows you to achieve native performance in your cross-platform code. This advantage contrasts to vanilla React Native since React Native does not offer a single codebase in the native layer, it requires implementation in for each platform’s language.

Further

The You.i Engine’s API surface in C++ has many capabilities which React Native does not, therefore, you can follow the same approach presented here to extend your components functionality when you need so. See the API Documentation to explore the possibilities: https://developer.youi.tv/API_Docs/5.15/core/html/index.html.

Demo Code

five_fifteen.zip

Learn More

You.i is a closed source SDK that enables you to build to 11+ platforms with a single codebase either in React Native or C++. To learn more visit them here.