Summary: Previously, we had no information about the positioning of the view until after we had attached it. We have the position information attached to the shadow node, but this attaches it to the DrawView as well. It also removes the need for AbstractClippingDrawCommand.
Reviewed By: ahmedre
Differential Revision: D3609092
Summary: Since Nodes' manageChildren doesn't enqueue the child updates immediately, commands were being directed to non-updated views. Previously we applied updates for the shadow node before dispatching the command, but we can instead wait to fire commands until after we update the view hierarchy.
Reviewed By: ahmedre
Differential Revision: D3568541
Summary:
Previously, to fix the issue of commands happening before the Views
were made and attached to the hierarchy, a check was added to see if a node
had not been mounted to a View, to update its hierarchy. In reality, we need
to do this irrespective, since a node could be mounted to a View, but its
children may not yet be attached, for example. Note that if there is nothing
to be done, this won't do extra work (i.e. applyUpdates recursively goes
through the tree from the node on which we did the operation to apply updates,
but if there are no updates, we stop traversing that praticular subtree).
Reviewed By: sriramramani
Differential Revision: D3511462
Summary:
The TextInput spannables are being set wrong by Nodes. Consequently,
when you hit space after a word, anything you type is highlighted, though it
shouldn't be.
Differential Revision: D3507516
Summary:
Depends on D3120798
Depends on D3120631
Enables D3120631 for nodes. This implementation seems to work but let me know if I'm doing something really stupid.
Reviewed By: ahmedre
Differential Revision: D3120814
Summary:
During the patch for fixing the order of UI operations, we apply
updates to any node receiving a ViewManager command in order to ensure that
nodes that were not yet mounted to a View and not yet attached to their parent
would be properly able to receive the event. However, if a node is already a
view, calling the update could cause unwanted things to happen (for example,
the View's bounds changing improperly), because we're only traversing that
node of the tree and down (instead of the entire tree). This fixes the issue
by only applying updates to the node if the view mount state has changed.
Reviewed By: sriramramani
Differential Revision: D3448356
Summary:
Historically, removeClippedSubviews for Nodes would not clip views
that overflowed their parent. This patch changes that, so that Nodes can
properly clip views when they are off screen (even if they have descendants
that overflow the bounds of their parent).
This is done by calculating a set of offsets from the actual width and
height of the view, and using those in the clipping calculations.
Reviewed By: sriramramani
Differential Revision: D3409859
Summary:
As of D3235050, Nodes supports the optimization of removing clipped
subviews from the hierarchy. However, because Nodes supports overflow:visible,
this could cause issues when DrawCommands overflow the bounds of their parent
container. This patch fixes this by not clipping any overflowing Nodes.
Reviewed By: astreet
Differential Revision: D3235072
Summary: Before this patch, we only collected virtual nodes in NodeRegions, because NodeRegions are only needed to implement ReactCompoundView.reactTargetForTouch() which is only interested in virtual nodes. In the next patch, FlatViewGroup will implement ReactCompoundViewGroup interface which requires knowledge of both virtual and non-virtual children. As a step towards that, we need to include non-virtual nodes in NodeRegions. This patch is implementing that. By itself, it should have not cause any changes in application behavior: we add non-virtual nodes to NodeRegions and mark them as non-virtual, then skip all non-virtual nodes in reactTagForTouch().
Reviewed By: ahmedre
Differential Revision: D3018047
Summary: Before the patch, order of NodeRegions was inconsistent. Given parent A and 3 children B, C and D, we collect DrawCommands like this: A, B, C, D but NodeRegions were collected as B, C, D, A which neither matches draw order, nor a reverse of it. This patch changes it so that NodeRegions are collected in drawing order (A, B, C, D) and we iterate backwards to find correct touch target (in case they overlap).
Reviewed By: ahmedre
Differential Revision: D3018034
Summary:
This code reverts D3004541, since it fixes the symptom instead
of the root cause. Root cause fix is in D3011191.
Differential Revision: D3011291
Summary: @public Split dispatchViewUpdates into two methods, which enables subclasses to commit pending ui operations, even when no root node is present.
Differential Revision: D3011191
Summary:
Groups had a crash when running with React with Nodes when returning
from a search screen. This was due to the fact that the node representing a
ShimmerFrameLayout was being dropped, and then later we were trying to detach
it. Since the view was already dropped, we shouldn't try to detach it since
it's already dropped and removed from the view hierarchy.
Reviewed By: sriramramani
Differential Revision: D3004541
Summary:
We keep a list of FlatShadowNodes that mount to Views that we want to delete, and only flush it at the end of an update cycle. This results in a situation where a root view is being removed before children are removed, which results in a crash in NativeViewHierarchyManager because a View that we are trying to remove no longer exists. There are a few approaches to fix the issue:
a) make a check if a View exists before removing it. While works, it removes a bug protection when we erroneously trying to remove a View that no longer exists (such as this). I'd prefer to keep the check in place
b) flush the views-to-drop queue. This works, but does some extra work in UI thread (namely, removing Views that would be removed anyway)
c) trim the views-to-drop queue to remove any Views that will be removed anyway. This does a tiny bit of extra work in BG thread, but less work in UI thread.
This diff implements option c).
Reviewed By: ahmedre
Differential Revision: D2990105
Summary: We are currently traversing entire tree every time there is any update to any of the nodes, which is hugely inefficient. Instead, we can early out for nodes that are known to contain no updates. This saves a lof of CPU. This optimization can be turned off, and an Exception will be thrown if there was an unexpected update.
Reviewed By: ahmedre
Differential Revision: D2975706
Summary: Right now invalidate always tell the root node that the tree is dirty, and next update will traverse the entire tree in search of changes. While this works correctly, it's not the most efficient implementation. It is more efficient to store dirty flag in every node, and skip entire subtrees if this node and all descendants are already up to date. This diff is a first step towards that optimization.
Reviewed By: ahmedre
Differential Revision: D2955197
Summary:
OnLayoutEvent was incorrectly dispatching x/y coordinates relative to native View instead of relative to parent. This was causing issues in many cases, such as when Node was mounting to a View, in which case x/y would always be 0. This diff is fixing it.
This diff also caches last OnLayoutEvent to avoid dispatching the event on every layout even when layout didn't chance.
Reviewed By: ahmedre
Differential Revision: D2955087
Summary:
There are 2 reasons why someone would call StateBuilder.ensureBackingViewIsCreated():
1) to make sure a View is created, because we are going to use it NOW
2) make sure react styles are applied to View, which doesn't really need the View to be created immediately
This diff is splitting the method into 2, without changing behavior. Difference between the methods' signatures is coming from the fact that 1) never takes styles and 2) possibly takes styles.
This is a pure refactoring diff and should have no change in functionality or behavior.
Reviewed By: ahmedre
Differential Revision: D2916697
Summary:
There is a rare case in React Native when a hierarchy is created and then *immediately* discarded all in one transaction. This is causing a View leak, because a View is created and then never attached to a hierarchy because it shadow node got detached. Since we never attached the View to the hierarchy, we cannot do a detach either and the View is kept in NativeViewHierarchyManager forever, causing a View leak. To fix the issue, don't create a backing View whenever a shadow node mounts to a View. Instead, put the create request into a queue and ONLY execute it if a shadow node is still attached to a hierarchy. This is fixing the View leak because will not create a View unless it's going to be attached somewhere.
The same logic applies to View style update (we don't want to update a View that is going to be detached from a hierarchy).
Reviewed By: ahmedre
Differential Revision: D2916826
Summary:
There are 2 reasons why someone would call StateBuilder.ensureBackingViewIsCreated():
1) to make sure a View is created, because we are going to use it NOW
2) make sure react styles are applied to View, which doesn't really need the View to be created immediately
This diff is splitting the method into 2, without changing behavior. Difference between the methods' signatures is coming from the fact that 1) never takes styles and 2) possibly takes styles.
This is a pure refactoring diff and should have no change in functionality or behavior.
Reviewed By: ahmedre
Differential Revision: D2916697
Summary:
When layout happens, left/top/right/bottom coordinates (and thus clip-*, too) are sometimes not at a pixel boundary for 2 reasons:
a) width/height in Flexbox are floats, and can contain any value, not just a whole pixel. E.g. style={{width: 3.1415926}} is perfectly valid
b) floating point arithmetics sometimes leads to values barely outside of pixel boundaries (width 18.0f can become 18.000001f when a sibling/child size changes).
a) is \"breaking\" screenshot tests, which slightly but differ from reference implementation
b) causes extra View updates/redraws for no reason
This patch is rounding DrawCommand bounds to a whole pixel to avoid these 2 issues.
Reviewed By: ahmedre
Differential Revision: D2934401
Summary:
Many of StateBuilder methods take a lot of arguments, which makes it hard to read. This patch is doing a bit of cleanup by removing tag from the method signatures, because tag can be easily obtained from the node itself.
This is a pure refactoring diff and should have no functional changes.
Reviewed By: ahmedre
Differential Revision: D2915815
Summary:
The current AndroidView stipulates that the backing shadow node can't
be a FlatShadowNode. In some cases, however, we want to apply some of the same
logic (ex not adding NodeRegions, etc) to other ViewManagers that have a
FlatShadowNode backing (and that don't necessarily create a FlatViewGroup).
This commit renames AndroidView to NativeViewWrapper, and re-introduces
AndroidView as an interface, so that logic for padding, NodeRegions, etc can
be shared.
Differential Revision: D2942387
Summary: Not every CSSNode in a hierarchy is a FlatShadowNode, some virtual nodes can be ReactShadowNodes for compatibility with ART nodes. This diff fixes StateBuilder unconditionally casting a node to FlatShadowNode, which is causing a crash in Groups.
Reviewed By: nickholub
Differential Revision: D2941031
Summary:
@public Relax the constraint on ReactTextInputManager.
The TextInput Advanced screen looked different with and without
nodes, namely child Text items were not being rendered on the Nodes version.
This patch fixes that.
Differential Revision: D2930800
Summary:
Previously, every node would be clipped by its own boundaries. This made sense, because
a) RCTView cannot draw outside of its bounds, so clipping is harmless
b) all other node types were not allowed to control overflow behavior, and for them overflow was implicitly set to hidden, which means it should clip.
This diff changes the logic so that overflow can be set on any node (image, text, view etc).
The change has an important implication that make the diff a little trickier that it could be: AbstractDrawCommand clipping logic needs to be relaxed. Previously, clip bounds were *always* intersected with node bounds, which means that clip rect was always within node rect. This is no longer true, and thus shouldClip() logic needs to be modified.
Because of the above, RCTText no longer needs to artificially clip bounds against layout width/height.
Note: RCTImage is still not working with overflow: visible correctly (it still always clips). This is fixed in a follow up diff.
Reviewed By: ahmedre
Differential Revision: D2867849
Summary: A simple refactoring diff that should make code a little easier to read. No functional changes.
Reviewed By: ahmedre
Differential Revision: D2867718
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