private void measureChildNode(int widthMeasureSpec, int heightMeasureSpec) { int childNodeViewsCount = mChildNodeViews.size(); for (int i = 0; i < childNodeViewsCount; i++) { UDView nodeView = mChildNodeViews.get(i); View view = nodeView.getView(); CSSNode node = nodeView.getCssNode(); if (node.getSizeToFit()) { int margins = (int) (node.getMargin().get(Spacing.LEFT) + node.getMargin().get(Spacing.RIGHT)); measureChild(view, widthMeasureSpec - margins, heightMeasureSpec); node.setNoDirtyStyleWidth(view.getMeasuredWidth()); node.setNoDirtyStyleHeight(view.getMeasuredHeight()); } if (view instanceof LVViewGroup) { LVViewGroup viewGroup = (LVViewGroup) view; if (viewGroup.getCssNode().getChildCount() > 0) { viewGroup.measureChildNode(widthMeasureSpec, heightMeasureSpec); } } } }
public static void parseFlexNodeCSS(CSSNode node, String cssString) { if (mCssString == cssString || mNode == node) { return; } mCssString = cssString; mNode = node; resetInlineMap(); setPosition(); setDirection(); setAlignItems(); setAlignSelf(); setAlignContent(); setJustfiyContent(); setFlex(); setFlexWrap(); setSize(); setMinSize(); setMaxSize(); setMargin(); setPadding(); setSizeToFit(); }
@Override public void measure(CSSNode node, float width, float height, MeasureOutput measureOutput) { // measure() should never be called before setThemedContext() EditText editText = Assertions.assertNotNull(mEditText); measureOutput.width = width; editText.setTextSize( TypedValue.COMPLEX_UNIT_PX, mFontSize == UNSET ? (int) Math.ceil(PixelUtil.toPixelFromSP(ViewDefaults.FONT_SIZE_SP)) : mFontSize); mComputedPadding = spacingToFloatArray(getPadding()); editText.setPadding( (int) Math.ceil(getPadding().get(Spacing.LEFT)), (int) Math.ceil(getPadding().get(Spacing.TOP)), (int) Math.ceil(getPadding().get(Spacing.RIGHT)), (int) Math.ceil(getPadding().get(Spacing.BOTTOM))); if (mNumberOfLines != UNSET) { editText.setLines(mNumberOfLines); } editText.measure(0 /* unspecified */, 0 /* unspecified */); measureOutput.height = editText.getMeasuredHeight(); }
@Override public void measure(CSSNode node, float width, float height, MeasureOutput measureOutput) { if (!mMeasured) { // Create a switch with the default config and measure it; since we don't (currently) // support setting custom switch text, this is fine, as all switches will measure the same // on a specific device/theme/locale combination. ReactSwitch reactSwitch = new ReactSwitch(getThemedContext()); final int spec = View.MeasureSpec.makeMeasureSpec( ViewGroup.LayoutParams.WRAP_CONTENT, View.MeasureSpec.UNSPECIFIED); reactSwitch.measure(spec, spec); mWidth = reactSwitch.getMeasuredWidth(); mHeight = reactSwitch.getMeasuredHeight(); mMeasured = true; } measureOutput.width = mWidth; measureOutput.height = mHeight; }
@Override public void measure(CSSNode node, float width, float height, MeasureOutput measureOutput) { final int style = ReactProgressBarViewManager.getStyleFromString(getStyle()); if (!mMeasured.contains(style)) { ProgressBar progressBar = new ProgressBar(getThemedContext(), null, style); final int spec = View.MeasureSpec.makeMeasureSpec( ViewGroup.LayoutParams.WRAP_CONTENT, View.MeasureSpec.UNSPECIFIED); progressBar.measure(spec, spec); mHeight.put(style, progressBar.getMeasuredHeight()); mWidth.put(style, progressBar.getMeasuredWidth()); mMeasured.add(style); } measureOutput.height = mHeight.get(style); measureOutput.width = mWidth.get(style); }
@Override public void measure(CSSNode node, float width, MeasureOutput measureOutput) { if (!mMeasured) { Spinner reactSwitch = new Spinner(getThemedContext()); final int spec = View.MeasureSpec.makeMeasureSpec( ViewGroup.LayoutParams.WRAP_CONTENT, View.MeasureSpec.UNSPECIFIED ); reactSwitch.measure(spec, spec); mWidth = reactSwitch.getMeasuredWidth(); mHeight = reactSwitch.getMeasuredHeight(); mMeasured = true; } measureOutput.width = mWidth; measureOutput.height = mHeight; }
@Override public void measure(CSSNode node, float width, float height, MeasureOutput measureOutput) { // measure() should never be called before setThemedContext() EditText editText = Assertions.assertNotNull(mEditText); measureOutput.width = width; editText.setTextSize( TypedValue.COMPLEX_UNIT_PX, mFontSize == UNSET ? (int) Math.ceil(PixelUtil.toPixelFromSP(ViewDefaults.FONT_SIZE_SP)) : mFontSize); mComputedPadding = spacingToFloatArray(getPadding()); editText.setPadding( (int) Math.ceil(getPadding().get(Spacing.LEFT)), (int) Math.ceil(getPadding().get(Spacing.TOP)), (int) Math.ceil(getPadding().get(Spacing.RIGHT)), (int) Math.ceil(getPadding().get(Spacing.BOTTOM))); if (mNumberOfLines != UNSET) { editText.setLines(mNumberOfLines); } editText.measure(MEASURE_SPEC, MEASURE_SPEC); measureOutput.height = editText.getMeasuredHeight(); }
@Override public void measure( CSSNode node, float width, CSSMeasureMode widthMode, float height, CSSMeasureMode heightMode, MeasureOutput measureOutput) { if (!mMeasured) { // Create a switch with the default config and measure it; since we don't (currently) // support setting custom switch text, this is fine, as all switches will measure the same // on a specific device/theme/locale combination. ReactSwitch reactSwitch = new ReactSwitch(getThemedContext()); final int spec = View.MeasureSpec.makeMeasureSpec( ViewGroup.LayoutParams.WRAP_CONTENT, View.MeasureSpec.UNSPECIFIED); reactSwitch.measure(spec, spec); mWidth = reactSwitch.getMeasuredWidth(); mHeight = reactSwitch.getMeasuredHeight(); mMeasured = true; } measureOutput.width = mWidth; measureOutput.height = mHeight; }
@Override public void measure( CSSNode node, float width, CSSMeasureMode widthMode, float height, CSSMeasureMode heightMode, MeasureOutput measureOutput) { final int style = ReactProgressBarViewManager.getStyleFromString(getStyle()); if (!mMeasured.contains(style)) { ProgressBar progressBar = ReactProgressBarViewManager.createProgressBar(getThemedContext(), style); final int spec = View.MeasureSpec.makeMeasureSpec( ViewGroup.LayoutParams.WRAP_CONTENT, View.MeasureSpec.UNSPECIFIED); progressBar.measure(spec, spec); mHeight.put(style, progressBar.getMeasuredHeight()); mWidth.put(style, progressBar.getMeasuredWidth()); mMeasured.add(style); } measureOutput.height = mHeight.get(style); measureOutput.width = mWidth.get(style); }
@Override public void measure( CSSNode node, float width, CSSMeasureMode widthMode, float height, CSSMeasureMode heightMode, MeasureOutput measureOutput) { if (!mMeasured) { SeekBar reactSlider = new ReactSlider(getThemedContext(), null, STYLE); final int spec = View.MeasureSpec.makeMeasureSpec( ViewGroup.LayoutParams.WRAP_CONTENT, View.MeasureSpec.UNSPECIFIED); reactSlider.measure(spec, spec); mWidth = reactSlider.getMeasuredWidth(); mHeight = reactSlider.getMeasuredHeight(); mMeasured = true; } measureOutput.width = mWidth; measureOutput.height = mHeight; }
/** * We need to set the styleWidth and styleHeight of the one child (represented by the <View/> * within the <RCTModalHostView/> in Modal.js. This needs to fill the entire window. */ @Override @TargetApi(16) public void addChildAt(CSSNode child, int i) { super.addChildAt(child, i); Context context = getThemedContext(); WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); Display display = wm.getDefaultDisplay(); // getCurrentSizeRange will return the min and max width and height that the window can be display.getCurrentSizeRange(mMinPoint, mMaxPoint); int width, height; int rotation = display.getRotation(); if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) { // If we are vertical the width value comes from min width and height comes from max height width = mMinPoint.x; height = mMaxPoint.y; } else { // If we are horizontal the width value comes from max width and height comes from min height width = mMaxPoint.x; height = mMinPoint.y; } child.setStyleWidth(width); child.setStyleHeight(height); }
private void assignNodeLayoutParams() { int childNodeViewsCount = mChildNodeViews.size(); for (int i = 0; i < childNodeViewsCount; i++) { UDView nodeView = mChildNodeViews.get(i); View view = nodeView.getView(); CSSNode node = nodeView.getCssNode(); if (view != null && node != null) { int x = (int) node.getLayoutX(); int y = (int) node.getLayoutY(); int width = (int) node.getLayoutWidth(); int height = (int) node.getLayoutHeight(); RelativeLayout.LayoutParams lParams = (LayoutParams) view.getLayoutParams(); if (lParams == null) { lParams = new RelativeLayout.LayoutParams(width, height); } else { lParams.width = width; lParams.height = height; } lParams.setMargins(x, y, 0, 0); view.setLayoutParams(lParams); if (view instanceof LVViewGroup) { LVViewGroup viewGroup = (LVViewGroup) view; if (viewGroup.getCssNode().getChildCount() > 0) { viewGroup.assignNodeLayoutParams(); } } } } }
public UDView setFlexCss(String cssString) { if (mFlexCss == null || !mFlexCss.equals(cssString)) { CSSNode node = getCssNode(); FlexboxCSSParser.parseFlexNodeCSS(node, cssString); mFlexCss = cssString; } return this; }
@Override public void addChildAt(CSSNode child, int i) { super.addChildAt(child, i); markUpdated(); ReactShadowNode node = (ReactShadowNode) child; int increase = node.mIsLayoutOnly ? node.mTotalNativeChildren : 1; mTotalNativeChildren += increase; updateNativeChildrenCountInParent(increase); }
@Override public void measure( CSSNode node, float width, CSSMeasureMode widthMode, float height, CSSMeasureMode heightMode, MeasureOutput measureOutput) { // measure() should never be called before setThemedContext() EditText editText = Assertions.assertNotNull(mEditText); measureOutput.width = widthMode == CSSMeasureMode.UNDEFINED ? CSSConstants.UNDEFINED : width; editText.setTextSize( TypedValue.COMPLEX_UNIT_PX, mFontSize == UNSET ? (int) Math.ceil(PixelUtil.toPixelFromSP(ViewDefaults.FONT_SIZE_SP)) : mFontSize); mComputedPadding = spacingToFloatArray(getPadding()); editText.setPadding( (int) Math.ceil(getPadding().get(Spacing.LEFT)), (int) Math.ceil(getPadding().get(Spacing.TOP)), (int) Math.ceil(getPadding().get(Spacing.RIGHT)), (int) Math.ceil(getPadding().get(Spacing.BOTTOM))); if (mNumberOfLines != UNSET) { editText.setLines(mNumberOfLines); } editText.measure(0 /* unspecified */, 0 /* unspecified */); measureOutput.height = editText.getMeasuredHeight(); }
@Override public void measure( CSSNode node, float width, CSSMeasureMode widthMode, float height, CSSMeasureMode heightMode, MeasureOutput measureOutput) { throw new IllegalStateException("SurfaceView should have explicit width and height set"); }
@Override public void measure(CSSNode node, float width, float height, MeasureOutput measureOutput) { throw new IllegalStateException("SurfaceView should have explicit width and height set"); }
private static void buildSpannedFromTextCSSNode( ReactTextShadowNode textCSSNode, SpannableStringBuilder sb, List<SetSpanOperation> ops) { int start = sb.length(); if (textCSSNode.mText != null) { sb.append(textCSSNode.mText); } for (int i = 0, length = textCSSNode.getChildCount(); i < length; i++) { CSSNode child = textCSSNode.getChildAt(i); if (child instanceof ReactTextShadowNode) { buildSpannedFromTextCSSNode((ReactTextShadowNode) child, sb, ops); } else if (child instanceof ReactTextInlineImageShadowNode) { // We make the image take up 1 character in the span and put a corresponding character into // the text so that the image doesn't run over any following text. sb.append(INLINE_IMAGE_PLACEHOLDER); ops.add( new SetSpanOperation( sb.length() - INLINE_IMAGE_PLACEHOLDER.length(), sb.length(), ((ReactTextInlineImageShadowNode) child).buildInlineImageSpan())); } else { throw new IllegalViewOperationException("Unexpected view type nested under text node: " + child.getClass()); } ((ReactShadowNode) child).markUpdateSeen(); } int end = sb.length(); if (end >= start) { if (textCSSNode.mIsColorSet) { ops.add(new SetSpanOperation(start, end, new ForegroundColorSpan(textCSSNode.mColor))); } if (textCSSNode.mIsBackgroundColorSet) { ops.add(new SetSpanOperation( start, end, new BackgroundColorSpan(textCSSNode.mBackgroundColor))); } if (textCSSNode.mFontSize != UNSET) { ops.add(new SetSpanOperation(start, end, new AbsoluteSizeSpan(textCSSNode.mFontSize))); } if (textCSSNode.mFontStyle != UNSET || textCSSNode.mFontWeight != UNSET || textCSSNode.mFontFamily != null) { ops.add(new SetSpanOperation( start, end, new CustomStyleSpan( textCSSNode.mFontStyle, textCSSNode.mFontWeight, textCSSNode.mFontFamily, textCSSNode.getThemedContext().getAssets()))); } if (textCSSNode.mTextShadowOffsetDx != 0 || textCSSNode.mTextShadowOffsetDy != 0) { ops.add(new SetSpanOperation( start, end, new ShadowStyleSpan( textCSSNode.mTextShadowOffsetDx, textCSSNode.mTextShadowOffsetDy, textCSSNode.mTextShadowRadius, textCSSNode.mTextShadowColor))); } ops.add(new SetSpanOperation(start, end, new ReactTagSpan(textCSSNode.getReactTag()))); } }
@Override public void measure(CSSNode node, float width, float height, MeasureOutput measureOutput) { // TODO(5578671): Handle text direction (see View#getTextDirectionHeuristic) ReactTextShadowNode reactCSSNode = (ReactTextShadowNode) node; TextPaint textPaint = sTextPaintInstance; Layout layout; Spanned text = Assertions.assertNotNull( reactCSSNode.mPreparedSpannableText, "Spannable element has not been prepared in onBeforeLayout"); BoringLayout.Metrics boring = BoringLayout.isBoring(text, textPaint); float desiredWidth = boring == null ? Layout.getDesiredWidth(text, textPaint) : Float.NaN; if (boring == null && (CSSConstants.isUndefined(width) || (!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 (boring != null && (CSSConstants.isUndefined(width) || 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(); measureOutput.width = layout.getWidth(); if (reactCSSNode.mNumberOfLines != UNSET && reactCSSNode.mNumberOfLines < layout.getLineCount()) { measureOutput.height = layout.getLineBottom(reactCSSNode.mNumberOfLines - 1); } if (reactCSSNode.mLineHeight != UNSET) { int lines = reactCSSNode.mNumberOfLines != UNSET ? Math.min(reactCSSNode.mNumberOfLines, layout.getLineCount()) : layout.getLineCount(); float lineHeight = PixelUtil.toPixelFromSP(reactCSSNode.mLineHeight); measureOutput.height = lineHeight * lines; } }
private static void buildSpannedFromTextCSSNode( ReactTextShadowNode textCSSNode, SpannableStringBuilder sb, List<SetSpanOperation> ops) { int start = sb.length(); if (textCSSNode.mText != null) { sb.append(textCSSNode.mText); } for (int i = 0, length = textCSSNode.getChildCount(); i < length; i++) { CSSNode child = textCSSNode.getChildAt(i); if (child instanceof ReactTextShadowNode) { buildSpannedFromTextCSSNode((ReactTextShadowNode) child, sb, ops); } else if (child instanceof ReactTextInlineImageShadowNode) { // We make the image take up 1 character in the span and put a corresponding character into // the text so that the image doesn't run over any following text. sb.append(INLINE_IMAGE_PLACEHOLDER); ops.add( new SetSpanOperation( sb.length() - INLINE_IMAGE_PLACEHOLDER.length(), sb.length(), ((ReactTextInlineImageShadowNode) child).buildInlineImageSpan())); } else { throw new IllegalViewOperationException("Unexpected view type nested under text node: " + child.getClass()); } ((ReactShadowNode) child).markUpdateSeen(); } int end = sb.length(); if (end >= start) { if (textCSSNode.mIsColorSet) { ops.add(new SetSpanOperation(start, end, new ForegroundColorSpan(textCSSNode.mColor))); } if (textCSSNode.mIsBackgroundColorSet) { ops.add(new SetSpanOperation( start, end, new BackgroundColorSpan(textCSSNode.mBackgroundColor))); } if (textCSSNode.mFontSize != UNSET) { ops.add(new SetSpanOperation(start, end, new AbsoluteSizeSpan(textCSSNode.mFontSize))); } if (textCSSNode.mFontStyle != UNSET || textCSSNode.mFontWeight != UNSET || textCSSNode.mFontFamily != null) { ops.add(new SetSpanOperation( start, end, new CustomStyleSpan( textCSSNode.mFontStyle, textCSSNode.mFontWeight, textCSSNode.mFontFamily, textCSSNode.getThemedContext().getAssets()))); } if (textCSSNode.mIsUnderlineTextDecorationSet) { ops.add(new SetSpanOperation(start, end, new UnderlineSpan())); } if (textCSSNode.mIsLineThroughTextDecorationSet) { ops.add(new SetSpanOperation(start, end, new StrikethroughSpan())); } if (textCSSNode.mTextShadowOffsetDx != 0 || textCSSNode.mTextShadowOffsetDy != 0) { ops.add(new SetSpanOperation( start, end, new ShadowStyleSpan( textCSSNode.mTextShadowOffsetDx, textCSSNode.mTextShadowOffsetDy, textCSSNode.mTextShadowRadius, textCSSNode.mTextShadowColor))); } ops.add(new SetSpanOperation(start, end, new ReactTagSpan(textCSSNode.getReactTag()))); } }
@Override public void measure( CSSNode node, float width, CSSMeasureMode widthMode, float height, CSSMeasureMode heightMode, MeasureOutput measureOutput) { // TODO(5578671): Handle text direction (see View#getTextDirectionHeuristic) ReactTextShadowNode reactCSSNode = (ReactTextShadowNode) node; TextPaint textPaint = sTextPaintInstance; Layout layout; Spanned text = Assertions.assertNotNull( reactCSSNode.mPreparedSpannableText, "Spannable element has not been prepared in onBeforeLayout"); BoringLayout.Metrics boring = BoringLayout.isBoring(text, textPaint); float desiredWidth = boring == null ? 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 (boring == null && (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 (boring != null && (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(); measureOutput.width = layout.getWidth(); if (reactCSSNode.mNumberOfLines != UNSET && reactCSSNode.mNumberOfLines < layout.getLineCount()) { measureOutput.height = layout.getLineBottom(reactCSSNode.mNumberOfLines - 1); } if (reactCSSNode.mLineHeight != UNSET) { int lines = reactCSSNode.mNumberOfLines != UNSET ? Math.min(reactCSSNode.mNumberOfLines, layout.getLineCount()) : layout.getLineCount(); float lineHeight = PixelUtil.toPixelFromSP(reactCSSNode.mLineHeight); measureOutput.height = lineHeight * lines; } }
@Override public void measure(CSSNode node, float width, MeasureOutput measureOutput) { measureOutput.width = width; }