feat: support block selection (#594)

This commit is contained in:
hstyi
2025-05-19 18:31:51 +08:00
committed by GitHub
parent dc4333da21
commit 15a0d642ff
3 changed files with 89 additions and 25 deletions

View File

@@ -21,6 +21,16 @@ interface SelectionModel {
*/
fun setSelection(startPosition: Position, endPosition: Position)
/**
* 设置块选中模式
*/
fun setBlockSelection(block: Boolean)
/**
* 是否是块选中模式
*/
fun isBlockSelection(): Boolean
/**
* 获取开始选中的位置
*/

View File

@@ -7,6 +7,7 @@ import kotlin.math.min
open class SelectionModelImpl(private val terminal: Terminal) : SelectionModel {
private var startPosition = Position.unknown
private var endPosition = Position.unknown
private var block = false
private val document = terminal.getDocument()
internal companion object {
@@ -67,29 +68,60 @@ open class SelectionModelImpl(private val terminal: Terminal) : SelectionModel {
return sb.toString()
}
val iterator = getChars(getSelectionStartPosition(), getSelectionEndPosition())
while (iterator.hasNext()) {
val line = iterator.next()
val chars = line.chars()
if (chars.isEmpty() || chars.first().first.isNull) {
continue
}
val start = getSelectionStartPosition()
val end = getSelectionEndPosition()
for (e in chars) {
if (e.first.isSoftHyphen) {
continue
} else if (e.first.isNull) {
break
if (isBlockSelection()) {
val left = min(start.x, end.x)
val right = max(start.x, end.x)
val top = min(start.y, end.y)
val bottom = max(start.y, end.y)
for (lineNum in top..bottom) {
val line = document.getLine(lineNum)
val chars = line.chars()
// 块选中要处理超出边界
val from = (left - 1).coerceAtLeast(0)
val to = right.coerceAtMost(chars.size)
if (from < to) {
val selected = chars.subList(from, to)
.filter { !it.first.isNull && !it.first.isSoftHyphen }
.joinToString("") { it.first.toString() }
sb.append(selected)
}
if (lineNum != bottom) {
sb.appendLine()
}
sb.append(e.first)
}
if (line.wrapped) {
continue
}
} else {
val iterator = getChars(start, end)
while (iterator.hasNext()) {
val line = iterator.next()
val chars = line.chars()
if (chars.isEmpty() || chars.first().first.isNull) {
continue
}
if (iterator.hasNext()) {
sb.appendLine()
for (e in chars) {
if (e.first.isSoftHyphen) {
continue
} else if (e.first.isNull) {
break
}
sb.append(e.first)
}
if (line.wrapped) {
continue
}
if (iterator.hasNext()) {
sb.appendLine()
}
}
}
@@ -171,6 +203,12 @@ open class SelectionModelImpl(private val terminal: Terminal) : SelectionModel {
fireSelectionChanged()
}
override fun setBlockSelection(block: Boolean) {
this.block = block
}
override fun isBlockSelection() = block
override fun getSelectionStartPosition(): Position {
return startPosition
}
@@ -202,13 +240,20 @@ open class SelectionModelImpl(private val terminal: Terminal) : SelectionModel {
}
override fun hasSelection(x: Int, y: Int): Boolean {
return hasSelection() && isPointInsideArea(
startPosition,
endPosition,
x,
y,
terminal.getTerminalModel().getCols()
)
if (hasSelection().not()) return false
// 如果是块选中
if (isBlockSelection()) {
val left = min(startPosition.x, endPosition.x)
val right = max(startPosition.x, endPosition.x)
val top = min(startPosition.y, endPosition.y)
val bottom = max(startPosition.y, endPosition.y)
return x in left..right && y in top..bottom
}
return isPointInsideArea(startPosition, endPosition, x, y, terminal.getTerminalModel().getCols())
}

View File

@@ -134,6 +134,8 @@ class TerminalPanelMouseSelectionAdapter(private val terminalPanel: TerminalPane
// 如果不判断的话可能会导致移动了一点点就就进入选择状态了
val diff = terminalPanel.getAverageCharWidth() / 5.0
if (abs(mousePressedPoint.y - e.y) >= diff || abs(mousePressedPoint.x - e.x) >= diff) {
// 设置选中模式
terminal.getSelectionModel().setBlockSelection(isOnlyAltDown(e))
beginSelect(
Position(x = mousePressedPoint.x, y = mousePressedPoint.y),
)
@@ -141,6 +143,13 @@ class TerminalPanelMouseSelectionAdapter(private val terminalPanel: TerminalPane
}
}
private fun isOnlyAltDown(e: MouseEvent): Boolean {
return e.isAltDown &&
e.isMetaDown.not() &&
e.isControlDown.not() &&
e.isShiftDown.not() &&
e.isAltGraphDown.not()
}
private fun beginSelect(position: Position) {