Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ASTextNode2] Integrate Google's changes to ASTN2 and accessibility #2017

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
5 changes: 5 additions & 0 deletions Source/ASDisplayNode+Beta.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,11 @@ AS_CATEGORY_IMPLEMENTABLE
*/
- (void)enableSubtreeRasterization;

/**
* @abstract Top, left, bottom, right padding values for the node. Only used in yoga
*/
@property (readonly) UIEdgeInsets paddings;

@end

NS_ASSUME_NONNULL_END
2 changes: 2 additions & 0 deletions Source/ASDisplayNode+Yoga.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ ASDK_EXTERN void ASDisplayNodePerformBlockOnEveryYogaChild(ASDisplayNode * _Null
@interface ASDisplayNode (Yoga)

@property (copy) NSArray *yogaChildren;
@property (readonly, weak) ASDisplayNode *yogaParent;

- (void)addYogaChild:(ASDisplayNode *)child;
- (void)removeYogaChild:(ASDisplayNode *)child;
- (void)insertYogaChild:(ASDisplayNode *)child atIndex:(NSUInteger)index;

- (void)semanticContentAttributeDidChange:(UISemanticContentAttribute)attribute;
- (UIUserInterfaceLayoutDirection)yogaLayoutDirection;

@property BOOL yogaLayoutInProgress;
// TODO: Make this atomic (lock).
Expand Down
13 changes: 12 additions & 1 deletion Source/ASDisplayNode+Yoga.mm
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,14 @@
#import <AsyncDisplayKit/ASLayout.h>
#import <AsyncDisplayKit/ASLayoutElementStylePrivate.h>
#import <AsyncDisplayKit/ASNodeController+Beta.h>

#import <AsyncDisplayKit/ASDisplayNode+LayoutSpec.h>
#import <AsyncDisplayKit/ASDisplayNode+Yoga2.h>

#define YOGA_LAYOUT_LOGGING 0

// Access style property directly or use the getter to create one
#define _LOCKED_ACCESS_STYLE() (_style ?: [self _locked_style])

#pragma mark - ASDisplayNode+Yoga

@interface ASDisplayNode (YogaPrivate)
Expand Down Expand Up @@ -131,6 +134,14 @@ - (void)semanticContentAttributeDidChange:(UISemanticContentAttribute)attribute
? YGDirectionLTR : YGDirectionRTL);
}

- (UIUserInterfaceLayoutDirection)yogaLayoutDirection
{
AS::Yoga2::AssertEnabled(self);
return _LOCKED_ACCESS_STYLE().direction == YGDirectionRTL
? UIUserInterfaceLayoutDirectionRightToLeft
: UIUserInterfaceLayoutDirectionLeftToRight;
}

- (void)setYogaParent:(ASDisplayNode *)yogaParent
{
ASLockScopeSelf();
Expand Down
47 changes: 47 additions & 0 deletions Source/ASDisplayNode+Yoga2.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//
// ASDisplayNode+Yoga2.h
// AsyncDisplayKit
//
// Created by Adlai Holler on 3/8/19.
// Copyright © 2019 Pinterest. All rights reserved.
//

#if defined(__cplusplus)

#import <AsyncDisplayKit/AsyncDisplayKit.h>
#import <AsyncDisplayKit/ASAvailability.h>
#import <UIKit/UIKit.h>

#if YOGA
#import YOGA_HEADER_PATH
#endif

NS_ASSUME_NONNULL_BEGIN

namespace AS {
namespace Yoga2 {

/**
* Returns whether Yoga2 is enabled for this node.
*/
bool GetEnabled(ASDisplayNode *node);

inline void AssertEnabled() {
ASDisplayNodeCAssert(false, @"Expected Yoga2 to be enabled.");
}

inline void AssertEnabled(ASDisplayNode *node) {
ASDisplayNodeCAssert(GetEnabled(node), @"Expected Yoga2 to be enabled.");
}

inline void AssertDisabled(ASDisplayNode *node) {
ASDisplayNodeCAssert(!GetEnabled(node), @"Expected Yoga2 to be disabled.");
}


} // namespace Yoga2
} // namespace AS

NS_ASSUME_NONNULL_END

#endif // defined(__cplusplus)
46 changes: 46 additions & 0 deletions Source/ASDisplayNode+Yoga2.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//
// ASDisplayNode+Yoga2.mm
// AsyncDisplayKit
//
// Created by Adlai Holler on 3/8/19.
// Copyright © 2019 Pinterest. All rights reserved.
//

#import <AsyncDisplayKit/ASDisplayNode+Yoga2.h>
#import <AsyncDisplayKit/ASAvailability.h>
#import <AsyncDisplayKit/ASBaseDefines.h>

#if YOGA

#import <AsyncDisplayKit/ASAssert.h>
#import <AsyncDisplayKit/ASDisplayNode+FrameworkPrivate.h>
#import <AsyncDisplayKit/ASDisplayNodeInternal.h>
#import <AsyncDisplayKit/ASInternalHelpers.h>
#import <AsyncDisplayKit/ASLayoutElementStylePrivate.h>

#import YOGA_HEADER_PATH

namespace AS {
namespace Yoga2 {

bool GetEnabled(ASDisplayNode *node) {
if (node) {
MutexLocker l(node->__instanceLock__);
return node->_flags.yoga;
} else {
return false;
}
}

#else // !YOGA

namespace AS {
namespace Yoga2 {

bool GetEnabled(ASDisplayNode *node) { return false; }

#endif // YOGA

} // namespace Yoga2
} // namespace AS

17 changes: 17 additions & 0 deletions Source/ASDisplayNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ typedef UIViewController * _Nonnull(^ASDisplayNodeViewControllerBlock)(void);
*/
typedef CALayer * _Nonnull(^ASDisplayNodeLayerBlock)(void);

/**
* Accessibility elements creation block. Used to specify accessibility elements of the node.
*/
typedef NSArray *_Nullable (^ASDisplayNodeAccessibilityElementsBlock)(void);

/**
* ASDisplayNode loaded callback block. This block is called BEFORE the -didLoad method and is always called on the main thread.
*/
Expand Down Expand Up @@ -817,6 +822,18 @@ ASDK_EXTERN NSInteger const ASDefaultDrawingPriority;

@end

@interface ASDisplayNode (CustomAccessibilityBehavior)

/**
* Set the block that should be used to determining the accessibility elements of the node.
* When set, the accessibility-related logic (e.g. label aggregation) will not be triggered.
*
* @param block The block that returns the accessibility elements of the node.
*/
- (void)setAccessibilityElementsBlock:(ASDisplayNodeAccessibilityElementsBlock)block;

@end

@interface ASDisplayNode (ASLayoutElement) <ASLayoutElement>

/**
Expand Down
16 changes: 16 additions & 0 deletions Source/ASDisplayNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -936,6 +936,22 @@ - (void)__setNodeController:(ASNodeController *)controller
}
}

- (UIEdgeInsets)paddings {
MutexLocker l(__instanceLock__);
#if YOGA
if (_flags.yoga) {
YGNodeRef yogaNode = _style.yogaNode;
CGFloat top = YGNodeLayoutGetPadding(yogaNode, YGEdgeTop);
CGFloat left = YGNodeLayoutGetPadding(yogaNode, YGEdgeLeft);
CGFloat bottom = YGNodeLayoutGetPadding(yogaNode, YGEdgeBottom);
CGFloat right = YGNodeLayoutGetPadding(yogaNode, YGEdgeRight);
return UIEdgeInsetsMake(top, left, bottom, right);
}
#endif // YOGA
return UIEdgeInsetsZero;

}

- (void)checkResponderCompatibility
{
#if ASDISPLAYNODE_ASSERTIONS_ENABLED
Expand Down
3 changes: 3 additions & 0 deletions Source/ASExperimentalFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ typedef NS_OPTIONS(NSUInteger, ASExperimentalFeatures) {
ASExperimentalDisableGlobalTextkitLock = 1 << 10, // exp_disable_global_textkit_lock
ASExperimentalMainThreadOnlyDataController = 1 << 11, // exp_main_thread_only_data_controller
ASExperimentalRangeUpdateOnChangesetUpdate = 1 << 12, // exp_range_update_on_changeset_update
ASExperimentalDoNotCacheAccessibilityElements = 1 << 23, // exp_do_not_cache_accessibility_elements
ASExperimentalEnableNodeIsHiddenFromAcessibility = 1 << 26, // exp_enable_node_is_hidden_from_accessibility
ASExperimentalEnableAcessibilityElementsReturnNil = 1 << 27, // exp_enable_accessibility_elements_return_nil
ASExperimentalFeatureAll = 0xFFFFFFFF
};

Expand Down
5 changes: 4 additions & 1 deletion Source/ASExperimentalFeatures.mm
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@
@"exp_optimize_data_controller_pipeline",
@"exp_disable_global_textkit_lock",
@"exp_main_thread_only_data_controller",
@"exp_range_update_on_changeset_update"]));
@"exp_range_update_on_changeset_update",
@"exp_do_not_cache_accessibility_elements",
@"exp_enable_node_is_hidden_from_accessibility",
@"exp_enable_accessibility_elements_return_nil"]));
if (flags == ASExperimentalFeatureAll) {
return allNames;
}
Expand Down
25 changes: 23 additions & 2 deletions Source/ASTextNode2.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,20 @@

NS_ASSUME_NONNULL_BEGIN

/**
* Get and Set ASTextNode to use:
* a) Intrinsic size fix for NSAtrributedStrings with no paragraph styles and
* b) Yoga direction to determine alignment of NSTextAlignmentNatural text nodes.
*/
BOOL ASGetEnableTextNode2ImprovedRTL(void);
void ASSetEnableTextNode2ImprovedRTL(BOOL enable);

/**
* Get and Set ASTextLayout and ASTextNode2 to enable to calculation of visible text range.
*/
BOOL ASGetEnableTextTruncationVisibleRange(void);
void ASSetEnableTextTruncationVisibleRange(BOOL enable);

/**
@abstract Draws interactive rich text.
@discussion Backed by the code in TextExperiment folder, on top of CoreText.
Expand Down Expand Up @@ -171,7 +185,7 @@ NS_ASSUME_NONNULL_BEGIN
@param point The point, in the receiver's coordinate system.
@param attributeNameOut The name of the attribute at the point. Can be NULL.
@param rangeOut The ultimate range of the found text. Can be NULL.
@result YES if an entity exists at `point`; NO otherwise.
@result The entity if it exists at `point`; nil otherwise.
*/
- (nullable id)linkAttributeValueAtPoint:(CGPoint)point attributeName:(out NSString * _Nullable * _Nullable)attributeNameOut range:(out NSRange * _Nullable)rangeOut AS_WARN_UNUSED_RESULT;

Expand Down Expand Up @@ -212,7 +226,14 @@ NS_ASSUME_NONNULL_BEGIN
@discussion If you still want to handle tap truncation action when passthroughNonlinkTouches is YES,
you should set the alwaysHandleTruncationTokenTap to YES.
*/
@property (nonatomic) BOOL passthroughNonlinkTouches;
@property BOOL passthroughNonlinkTouches;

/**
@abstract Whether additionalTruncationMessage is interactive.
@discussion This affects whether touches on additionalTruncationMessage will be intercepted when
passthroughNonlinkTouches is YES.
*/
@property BOOL additionalTruncationMessageIsInteractive;

/**
@abstract Always handle tap truncationAction, even the passthroughNonlinkTouches is YES. Default is NO.
Expand Down
Loading