Merge "Implement Ctrl+B/I/U shorcut"
This commit is contained in:
@@ -636,6 +636,7 @@ package android {
|
||||
field public static final int ellipsize = 16842923; // 0x10100ab
|
||||
field public static final int ems = 16843096; // 0x1010158
|
||||
field public static final int enableOnBackInvokedCallback = 16844396; // 0x101066c
|
||||
field public static final int enableTextStylingShortcuts;
|
||||
field public static final int enableVrMode = 16844069; // 0x1010525
|
||||
field public static final int enabled = 16842766; // 0x101000e
|
||||
field public static final int end = 16843996; // 0x10104dc
|
||||
@@ -2113,6 +2114,7 @@ package android {
|
||||
field public static final int addToDictionary = 16908330; // 0x102002a
|
||||
field public static final int autofill = 16908355; // 0x1020043
|
||||
field public static final int background = 16908288; // 0x1020000
|
||||
field public static final int bold;
|
||||
field public static final int button1 = 16908313; // 0x1020019
|
||||
field public static final int button2 = 16908314; // 0x102001a
|
||||
field public static final int button3 = 16908315; // 0x102001b
|
||||
@@ -2138,6 +2140,7 @@ package android {
|
||||
field public static final int inputExtractAccessories = 16908378; // 0x102005a
|
||||
field public static final int inputExtractAction = 16908377; // 0x1020059
|
||||
field public static final int inputExtractEditText = 16908325; // 0x1020025
|
||||
field public static final int italic;
|
||||
field @Deprecated public static final int keyboardView = 16908326; // 0x1020026
|
||||
field public static final int list = 16908298; // 0x102000a
|
||||
field public static final int list_container = 16908351; // 0x102003f
|
||||
@@ -2169,6 +2172,7 @@ package android {
|
||||
field public static final int textAssist = 16908353; // 0x1020041
|
||||
field public static final int title = 16908310; // 0x1020016
|
||||
field public static final int toggle = 16908311; // 0x1020017
|
||||
field public static final int underline;
|
||||
field public static final int undo = 16908338; // 0x1020032
|
||||
field public static final int widget_frame = 16908312; // 0x1020018
|
||||
}
|
||||
@@ -55995,9 +55999,11 @@ package android.widget {
|
||||
ctor public EditText(android.content.Context, android.util.AttributeSet, int, int);
|
||||
method public void extendSelection(int);
|
||||
method public android.text.Editable getText();
|
||||
method public boolean isStyleShortcutEnabled();
|
||||
method public void selectAll();
|
||||
method public void setSelection(int, int);
|
||||
method public void setSelection(int);
|
||||
method public void setStyleShortcutsEnabled(boolean);
|
||||
}
|
||||
|
||||
public interface ExpandableListAdapter {
|
||||
|
||||
351
core/java/android/text/style/SpanUtils.java
Normal file
351
core/java/android/text/style/SpanUtils.java
Normal file
@@ -0,0 +1,351 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* 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 android.text.style;
|
||||
|
||||
import android.annotation.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.graphics.Typeface;
|
||||
import android.text.Spannable;
|
||||
import android.text.Spanned;
|
||||
import android.util.LongArray;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public class SpanUtils {
|
||||
private SpanUtils() {} // Do not instantiate
|
||||
|
||||
/**
|
||||
* Toggle the bold state of the given range.
|
||||
*
|
||||
* If there is at least one character is not bold in the given range, make the entire region to
|
||||
* be bold. If all characters of the given range is already bolded, this method removes bold
|
||||
* style from the given selection.
|
||||
*
|
||||
* @param spannable a spannable string
|
||||
* @param min minimum inclusive index of the selection.
|
||||
* @param max maximum exclusive index of the selection.
|
||||
* @return true if the selected region is toggled.
|
||||
*/
|
||||
public static boolean toggleBold(@NonNull Spannable spannable,
|
||||
@IntRange(from = 0) int min, @IntRange(from = 0) int max) {
|
||||
|
||||
if (min == max) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final StyleSpan[] boldSpans = spannable.getSpans(min, max, StyleSpan.class);
|
||||
final ArrayList<StyleSpan> filteredBoldSpans = new ArrayList<>();
|
||||
for (StyleSpan span : boldSpans) {
|
||||
if ((span.getStyle() & Typeface.BOLD) == Typeface.BOLD) {
|
||||
filteredBoldSpans.add(span);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isCovered(spannable, filteredBoldSpans, min, max)) {
|
||||
// At least one character doesn't have bold style. Making given region bold.
|
||||
spannable.setSpan(
|
||||
new StyleSpan(Typeface.BOLD), min, max, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Span covers the entire selection. Removing spans from tha region.
|
||||
for (int si = 0; si < filteredBoldSpans.size(); ++si) {
|
||||
final StyleSpan span = filteredBoldSpans.get(si);
|
||||
final int start = spannable.getSpanStart(span);
|
||||
final int end = spannable.getSpanEnd(span);
|
||||
final int flag = spannable.getSpanFlags(span);
|
||||
|
||||
// If BOLD_ITALIC style is attached, need to set ITALIC span to the subtracted range.
|
||||
final boolean needItalicSpan = (span.getStyle() & Typeface.ITALIC) == Typeface.ITALIC;
|
||||
|
||||
if (start < min) {
|
||||
if (end > max) {
|
||||
// selection: ------------|===================|----------------
|
||||
// span: <-------------------------------->
|
||||
// result: <-------> <---->
|
||||
spannable.setSpan(span, start, min, flag);
|
||||
spannable.setSpan(new StyleSpan(span.getStyle()), max, end, flag);
|
||||
if (needItalicSpan) {
|
||||
spannable.setSpan(new StyleSpan(Typeface.ITALIC), min, max, flag);
|
||||
}
|
||||
} else {
|
||||
// selection: ------------|===================|----------------
|
||||
// span: <----------->
|
||||
// result: <------->
|
||||
spannable.setSpan(span, start, min, flag);
|
||||
if (needItalicSpan) {
|
||||
spannable.setSpan(new StyleSpan(Typeface.ITALIC), min, end, flag);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (end > max) {
|
||||
// selection: ------------|===================|----------------
|
||||
// span: <------------------------>
|
||||
// result: <------------>
|
||||
spannable.setSpan(span, max, end, flag);
|
||||
if (needItalicSpan) {
|
||||
spannable.setSpan(new StyleSpan(Typeface.ITALIC), max, end, flag);
|
||||
}
|
||||
} else {
|
||||
// selection: ------------|===================|----------------
|
||||
// span: <----------->
|
||||
// result:
|
||||
spannable.removeSpan(span);
|
||||
if (needItalicSpan) {
|
||||
spannable.setSpan(new StyleSpan(Typeface.ITALIC), start, end, flag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle the italic state of the given range.
|
||||
*
|
||||
* If there is at least one character is not italic in the given range, make the entire region
|
||||
* to be italic. If all characters of the given range is already italic, this method removes
|
||||
* italic style from the given selection.
|
||||
*
|
||||
* @param spannable a spannable string
|
||||
* @param min minimum inclusive index of the selection.
|
||||
* @param max maximum exclusive index of the selection.
|
||||
* @return true if the selected region is toggled.
|
||||
*/
|
||||
public static boolean toggleItalic(@NonNull Spannable spannable,
|
||||
@IntRange(from = 0) int min, @IntRange(from = 0) int max) {
|
||||
|
||||
if (min == max) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final StyleSpan[] boldSpans = spannable.getSpans(min, max, StyleSpan.class);
|
||||
final ArrayList<StyleSpan> filteredBoldSpans = new ArrayList<>();
|
||||
for (StyleSpan span : boldSpans) {
|
||||
if ((span.getStyle() & Typeface.ITALIC) == Typeface.ITALIC) {
|
||||
filteredBoldSpans.add(span);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isCovered(spannable, filteredBoldSpans, min, max)) {
|
||||
// At least one character doesn't have italic style. Making given region italic.
|
||||
spannable.setSpan(
|
||||
new StyleSpan(Typeface.ITALIC), min, max, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Span covers the entire selection. Removing spans from tha region.
|
||||
for (int si = 0; si < filteredBoldSpans.size(); ++si) {
|
||||
final StyleSpan span = filteredBoldSpans.get(si);
|
||||
final int start = spannable.getSpanStart(span);
|
||||
final int end = spannable.getSpanEnd(span);
|
||||
final int flag = spannable.getSpanFlags(span);
|
||||
|
||||
// If BOLD_ITALIC style is attached, need to set BOLD span to the subtracted range.
|
||||
final boolean needBoldSpan = (span.getStyle() & Typeface.BOLD) == Typeface.BOLD;
|
||||
|
||||
if (start < min) {
|
||||
if (end > max) {
|
||||
// selection: ------------|===================|----------------
|
||||
// span: <-------------------------------->
|
||||
// result: <-------> <---->
|
||||
spannable.setSpan(span, start, min, flag);
|
||||
spannable.setSpan(new StyleSpan(span.getStyle()), max, end, flag);
|
||||
if (needBoldSpan) {
|
||||
spannable.setSpan(new StyleSpan(Typeface.BOLD), min, max, flag);
|
||||
}
|
||||
} else {
|
||||
// selection: ------------|===================|----------------
|
||||
// span: <----------->
|
||||
// result: <------->
|
||||
spannable.setSpan(span, start, min, flag);
|
||||
if (needBoldSpan) {
|
||||
spannable.setSpan(new StyleSpan(Typeface.BOLD), min, end, flag);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (end > max) {
|
||||
// selection: ------------|===================|----------------
|
||||
// span: <------------------------>
|
||||
// result: <------------>
|
||||
spannable.setSpan(span, max, end, flag);
|
||||
if (needBoldSpan) {
|
||||
spannable.setSpan(new StyleSpan(Typeface.BOLD), max, end, flag);
|
||||
}
|
||||
} else {
|
||||
// selection: ------------|===================|----------------
|
||||
// span: <----------->
|
||||
// result:
|
||||
spannable.removeSpan(span);
|
||||
if (needBoldSpan) {
|
||||
spannable.setSpan(new StyleSpan(Typeface.BOLD), start, end, flag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle the underline state of the given range.
|
||||
*
|
||||
* If there is at least one character is not underlined in the given range, make the entire
|
||||
* region to underlined. If all characters of the given range is already underlined, this
|
||||
* method removes underline from the given selection.
|
||||
*
|
||||
* @param spannable a spannable string
|
||||
* @param min minimum inclusive index of the selection.
|
||||
* @param max maximum exclusive index of the selection.
|
||||
* @return true if the selected region is toggled.
|
||||
*/
|
||||
public static boolean toggleUnderline(@NonNull Spannable spannable,
|
||||
@IntRange(from = 0) int min, @IntRange(from = 0) int max) {
|
||||
|
||||
if (min == max) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final List<UnderlineSpan> spans =
|
||||
Arrays.asList(spannable.getSpans(min, max, UnderlineSpan.class));
|
||||
|
||||
if (!isCovered(spannable, spans, min, max)) {
|
||||
// At least one character doesn't have underline style. Making given region underline.
|
||||
spannable.setSpan(new UnderlineSpan(), min, max, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||
return true;
|
||||
}
|
||||
// Span covers the entire selection. Removing spans from tha region.
|
||||
for (int si = 0; si < spans.size(); ++si) {
|
||||
final UnderlineSpan span = spans.get(si);
|
||||
final int start = spannable.getSpanStart(span);
|
||||
final int end = spannable.getSpanEnd(span);
|
||||
final int flag = spannable.getSpanFlags(span);
|
||||
|
||||
if (start < min) {
|
||||
if (end > max) {
|
||||
// selection: ------------|===================|----------------
|
||||
// span: <-------------------------------->
|
||||
// result: <-------> <---->
|
||||
spannable.setSpan(span, start, min, flag);
|
||||
spannable.setSpan(new UnderlineSpan(), max, end, flag);
|
||||
} else {
|
||||
// selection: ------------|===================|----------------
|
||||
// span: <----------->
|
||||
// result: <------->
|
||||
spannable.setSpan(span, start, min, flag);
|
||||
}
|
||||
} else {
|
||||
if (end > max) {
|
||||
// selection: ------------|===================|----------------
|
||||
// span: <------------------------>
|
||||
// result: <------------>
|
||||
spannable.setSpan(span, max, end, flag);
|
||||
} else {
|
||||
// selection: ------------|===================|----------------
|
||||
// span: <----------->
|
||||
// result:
|
||||
spannable.removeSpan(span);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static long pack(int from, int to) {
|
||||
return ((long) from) << 32 | (long) to;
|
||||
}
|
||||
|
||||
private static int min(long packed) {
|
||||
return (int) (packed >> 32);
|
||||
}
|
||||
|
||||
private static int max(long packed) {
|
||||
return (int) (packed & 0xFFFFFFFFL);
|
||||
}
|
||||
|
||||
private static boolean hasIntersection(int aMin, int aMax, int bMin, int bMax) {
|
||||
return aMin < bMax && bMin < aMax;
|
||||
}
|
||||
|
||||
private static long intersection(int aMin, int aMax, int bMin, int bMax) {
|
||||
return pack(Math.max(aMin, bMin), Math.min(aMax, bMax));
|
||||
}
|
||||
|
||||
private static <T> boolean isCovered(@NonNull Spannable spannable, @NonNull List<T> spans,
|
||||
@IntRange(from = 0) int min, @IntRange(from = 0) int max) {
|
||||
|
||||
if (min == max) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LongArray uncoveredRanges = new LongArray();
|
||||
LongArray nextUncoveredRanges = new LongArray();
|
||||
|
||||
uncoveredRanges.add(pack(min, max));
|
||||
|
||||
for (int si = 0; si < spans.size(); ++si) {
|
||||
final T span = spans.get(si);
|
||||
final int start = spannable.getSpanStart(span);
|
||||
final int end = spannable.getSpanEnd(span);
|
||||
|
||||
for (int i = 0; i < uncoveredRanges.size(); ++i) {
|
||||
final long packed = uncoveredRanges.get(i);
|
||||
final int uncoveredStart = min(packed);
|
||||
final int uncoveredEnd = max(packed);
|
||||
|
||||
if (!hasIntersection(start, end, uncoveredStart, uncoveredEnd)) {
|
||||
// This span doesn't affect this uncovered range. Try next span.
|
||||
nextUncoveredRanges.add(packed);
|
||||
} else {
|
||||
// This span has an intersection with uncovered range. Update the uncovered
|
||||
// range.
|
||||
long intersectionPack = intersection(start, end, uncoveredStart, uncoveredEnd);
|
||||
int intersectStart = min(intersectionPack);
|
||||
int intersectEnd = max(intersectionPack);
|
||||
|
||||
// Uncovered Range : ----------|=======================|-------------
|
||||
// Intersection : <---------->
|
||||
// Remaining uncovered ranges: ----------|=====|----------|======|-------------
|
||||
if (uncoveredStart != intersectStart) {
|
||||
// There is still uncovered area on the left.
|
||||
nextUncoveredRanges.add(pack(uncoveredStart, intersectStart));
|
||||
}
|
||||
if (intersectEnd != uncoveredEnd) {
|
||||
// There is still uncovered area on the right.
|
||||
nextUncoveredRanges.add(pack(intersectEnd, uncoveredEnd));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nextUncoveredRanges.size() == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Swap the uncoveredRanges and nextUncoveredRanges and clear the next one.
|
||||
final LongArray tmp = nextUncoveredRanges;
|
||||
nextUncoveredRanges = uncoveredRanges;
|
||||
uncoveredRanges = tmp;
|
||||
nextUncoveredRanges.clear();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -17,13 +17,17 @@
|
||||
package android.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.text.Editable;
|
||||
import android.text.Selection;
|
||||
import android.text.Spannable;
|
||||
import android.text.TextUtils;
|
||||
import android.text.method.ArrowKeyMovementMethod;
|
||||
import android.text.method.MovementMethod;
|
||||
import android.text.style.SpanUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
/*
|
||||
* This is supposed to be a *very* thin veneer over TextView.
|
||||
@@ -69,8 +73,18 @@ import android.util.AttributeSet;
|
||||
* See {@link android.R.styleable#EditText EditText Attributes},
|
||||
* {@link android.R.styleable#TextView TextView Attributes},
|
||||
* {@link android.R.styleable#View View Attributes}
|
||||
*
|
||||
* @attr ref android.R.styleable#EditText_enableTextStylingShortcuts
|
||||
*/
|
||||
public class EditText extends TextView {
|
||||
|
||||
// True if the style shortcut is enabled.
|
||||
private boolean mStyleShortcutsEnabled = false;
|
||||
|
||||
private static final int ID_BOLD = android.R.id.bold;
|
||||
private static final int ID_ITALIC = android.R.id.italic;
|
||||
private static final int ID_UNDERLINE = android.R.id.underline;
|
||||
|
||||
public EditText(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
@@ -85,6 +99,20 @@ public class EditText extends TextView {
|
||||
|
||||
public EditText(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
|
||||
final Resources.Theme theme = context.getTheme();
|
||||
final TypedArray a = theme.obtainStyledAttributes(attrs,
|
||||
com.android.internal.R.styleable.EditText, defStyleAttr, defStyleRes);
|
||||
|
||||
final int n = a.getIndexCount();
|
||||
for (int i = 0; i < n; ++i) {
|
||||
int attr = a.getIndex(i);
|
||||
switch (attr) {
|
||||
case com.android.internal.R.styleable.EditText_enableTextStylingShortcuts:
|
||||
mStyleShortcutsEnabled = a.getBoolean(attr, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -178,4 +206,77 @@ public class EditText extends TextView {
|
||||
protected boolean supportsAutoSizeText() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyShortcut(int keyCode, KeyEvent event) {
|
||||
if (event.hasModifiers(KeyEvent.META_CTRL_ON)) {
|
||||
// Handle Ctrl-only shortcuts.
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_B:
|
||||
if (mStyleShortcutsEnabled && hasSelection()) {
|
||||
return onTextContextMenuItem(ID_BOLD);
|
||||
}
|
||||
break;
|
||||
case KeyEvent.KEYCODE_I:
|
||||
if (mStyleShortcutsEnabled && hasSelection()) {
|
||||
return onTextContextMenuItem(ID_ITALIC);
|
||||
}
|
||||
break;
|
||||
case KeyEvent.KEYCODE_U:
|
||||
if (mStyleShortcutsEnabled && hasSelection()) {
|
||||
return onTextContextMenuItem(ID_UNDERLINE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return super.onKeyShortcut(keyCode, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTextContextMenuItem(int id) {
|
||||
// TODO: Move to switch-case once the resource ID is finalized.
|
||||
if (id == ID_BOLD || id == ID_ITALIC || id == ID_UNDERLINE) {
|
||||
return performStylingAction(id);
|
||||
}
|
||||
return super.onTextContextMenuItem(id);
|
||||
}
|
||||
|
||||
private boolean performStylingAction(int actionId) {
|
||||
final int selectionStart = getSelectionStart();
|
||||
final int selectionEnd = getSelectionEnd();
|
||||
if (selectionStart < 0 || selectionEnd < 0) {
|
||||
return false; // There is no selection.
|
||||
}
|
||||
int min = Math.min(selectionStart, selectionEnd);
|
||||
int max = Math.max(selectionStart, selectionEnd);
|
||||
|
||||
|
||||
Spannable spannable = getText();
|
||||
if (actionId == ID_BOLD) {
|
||||
return SpanUtils.toggleBold(spannable, min, max);
|
||||
} else if (actionId == ID_ITALIC) {
|
||||
return SpanUtils.toggleItalic(spannable, min, max);
|
||||
} else if (actionId == ID_UNDERLINE) {
|
||||
return SpanUtils.toggleUnderline(spannable, min, max);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables styls shortcuts, e.g. Ctrl+B for making text bold.
|
||||
*
|
||||
* @param enabled true for enabled, false for disabled.
|
||||
*/
|
||||
public void setStyleShortcutsEnabled(boolean enabled) {
|
||||
mStyleShortcutsEnabled = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if style shortcut is enabled, otherwise returns false.
|
||||
* @return true if style shortcut is enabled, otherwise returns false.
|
||||
*/
|
||||
public boolean isStyleShortcutEnabled() {
|
||||
return mStyleShortcutsEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4509,6 +4509,8 @@
|
||||
</attr>
|
||||
</declare-styleable>
|
||||
<declare-styleable name="EditText">
|
||||
<!-- Enables styling shortcuts, e.g. Ctrl+B for bold. This is off by default. -->
|
||||
<attr name="enableTextStylingShortcuts" format="boolean" />
|
||||
</declare-styleable>
|
||||
<declare-styleable name="FastScroll">
|
||||
<!-- Drawable used for the scroll bar thumb. -->
|
||||
|
||||
@@ -70,6 +70,12 @@
|
||||
<item type="id" name="cut" />
|
||||
<item type="id" name="copy" />
|
||||
<item type="id" name="paste" />
|
||||
<!-- Editor action that makes selected text bold. -->
|
||||
<item type="id" name="bold" />
|
||||
<!-- Editor action that makes selected text italic. -->
|
||||
<item type="id" name="italic" />
|
||||
<!-- Editor action that makes selected text underline. -->
|
||||
<item type="id" name="underline" />
|
||||
<item type="id" name="copyUrl" />
|
||||
<item type="id" name="selectTextMode" />
|
||||
<item type="id" name="switchInputMethod" />
|
||||
|
||||
@@ -115,9 +115,13 @@
|
||||
<public name="handwritingBoundsOffsetRight" />
|
||||
<public name="handwritingBoundsOffsetBottom" />
|
||||
<public name="accessibilityDataPrivate" />
|
||||
<public name="enableTextStylingShortcuts" />
|
||||
</staging-public-group>
|
||||
|
||||
<staging-public-group type="id" first-id="0x01cd0000">
|
||||
<public name="bold" />
|
||||
<public name="italic" />
|
||||
<public name="underline" />
|
||||
</staging-public-group>
|
||||
|
||||
<staging-public-group type="style" first-id="0x01cc0000">
|
||||
|
||||
Reference in New Issue
Block a user