Skip to content

Commit

Permalink
Add interface to enable spans to pre and post draw on canvas
Browse files Browse the repository at this point in the history
Summary: This diff introduces a new interface that can be used to process custom span that need to provide extra drawing capabilities as described in the [Composable Spans RFC](https://docs.google.com/document/d/1EKlAx9dMWsrltmmN1Ge9NfsXSycSXKwCLcmoCoxbwlE/edit#bookmark=id.hwdk13oj54fd)

Reviewed By: rooju

Differential Revision: D53533455

fbshipit-source-id: 3b26102e0e6af2fd977e4a3e6243c6b939db222c
  • Loading branch information
AlexBalo authored and facebook-github-bot committed Mar 27, 2024
1 parent 229135d commit 3a698c2
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 4 deletions.
9 changes: 7 additions & 2 deletions litho-rendercore-text/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ load(
"//tools/build_defs/oss:litho_defs.bzl",
"LITHO_ANDROIDSUPPORT_TARGET",
"LITHO_INFERANNOTATIONS_TARGET",
"LITHO_RENDERCORE_PRIMITIVES_TARGET",
"LITHO_RENDERCORE_TARGET",
"LITHO_RENDERCORE_TEXT_TARGET",
"LITHO_TEXTLAYOUTBUILDER_TARGET",
Expand All @@ -13,9 +14,12 @@ load(

litho_android_library(
name = "litho-rendercore-text",
srcs = glob(["src/main/java/**/*.java"]),
srcs = glob([
"src/main/java/**/*.java",
"src/main/java/**/*.kt",
]),
autoglob = False,
language = "JAVA",
pure_kotlin = False,
visibility = [
"PUBLIC",
],
Expand All @@ -25,6 +29,7 @@ litho_android_library(
LITHO_TEXTLAYOUTBUILDER_TARGET,
LITHO_ANDROIDSUPPORT_TARGET,
LITHO_INFERANNOTATIONS_TARGET,
LITHO_RENDERCORE_PRIMITIVES_TARGET,
"//fbandroid/android_res/rendercore:rendercore",
"//fbandroid/java/com/facebook/proguard/annotations:annotations",
],
Expand Down
2 changes: 2 additions & 0 deletions litho-rendercore-text/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/

apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'

android {
compileSdkVersion rootProject.compileSdkVersion
Expand All @@ -33,6 +34,7 @@ android {
}

dependencies {
implementation project(':litho-rendercore-primitives')
implementation project(':litho-rendercore-res')
api project(':litho-rendercore')

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* 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.
*/

package com.facebook.rendercore.text

import android.graphics.Canvas
import android.text.Layout

/**
* A span interface that allows users to receive callbacks with the [Canvas] so that extra drawing
* capabilities as made available through the composable and extensible spans styles.
*/
interface OnPrePostDrawSpan {
fun draw(
canvas: Canvas,
text: CharSequence,
start: Int,
end: Int,
layout: Layout,
drawCommand: Command
)

interface Command {
fun draw(canvas: Canvas)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.view.AccessibilityDelegateCompat;
import androidx.core.view.ViewCompat;
Expand All @@ -51,7 +52,10 @@
import com.facebook.rendercore.text.accessibility.RCAccessibleClickableSpan;
import java.util.List;

/** A pared-down TextView that only displays text. */
/**
* A custom Android View that behaves like a TextView providing support for spans, custom drawing
* and truncations.
*/
@DoNotStrip
public class RCTextView extends View {
private static final String ACCESSIBILITY_CLASS_BUTTON = "android.widget.Button";
Expand Down Expand Up @@ -112,13 +116,59 @@ public void draw(Canvas canvas) {
shouldRestore = true;
}

mLayout.draw(canvas, getSelectionPath(), mHighlightPaint, 0);
final OnPrePostDrawSpan[] onPrePostDrawSpans = getOnPrePostDrawableSpans();
if (onPrePostDrawSpans.length == 0) {
drawLayout(canvas);
} else {
drawInternal(onPrePostDrawSpans, canvas);
}

if (shouldRestore) {
canvas.restoreToCount(saveCount);
}
}

private void drawInternal(final OnPrePostDrawSpan[] onOnPrePostDrawSpans, final Canvas canvas) {
OnPrePostDrawSpan.Command currentDrawAction =
new OnPrePostDrawSpan.Command() {
@Override
public void draw(@NonNull Canvas canvas) {
drawLayout(canvas);
}
};
final Spanned text = (Spanned) mText;
// OnPrePostDraw spans are retrieved in the same order in which they are applied (i.e. in order
// of pre-order traversal of the composable span tree). We want to have their onDraw calls
// invoked in the same order. In order to chain them in that order, we need to walk the spans
// list backward.
int length = onOnPrePostDrawSpans.length;
for (int i = length - 1; i >= 0; i--) {
OnPrePostDrawSpan onDraw = onOnPrePostDrawSpans[i];
OnPrePostDrawSpan.Command previousAction = currentDrawAction;
int spanStart = text.getSpanStart(onDraw);
int spanEnd = text.getSpanEnd(onDraw);
currentDrawAction =
new OnPrePostDrawSpan.Command() {
@Override
public void draw(@NonNull Canvas canvas) {
onDraw.draw(canvas, mText, spanStart, spanEnd, mLayout, previousAction);
}
};
}
currentDrawAction.draw(canvas);
}

private void drawLayout(Canvas canvas) {
mLayout.draw(canvas, getSelectionPath(), mHighlightPaint, 0);
}

private OnPrePostDrawSpan[] getOnPrePostDrawableSpans() {
if (!(mText instanceof Spanned)) {
return new OnPrePostDrawSpan[0];
}
return ((Spanned) mText).getSpans(0, mText.length(), OnPrePostDrawSpan.class);
}

public void mount(TextLayout textLayout) {
final ColorStateList colorStateList = textLayout.textStyle.textColorStateList;
mText = textLayout.processedText;
Expand Down

0 comments on commit 3a698c2

Please sign in to comment.