pull/1395/head
parent
2bc94c0f7b
commit
3f5ded2c4e
@ -1,449 +1,422 @@ |
|||||||
package io.legado.app.ui.widget.code; |
package io.legado.app.ui.widget.code |
||||||
|
|
||||||
import android.content.Context; |
import android.content.Context |
||||||
import android.graphics.Canvas; |
import android.graphics.Canvas |
||||||
import android.graphics.Paint; |
import android.graphics.Paint |
||||||
import android.graphics.Rect; |
import android.graphics.Paint.FontMetricsInt |
||||||
import android.os.Handler; |
import android.graphics.Rect |
||||||
import android.text.Editable; |
import android.os.Handler |
||||||
import android.text.InputFilter; |
import android.os.Looper |
||||||
import android.text.Layout; |
import android.text.* |
||||||
import android.text.Spannable; |
import android.text.style.BackgroundColorSpan |
||||||
import android.text.SpannableStringBuilder; |
import android.text.style.ForegroundColorSpan |
||||||
import android.text.Spanned; |
import android.text.style.ReplacementSpan |
||||||
import android.text.TextWatcher; |
import android.util.AttributeSet |
||||||
import android.text.style.BackgroundColorSpan; |
import androidx.annotation.ColorInt |
||||||
import android.text.style.ForegroundColorSpan; |
import androidx.appcompat.widget.AppCompatMultiAutoCompleteTextView |
||||||
import android.text.style.ReplacementSpan; |
import java.util.* |
||||||
import android.util.AttributeSet; |
import java.util.regex.Matcher |
||||||
|
import java.util.regex.Pattern |
||||||
import androidx.annotation.ColorInt; |
import kotlin.math.roundToInt |
||||||
import androidx.annotation.NonNull; |
|
||||||
import androidx.appcompat.widget.AppCompatMultiAutoCompleteTextView; |
@Suppress("unused") |
||||||
|
class CodeView : AppCompatMultiAutoCompleteTextView { |
||||||
import java.util.Arrays; |
private var tabWidth = 0 |
||||||
import java.util.HashMap; |
private var tabWidthInCharacters = 0 |
||||||
import java.util.List; |
private var mUpdateDelayTime = 500 |
||||||
import java.util.Map; |
private var modified = true |
||||||
import java.util.SortedMap; |
private var highlightWhileTextChanging = true |
||||||
import java.util.TreeMap; |
private var hasErrors = false |
||||||
import java.util.regex.Matcher; |
private var mRemoveErrorsWhenTextChanged = true |
||||||
import java.util.regex.Pattern; |
private val mUpdateHandler = Handler(Looper.getMainLooper()) |
||||||
|
private var mAutoCompleteTokenizer: Tokenizer? = null |
||||||
@SuppressWarnings("unused") |
private val displayDensity = resources.displayMetrics.density |
||||||
public class CodeView extends AppCompatMultiAutoCompleteTextView { |
private val mErrorHashSet: SortedMap<Int, Int> = TreeMap() |
||||||
|
private val mSyntaxPatternMap: MutableMap<Pattern, Int> = HashMap() |
||||||
private int tabWidth; |
private var mIndentCharacterList = mutableListOf('{', '+', '-', '*', '/', '=') |
||||||
private int tabWidthInCharacters; |
|
||||||
private int mUpdateDelayTime = 500; |
constructor(context: Context?) : super(context!!) { |
||||||
|
initEditorView() |
||||||
private boolean modified = true; |
} |
||||||
private boolean highlightWhileTextChanging = true; |
|
||||||
|
constructor(context: Context?, attrs: AttributeSet?) : super( |
||||||
private boolean hasErrors = false; |
context!!, attrs |
||||||
private boolean mRemoveErrorsWhenTextChanged = true; |
) { |
||||||
|
initEditorView() |
||||||
private final Handler mUpdateHandler = new Handler(); |
} |
||||||
private Tokenizer mAutoCompleteTokenizer; |
|
||||||
private final float displayDensity = getResources().getDisplayMetrics().density; |
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super( |
||||||
|
context!!, attrs, defStyleAttr |
||||||
private static final Pattern PATTERN_LINE = Pattern.compile("(^.+$)+", Pattern.MULTILINE); |
) { |
||||||
private static final Pattern PATTERN_TRAILING_WHITE_SPACE = Pattern.compile("[\\t ]+$", Pattern.MULTILINE); |
initEditorView() |
||||||
|
} |
||||||
private final SortedMap<Integer, Integer> mErrorHashSet = new TreeMap<>(); |
|
||||||
private final Map<Pattern, Integer> mSyntaxPatternMap = new HashMap<>(); |
private fun initEditorView() { |
||||||
private List<Character> mIndentCharacterList = Arrays.asList('{', '+', '-', '*', '/', '='); |
|
||||||
|
|
||||||
public CodeView(Context context) { |
|
||||||
super(context); |
|
||||||
initEditorView(); |
|
||||||
} |
|
||||||
|
|
||||||
public CodeView(Context context, AttributeSet attrs) { |
|
||||||
super(context, attrs); |
|
||||||
initEditorView(); |
|
||||||
} |
|
||||||
|
|
||||||
public CodeView(Context context, AttributeSet attrs, int defStyleAttr) { |
|
||||||
super(context, attrs, defStyleAttr); |
|
||||||
initEditorView(); |
|
||||||
} |
|
||||||
|
|
||||||
private void initEditorView() { |
|
||||||
if (mAutoCompleteTokenizer == null) { |
if (mAutoCompleteTokenizer == null) { |
||||||
mAutoCompleteTokenizer = new KeywordTokenizer(); |
mAutoCompleteTokenizer = KeywordTokenizer() |
||||||
} |
} |
||||||
|
setTokenizer(mAutoCompleteTokenizer) |
||||||
setTokenizer(mAutoCompleteTokenizer); |
filters = arrayOf( |
||||||
|
InputFilter { source, start, end, dest, dStart, dEnd -> |
||||||
setFilters(new InputFilter[]{ |
if (modified && end - start == 1 && start < source.length && dStart < dest.length) { |
||||||
new InputFilter() { |
val c = source[start] |
||||||
@Override |
|
||||||
public CharSequence filter(CharSequence source, int start, int end, |
|
||||||
Spanned dest, int dstart, int dend) { |
|
||||||
if (modified && |
|
||||||
end - start == 1 && |
|
||||||
start < source.length() && |
|
||||||
dstart < dest.length()) { |
|
||||||
char c = source.charAt(start); |
|
||||||
|
|
||||||
if (c == '\n') { |
if (c == '\n') { |
||||||
return autoIndent(source, dest, dstart, dend); |
return@InputFilter autoIndent(source, dest, dStart, dEnd) |
||||||
} |
} |
||||||
} |
} |
||||||
return source; |
source |
||||||
} |
} |
||||||
|
) |
||||||
|
addTextChangedListener(mEditorTextWatcher) |
||||||
} |
} |
||||||
}); |
|
||||||
addTextChangedListener(mEditorTextWatcher); |
|
||||||
} |
|
||||||
|
|
||||||
private CharSequence autoIndent(CharSequence source, Spanned dest, int dstart, int dend) { |
|
||||||
String indent = ""; |
|
||||||
int istart = dstart - 1; |
|
||||||
|
|
||||||
boolean dataBefore = false; |
|
||||||
int pt = 0; |
|
||||||
|
|
||||||
for (; istart > -1; --istart) { |
|
||||||
char c = dest.charAt(istart); |
|
||||||
|
|
||||||
if (c == '\n') break; |
|
||||||
|
|
||||||
|
private fun autoIndent( |
||||||
|
source: CharSequence, |
||||||
|
dest: Spanned, |
||||||
|
dStart: Int, |
||||||
|
dEnd: Int |
||||||
|
): CharSequence { |
||||||
|
var indent = "" |
||||||
|
var iStart = dStart - 1 |
||||||
|
var dataBefore = false |
||||||
|
var pt = 0 |
||||||
|
while (iStart > -1) { |
||||||
|
val c = dest[iStart] |
||||||
|
if (c == '\n') break |
||||||
if (c != ' ' && c != '\t') { |
if (c != ' ' && c != '\t') { |
||||||
if (!dataBefore) { |
if (!dataBefore) { |
||||||
if (mIndentCharacterList.contains(c)) --pt; |
if (mIndentCharacterList.contains(c)) --pt |
||||||
dataBefore = true; |
dataBefore = true |
||||||
} |
} |
||||||
|
|
||||||
if (c == '(') { |
if (c == '(') { |
||||||
--pt; |
--pt |
||||||
} else if (c == ')') { |
} else if (c == ')') { |
||||||
++pt; |
++pt |
||||||
} |
} |
||||||
} |
} |
||||||
|
--iStart |
||||||
} |
} |
||||||
|
if (iStart > -1) { |
||||||
if (istart > -1) { |
val charAtCursor = dest[dStart] |
||||||
char charAtCursor = dest.charAt(dstart); |
var iEnd: Int = ++iStart |
||||||
int iend; |
while (iEnd < dEnd) { |
||||||
|
val c = dest[iEnd] |
||||||
for (iend = ++istart; iend < dend; ++iend) { |
if (charAtCursor != '\n' && c == '/' && iEnd + 1 < dEnd && dest[iEnd] == c) { |
||||||
char c = dest.charAt(iend); |
iEnd += 2 |
||||||
|
break |
||||||
if (charAtCursor != '\n' && |
|
||||||
c == '/' && |
|
||||||
iend + 1 < dend && |
|
||||||
dest.charAt(iend) == c) { |
|
||||||
iend += 2; |
|
||||||
break; |
|
||||||
} |
} |
||||||
|
|
||||||
if (c != ' ' && c != '\t') { |
if (c != ' ' && c != '\t') { |
||||||
break; |
break |
||||||
} |
} |
||||||
|
++iEnd |
||||||
} |
} |
||||||
|
indent += dest.subSequence(iStart, iEnd) |
||||||
indent += dest.subSequence(istart, iend); |
|
||||||
} |
} |
||||||
|
|
||||||
if (pt < 0) { |
if (pt < 0) { |
||||||
indent += "\t"; |
indent += "\t" |
||||||
} |
} |
||||||
|
return source.toString() + indent |
||||||
return source + indent; |
|
||||||
} |
} |
||||||
|
|
||||||
private void highlightSyntax(Editable editable) { |
private fun highlightSyntax(editable: Editable) { |
||||||
if (mSyntaxPatternMap.isEmpty()) return; |
if (mSyntaxPatternMap.isEmpty()) return |
||||||
|
for (pattern in mSyntaxPatternMap.keys) { |
||||||
for (Pattern pattern : mSyntaxPatternMap.keySet()) { |
val color = mSyntaxPatternMap[pattern]!! |
||||||
int color = mSyntaxPatternMap.get(pattern); |
val m = pattern.matcher(editable) |
||||||
for (Matcher m = pattern.matcher(editable); m.find(); ) { |
while (m.find()) { |
||||||
createForegroundColorSpan(editable, m, color); |
createForegroundColorSpan(editable, m, color) |
||||||
} |
} |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
private void highlightErrorLines(Editable editable) { |
private fun highlightErrorLines(editable: Editable) { |
||||||
if (mErrorHashSet.isEmpty()) return; |
if (mErrorHashSet.isEmpty()) return |
||||||
int maxErrorLineValue = mErrorHashSet.lastKey(); |
val maxErrorLineValue = mErrorHashSet.lastKey() |
||||||
|
var lineNumber = 0 |
||||||
int lineNumber = 0; |
val matcher = PATTERN_LINE.matcher(editable) |
||||||
Matcher matcher = PATTERN_LINE.matcher(editable); |
|
||||||
while (matcher.find()) { |
while (matcher.find()) { |
||||||
if (mErrorHashSet.containsKey(lineNumber)) { |
if (mErrorHashSet.containsKey(lineNumber)) { |
||||||
int color = mErrorHashSet.get(lineNumber); |
val color = mErrorHashSet[lineNumber]!! |
||||||
createBackgroundColorSpan(editable, matcher, color); |
createBackgroundColorSpan(editable, matcher, color) |
||||||
} |
} |
||||||
lineNumber = lineNumber + 1; |
lineNumber += 1 |
||||||
if (lineNumber > maxErrorLineValue) break; |
if (lineNumber > maxErrorLineValue) break |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
private void createForegroundColorSpan(Editable editable, Matcher matcher, @ColorInt int color) { |
private fun createForegroundColorSpan( |
||||||
editable.setSpan(new ForegroundColorSpan(color), |
editable: Editable, |
||||||
|
matcher: Matcher, |
||||||
|
@ColorInt color: Int |
||||||
|
) { |
||||||
|
editable.setSpan( |
||||||
|
ForegroundColorSpan(color), |
||||||
matcher.start(), matcher.end(), |
matcher.start(), matcher.end(), |
||||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); |
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE |
||||||
|
) |
||||||
} |
} |
||||||
|
|
||||||
private void createBackgroundColorSpan(Editable editable, Matcher matcher, @ColorInt int color) { |
private fun createBackgroundColorSpan( |
||||||
editable.setSpan(new BackgroundColorSpan(color), |
editable: Editable, |
||||||
|
matcher: Matcher, |
||||||
|
@ColorInt color: Int |
||||||
|
) { |
||||||
|
editable.setSpan( |
||||||
|
BackgroundColorSpan(color), |
||||||
matcher.start(), matcher.end(), |
matcher.start(), matcher.end(), |
||||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); |
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE |
||||||
|
) |
||||||
} |
} |
||||||
|
|
||||||
private Editable highlight(Editable editable) { |
private fun highlight(editable: Editable): Editable { |
||||||
if (editable.length() == 0) return editable; |
if (editable.isEmpty()) return editable |
||||||
|
|
||||||
try { |
try { |
||||||
clearSpans(editable); |
clearSpans(editable) |
||||||
highlightErrorLines(editable); |
highlightErrorLines(editable) |
||||||
highlightSyntax(editable); |
highlightSyntax(editable) |
||||||
} catch (IllegalStateException e) { |
} catch (e: IllegalStateException) { |
||||||
e.printStackTrace(); |
e.printStackTrace() |
||||||
} |
} |
||||||
return editable; |
return editable |
||||||
} |
} |
||||||
|
|
||||||
private void highlightWithoutChange(Editable editable) { |
private fun highlightWithoutChange(editable: Editable) { |
||||||
modified = false; |
modified = false |
||||||
highlight(editable); |
highlight(editable) |
||||||
modified = true; |
modified = true |
||||||
} |
} |
||||||
|
|
||||||
public void setTextHighlighted(CharSequence text) { |
fun setTextHighlighted(text: CharSequence?) { |
||||||
if (text == null || text.length() == 0) return; |
if (text.isNullOrEmpty()) return |
||||||
|
cancelHighlighterRender() |
||||||
cancelHighlighterRender(); |
removeAllErrorLines() |
||||||
|
modified = false |
||||||
removeAllErrorLines(); |
setText(highlight(SpannableStringBuilder(text))) |
||||||
|
modified = true |
||||||
modified = false; |
|
||||||
setText(highlight(new SpannableStringBuilder(text))); |
|
||||||
modified = true; |
|
||||||
} |
} |
||||||
|
|
||||||
public void setTabWidth(int characters) { |
fun setTabWidth(characters: Int) { |
||||||
if (tabWidthInCharacters == characters) return; |
if (tabWidthInCharacters == characters) return |
||||||
tabWidthInCharacters = characters; |
tabWidthInCharacters = characters |
||||||
tabWidth = Math.round(getPaint().measureText("m") * characters); |
tabWidth = (paint.measureText("m") * characters).roundToInt() |
||||||
} |
} |
||||||
|
|
||||||
private void clearSpans(Editable editable) { |
private fun clearSpans(editable: Editable) { |
||||||
int length = editable.length(); |
val length = editable.length |
||||||
ForegroundColorSpan[] foregroundSpans = editable.getSpans( |
val foregroundSpans = editable.getSpans( |
||||||
0, length, ForegroundColorSpan.class); |
0, length, ForegroundColorSpan::class.java |
||||||
|
) |
||||||
for (int i = foregroundSpans.length; i-- > 0; ) |
run { |
||||||
editable.removeSpan(foregroundSpans[i]); |
var i = foregroundSpans.size |
||||||
|
while (i-- > 0) { |
||||||
BackgroundColorSpan[] backgroundSpans = editable.getSpans( |
editable.removeSpan(foregroundSpans[i]) |
||||||
0, length, BackgroundColorSpan.class); |
} |
||||||
|
} |
||||||
for (int i = backgroundSpans.length; i-- > 0; ) |
val backgroundSpans = editable.getSpans( |
||||||
editable.removeSpan(backgroundSpans[i]); |
0, length, BackgroundColorSpan::class.java |
||||||
|
) |
||||||
|
var i = backgroundSpans.size |
||||||
|
while (i-- > 0) { |
||||||
|
editable.removeSpan(backgroundSpans[i]) |
||||||
} |
} |
||||||
|
|
||||||
public void cancelHighlighterRender() { |
|
||||||
mUpdateHandler.removeCallbacks(mUpdateRunnable); |
|
||||||
} |
} |
||||||
|
|
||||||
private void convertTabs(Editable editable, int start, int count) { |
fun cancelHighlighterRender() { |
||||||
if (tabWidth < 1) return; |
mUpdateHandler.removeCallbacks(mUpdateRunnable) |
||||||
|
} |
||||||
String s = editable.toString(); |
|
||||||
|
|
||||||
for (int stop = start + count; |
private fun convertTabs(editable: Editable, start: Int, count: Int) { |
||||||
(start = s.indexOf("\t", start)) > -1 && start < stop; |
var startIndex = start |
||||||
++start) { |
if (tabWidth < 1) return |
||||||
editable.setSpan(new TabWidthSpan(), |
val s = editable.toString() |
||||||
start, |
val stop = startIndex + count |
||||||
start + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); |
while (s.indexOf("\t", startIndex).also { startIndex = it } > -1 && startIndex < stop) { |
||||||
|
editable.setSpan( |
||||||
|
TabWidthSpan(), |
||||||
|
startIndex, |
||||||
|
startIndex + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE |
||||||
|
) |
||||||
|
++startIndex |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
public void setSyntaxPatternsMap(Map<Pattern, Integer> syntaxPatterns) { |
fun setSyntaxPatternsMap(syntaxPatterns: Map<Pattern, Int>?) { |
||||||
if (!mSyntaxPatternMap.isEmpty()) mSyntaxPatternMap.clear(); |
if (mSyntaxPatternMap.isNotEmpty()) mSyntaxPatternMap.clear() |
||||||
mSyntaxPatternMap.putAll(syntaxPatterns); |
mSyntaxPatternMap.putAll(syntaxPatterns!!) |
||||||
} |
} |
||||||
|
|
||||||
public void addSyntaxPattern(Pattern pattern, @ColorInt int Color) { |
fun addSyntaxPattern(pattern: Pattern, @ColorInt Color: Int) { |
||||||
mSyntaxPatternMap.put(pattern, Color); |
mSyntaxPatternMap[pattern] = Color |
||||||
} |
} |
||||||
|
|
||||||
public void removeSyntaxPattern(Pattern pattern) { |
fun removeSyntaxPattern(pattern: Pattern) { |
||||||
mSyntaxPatternMap.remove(pattern); |
mSyntaxPatternMap.remove(pattern) |
||||||
} |
} |
||||||
|
|
||||||
public int getSyntaxPatternsSize() { |
fun getSyntaxPatternsSize(): Int { |
||||||
return mSyntaxPatternMap.size(); |
return mSyntaxPatternMap.size |
||||||
} |
} |
||||||
|
|
||||||
public void resetSyntaxPatternList() { |
fun resetSyntaxPatternList() { |
||||||
mSyntaxPatternMap.clear(); |
mSyntaxPatternMap.clear() |
||||||
} |
} |
||||||
|
|
||||||
public void setAutoIndentCharacterList(List<Character> characterList) { |
fun setAutoIndentCharacterList(characterList: MutableList<Char>) { |
||||||
mIndentCharacterList = characterList; |
mIndentCharacterList = characterList |
||||||
} |
} |
||||||
|
|
||||||
public void clearAutoIndentCharacterList() { |
fun clearAutoIndentCharacterList() { |
||||||
mIndentCharacterList.clear(); |
mIndentCharacterList.clear() |
||||||
} |
} |
||||||
|
|
||||||
public List<Character> getAutoIndentCharacterList() { |
fun getAutoIndentCharacterList(): List<Char> { |
||||||
return mIndentCharacterList; |
return mIndentCharacterList |
||||||
} |
} |
||||||
|
|
||||||
public void addErrorLine(int lineNum, int color) { |
fun addErrorLine(lineNum: Int, color: Int) { |
||||||
mErrorHashSet.put(lineNum, color); |
mErrorHashSet[lineNum] = color |
||||||
hasErrors = true; |
hasErrors = true |
||||||
} |
} |
||||||
|
|
||||||
public void removeErrorLine(int lineNum) { |
fun removeErrorLine(lineNum: Int) { |
||||||
mErrorHashSet.remove(lineNum); |
mErrorHashSet.remove(lineNum) |
||||||
hasErrors = mErrorHashSet.size() > 0; |
hasErrors = mErrorHashSet.size > 0 |
||||||
} |
} |
||||||
|
|
||||||
public void removeAllErrorLines() { |
fun removeAllErrorLines() { |
||||||
mErrorHashSet.clear(); |
mErrorHashSet.clear() |
||||||
hasErrors = false; |
hasErrors = false |
||||||
} |
} |
||||||
|
|
||||||
public int getErrorsSize() { |
fun getErrorsSize(): Int { |
||||||
return mErrorHashSet.size(); |
return mErrorHashSet.size |
||||||
} |
} |
||||||
|
|
||||||
public String getTextWithoutTrailingSpace() { |
fun getTextWithoutTrailingSpace(): String { |
||||||
return PATTERN_TRAILING_WHITE_SPACE |
return PATTERN_TRAILING_WHITE_SPACE |
||||||
.matcher(getText()) |
.matcher(text) |
||||||
.replaceAll(""); |
.replaceAll("") |
||||||
} |
} |
||||||
|
|
||||||
public void setAutoCompleteTokenizer(Tokenizer tokenizer) { |
fun setAutoCompleteTokenizer(tokenizer: Tokenizer?) { |
||||||
mAutoCompleteTokenizer = tokenizer; |
mAutoCompleteTokenizer = tokenizer |
||||||
} |
} |
||||||
|
|
||||||
public void setRemoveErrorsWhenTextChanged(boolean removeErrors) { |
fun setRemoveErrorsWhenTextChanged(removeErrors: Boolean) { |
||||||
mRemoveErrorsWhenTextChanged = removeErrors; |
mRemoveErrorsWhenTextChanged = removeErrors |
||||||
} |
} |
||||||
|
|
||||||
public void reHighlightSyntax() { |
fun reHighlightSyntax() { |
||||||
highlightSyntax(getEditableText()); |
highlightSyntax(editableText) |
||||||
} |
} |
||||||
|
|
||||||
public void reHighlightErrors() { |
fun reHighlightErrors() { |
||||||
highlightErrorLines(getEditableText()); |
highlightErrorLines(editableText) |
||||||
} |
} |
||||||
|
|
||||||
public boolean isHasError() { |
fun isHasError(): Boolean { |
||||||
return hasErrors; |
return hasErrors |
||||||
} |
} |
||||||
|
|
||||||
public void setUpdateDelayTime(int time) { |
fun setUpdateDelayTime(time: Int) { |
||||||
mUpdateDelayTime = time; |
mUpdateDelayTime = time |
||||||
} |
} |
||||||
|
|
||||||
public int getUpdateDelayTime() { |
fun getUpdateDelayTime(): Int { |
||||||
return mUpdateDelayTime; |
return mUpdateDelayTime |
||||||
} |
} |
||||||
|
|
||||||
public void setHighlightWhileTextChanging(boolean updateWhileTextChanging) { |
fun setHighlightWhileTextChanging(updateWhileTextChanging: Boolean) { |
||||||
this.highlightWhileTextChanging = updateWhileTextChanging; |
highlightWhileTextChanging = updateWhileTextChanging |
||||||
} |
} |
||||||
|
|
||||||
@Override |
override fun showDropDown() { |
||||||
public void showDropDown() { |
val screenPoint = IntArray(2) |
||||||
int[] screenPoint = new int[2]; |
getLocationOnScreen(screenPoint) |
||||||
getLocationOnScreen(screenPoint); |
val displayFrame = Rect() |
||||||
|
getWindowVisibleDisplayFrame(displayFrame) |
||||||
final Rect displayFrame = new Rect(); |
val position = selectionStart |
||||||
getWindowVisibleDisplayFrame(displayFrame); |
val layout = layout |
||||||
|
val line = layout.getLineForOffset(position) |
||||||
int position = getSelectionStart(); |
val verticalDistanceInDp = (750 + 140 * line) / displayDensity |
||||||
Layout layout = getLayout(); |
dropDownVerticalOffset = verticalDistanceInDp.toInt() |
||||||
int line = layout.getLineForOffset(position); |
val horizontalDistanceInDp = layout.getPrimaryHorizontal(position) / displayDensity |
||||||
|
dropDownHorizontalOffset = horizontalDistanceInDp.toInt() |
||||||
float verticalDistanceInDp = (750 + 140 * line) / displayDensity; |
super.showDropDown() |
||||||
setDropDownVerticalOffset((int) verticalDistanceInDp); |
|
||||||
|
|
||||||
float horizontalDistanceInDp = layout.getPrimaryHorizontal(position) / displayDensity; |
|
||||||
setDropDownHorizontalOffset((int) horizontalDistanceInDp); |
|
||||||
super.showDropDown(); |
|
||||||
} |
} |
||||||
|
|
||||||
private final Runnable mUpdateRunnable = new Runnable() { |
private val mUpdateRunnable = Runnable { |
||||||
@Override |
val source = text |
||||||
public void run() { |
highlightWithoutChange(source) |
||||||
Editable source = getText(); |
|
||||||
highlightWithoutChange(source); |
|
||||||
} |
} |
||||||
}; |
private val mEditorTextWatcher: TextWatcher = object : TextWatcher { |
||||||
|
private var start = 0 |
||||||
private final TextWatcher mEditorTextWatcher = new TextWatcher() { |
private var count = 0 |
||||||
|
override fun beforeTextChanged( |
||||||
private int start; |
charSequence: CharSequence, |
||||||
private int count; |
start: Int, |
||||||
|
before: Int, |
||||||
@Override |
count: Int |
||||||
public void beforeTextChanged(CharSequence charSequence, int start, int before, int count) { |
) { |
||||||
this.start = start; |
this.start = start |
||||||
this.count = count; |
this.count = count |
||||||
} |
} |
||||||
|
|
||||||
@Override |
override fun onTextChanged( |
||||||
public void onTextChanged(CharSequence charSequence, int start, int before, int count) { |
charSequence: CharSequence, |
||||||
if (!modified) return; |
start: Int, |
||||||
|
before: Int, |
||||||
|
count: Int |
||||||
|
) { |
||||||
|
if (!modified) return |
||||||
if (highlightWhileTextChanging) { |
if (highlightWhileTextChanging) { |
||||||
if (mSyntaxPatternMap.size() > 0) { |
if (mSyntaxPatternMap.isNotEmpty()) { |
||||||
convertTabs(getEditableText(), start, count); |
convertTabs(editableText, start, count) |
||||||
mUpdateHandler.postDelayed(mUpdateRunnable, mUpdateDelayTime); |
mUpdateHandler.postDelayed(mUpdateRunnable, mUpdateDelayTime.toLong()) |
||||||
} |
} |
||||||
} |
} |
||||||
|
if (mRemoveErrorsWhenTextChanged) removeAllErrorLines() |
||||||
if (mRemoveErrorsWhenTextChanged) removeAllErrorLines(); |
|
||||||
} |
} |
||||||
|
|
||||||
@Override |
override fun afterTextChanged(editable: Editable) { |
||||||
public void afterTextChanged(Editable editable) { |
|
||||||
if (!highlightWhileTextChanging) { |
if (!highlightWhileTextChanging) { |
||||||
if (!modified) return; |
if (!modified) return |
||||||
|
cancelHighlighterRender() |
||||||
cancelHighlighterRender(); |
if (mSyntaxPatternMap.isNotEmpty()) { |
||||||
|
convertTabs(editableText, start, count) |
||||||
if (mSyntaxPatternMap.size() > 0) { |
mUpdateHandler.postDelayed(mUpdateRunnable, mUpdateDelayTime.toLong()) |
||||||
convertTabs(getEditableText(), start, count); |
} |
||||||
mUpdateHandler.postDelayed(mUpdateRunnable, mUpdateDelayTime); |
|
||||||
} |
} |
||||||
} |
} |
||||||
} |
} |
||||||
}; |
|
||||||
|
|
||||||
private final class TabWidthSpan extends ReplacementSpan { |
|
||||||
|
|
||||||
@Override |
private inner class TabWidthSpan : ReplacementSpan() { |
||||||
public int getSize( |
override fun getSize( |
||||||
@NonNull Paint paint, |
paint: Paint, |
||||||
CharSequence text, |
text: CharSequence, |
||||||
int start, |
start: Int, |
||||||
int end, |
end: Int, |
||||||
Paint.FontMetricsInt fm) { |
fm: FontMetricsInt? |
||||||
return tabWidth; |
): Int { |
||||||
|
return tabWidth |
||||||
} |
} |
||||||
|
|
||||||
@Override |
override fun draw( |
||||||
public void draw( |
canvas: Canvas, |
||||||
@NonNull Canvas canvas, |
text: CharSequence, |
||||||
CharSequence text, |
start: Int, |
||||||
int start, |
end: Int, |
||||||
int end, |
x: Float, |
||||||
float x, |
top: Int, |
||||||
int top, |
y: Int, |
||||||
int y, |
bottom: Int, |
||||||
int bottom, |
paint: Paint |
||||||
@NonNull Paint paint) { |
) { |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
|
companion object { |
||||||
|
private val PATTERN_LINE = Pattern.compile("(^.+$)+", Pattern.MULTILINE) |
||||||
|
private val PATTERN_TRAILING_WHITE_SPACE = Pattern.compile("[\\t ]+$", Pattern.MULTILINE) |
||||||
|
} |
||||||
} |
} |
@ -1,31 +1,25 @@ |
|||||||
package io.legado.app.ui.widget.code; |
package io.legado.app.ui.widget.code |
||||||
|
|
||||||
import android.widget.MultiAutoCompleteTextView; |
import android.widget.MultiAutoCompleteTextView |
||||||
|
import kotlin.math.max |
||||||
public class KeywordTokenizer implements MultiAutoCompleteTextView.Tokenizer { |
|
||||||
|
class KeywordTokenizer : MultiAutoCompleteTextView.Tokenizer { |
||||||
@Override |
override fun findTokenStart(charSequence: CharSequence, cursor: Int): Int { |
||||||
public int findTokenStart(CharSequence charSequence, int cursor) { |
var sequenceStr = charSequence.toString() |
||||||
String sequenceStr = charSequence.toString(); |
sequenceStr = sequenceStr.substring(0, cursor) |
||||||
sequenceStr = sequenceStr.substring(0, cursor); |
val spaceIndex = sequenceStr.lastIndexOf(" ") |
||||||
|
val lineIndex = sequenceStr.lastIndexOf("\n") |
||||||
int spaceIndex = sequenceStr.lastIndexOf(" "); |
val bracketIndex = sequenceStr.lastIndexOf("(") |
||||||
int lineIndex = sequenceStr.lastIndexOf("\n"); |
val index = max(0, max(spaceIndex, max(lineIndex, bracketIndex))) |
||||||
int bracketIndex = sequenceStr.lastIndexOf("("); |
if (index == 0) return 0 |
||||||
|
return if (index + 1 < charSequence.length) index + 1 else index |
||||||
int index = Math.max(0, Math.max(spaceIndex, Math.max(lineIndex, bracketIndex))); |
|
||||||
if (index == 0) return 0; |
|
||||||
return (index + 1 < charSequence.length()) ? index + 1 : index; |
|
||||||
} |
} |
||||||
|
|
||||||
@Override |
override fun findTokenEnd(charSequence: CharSequence, cursor: Int): Int { |
||||||
public int findTokenEnd(CharSequence charSequence, int cursor) { |
return charSequence.length |
||||||
return charSequence.length(); |
|
||||||
} |
} |
||||||
|
|
||||||
@Override |
override fun terminateToken(charSequence: CharSequence): CharSequence { |
||||||
public CharSequence terminateToken(CharSequence charSequence) { |
return charSequence |
||||||
return charSequence; |
|
||||||
} |
} |
||||||
|
|
||||||
} |
} |
Loading…
Reference in new issue