package com.reacttwitter.widgets;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.support.v4.content.ContextCompat;
import android.text.BoringLayout;
import android.text.Layout;
import android.text.StaticLayout;
import android.text.TextPaint;
import com.facebook.csslayout.CSSConstants;
import com.facebook.csslayout.CSSMeasureMode;
import com.facebook.csslayout.CSSNode;
import com.facebook.csslayout.MeasureOutput;
import com.facebook.react.uimanager.LayoutShadowNode;
import com.facebook.react.uimanager.PixelUtil;
import com.facebook.react.uimanager.annotations.ReactProp;
import javax.annotation.Nullable;
class ReactButtonShadowNode extends LayoutShadowNode implements CSSNode.MeasureFunction {
private static final TextPaint textPaintInstance = new TextPaint();
static {
textPaintInstance.setFlags(TextPaint.ANTI_ALIAS_FLAG);
textPaintInstance.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD)); // our button has a bold typeface by default
}
private String text;
private final Rect padding;
ReactButtonShadowNode() {
padding = new Rect();
setMeasureFunction(this);
}
@Override
protected void markUpdated() {
super.markUpdated();
super.dirty();
}
@ReactProp(name = "text")
public void setText(@Nullable String text) {
this.text = text;
markUpdated();
}
@ReactProp(name = "textSize", defaultInt = 14)
public void setTextSize(int textSize) {
textPaintInstance.setTextSize(PixelUtil.toPixelFromSP(textSize));
markUpdated();
}
@ReactProp(name = "backgroundImage")
public void setBackgroundImage(String backgroundImage) {
Drawable drawable = getDrawableByName(backgroundImage);
if (drawable == null) {
padding.set(0, 0, 0, 0);
return;
}
drawable.getPadding(this.padding);
markUpdated();
}
private Drawable getDrawableByName(String name) {
int resId = getThemedContext().getResources().getIdentifier(name.toLowerCase(), "drawable", getThemedContext().getPackageName());
if (resId == 0) {
return null;
}
return ContextCompat.getDrawable(getThemedContext(), resId);
}
@Override
public void measure(
CSSNode node,
float width,
CSSMeasureMode widthMode,
float height,
CSSMeasureMode heightMode,
MeasureOutput measureOutput) {
// Code snippet copied from ReactTextShadowNode, and slightly adapted to our use case
// TODO(5578671): Handle text direction (see View#getTextDirectionHeuristic)
TextPaint textPaint = textPaintInstance;
Layout layout;
BoringLayout.Metrics boring = BoringLayout.isBoring(text, textPaint);
boolean isBoringLayout = boring != null;
float desiredWidth = !isBoringLayout ?
Layout.getDesiredWidth(text, textPaint) : Float.NaN;
// technically, width should never be negative, but there is currently a bug in
boolean unconstrainedWidth = widthMode == CSSMeasureMode.UNDEFINED || width < 0;
if (!isBoringLayout &&
(unconstrainedWidth ||
(!CSSConstants.isUndefined(desiredWidth) && desiredWidth <= width))) {
// Is used when the width is not known and the text is not boring, ie. if it contains
// unicode characters.
layout = new StaticLayout(
text,
textPaint,
(int) Math.ceil(desiredWidth),
Layout.Alignment.ALIGN_NORMAL,
1,
0,
true
);
} else if (isBoringLayout && (unconstrainedWidth || boring.width <= width)) {
// Is used for single-line, boring text when the width is either unknown or bigger
// than the width of the text.
layout = BoringLayout.make(
text,
textPaint,
boring.width,
Layout.Alignment.ALIGN_NORMAL,
1,
0,
boring,
true
);
} else {
// Is used for multiline, boring text and the width is known.
layout = new StaticLayout(
text,
textPaint,
(int) width,
Layout.Alignment.ALIGN_NORMAL,
1,
0,
true
);
}
measureOutput.height = layout.getHeight() + (padding.top + padding.bottom);
measureOutput.width = layout.getWidth() + (padding.left + padding.right);
}
}