mirror of
https://github.com/TermoraDev/termora.git
synced 2026-01-16 02:12: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 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 {
|
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,7 +68,37 @@ open class SelectionModelImpl(private val terminal: Terminal) : SelectionModel {
|
|||||||
return sb.toString()
|
return sb.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
val iterator = getChars(getSelectionStartPosition(), getSelectionEndPosition())
|
val start = getSelectionStartPosition()
|
||||||
|
val end = getSelectionEndPosition()
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
val iterator = getChars(start, end)
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
val line = iterator.next()
|
val line = iterator.next()
|
||||||
val chars = line.chars()
|
val chars = line.chars()
|
||||||
@@ -92,6 +123,7 @@ open class SelectionModelImpl(private val terminal: Terminal) : SelectionModel {
|
|||||||
sb.appendLine()
|
sb.appendLine()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (sb.isNotEmpty() && sb.last() == ControlCharacters.LF) {
|
if (sb.isNotEmpty() && sb.last() == ControlCharacters.LF) {
|
||||||
sb.deleteCharAt(sb.length - 1)
|
sb.deleteCharAt(sb.length - 1)
|
||||||
@@ -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())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user