mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
Use ActionMode instead of popup window in editor
Updated editor
This commit is contained in:
Binary file not shown.
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.views.editor;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.view.ActionMode;
|
||||
import androidx.appcompat.view.menu.MenuBuilder;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.itsaky.androidide.R;
|
||||
|
||||
import io.github.rosemoe.sora.event.ClickEvent;
|
||||
import io.github.rosemoe.sora.event.LongPressEvent;
|
||||
import io.github.rosemoe.sora.event.SelectionChangeEvent;
|
||||
import io.github.rosemoe.sora.widget.component.EditorTextActionWindow;
|
||||
|
||||
/**
|
||||
* Text action mode actions for the editor.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class EditorTextActionMode {
|
||||
|
||||
private final IDEEditor editor;
|
||||
private ActionMode actionMode;
|
||||
|
||||
public EditorTextActionMode (IDEEditor editor) {
|
||||
this.editor = editor;
|
||||
|
||||
// Disable default text action window
|
||||
this.editor.getComponent (EditorTextActionWindow.class).setEnabled (false);
|
||||
|
||||
this.editor.subscribeEvent (SelectionChangeEvent.class, ((event, unsubscribe) -> {
|
||||
final var left = event.getLeft ();
|
||||
final var right = event.getRight ();
|
||||
if (left.index != right.index) {
|
||||
showAction();
|
||||
}
|
||||
}));
|
||||
|
||||
// When click event is received, finish the action mode if it is started
|
||||
this.editor.subscribeEvent (ClickEvent.class, (event, unsubscribe) -> exit ());
|
||||
|
||||
// Start action mode even if no text is selected
|
||||
this.editor.subscribeEvent (LongPressEvent.class, (event, unsubscribe) -> {
|
||||
if (actionMode == null) {
|
||||
showAction ();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showAction () {
|
||||
if (actionMode != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
actionMode = ((AppCompatActivity) editor.getContext ()).startSupportActionMode (new ActionMode.Callback () {
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
@Override
|
||||
public boolean onCreateActionMode (ActionMode mode, Menu menu) {
|
||||
if (menu instanceof MenuBuilder) {
|
||||
MenuBuilder builder = (MenuBuilder) menu;
|
||||
builder.setOptionalIconsVisible(true);
|
||||
}
|
||||
mode.setTitle(android.R.string.selectTextMode);
|
||||
TypedArray array = editor.getContext().getTheme().obtainStyledAttributes(new int[]{
|
||||
android.R.attr.actionModeSelectAllDrawable,
|
||||
android.R.attr.actionModeCutDrawable,
|
||||
android.R.attr.actionModeCopyDrawable,
|
||||
android.R.attr.actionModePasteDrawable,
|
||||
});
|
||||
|
||||
menu.add (0, 8, 0, R.string.action_expand_selection)
|
||||
.setShowAsActionFlags (1)
|
||||
.setIcon (createDrawable (R.drawable.ic_expand_selection));
|
||||
|
||||
menu.add(0, 0, 0, editor.getContext().getString(android.R.string.selectAll))
|
||||
.setShowAsActionFlags(1)
|
||||
.setIcon(array.getDrawable(0));
|
||||
|
||||
if (editor.isEditable()) {
|
||||
menu.add(0, 1, 0, editor.getContext().getString(android.R.string.cut))
|
||||
.setShowAsActionFlags(1)
|
||||
.setIcon(array.getDrawable(1));
|
||||
}
|
||||
|
||||
menu.add(0, 2, 0, editor.getContext().getString(android.R.string.copy))
|
||||
.setShowAsActionFlags(1)
|
||||
.setIcon(array.getDrawable(2));
|
||||
|
||||
if (editor.isEditable()) {
|
||||
menu.add(0, 3, 0, editor.getContext().getString(android.R.string.paste))
|
||||
.setShowAsActionFlags(1)
|
||||
.setIcon(array.getDrawable(3));
|
||||
}
|
||||
|
||||
menu.add(0, 4, 0, editor.getContext().getString(com.itsaky.androidide.R.string.menu_navigate_definition))
|
||||
.setShowAsActionFlags(1)
|
||||
.setIcon(createDrawable(com.itsaky.androidide.R.drawable.ic_goto_definition));
|
||||
|
||||
menu.add(0, 5, 0, editor.getContext().getString(com.itsaky.androidide.R.string.menu_navigate_references))
|
||||
.setShowAsActionFlags(1)
|
||||
.setIcon(createDrawable(com.itsaky.androidide.R.drawable.ic_find_references));
|
||||
|
||||
menu.add(0, 6, 0, editor.getContext().getString(com.itsaky.androidide.R.string.menu_comment_line))
|
||||
.setShowAsActionFlags(1)
|
||||
.setIcon(createDrawable(com.itsaky.androidide.R.drawable.ic_comment_line));
|
||||
|
||||
menu.add(0, 7, 0, editor.getContext().getString(com.itsaky.androidide.R.string.menu_uncomment_line))
|
||||
.setShowAsActionFlags(1)
|
||||
.setIcon(createDrawable(com.itsaky.androidide.R.drawable.ic_uncomment_line));
|
||||
|
||||
array.recycle();
|
||||
return true;
|
||||
}
|
||||
|
||||
private Drawable createDrawable(int icon) {
|
||||
final Drawable d = ContextCompat.getDrawable(editor.getContext(), icon);
|
||||
if (d != null) {
|
||||
d.setColorFilter(ContextCompat.getColor(editor.getContext(), com.itsaky.androidide.R.color.secondaryColor), PorterDuff.Mode.SRC_ATOP);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareActionMode (ActionMode mode, Menu menu) {
|
||||
if (editor.getFile() == null) return false;
|
||||
|
||||
final String name = editor.getFile().getName();
|
||||
final boolean isJava = name.endsWith(".java") || name.endsWith(".gradle");
|
||||
final boolean isXml = name.endsWith(".xml");
|
||||
final MenuItem def = menu.findItem(4);
|
||||
final MenuItem ref = menu.findItem(5);
|
||||
final MenuItem comment = menu.findItem(6);
|
||||
final MenuItem uncomment = menu.findItem(7);
|
||||
|
||||
comment.setEnabled(isJava || isXml).getIcon().setAlpha(isJava || isXml ? 255 : 76);
|
||||
uncomment.setEnabled(isJava || isXml).getIcon().setAlpha(isJava || isXml ? 255 : 76);
|
||||
|
||||
// These menu items may, or may not be added
|
||||
// So we need to check if its null or not
|
||||
// before we perform any further actions
|
||||
if (def != null) {
|
||||
def.getIcon().setAlpha(isJava ? 255 : 76);
|
||||
def.setVisible(isJava);
|
||||
}
|
||||
|
||||
if (ref != null) {
|
||||
ref.getIcon().setAlpha(isJava ? 255 : 76);
|
||||
ref.setVisible(isJava);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onActionItemClicked (ActionMode mode, MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case 0:
|
||||
editor.selectAll();
|
||||
break;
|
||||
case 1:
|
||||
editor.cutText();
|
||||
break;
|
||||
case 2:
|
||||
editor.copyText();
|
||||
break;
|
||||
case 3:
|
||||
editor.pasteText();
|
||||
break;
|
||||
case 4:
|
||||
editor.findDefinition();
|
||||
break;
|
||||
case 5:
|
||||
editor.findReferences();
|
||||
break;
|
||||
case 6:
|
||||
editor.commentLine();
|
||||
break;
|
||||
case 7:
|
||||
editor.uncommentLine();
|
||||
break;
|
||||
case 8 :
|
||||
editor.expandSelection ();
|
||||
break;
|
||||
}
|
||||
|
||||
final var id = item.getItemId ();
|
||||
if (id == 0 || id == 8) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return exit ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyActionMode (ActionMode mode) {
|
||||
actionMode = null;
|
||||
editor.setSelection (editor.getCursorRange ().getStart ());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public boolean exit () {
|
||||
if (actionMode != null) {
|
||||
actionMode.finish();
|
||||
actionMode = null;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -67,6 +67,9 @@ public class IDEEditor extends CodeEditor {
|
||||
private IDELanguageClientImpl mLanguageClient;
|
||||
private SignatureHelpWindow mSignatureHelpWindow;
|
||||
|
||||
@SuppressWarnings("FieldCanBeLocal,unused")
|
||||
private final EditorTextActionMode mActionMode;
|
||||
|
||||
public static final String KEY_FILE = "editor_file";
|
||||
private static final Logger LOG = Logger.instance ("IDEEditor");
|
||||
|
||||
@@ -85,6 +88,8 @@ public class IDEEditor extends CodeEditor {
|
||||
public IDEEditor (Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
|
||||
this.mActionMode = new EditorTextActionMode (this);
|
||||
|
||||
setColorScheme (new SchemeAndroidIDE ());
|
||||
subscribeEvent (ContentChangeEvent.class, (event, unsubscribe) -> notifyContentChanged ());
|
||||
|
||||
@@ -106,6 +111,14 @@ public class IDEEditor extends CodeEditor {
|
||||
range.getEnd ().getColumn ());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the selection of this editor to the given position.
|
||||
* @param position The position to select.
|
||||
*/
|
||||
public void setSelection (@NonNull Position position) {
|
||||
setSelection (position.getLine (), position.getColumn ());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the file that this editor is currently editing.
|
||||
* @param file The file to set.
|
||||
|
||||
@@ -31,6 +31,7 @@ import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.blankj.utilcode.util.SizeUtils;
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
import com.itsaky.lsp.models.SignatureHelp;
|
||||
import com.itsaky.lsp.models.SignatureInformation;
|
||||
|
||||
@@ -45,6 +46,8 @@ public class SignatureHelpWindow extends EditorPopupWindow {
|
||||
|
||||
private final TextView text;
|
||||
|
||||
private static final Logger LOG = Logger.instance ("SignatureHelpWindow");
|
||||
|
||||
/**
|
||||
* Create a signature help popup window for editor
|
||||
*
|
||||
@@ -78,6 +81,7 @@ public class SignatureHelpWindow extends EditorPopupWindow {
|
||||
}
|
||||
|
||||
public void setSignatureHelp (SignatureHelp signature) {
|
||||
|
||||
if (signature == null || signature.getSignatures ().isEmpty ()) {
|
||||
if (isShowing ()) {
|
||||
dismiss ();
|
||||
|
||||
@@ -114,12 +114,12 @@ public class GraphicTextRow {
|
||||
if (mText.widthCache != null) {
|
||||
float w = 0f;
|
||||
var cache = mText.widthCache;
|
||||
for (int i = start; i < mEnd;i++) {
|
||||
for (int i = start; i <= mEnd;i++) {
|
||||
if (w > advance) {
|
||||
mBuffer[0] = Math.max(start, i - 1);
|
||||
mBuffer[1] = i > start ? w - cache[i - 1] : w;
|
||||
return mBuffer;
|
||||
} else {
|
||||
} else if(i < mEnd) {
|
||||
w += cache[i];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,13 +38,13 @@ import io.github.rosemoe.sora.widget.schemes.EditorColorScheme;
|
||||
* @see Builder
|
||||
*/
|
||||
public class MappedSpans implements Spans {
|
||||
|
||||
|
||||
private final List<List<Span>> spanMap;
|
||||
|
||||
|
||||
private MappedSpans(@NonNull List<List<Span>> spanMap) {
|
||||
this.spanMap = spanMap;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void adjustOnInsert(CharPosition start, CharPosition end) {
|
||||
var startLine = start.line;
|
||||
@@ -294,12 +294,12 @@ public class MappedSpans implements Spans {
|
||||
}
|
||||
spanMap.set(line, spans);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void addLineAt(int line, List<Span> spans) {
|
||||
spanMap.add(line, spans);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void deleteLineAt(int line) {
|
||||
spanMap.remove(line);
|
||||
|
||||
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
* sora-editor - the awesome code editor for Android
|
||||
* https://github.com/Rosemoe/sora-editor
|
||||
* Copyright (C) 2020-2022 Rosemoe
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*
|
||||
* Please contact Rosemoe by email 2073412493@qq.com if you need
|
||||
* additional information or have any questions
|
||||
*/
|
||||
package io.github.rosemoe.sora.text;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.icu.text.Bidi;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.IntRange;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
/**
|
||||
* Bidi algorithm for text directions.
|
||||
* Replacement of android.text.AndroidBidi
|
||||
*/
|
||||
@SuppressLint("PrivateApi")
|
||||
public final class AndroidBidi {
|
||||
|
||||
public final static int RUN_LENGTH_MASK = 0x03ffffff;
|
||||
public static final int RUN_LEVEL_SHIFT = 26;
|
||||
public static final int RUN_LEVEL_MASK = 0x3f;
|
||||
public static final int RUN_RTL_FLAG = 1 << RUN_LEVEL_SHIFT;
|
||||
|
||||
public static final int DIR_LEFT_TO_RIGHT = 1;
|
||||
public static final int DIR_RIGHT_TO_LEFT = -1;
|
||||
|
||||
public static final Directions DIRS_ALL_LEFT_TO_RIGHT =
|
||||
new Directions(new int[] { 0, RUN_LENGTH_MASK });
|
||||
|
||||
public static final Directions DIRS_ALL_RIGHT_TO_LEFT =
|
||||
new Directions(new int[] { 0, RUN_LENGTH_MASK | RUN_RTL_FLAG });
|
||||
|
||||
public static final int DIR_REQUEST_LTR = 1;
|
||||
public static final int DIR_REQUEST_RTL = -1;
|
||||
public static final int DIR_REQUEST_DEFAULT_LTR = 2;
|
||||
public static final int DIR_REQUEST_DEFAULT_RTL = -2;
|
||||
|
||||
private static Class<?> bidiClass;
|
||||
static {
|
||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
|
||||
try {
|
||||
bidiClass = Class.forName("android.text.AndroidBidi");
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
bidiClass = null;
|
||||
}
|
||||
} else {
|
||||
bidiClass = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static int bidi(int dir, char[] chs, byte[] chInfo) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.P)
|
||||
private static int bidiImpl(int dir, char[] chs, byte[] chInfo) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.Q)
|
||||
private static int bidiImplQ(int dir, char[] chs, byte[] chInfo) {
|
||||
if (chs == null || chInfo == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
||||
final int length = chs.length;
|
||||
if (chInfo.length < length) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
final byte paraLevel;
|
||||
switch (dir) {
|
||||
case DIR_REQUEST_RTL: paraLevel = Bidi.RTL; break;
|
||||
case DIR_REQUEST_DEFAULT_LTR: paraLevel = Bidi.LEVEL_DEFAULT_LTR; break;
|
||||
case DIR_REQUEST_DEFAULT_RTL: paraLevel = Bidi.LEVEL_DEFAULT_RTL; break;
|
||||
case DIR_REQUEST_LTR:
|
||||
default: paraLevel = Bidi.LTR; break;
|
||||
}
|
||||
final Bidi icuBidi = new Bidi(length /* maxLength */, 0 /* maxRunCount */);
|
||||
icuBidi.setPara(chs, paraLevel, null /* embeddingLevels */);
|
||||
for (int i = 0; i < length; i++) {
|
||||
chInfo[i] = icuBidi.getLevelAt(i);
|
||||
}
|
||||
final byte result = icuBidi.getParaLevel();
|
||||
return (result & 0x1) == 0 ? DIR_LEFT_TO_RIGHT : DIR_RIGHT_TO_LEFT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns run direction information for a line within a paragraph.
|
||||
*
|
||||
* @param dir baseline direction, either Layout.DIR_LEFT_TO_RIGHT or
|
||||
* Layout.DIR_RIGHT_TO_LEFT
|
||||
* @param levels levels as returned from {@link #bidi}
|
||||
* @param lstart start of the line in the levels array
|
||||
* @param chars the character array (used to determine whitespace)
|
||||
* @param cstart the start of the line in the chars array
|
||||
* @param len the length of the line
|
||||
* @return the directions
|
||||
*/
|
||||
public static Directions directions(int dir, byte[] levels, int lstart,
|
||||
char[] chars, int cstart, int len) {
|
||||
if (len == 0) {
|
||||
return DIRS_ALL_LEFT_TO_RIGHT;
|
||||
}
|
||||
|
||||
int baseLevel = dir == DIR_LEFT_TO_RIGHT ? 0 : 1;
|
||||
int curLevel = levels[lstart];
|
||||
int minLevel = curLevel;
|
||||
int runCount = 1;
|
||||
for (int i = lstart + 1, e = lstart + len; i < e; ++i) {
|
||||
int level = levels[i];
|
||||
if (level != curLevel) {
|
||||
curLevel = level;
|
||||
++runCount;
|
||||
}
|
||||
}
|
||||
|
||||
// add final run for trailing counter-directional whitespace
|
||||
int visLen = len;
|
||||
if ((curLevel & 1) != (baseLevel & 1)) {
|
||||
// look for visible end
|
||||
while (--visLen >= 0) {
|
||||
char ch = chars[cstart + visLen];
|
||||
|
||||
if (ch == '\n') {
|
||||
--visLen;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ch != ' ' && ch != '\t') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
++visLen;
|
||||
if (visLen != len) {
|
||||
++runCount;
|
||||
}
|
||||
}
|
||||
|
||||
if (runCount == 1 && minLevel == baseLevel) {
|
||||
// we're done, only one run on this line
|
||||
if ((minLevel & 1) != 0) {
|
||||
return DIRS_ALL_RIGHT_TO_LEFT;
|
||||
}
|
||||
return DIRS_ALL_LEFT_TO_RIGHT;
|
||||
}
|
||||
|
||||
int[] ld = new int[runCount * 2];
|
||||
int maxLevel = minLevel;
|
||||
int levelBits = minLevel << RUN_LEVEL_SHIFT;
|
||||
{
|
||||
// Start of first pair is always 0, we write
|
||||
// length then start at each new run, and the
|
||||
// last run length after we're done.
|
||||
int n = 1;
|
||||
int prev = lstart;
|
||||
curLevel = minLevel;
|
||||
for (int i = lstart, e = lstart + visLen; i < e; ++i) {
|
||||
int level = levels[i];
|
||||
if (level != curLevel) {
|
||||
curLevel = level;
|
||||
if (level > maxLevel) {
|
||||
maxLevel = level;
|
||||
} else if (level < minLevel) {
|
||||
minLevel = level;
|
||||
}
|
||||
// XXX ignore run length limit of 2^RUN_LEVEL_SHIFT
|
||||
ld[n++] = (i - prev) | levelBits;
|
||||
ld[n++] = i - lstart;
|
||||
levelBits = curLevel << RUN_LEVEL_SHIFT;
|
||||
prev = i;
|
||||
}
|
||||
}
|
||||
ld[n] = (lstart + visLen - prev) | levelBits;
|
||||
if (visLen < len) {
|
||||
ld[++n] = visLen;
|
||||
ld[++n] = (len - visLen) | (baseLevel << RUN_LEVEL_SHIFT);
|
||||
}
|
||||
}
|
||||
|
||||
// See if we need to swap any runs.
|
||||
// If the min level run direction doesn't match the base
|
||||
// direction, we always need to swap (at this point
|
||||
// we have more than one run).
|
||||
// Otherwise, we don't need to swap the lowest level.
|
||||
// Since there are no logically adjacent runs at the same
|
||||
// level, if the max level is the same as the (new) min
|
||||
// level, we have a series of alternating levels that
|
||||
// is already in order, so there's no more to do.
|
||||
//
|
||||
boolean swap;
|
||||
if ((minLevel & 1) == baseLevel) {
|
||||
minLevel += 1;
|
||||
swap = maxLevel > minLevel;
|
||||
} else {
|
||||
swap = runCount > 1;
|
||||
}
|
||||
if (swap) {
|
||||
for (int level = maxLevel - 1; level >= minLevel; --level) {
|
||||
for (int i = 0; i < ld.length; i += 2) {
|
||||
if (levels[ld[i]] >= level) {
|
||||
int e = i + 2;
|
||||
while (e < ld.length && levels[ld[e]] >= level) {
|
||||
e += 2;
|
||||
}
|
||||
for (int low = i, hi = e - 2; low < hi; low += 2, hi -= 2) {
|
||||
int x = ld[low]; ld[low] = ld[hi]; ld[hi] = x;
|
||||
x = ld[low+1]; ld[low+1] = ld[hi+1]; ld[hi+1] = x;
|
||||
}
|
||||
i = e + 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return new Directions(ld);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replacement of {@link android.text.Layout.Directions}
|
||||
*/
|
||||
public static class Directions {
|
||||
|
||||
private final int[] mDirections;
|
||||
|
||||
public Directions(int[] directions) {
|
||||
mDirections = directions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns number of BiDi runs.
|
||||
*/
|
||||
public @IntRange(from = 0) int getRunCount() {
|
||||
return mDirections.length / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the start offset of the BiDi run.
|
||||
*
|
||||
* @param runIndex the index of the BiDi run
|
||||
* @return the start offset of the BiDi run.
|
||||
*/
|
||||
public @IntRange(from = 0) int getRunStart(@IntRange(from = 0) int runIndex) {
|
||||
return mDirections[runIndex * 2];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length of the BiDi run.
|
||||
*
|
||||
* Note that this method may return too large number due to reducing the number of object
|
||||
* allocations. The too large number means the remaining part is assigned to this run. The
|
||||
* caller must clamp the returned value.
|
||||
*
|
||||
* @param runIndex the index of the BiDi run
|
||||
* @return the length of the BiDi run.
|
||||
*/
|
||||
public @IntRange(from = 0) int getRunLength(@IntRange(from = 0) int runIndex) {
|
||||
return mDirections[runIndex * 2 + 1] & RUN_LENGTH_MASK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the BiDi run is RTL.
|
||||
*
|
||||
* @param runIndex the index of the BiDi run
|
||||
* @return true if the BiDi run is RTL.
|
||||
*/
|
||||
public boolean isRunRtl(int runIndex) {
|
||||
return (mDirections[runIndex * 2 + 1] & RUN_RTL_FLAG) != 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -74,4 +74,6 @@ public class ICUUtils {
|
||||
return IntPair.pack(start, end);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -782,7 +782,7 @@ public class CodeEditor extends View implements ContentListener, StyleReceiver,
|
||||
|
||||
/**
|
||||
* Set the editor's language.
|
||||
* A language is a for auto-completion,highlight and auto indent analysis.
|
||||
* A language is a tool for auto-completion,highlight and auto indent analysis.
|
||||
*
|
||||
* @param lang New EditorLanguage for editor
|
||||
*/
|
||||
@@ -1847,7 +1847,7 @@ public class CodeEditor extends View implements ContentListener, StyleReceiver,
|
||||
if (ch == ' ') {
|
||||
paintCount = 1;
|
||||
} else if (ch == '\t') {
|
||||
if ((getNonPrintableFlags() & FLAG_DRAW_TAB_SAME_AS_SPACE) != 0) {
|
||||
if ((getNonPrintablePaintingFlags() & FLAG_DRAW_TAB_SAME_AS_SPACE) != 0) {
|
||||
paintCount = getTabWidth();
|
||||
} else {
|
||||
paintLine = true;
|
||||
@@ -2914,7 +2914,7 @@ public class CodeEditor extends View implements ContentListener, StyleReceiver,
|
||||
* @see #FLAG_DRAW_LINE_SEPARATOR
|
||||
* @see #FLAG_DRAW_WHITESPACE_IN_SELECTION
|
||||
*/
|
||||
public int getNonPrintableFlags() {
|
||||
public int getNonPrintablePaintingFlags() {
|
||||
return mNonPrintableOptions;
|
||||
}
|
||||
|
||||
@@ -4413,8 +4413,6 @@ public class CodeEditor extends View implements ContentListener, StyleReceiver,
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
drawView(canvas);
|
||||
int x = (int) (getWidth() - getDpUnit() * 50);
|
||||
canvas.drawLine(x, 0, x, getHeight(), mPaintGraph);
|
||||
// Update magnifier
|
||||
if ((mLastCursorState != mCursorBlink.visibility || !mEventHandler.getScroller().isFinished()) && mEventHandler.mMagnifier.isShowing()) {
|
||||
mLastCursorState = mCursorBlink.visibility;
|
||||
|
||||
Reference in New Issue
Block a user