From 2e351ef53e6c1c102215c449e145793b2e66beb9 Mon Sep 17 00:00:00 2001 From: Aziz Murtazaev Date: Mon, 13 Feb 2017 05:14:54 -0800 Subject: [PATCH] Add support for minEms, minWidth, maxEms, maxWidth in TextLayoutBuilder Summary: Some attributes from `TextView` are still missing in `TextSpec`, specifically `maxEms` attribute was requested from engineers who are doing NewsFeed conversions to Components. `TextSpec` uses `TextLayoutBuilder` to do its layout and this diff adds support for `ems` and related attributes to `TextLayoutBuilder`, specifically `minEms`, `maxEms`, `minWidth`, `maxWidth`. Reviewed By: sriramramani Differential Revision: D4544590 fbshipit-source-id: 5f402d844811c5612af447150dc686c164e16f62 --- .../textlayoutbuilder/TextLayoutBuilder.java | 107 ++++++++++++++++++ .../TextLayoutBuilderTest.java | 40 +++++++ 2 files changed, 147 insertions(+) diff --git a/library/src/main/java/com/facebook/fbui/textlayoutbuilder/TextLayoutBuilder.java b/library/src/main/java/com/facebook/fbui/textlayoutbuilder/TextLayoutBuilder.java index 8daaca3..57236b1 100644 --- a/library/src/main/java/com/facebook/fbui/textlayoutbuilder/TextLayoutBuilder.java +++ b/library/src/main/java/com/facebook/fbui/textlayoutbuilder/TextLayoutBuilder.java @@ -58,6 +58,14 @@ public class TextLayoutBuilder { // Default maxLines. public static final int DEFAULT_MAX_LINES = Integer.MAX_VALUE; + private static final int EMS = 1; + private static final int PIXELS = 2; + + private int mMinWidth = 0; + private int mMinWidthMode = PIXELS; + private int mMaxWidth = Integer.MAX_VALUE; + private int mMaxWidthMode = PIXELS; + // Cache for text layouts. @VisibleForTesting static final LruCache sCache = new LruCache<>(100); @@ -101,6 +109,10 @@ void createNewPaintIfNeeded() { } } + int getLineHeight() { + return Math.round(paint.getFontMetricsInt(null) * spacingMult + spacingAdd); + } + @Override public int hashCode() { int hashCode = 1; @@ -618,6 +630,88 @@ public TextLayoutBuilder setGlyphWarmer(GlyphWarmer glyphWarmer) { return this; } + /** + * Sets the min width expressed in ems (equivalent to setMinEms() in TextView) + * + * @param minEms min width expressed in ems + */ + public TextLayoutBuilder setMinEms(int minEms) { + mMinWidth = minEms; + mMinWidthMode = EMS; + return this; + } + + /** + * @return the min width expressed in ems (equivalent to getMinEms() in TextView) or -1 + * if min width is set in pixels instead by using {@link #setMinWidth(int)} + * + * @see #setMinEms(int) + */ + public int getMinEms() { + return mMinWidthMode == EMS ? mMinWidth : -1; + } + + /** + * Sets the min width expressed in pixels + * @param minWidth + */ + public TextLayoutBuilder setMinWidth(@Px int minWidth) { + mMinWidth = minWidth; + mMinWidthMode = PIXELS; + return this; + } + + /** + * @return the min width expressed in pixels or -1 if the min width was set in ems instead + * + * @see #setMinWidth(int) + */ + @Px + public int getMinWidth() { + return mMinWidthMode == PIXELS ? mMinWidth : -1; + } + + /** + * Sets the max width expressed in ems (equivalent to setMaxEms() in TextView) + * + * @param maxEms max width expressed in ems + */ + public TextLayoutBuilder setMaxEms(int maxEms) { + mMaxWidth = maxEms; + mMaxWidthMode = EMS; + return this; + } + + /** + * @return the max width expressed in ems (equivalent to getMaxEms() in TextView) or -1 + * if max width is set in pixels instead by using {@link #setMaxWidth(int)} + * + * @see #setMaxEms(int) + */ + public int getMaxEms() { + return mMaxWidthMode == EMS ? mMaxWidth : -1; + } + + /** + * Sets the max width expressed in pixels + * @param maxWidth + */ + public TextLayoutBuilder setMaxWidth(@Px int maxWidth) { + mMaxWidth = maxWidth; + mMaxWidthMode = PIXELS; + return this; + } + + /** + * @return the max width expressed in pixels or -1 if the max width was set in ems instead + * + * @see #setMaxWidth(int) + */ + @Px + public int getMaxWidth() { + return mMaxWidthMode == PIXELS ? mMaxWidth : -1; + } + /** * Builds and returns a {@link Layout}. * @@ -685,6 +779,19 @@ public Layout build() { throw new IllegalStateException("Unexpected measure mode " + mParams.measureMode); } + final int lineHeight = mParams.getLineHeight(); + if (mMaxWidthMode == EMS) { + width = Math.min(width, mMaxWidth * lineHeight); + } else { + width = Math.min(width, mMaxWidth); + } + + if (mMinWidthMode == EMS) { + width = Math.max(width, mMinWidth * lineHeight); + } else { + width = Math.max(width, mMinWidth); + } + Layout layout; if (metrics != null) { layout = BoringLayout.make( diff --git a/library/src/test/java/com/facebook/fbui/textlayoutbuilder/TextLayoutBuilderTest.java b/library/src/test/java/com/facebook/fbui/textlayoutbuilder/TextLayoutBuilderTest.java index bd4a0c4..562a0ec 100644 --- a/library/src/test/java/com/facebook/fbui/textlayoutbuilder/TextLayoutBuilderTest.java +++ b/library/src/test/java/com/facebook/fbui/textlayoutbuilder/TextLayoutBuilderTest.java @@ -199,6 +199,46 @@ public void testMaxLines() { assertEquals(mLayout.getLineCount(), 2); } + @Test + public void testMinEms() { + mBuilder + .setText(LONG_TEXT) + .setMinEms(10) + .build(); + assertEquals(mBuilder.getMinEms(), 10); + assertEquals(mBuilder.getMinWidth(), -1); + } + + @Test + public void testMaxEms() { + mBuilder + .setText(LONG_TEXT) + .setMaxEms(10) + .build(); + assertEquals(mBuilder.getMaxEms(), 10); + assertEquals(mBuilder.getMaxWidth(), -1); + } + + @Test + public void testMinWidth() { + mBuilder + .setText(LONG_TEXT) + .setMinWidth(100) + .build(); + assertEquals(mBuilder.getMinWidth(), 100); + assertEquals(mBuilder.getMinEms(), -1); + } + + @Test + public void testMaxWidth() { + mBuilder + .setText(LONG_TEXT) + .setMaxWidth(100) + .build(); + assertEquals(mBuilder.getMaxWidth(), 100); + assertEquals(mBuilder.getMaxEms(), -1); + } + @Test public void testDrawableState() { int[] drawableState = {0, 1};