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 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 { open class SelectionModelImpl(private val terminal: Terminal) : SelectionModel {
private var startPosition = Position.unknown private var startPosition = Position.unknown
private var endPosition = Position.unknown private var endPosition = Position.unknown
private var block = false
private val document = terminal.getDocument() private val document = terminal.getDocument()
internal companion object { internal companion object {
@@ -67,29 +68,60 @@ open class SelectionModelImpl(private val terminal: Terminal) : SelectionModel {
return sb.toString() return sb.toString()
} }
val iterator = getChars(getSelectionStartPosition(), getSelectionEndPosition()) val start = getSelectionStartPosition()
while (iterator.hasNext()) { val end = getSelectionEndPosition()
val line = iterator.next()
val chars = line.chars()
if (chars.isEmpty() || chars.first().first.isNull) {
continue
}
for (e in chars) { if (isBlockSelection()) {
if (e.first.isSoftHyphen) { val left = min(start.x, end.x)
continue val right = max(start.x, end.x)
} else if (e.first.isNull) { val top = min(start.y, end.y)
break 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) { } else {
continue 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()) { for (e in chars) {
sb.appendLine() 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() fireSelectionChanged()
} }
override fun setBlockSelection(block: Boolean) {
this.block = block
}
override fun isBlockSelection() = block
override fun getSelectionStartPosition(): Position { override fun getSelectionStartPosition(): Position {
return startPosition return startPosition
} }
@@ -202,13 +240,20 @@ open class SelectionModelImpl(private val terminal: Terminal) : SelectionModel {
} }
override fun hasSelection(x: Int, y: Int): Boolean { override fun hasSelection(x: Int, y: Int): Boolean {
return hasSelection() && isPointInsideArea(
startPosition, if (hasSelection().not()) return false
endPosition,
x, // 如果是块选中
y, if (isBlockSelection()) {
terminal.getTerminalModel().getCols() 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 val diff = terminalPanel.getAverageCharWidth() / 5.0
if (abs(mousePressedPoint.y - e.y) >= diff || abs(mousePressedPoint.x - e.x) >= diff) { if (abs(mousePressedPoint.y - e.y) >= diff || abs(mousePressedPoint.x - e.x) >= diff) {
// 设置选中模式
terminal.getSelectionModel().setBlockSelection(isOnlyAltDown(e))
beginSelect( beginSelect(
Position(x = mousePressedPoint.x, y = mousePressedPoint.y), 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) { private fun beginSelect(position: Position) {