mirror of
https://github.com/TermoraDev/termora.git
synced 2026-01-15 18:02:58 +08:00
feat: support block selection (#594)
This commit is contained in:
@@ -21,6 +21,16 @@ interface SelectionModel {
|
||||
*/
|
||||
fun setSelection(startPosition: Position, endPosition: Position)
|
||||
|
||||
/**
|
||||
* 设置块选中模式
|
||||
*/
|
||||
fun setBlockSelection(block: Boolean)
|
||||
|
||||
/**
|
||||
* 是否是块选中模式
|
||||
*/
|
||||
fun isBlockSelection(): Boolean
|
||||
|
||||
/**
|
||||
* 获取开始选中的位置
|
||||
*/
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user