6.7.0 - Alpha23 - 代码编辑器支持 "-", "=", "(", "[", "{", "<" 等按键长按功能

This commit is contained in:
SuperMonster003
2026-03-08 15:38:35 +08:00
parent 417fd4c2f4
commit 770bd657e0
8 changed files with 172 additions and 55 deletions

View File

@@ -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)_",

View File

@@ -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);

View File

@@ -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)
} }
} }

View File

@@ -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);
}
} }

View File

@@ -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 {
// # } // # }
// # } // # }
} }

View File

@@ -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();
}
}
}

View File

@@ -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

View File

@@ -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