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

Remove extra padding on text blocks in Android #1560

Merged
merged 10 commits into from
Jan 16, 2020
5 changes: 5 additions & 0 deletions RELEASE-NOTES.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
1.21.0
------
* [Android] Reduced padding around text
* Fixed intermittent crash that occurred with very long posts

1.20.0
------
* Fix bug where image placeholders would sometimes not be shown
Expand Down
2 changes: 1 addition & 1 deletion gutenberg
Submodule gutenberg updated 86 files
+2 −2 lib/demo-block-templates/front-page.html
+7 −0 lib/edit-site-page.php
+11 −7 lib/template-loader.php
+5,295 −2,010 package-lock.json
+8 −7 package.json
+2 −0 packages/block-editor/src/components/block-list/block-mobile-floating-toolbar.native.scss
+2 −0 packages/block-editor/src/components/block-list/block-popover.js
+6 −34 packages/block-editor/src/components/block-list/block.js
+21 −3 packages/block-editor/src/components/block-list/breadcrumb.js
+0 −2 packages/block-editor/src/components/block-list/index.js
+4 −0 packages/block-editor/src/components/block-mover/style.scss
+8 −14 packages/block-editor/src/components/link-control/index.js
+29 −69 packages/block-editor/src/components/link-control/test/index.js
+2 −1 packages/block-editor/src/components/writing-flow/focus-capture.js
+4 −3 packages/block-editor/src/components/writing-flow/index.js
+0 −1 packages/block-library/src/gallery/gallery-image-style.native.scss
+1 −5 packages/block-library/src/heading/edit.native.js
+0 −4 packages/block-library/src/heading/editor.native.scss
+0 −4 packages/block-library/src/heading/style.native.scss
+7 −1 packages/block-library/src/navigation-link/editor.scss
+0 −8 packages/block-library/src/navigation/editor.scss
+0 −4 packages/block-library/src/paragraph/style.native.scss
+2 −5 packages/components/src/animate/stories/index.js
+1 −1 packages/components/src/base-control/stories/index.js
+1 −1 packages/components/src/button-group/stories/index.js
+1 −1 packages/components/src/button/stories/index.js
+1 −1 packages/components/src/card/stories/_index.js
+1 −1 packages/components/src/card/stories/body.js
+1 −1 packages/components/src/card/stories/divider.js
+1 −1 packages/components/src/card/stories/footer.js
+1 −1 packages/components/src/card/stories/header.js
+1 −1 packages/components/src/card/stories/media.js
+1 −1 packages/components/src/checkbox-control/stories/index.js
+5 −7 packages/components/src/clipboard-button/stories/index.js
+1 −1 packages/components/src/color-indicator/stories/index.js
+4 −18 packages/components/src/color-palette/stories/index.js
+1 −1 packages/components/src/color-picker/stories/index.js
+1 −1 packages/components/src/custom-select-control/stories/index.js
+2 −8 packages/components/src/dashicon/stories/index.js
+1 −1 packages/components/src/draggable/stories/index.js
+5 −21 packages/components/src/dropdown/stories/index.js
+1 −1 packages/components/src/external-link/stories/index.js
+1 −1 packages/components/src/font-size-picker/stories/index.js
+15 −5 packages/components/src/guide/stories/index.js
+1 −1 packages/components/src/icon/stories/index.js
+1 −1 packages/components/src/modal/stories/index.js
+1 −0 packages/components/src/navigable-container/container.js
+80 −0 packages/components/src/panel/stories/index.js
+1 −1 packages/components/src/popover/stories/index.js
+3 −15 packages/components/src/radio-control/stories/index.js
+1 −1 packages/components/src/range-control/stories/index.js
+1 −1 packages/components/src/resizable-box/stories/index.js
+1 −1 packages/components/src/scroll-lock/stories/index.js
+17 −14 packages/components/src/snackbar/stories/index.js
+2 −4 packages/components/src/spinner/stories/index.js
+1 −1 packages/components/src/tab-panel/stories/index.js
+6 −5 packages/components/src/text-highlight/stories/index.js
+1 −1 packages/components/src/text/stories/index.js
+1 −1 packages/components/src/tip/stories/index.js
+1 −1 packages/components/src/toggle-control/stories/index.js
+1 −1 packages/components/src/toolbar-group/stories/index.js
+27 −7 packages/components/src/toolbar/stories/index.js
+8 −5 packages/components/src/visually-hidden/stories/index.js
+3 −3 packages/e2e-test-utils/src/transform-block-to.js
+19 −0 packages/e2e-tests/specs/editor/various/__snapshots__/keyboard-navigable-blocks.test.js.snap
+36 −8 packages/e2e-tests/specs/editor/various/keyboard-navigable-blocks.test.js
+3 −0 packages/edit-site/package.json
+24 −3 packages/edit-site/src/components/block-editor/index.js
+24 −7 packages/edit-site/src/components/editor/index.js
+8 −0 packages/edit-site/src/components/header/index.js
+4 −0 packages/edit-site/src/components/header/style.scss
+56 −0 packages/edit-site/src/components/save-button/index.js
+2 −2 packages/editor/src/components/post-title/style.native.scss
+1 −6 packages/rich-text/src/component/index.native.js
+0 −1 packages/rich-text/src/component/style.native.scss
+74 −0 packages/scripts/scripts/packages-update.js
+0 −7 storybook/addons.js
+18 −0 storybook/main.js
+13 −0 storybook/manager.js
+0 −1 storybook/presets.js
+1 −10 storybook/preview.js
+1 −1 storybook/stories/docs/introduction.story.mdx
+1 −1 storybook/stories/playground/index.js
+384 −144 storybook/test/__snapshots__/index.js.snap
+2 −2 storybook/test/index.js
+11 −0 storybook/theme.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ protected ReactAztecText createViewInstance(ThemedReactContext reactContext) {
aztecText.setFocusableInTouchMode(false);
aztecText.setFocusable(false);
aztecText.setCalypsoMode(false);
aztecText.setPadding(0, 0, 0, 0);
// This is a temporary hack that sets the correct GB link color and underline
// see: https://github.com/wordpress-mobile/gutenberg-mobile/pull/1109
aztecText.setLinkFormatter(new LinkFormatter(aztecText,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
package org.wordpress.mobile.ReactNativeAztec;

import android.widget.EditText;

import androidx.annotation.Nullable;

import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.views.textinput.ReactTextInputShadowNode;

public class ReactAztecTextShadowNode extends ReactTextInputShadowNode {
private @Nullable ReadableMap mTextMap = null;
private @Nullable Integer mColor = null;
public class ReactAztecTextShadowNode extends ReactTextInputShadowNodeFork {

@Override
protected EditText createDummyEditText(ThemedReactContext themedContext) {
return new EditText(themedContext, null, 0);
}

Copy link
Contributor

@mchowning mchowning Nov 26, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of preventing the default padding from being set like I am doing, an alternative approach that seems work just as well is to explicitly set padding to 0 on the ShadowNode when it is constructed:

    public ReactAztecTextShadowNode() {
        super();
        setPadding(Spacing.START, 0);
        setPadding(Spacing.TOP, 0);
        setPadding(Spacing.END, 0);
        setPadding(Spacing.BOTTOM,0);
    }

I do not have a strong preference between these two approaches, although it does seem nice how ignoring the default padding does not have us setting a padding of 0 both here and in the ReactAztecManager. Both approaches do stop working if we remove the aztecText.setPadding(0, 0, 0, 0) line in ReactAztecManager (likely because then the default EditText background controls the padding on the view).

@ReactProp(name = PROP_TEXT)
public void setText(@Nullable ReadableMap inputMap) {
mTextMap = inputMap;
markUpdated();
}

@ReactProp(name = "color", customType = "Color")
public void setColor(@Nullable Integer color) {
mColor = color;
markUpdated();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
package org.wordpress.mobile.ReactNativeAztec;

import android.os.Build;
import android.text.Layout;
import android.util.TypedValue;
import android.view.ViewGroup;
import android.widget.EditText;

import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.core.view.ViewCompat;

import com.facebook.infer.annotation.Assertions;
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.uimanager.Spacing;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.UIViewOperationQueue;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.views.text.ReactBaseTextShadowNode;
import com.facebook.react.views.text.ReactTextUpdate;
import com.facebook.react.views.textinput.ReactTextInputLocalData;
import com.facebook.react.views.view.MeasureUtil;
import com.facebook.yoga.YogaMeasureFunction;
import com.facebook.yoga.YogaMeasureMode;
import com.facebook.yoga.YogaMeasureOutput;
import com.facebook.yoga.YogaNode;

/**
* This is a fork from {@link com.facebook.react.views.textinput.ReactTextInputShadowNode} for the purpose
* of customizing that class so that the construction of the dummy {@link EditText} instance
* can be overridden (see {@link ReactTextInputShadowNodeFork#createDummyEditText(ThemedReactContext)}).
*/
public class ReactTextInputShadowNodeFork extends ReactBaseTextShadowNode
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there some plan on how can we follow improvements in ReactAztecTextShadowNode or ReactBaseTextShadowNode which will be potentially presented in the new version of RN?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really. Ideally, we'll be able to get React Native itself updated to allow overriding the construction of the EditText instance like this. Otherwise (and until then) we will just have to manually pull in any updates.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got this fix merged into React Native, so once we start using a version of React Native that includes that change, we won't need to have this ReactTextInputShadowNodeFork anymore! 😄

implements YogaMeasureFunction {

private int mMostRecentEventCount = UNSET;
private @Nullable EditText mDummyEditText;
private @Nullable ReactTextInputLocalData mLocalData;

@VisibleForTesting public static final String PROP_TEXT = "text";
@VisibleForTesting public static final String PROP_PLACEHOLDER = "placeholder";
@VisibleForTesting public static final String PROP_SELECTION = "selection";

// Represents the {@code text} property only, not possible nested content.
private @Nullable String mText = null;
private @Nullable String mPlaceholder = null;
private int mSelectionStart = UNSET;
private int mSelectionEnd = UNSET;

public ReactTextInputShadowNodeFork() {
mTextBreakStrategy = (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) ?
Layout.BREAK_STRATEGY_SIMPLE : Layout.BREAK_STRATEGY_HIGH_QUALITY;

initMeasureFunction();
}

private void initMeasureFunction() {
setMeasureFunction(this);
}

@Override
public void setThemedContext(ThemedReactContext themedContext) {
super.setThemedContext(themedContext);

// {@code EditText} has by default a border at the bottom of its view
// called "underline". To have a native look and feel of the TextEdit
// we have to preserve it at least by default.
// The border (underline) has its padding set by the background image
// provided by the system (which vary a lot among versions and vendors
// of Android), and it cannot be changed.
// So, we have to enforce it as a default padding.
// TODO #7120264: Cache this stuff better.
EditText editText = createDummyEditText(getThemedContext());
setDefaultPadding(Spacing.START, ViewCompat.getPaddingStart(editText));
setDefaultPadding(Spacing.TOP, editText.getPaddingTop());
setDefaultPadding(Spacing.END, ViewCompat.getPaddingEnd(editText));
setDefaultPadding(Spacing.BOTTOM, editText.getPaddingBottom());

mDummyEditText = editText;

// We must measure the EditText without paddings, so we have to reset them.
mDummyEditText.setPadding(0, 0, 0, 0);

// This is needed to fix an android bug since 4.4.3 which will throw an NPE in measure,
// setting the layoutParams fixes it: https://code.google.com/p/android/issues/detail?id=75877
mDummyEditText.setLayoutParams(
new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
}

protected EditText createDummyEditText(ThemedReactContext themedContext) {
return new EditText(themedContext);
}

@Override
public long measure(
YogaNode node,
float width,
YogaMeasureMode widthMode,
float height,
YogaMeasureMode heightMode) {
// measure() should never be called before setThemedContext()
EditText editText = Assertions.assertNotNull(mDummyEditText);

if (mLocalData != null) {
mLocalData.apply(editText);
} else {
editText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextAttributes.getEffectiveFontSize());

if (mNumberOfLines != UNSET) {
editText.setLines(mNumberOfLines);
}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
editText.getBreakStrategy() != mTextBreakStrategy) {
editText.setBreakStrategy(mTextBreakStrategy);
}
}

// make sure the placeholder content is also being measured
editText.setHint(getPlaceholder());
editText.measure(
MeasureUtil.getMeasureSpec(width, widthMode),
MeasureUtil.getMeasureSpec(height, heightMode));

return YogaMeasureOutput.make(editText.getMeasuredWidth(), editText.getMeasuredHeight());
}

@Override
public boolean isVirtualAnchor() {
return true;
}

@Override
public boolean isYogaLeafNode() {
return true;
}

@Override
public void setLocalData(Object data) {
Assertions.assertCondition(data instanceof ReactTextInputLocalData);
mLocalData = (ReactTextInputLocalData) data;

// Telling to Yoga that the node should be remeasured on next layout pass.
dirty();

// Note: We should NOT mark the node updated (by calling {@code markUpdated}) here
// because the state remains the same.
}

@ReactProp(name = "mostRecentEventCount")
public void setMostRecentEventCount(int mostRecentEventCount) {
mMostRecentEventCount = mostRecentEventCount;
}

@ReactProp(name = PROP_TEXT)
public void setText(@Nullable String text) {
mText = text;
markUpdated();
}

public @Nullable String getText() {
return mText;
}

@ReactProp(name = PROP_PLACEHOLDER)
public void setPlaceholder(@Nullable String placeholder) {
mPlaceholder = placeholder;
markUpdated();
}

public @Nullable String getPlaceholder() {
return mPlaceholder;
}

@ReactProp(name = PROP_SELECTION)
public void setSelection(@Nullable ReadableMap selection) {
mSelectionStart = mSelectionEnd = UNSET;
if (selection == null)
return;

if (selection.hasKey("start") && selection.hasKey("end")) {
mSelectionStart = selection.getInt("start");
mSelectionEnd = selection.getInt("end");
markUpdated();
}
}

@Override
public void setTextBreakStrategy(@Nullable String textBreakStrategy) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
return;
}

if (textBreakStrategy == null || "simple".equals(textBreakStrategy)) {
mTextBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE;
} else if ("highQuality".equals(textBreakStrategy)) {
mTextBreakStrategy = Layout.BREAK_STRATEGY_HIGH_QUALITY;
} else if ("balanced".equals(textBreakStrategy)) {
mTextBreakStrategy = Layout.BREAK_STRATEGY_BALANCED;
} else {
throw new JSApplicationIllegalArgumentException("Invalid textBreakStrategy: " + textBreakStrategy);
}
}

@Override
public void onCollectExtraUpdates(UIViewOperationQueue uiViewOperationQueue) {
super.onCollectExtraUpdates(uiViewOperationQueue);

if (mMostRecentEventCount != UNSET) {
ReactTextUpdate reactTextUpdate =
new ReactTextUpdate(
spannedFromShadowNode(
this,
getText(),
/* supportsInlineViews: */ false,
/* nativeViewHierarchyOptimizer: */ null // only needed to support inline views
),
mMostRecentEventCount,
mContainsImages,
getPadding(Spacing.LEFT),
getPadding(Spacing.TOP),
getPadding(Spacing.RIGHT),
getPadding(Spacing.BOTTOM),
mTextAlign,
mTextBreakStrategy,
mJustificationMode,
mSelectionStart,
mSelectionEnd);
uiViewOperationQueue.enqueueUpdateExtraData(getReactTag(), reactTextUpdate);
}
}

@Override
public void setPadding(int spacingType, float padding) {
super.setPadding(spacingType, padding);
markUpdated();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@
<dimen name="margin_medium">16dp</dimen>
<dimen name="margin_large">32dp</dimen>

<dimen name="heading_vertical_padding">0dp</dimen>
</resources>
2 changes: 2 additions & 0 deletions react-native-aztec/ios/RNTAztecView/RCTAztecView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ class RCTAztecView: Aztec.TextView {
delegate = self
textContainerInset = .zero
contentInset = .zero
textContainer.lineFragmentPadding = 0
Aztec.Metrics.listTextIndentation = 24
addPlaceholder()
textDragInteraction?.isEnabled = false
storage.htmlConverter.characterToReplaceLastEmptyLine = Character(.zeroWidthSpace)
Expand Down
8 changes: 0 additions & 8 deletions src/_native.android.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,3 @@
// Fonts
$default-monospace-font: monospace;
$default-regular-font: serif;
$title-block-padding-top: 0;
$title-block-padding-bottom: 0;

$min-height-title: 53;
$min-height-paragraph: 50;
$min-height-heading: 60;

$floating-toolbar-height: 44;
8 changes: 0 additions & 8 deletions src/_native.ios.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,3 @@
// Fonts
$default-monospace-font: menlo;
$default-regular-font: 'Noto Serif';
$title-block-padding-top: 12;
$title-block-padding-bottom: 12;

$min-height-title: 30;
$min-height-paragraph: 24;
$min-height-heading: 30;

$floating-toolbar-height: 44;