6.7.0 - Alpha23 - 代码编辑器支持 "-", "=", "(", "[", "{", "<" 等按键长按功能
This commit is contained in:
@@ -153,6 +153,7 @@
|
|||||||
"代码编辑器 \"查找/替换\" 支持状态持久化及实时显示搜索计数信息",
|
"代码编辑器 \"查找/替换\" 支持状态持久化及实时显示搜索计数信息",
|
||||||
"代码编辑器提示保存时确保保存成功后再退出编辑器以降低保存失败率",
|
"代码编辑器提示保存时确保保存成功后再退出编辑器以降低保存失败率",
|
||||||
"代码编辑器保存按钮的状态更符合用户主观逻辑",
|
"代码编辑器保存按钮的状态更符合用户主观逻辑",
|
||||||
|
"代码编辑器支持 \"-\", \"=\", \"(\", \"[\", \"{\", \"<\" 等按键长按功能",
|
||||||
"打包应用打包过程对话框增加 \"中止\" 按钮",
|
"打包应用打包过程对话框增加 \"中止\" 按钮",
|
||||||
"打包应用打包过程对话框的显示方式并增加耗时统计",
|
"打包应用打包过程对话框的显示方式并增加耗时统计",
|
||||||
"打包应用页面默认勾选必要权限 (WAKE_LOCK/INTERNET/WRITE_EXTERNAL_STORAGE) _[`issue #397`](http://issues.autojs6.com/397)_",
|
"打包应用页面默认勾选必要权限 (WAKE_LOCK/INTERNET/WRITE_EXTERNAL_STORAGE) _[`issue #397`](http://issues.autojs6.com/397)_",
|
||||||
|
|||||||
@@ -362,7 +362,7 @@ public class EditorMenu {
|
|||||||
return tryDoing(() -> mEditor.setText(""));
|
return tryDoing(() -> mEditor.setText(""));
|
||||||
}
|
}
|
||||||
if (itemId == R.id.action_comment) {
|
if (itemId == R.id.action_comment) {
|
||||||
return tryDoing(mEditor.commentHelper::handle);
|
return tryDoing(mEditor.commentHelper::toggle);
|
||||||
}
|
}
|
||||||
if (itemId == R.id.action_beautify) {
|
if (itemId == R.id.action_beautify) {
|
||||||
return tryDoing(mEditorView::beautifyCode);
|
return tryDoing(mEditorView::beautifyCode);
|
||||||
|
|||||||
@@ -2566,7 +2566,17 @@ class EditorView : LinearLayout, OnHintClickListener, ClickCallback, ToolbarFrag
|
|||||||
completions[pos].let {
|
completions[pos].let {
|
||||||
when {
|
when {
|
||||||
// @Inspired by 抠脚本人 (https://github.com/little-alei) on Jul 13, 2023.
|
// @Inspired by 抠脚本人 (https://github.com/little-alei) on Jul 13, 2023.
|
||||||
it.insertText == "/" -> editor.commentHelper.handle()
|
it.insertText == "/" -> editor.commentHelper.toggle()
|
||||||
|
it.insertText == "=" -> editor.clear()
|
||||||
|
it.insertText == "-" -> editor.deleteLine()
|
||||||
|
it.insertText == "(" -> editor.moveCursor(-1)
|
||||||
|
it.insertText == ")" -> editor.moveCursor(1)
|
||||||
|
it.insertText == "[" -> editor.jumpToLineStart()
|
||||||
|
it.insertText == "]" -> editor.jumpToLineEnd()
|
||||||
|
it.insertText == "{" -> editor.jumpToStart()
|
||||||
|
it.insertText == "}" -> editor.jumpToEnd()
|
||||||
|
it.insertText == "<" -> editor.jumpToPrevLine()
|
||||||
|
it.insertText == ">" -> editor.jumpToNextLine()
|
||||||
it.url != null -> showManual(it.url, it.hint)
|
it.url != null -> showManual(it.url, it.hint)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,48 +5,42 @@ import android.os.Looper;
|
|||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.ViewConfiguration;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import org.autojs.autojs.model.autocomplete.CodeCompletions;
|
|
||||||
import org.autojs.autojs.groundwork.WrapContentLinearLayoutManager;
|
import org.autojs.autojs.groundwork.WrapContentLinearLayoutManager;
|
||||||
|
import org.autojs.autojs.model.autocomplete.CodeCompletions;
|
||||||
|
import org.autojs.autojs.util.ViewUtils;
|
||||||
import org.autojs.autojs6.R;
|
import org.autojs.autojs6.R;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Stardust on Feb 17, 2017.
|
* Created by Stardust on Feb 17, 2017.
|
||||||
|
* Modified by SuperMonster003 as of Mar 8, 2026.
|
||||||
*/
|
*/
|
||||||
public class CodeCompletionBar extends RecyclerView {
|
public class CodeCompletionBar extends RecyclerView {
|
||||||
|
|
||||||
public interface OnHintClickListener {
|
|
||||||
void onHintClick(CodeCompletions completions, int pos);
|
|
||||||
|
|
||||||
void onHintLongClick(CodeCompletions completions, int pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int mTextColor;
|
private int mTextColor;
|
||||||
private CodeCompletions mCodeCompletions;
|
private CodeCompletions mCodeCompletions;
|
||||||
private OnHintClickListener mOnHintClickListener;
|
private OnHintClickListener mOnHintClickListener;
|
||||||
private final OnClickListener mOnCodeCompletionItemClickListener = new OnClickListener() {
|
private final OnClickListener mOnCodeCompletionItemClickListener = new OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
int position = getChildViewHolder(v).getAdapterPosition();
|
int position = getChildViewHolder(v).getBindingAdapterPosition();
|
||||||
if (position >= 0 && position < mCodeCompletions.size()) {
|
if (position >= 0 && position < mCodeCompletions.size()) {
|
||||||
if (mOnHintClickListener != null) {
|
if (mOnHintClickListener != null) {
|
||||||
mOnHintClickListener.onHintClick(mCodeCompletions, position);
|
mOnHintClickListener.onHintClick(mCodeCompletions, position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private final OnLongClickListener mOnCodeCompletionItemLongClickListener = new OnLongClickListener() {
|
private final OnLongClickListener mOnCodeCompletionItemLongClickListener = new OnLongClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public boolean onLongClick(View v) {
|
public boolean onLongClick(View v) {
|
||||||
int position = getChildViewHolder(v).getAdapterPosition();
|
int position = getChildViewHolder(v).getBindingAdapterPosition();
|
||||||
if (position < 0 || position >= mCodeCompletions.size())
|
if (position < 0 || position >= mCodeCompletions.size())
|
||||||
return false;
|
return false;
|
||||||
if (mOnHintClickListener != null) {
|
if (mOnHintClickListener != null) {
|
||||||
@@ -127,7 +121,18 @@ public class CodeCompletionBar extends RecyclerView {
|
|||||||
super(itemView);
|
super(itemView);
|
||||||
itemView.setOnClickListener(mOnCodeCompletionItemClickListener);
|
itemView.setOnClickListener(mOnCodeCompletionItemClickListener);
|
||||||
itemView.setOnLongClickListener(mOnCodeCompletionItemLongClickListener);
|
itemView.setOnLongClickListener(mOnCodeCompletionItemLongClickListener);
|
||||||
|
itemView.setOnTouchListener(
|
||||||
|
new ViewUtils.DelayedLongPressTouchListener(
|
||||||
|
itemView.getContext(),
|
||||||
|
(long) (ViewConfiguration.getLongPressTimeout() * 1.25)
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface OnHintClickListener {
|
||||||
|
void onHintClick(CodeCompletions completions, int pos);
|
||||||
|
|
||||||
|
void onHintLongClick(CodeCompletions completions, int pos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import android.graphics.Canvas
|
|||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.os.SystemClock
|
import android.os.SystemClock
|
||||||
|
import android.text.Layout
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
@@ -316,14 +317,16 @@ class CodeEditor : HVScrollView {
|
|||||||
when (ev.actionMasked) {
|
when (ev.actionMasked) {
|
||||||
MotionEvent.ACTION_DOWN,
|
MotionEvent.ACTION_DOWN,
|
||||||
MotionEvent.ACTION_POINTER_DOWN,
|
MotionEvent.ACTION_POINTER_DOWN,
|
||||||
MotionEvent.ACTION_MOVE -> {
|
MotionEvent.ACTION_MOVE,
|
||||||
|
-> {
|
||||||
// Mark touching as early as possible.
|
// Mark touching as early as possible.
|
||||||
// zh-CN: 尽可能早地标记触摸中状态.
|
// zh-CN: 尽可能早地标记触摸中状态.
|
||||||
mUserTouching = true
|
mUserTouching = true
|
||||||
}
|
}
|
||||||
MotionEvent.ACTION_UP,
|
MotionEvent.ACTION_UP,
|
||||||
MotionEvent.ACTION_CANCEL,
|
MotionEvent.ACTION_CANCEL,
|
||||||
MotionEvent.ACTION_POINTER_UP -> {
|
MotionEvent.ACTION_POINTER_UP,
|
||||||
|
-> {
|
||||||
// Clear touching flag when gesture ends.
|
// Clear touching flag when gesture ends.
|
||||||
// zh-CN: 手势结束时清除触摸中标记.
|
// zh-CN: 手势结束时清除触摸中标记.
|
||||||
mUserTouching = false
|
mUserTouching = false
|
||||||
@@ -397,6 +400,10 @@ class CodeEditor : HVScrollView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun clear() {
|
||||||
|
codeEditText.setText("")
|
||||||
|
}
|
||||||
|
|
||||||
fun jumpToStart() {
|
fun jumpToStart() {
|
||||||
codeEditText.setSelection(0)
|
codeEditText.setSelection(0)
|
||||||
}
|
}
|
||||||
@@ -417,15 +424,46 @@ class CodeEditor : HVScrollView {
|
|||||||
val layout = codeEditText.layout
|
val layout = codeEditText.layout
|
||||||
val line = LayoutHelper.getLineOfChar(layout, minOf(codeEditText.selectionStart, codeEditText.selectionEnd))
|
val line = LayoutHelper.getLineOfChar(layout, minOf(codeEditText.selectionStart, codeEditText.selectionEnd))
|
||||||
if (line >= 0 && line < layout.lineCount) {
|
if (line >= 0 && line < layout.lineCount) {
|
||||||
val lineEnd = layout.getLineEnd(line)
|
codeEditText.setSelection(getLineEndWithoutTrailingNewline(layout, line))
|
||||||
val text = codeEditText.text.toString()
|
}
|
||||||
val finalPosition = when {
|
}
|
||||||
lineEnd > 0 && text[lineEnd - 1] == '\n' -> {
|
|
||||||
lineEnd - 1
|
fun jumpToNextLine() {
|
||||||
}
|
val layout = codeEditText.layout
|
||||||
else -> lineEnd
|
val cursor = minOf(codeEditText.selectionStart, codeEditText.selectionEnd)
|
||||||
}
|
val line = LayoutHelper.getLineOfChar(layout, cursor)
|
||||||
codeEditText.setSelection(finalPosition)
|
jumpToLinePreservingColumn(layout, line, line + 1, cursor)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun jumpToPrevLine() {
|
||||||
|
val layout = codeEditText.layout
|
||||||
|
val cursor = minOf(codeEditText.selectionStart, codeEditText.selectionEnd)
|
||||||
|
val line = LayoutHelper.getLineOfChar(layout, cursor)
|
||||||
|
jumpToLinePreservingColumn(layout, line, line - 1, cursor)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun jumpToLinePreservingColumn(layout: Layout, fromLine: Int, targetLine: Int, cursor: Int) {
|
||||||
|
if (fromLine !in 0 until layout.lineCount || targetLine !in 0 until layout.lineCount) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val fromLineStart = layout.getLineStart(fromLine)
|
||||||
|
val fromLineEnd = getLineEndWithoutTrailingNewline(layout, fromLine)
|
||||||
|
val fromLineCursor = cursor.coerceIn(fromLineStart, fromLineEnd)
|
||||||
|
val desiredColumn = fromLineCursor - fromLineStart
|
||||||
|
|
||||||
|
val targetLineStart = layout.getLineStart(targetLine)
|
||||||
|
val targetLineEnd = getLineEndWithoutTrailingNewline(layout, targetLine)
|
||||||
|
val targetCursor = (targetLineStart + desiredColumn).coerceAtMost(targetLineEnd)
|
||||||
|
codeEditText.setSelection(targetCursor)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getLineEndWithoutTrailingNewline(layout: Layout, line: Int): Int {
|
||||||
|
val lineEnd = layout.getLineEnd(line)
|
||||||
|
val text = codeEditText.text ?: return lineEnd
|
||||||
|
return if (lineEnd > 0 && lineEnd <= text.length && text[lineEnd - 1] == '\n') {
|
||||||
|
lineEnd - 1
|
||||||
|
} else {
|
||||||
|
lineEnd
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -739,13 +777,19 @@ class CodeEditor : HVScrollView {
|
|||||||
fun onCursorChange(line: String, cursor: Int)
|
fun onCursorChange(line: String, cursor: Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class CommentHelper : CodeEditorCommentHelper {
|
inner class CommentHelper {
|
||||||
|
|
||||||
private val prefix = "//"
|
private val prefix = "//"
|
||||||
|
|
||||||
override fun handle() = toggle()
|
fun toggle() {
|
||||||
|
if (isCommented()) {
|
||||||
|
uncomment()
|
||||||
|
} else {
|
||||||
|
comment()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun comment() {
|
fun comment() {
|
||||||
var selectionEnd = codeEditText.selectionEnd
|
var selectionEnd = codeEditText.selectionEnd
|
||||||
val insetPosition = getProperWhitespaceAmount()
|
val insetPosition = getProperWhitespaceAmount()
|
||||||
var hasEverMatched = false
|
var hasEverMatched = false
|
||||||
@@ -776,7 +820,7 @@ class CodeEditor : HVScrollView {
|
|||||||
codeEditText.setSelection(selectionEnd)
|
codeEditText.setSelection(selectionEnd)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun uncomment() {
|
fun uncomment() {
|
||||||
var selectionEnd = codeEditText.selectionEnd
|
var selectionEnd = codeEditText.selectionEnd
|
||||||
|
|
||||||
replaceSelectedLines(Regex("(\\s*)($prefix\\s?)(.*)")) { matchResult ->
|
replaceSelectedLines(Regex("(\\s*)($prefix\\s?)(.*)")) { matchResult ->
|
||||||
@@ -788,7 +832,7 @@ class CodeEditor : HVScrollView {
|
|||||||
codeEditText.setSelection(selectionEnd)
|
codeEditText.setSelection(selectionEnd)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isCommented(): Boolean {
|
fun isCommented(): Boolean {
|
||||||
var atLeastOneWithPrefix = false
|
var atLeastOneWithPrefix = false
|
||||||
val allMatched = getCoveredLinesText().split("\n").all {
|
val allMatched = getCoveredLinesText().split("\n").all {
|
||||||
it.matches(Regex("\\s*")) || it
|
it.matches(Regex("\\s*")) || it
|
||||||
@@ -859,4 +903,4 @@ class CodeEditor : HVScrollView {
|
|||||||
// # }
|
// # }
|
||||||
// # }
|
// # }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
package org.autojs.autojs.ui.edit.editor;
|
|
||||||
|
|
||||||
public interface CodeEditorCommentHelper {
|
|
||||||
|
|
||||||
void handle();
|
|
||||||
|
|
||||||
void comment();
|
|
||||||
|
|
||||||
void uncomment();
|
|
||||||
|
|
||||||
boolean isCommented();
|
|
||||||
|
|
||||||
default void toggle() {
|
|
||||||
if (isCommented()) {
|
|
||||||
uncomment();
|
|
||||||
} else {
|
|
||||||
comment();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -76,6 +76,7 @@ import org.autojs.autojs.util.StringUtils.key
|
|||||||
import org.autojs.autojs6.R
|
import org.autojs.autojs6.R
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.floor
|
import kotlin.math.floor
|
||||||
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
import android.text.TextUtils as AndroidTextUtils
|
import android.text.TextUtils as AndroidTextUtils
|
||||||
@@ -1689,6 +1690,83 @@ object ViewUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DelayedLongPressTouchListener(context: Context, longPressDelayMs: Long) : View.OnTouchListener {
|
||||||
|
|
||||||
|
private val longPressDelayMs: Long
|
||||||
|
private val touchSlop: Int
|
||||||
|
|
||||||
|
private var downX = 0f
|
||||||
|
private var downY = 0f
|
||||||
|
private var tracking = false
|
||||||
|
private var longPressed = false
|
||||||
|
private var pendingLongPress: Runnable? = null
|
||||||
|
|
||||||
|
init {
|
||||||
|
this.longPressDelayMs = max(0, longPressDelayMs)
|
||||||
|
this.touchSlop = ViewConfiguration.get(context).getScaledTouchSlop()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTouch(v: View, event: MotionEvent): Boolean =
|
||||||
|
when (event.getActionMasked()) {
|
||||||
|
MotionEvent.ACTION_DOWN -> {
|
||||||
|
downX = event.getX()
|
||||||
|
downY = event.getY()
|
||||||
|
tracking = true
|
||||||
|
longPressed = false
|
||||||
|
v.setPressed(true)
|
||||||
|
|
||||||
|
cancelPending(v)
|
||||||
|
pendingLongPress = Runnable {
|
||||||
|
if (tracking && v.isPressed()) {
|
||||||
|
longPressed = v.performLongClick()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v.postDelayed(pendingLongPress, longPressDelayMs)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
MotionEvent.ACTION_MOVE -> {
|
||||||
|
if (tracking) {
|
||||||
|
val dx = abs(event.getX() - downX)
|
||||||
|
val dy = abs(event.getY() - downY)
|
||||||
|
if (dx > touchSlop || dy > touchSlop) {
|
||||||
|
cancelGesture(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
MotionEvent.ACTION_UP -> {
|
||||||
|
cancelPending(v)
|
||||||
|
v.setPressed(false)
|
||||||
|
|
||||||
|
val shouldClick = tracking && !longPressed
|
||||||
|
tracking = false
|
||||||
|
if (shouldClick) {
|
||||||
|
v.performClick()
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
MotionEvent.ACTION_CANCEL -> {
|
||||||
|
cancelGesture(v)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cancelPending(v: View) {
|
||||||
|
if (pendingLongPress != null) {
|
||||||
|
v.removeCallbacks(pendingLongPress)
|
||||||
|
pendingLongPress = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cancelGesture(v: View) {
|
||||||
|
cancelPending(v)
|
||||||
|
v.setPressed(false)
|
||||||
|
tracking = false
|
||||||
|
longPressed = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class TextSizeScaleListener(private val textView: TextView) : ScaleGestureDetector.SimpleOnScaleGestureListener() {
|
private class TextSizeScaleListener(private val textView: TextView) : ScaleGestureDetector.SimpleOnScaleGestureListener() {
|
||||||
|
|
||||||
private val mMinTextSize = 4.0f
|
private val mMinTextSize = 4.0f
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#Sat Mar 07 23:49:47 CST 2026
|
#Sun Mar 08 15:18:59 CST 2026
|
||||||
BUILD_TIME=1772898587711
|
BUILD_TIME=1772954339154
|
||||||
COMPILE_SDK_VERSION=36
|
COMPILE_SDK_VERSION=36
|
||||||
IMAGE_QUANT_CMAKE_VERSION=3.22.1
|
IMAGE_QUANT_CMAKE_VERSION=3.22.1
|
||||||
IMAGE_QUANT_NDK_VERSION=26.1.10909125
|
IMAGE_QUANT_NDK_VERSION=26.1.10909125
|
||||||
@@ -27,6 +27,6 @@ RAPID_OCR_OPENCV_MOBILE_LABEL_VERSION=13
|
|||||||
RAPID_OCR_OPENCV_MOBILE_VERSION=4.5.3
|
RAPID_OCR_OPENCV_MOBILE_VERSION=4.5.3
|
||||||
TARGET_SDK_VERSION=36
|
TARGET_SDK_VERSION=36
|
||||||
TARGET_SDK_VERSION_INRT=29
|
TARGET_SDK_VERSION_INRT=29
|
||||||
VERSION_BUILD=3781
|
VERSION_BUILD=3782
|
||||||
VERSION_NAME=6.7.0 Alpha23
|
VERSION_NAME=6.7.0 Alpha23
|
||||||
VSCODE_EXT_REQUIRED_VERSION=1.0.13
|
VSCODE_EXT_REQUIRED_VERSION=1.0.13
|
||||||
|
|||||||
Reference in New Issue
Block a user