Summary: Small optimization that prevents empty NodeRegions from being collected. Those will never receive touch events anyway, so why bother allocating memory and iterating over them?
Reviewed By: ahmedre
Differential Revision: D2816355
Summary: RCTText may modify its DrawCommand on collectState(), which updateNodeRegion() relies on. This means that order of the two is important: call collectState() first, followed by updateNodeRegion(). This fixes the bug where am RCTText may sometimes not respond to clicks (because its NodeRegion would be empty).
Reviewed By: ahmedre
Differential Revision: D2816295
Summary: NodeRegions are touch regions within hosting View, and while in most cases they are the same as View boundaries, there is one case where it's not true: TextNodeRegion. When mounted to a View, TextNodeRegion will have a bounds of (0,0,width,height) which is clearly different from (left,top,right,bottom). Initially I assumed they would always be the same so we could use information stored in NodeRegion (should probably be called TouchRegion) to update node's View boundaries, but it breaks RCTTextView when it mount to a View (because it would either contain incorrect bounds, or View will be laid out incorrectly). Right now touch is not working on RCTView that mounts to a View. To fix the issue, separate the 2 concepts.
Reviewed By: ahmedre
Differential Revision: D2816268
Summary:
Everything (but Views) are drawn using AbstractDrawCommand (or its derivative, like DrawBorder or DrawBackgroundColor) and supports clipping properly. Views however are drawn using an assumption that Android will clip them manually. This is however not always true. One example is if an element A mounts to a View, and its parent B doesn't but has overflow: hidden and thus should clip the child.
There are 2 ways to fix this:
- pop every element that has overflow: hidden to its own View. In this case, its children will always be correctly clipped. This is however very inefficient, especially if overflow: hidden is default (which it is!) which means that almost every React element must be backed by an Android View.
- add clipping information to DrawView, similar to how AbstractDrawCommand has it.
This diff implements the latter approach.
Reviewed By: ahmedre
Differential Revision: D2792375
Summary: When setJSResponder() is called on a shadow node that doesn't mount to a View, React runtime will crash in NativeViewHierarchyManager because it will fail to find a corresponding View. To fix the issue, make sure we forceMountToView() before we call enqueueSetJSResponder().
Reviewed By: ahmedre
Differential Revision: D2779523
Summary:
Prior to this patch DrawCommands weren't get clipped by parent DrawCommands at all. For example, a <View> element with size 200x200 with overflow:hidden should clip its child which is 400x400, but this didn't happen. However, if parent <View> would mount to an Android View, it would clip the child regardless of the overflow attribute value (because Android Views always clip whatever is drawing inside that View against its boundaries).
This diff is fixing these issue, implementing overflow attribute support and making clipping behavior consistent between nodes that mount to View and nodes that don't.
Reviewed By: ahmedre
Differential Revision: D2768643
Summary: There is an OnLayoutEvent that needs to be dispatched when a ReactShadowNode gets re-laid out. Some applications rely on it, so we should support it. This diff adds this functionality.
Reviewed By: ahmedre
Differential Revision: D2768625
Summary: NodeRegion is only able to describe a rectangular region for touch, which is not good enough for text, where we want to be able to assign different touch ids to individual words (and those can span more than one line and in general have non-rectangular structure). This diff adds TextNodeRegion which inserts additional markers into text Layout to allow individual words to have unique react tags.
Reviewed By: ahmedre
Differential Revision: D2757387
Summary:
In ReactNative, we are fully controlling layout of all the Views, not allowing Android to layout anything for us. This is done by making onLayout() of the top-level View in the hierarchy to be empty. This works fine because we explicitly call measure/layout for all the Views when they need to be re-measured or re-laid out. There is however one case where this doesn't happen automatically: some Android Views such as DrawerLayout or ActionBar have children that don't have shadow nodes associated with them (such as a title in ActionBar). This results in situations where children of AndroidView will call requestLayout but they will never get relaid out, because shadow hierarchy doesn't know about them. Example: ActionBar has a seTitle method that will internally call TextView.setTitle() and that TextView will call requestLayout because its size may have changed. However, that TextView will never be remeasured or relaid out.
This diff is fixing it by keeping track of everyone who called requestLayout. Then, at the end of the update loop we go over the list a manually remeasure and relayout those Views.
Not a huge fan of how this is implemented (there MUST be a better way) but this works with least efforts. I'll see if I can improve it later.
Reviewed By: ahmedre
Differential Revision: D2757485
Summary:
There is currently a bug where we never release any Views that no longer display, still storing hard references in NativeViewHierarchyManager. This diff is fixing this bug, and here is how:
a) there is already logic in place to drop FlatShadowNodes (UIImplementation.removeShadowNode).
b) there is already logic in place to drop Views (NativeViewHierarchyManager.dropView(int reactTag) - used to private but needs to be protected so I can call it)
c) (the missing part) when we are about to drop a FlatShadowNode, check if it mount to a View (i.e. there is a View associated with that node), put it into a ArrayList. When we finished updates to a nodes hierarchy (which happens in non-UI thread), collect ids from those nodes and enqueue a UIOperation that will drop all the Views. We can either forward nodes as FlatShadowNode[], or only ids as int[]. Both should be fine, but as a rule of thumb we don't touch shadow node hierarchy from UI thread (as we don't touch Views from non-UI thread) so passing int[] is what this code is doing.
Reviewed By: ahmedre
Differential Revision: D2757310
Summary: Virtual nodes (such as RCTRawText and RCTVirtualText) have no state or node regions, don't need to be laid out, and thus should not be checked for state.
Reviewed By: ahmedre
Differential Revision: D2757089
Summary: Any padding that a FlatShadowNode is assigned by React runtime should be translated to a backing Android View so it looks correct and lays out children accordingly.
Reviewed By: sriramramani
Differential Revision: D2756562
Summary: This diff adds an `AndroidView` as a proxy for custom Views in FlatUIImplementation. Any ReactShadowNode that FlatUIImplementation doesn't recognize (because they don't extend from FlatShadowNode) will be wrapped with AndroidView to ensure that it measures and displays correctly. While not perfect, this is the easiest way to support custom Views (EditTexts, DrawerLayouts, ScrollViews etc).
Reviewed By: ahmedre
Differential Revision: D2751716
Summary: Normally, order or `measure/layout` and `onAttachedToWindow` shouldn't matter. However, `DrawerLayout` has a `boolean mFirstLayout` flag that it resets to true in `onAttachedToWindow` that makes it ignore first layout, and it leads to bugs. To fix the issue, we need to make sure that we first call `onAttachedToWindow` and only then we call `measure/layout`. The easiest way to do it is to delay measure/layout calls until all the views are attached to their parents. This diff implements the mentioned logic.
Reviewed By: sriramramani
Differential Revision: D2694973
Summary: @public There are some properties that we want to handle on a View level, as opposed to a FlatShadowNode level. For example, scale or alpha, that can be done very efficiently in hardware. Once we pop FlatShadowNode to a separate View, we need to apply these properties. This is where `BaseViewManager` comes in handy.
Reviewed By: sriramramani
Differential Revision: D2694290
Summary: @public When Android dispatches `MotionEvent` to `ReactRootView`, it needs to find a correspoding react node that should receive it. To be able to do it, we need to store boundaries of every `FlatShadowNode` in `FlatViewGroup`. Then we can iterate over node boundaries and find one that contains the touch event coordinates.
Reviewed By: sriramramani
Differential Revision: D2694197
Summary: @public This diff adds a `FlatShadowNode.forceMountToView()` method that will render its contents in it own `View`.
Reviewed By: sriramramani
Differential Revision: D2564502
Summary: @public This patch adds basic support for RCTImageView (only 'src', 'tintColor' and 'resizeMode' properties are supported for now), and a concept of AttachDetachListener that is required to support it to FlatUIImplementations.
Reviewed By: sriramramani
Differential Revision: D2564389
Summary: @public This is a pure refactoring diff makes `StateBuilder` code a little bit easier to read. This gets increasingly important as new features with similar logic are added to `StateBuilder`.
Reviewed By: sriramramani
Differential Revision: D2564342
Summary: @public Initial version of FlatUIImplementation lacks any primitives support (such as RCTText, RCTImageView or RCTView). This diff add the first part, RCTText (alongside with RCTVirtualText and RCTRawText).
Reviewed By: sriramramani
Differential Revision: D2693348