mirror of https://github.com/liamg/aminal.git
Implement three selection modes: char, word, and line. Make selection work in the same way as in Putty. (#191)
This commit is contained in:
parent
f7b162e83e
commit
22a5e8063a
232
buffer/buffer.go
232
buffer/buffer.go
|
@ -9,6 +9,13 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type SelectionMode int
|
||||||
|
const (
|
||||||
|
SelectionChar SelectionMode = iota // char-by-char selection
|
||||||
|
SelectionWord SelectionMode = iota // by word selection
|
||||||
|
SelectionLine SelectionMode = iota // whole line selection
|
||||||
|
)
|
||||||
|
|
||||||
type Buffer struct {
|
type Buffer struct {
|
||||||
lines []Line
|
lines []Line
|
||||||
displayChangeHandlers []chan bool
|
displayChangeHandlers []chan bool
|
||||||
|
@ -18,7 +25,8 @@ type Buffer struct {
|
||||||
dirty bool
|
dirty bool
|
||||||
selectionStart *Position
|
selectionStart *Position
|
||||||
selectionEnd *Position
|
selectionEnd *Position
|
||||||
selectionComplete bool // whether the selected text can update or whether it is final
|
selectionMode SelectionMode
|
||||||
|
isSelectionComplete bool
|
||||||
terminalState *TerminalState
|
terminalState *TerminalState
|
||||||
savedCharsets []*map[rune]rune
|
savedCharsets []*map[rune]rune
|
||||||
savedCurrentCharset int
|
savedCurrentCharset int
|
||||||
|
@ -29,10 +37,25 @@ type Position struct {
|
||||||
Col int
|
Col int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func comparePositions(pos1 *Position, pos2 *Position) int {
|
||||||
|
if pos1.Line < pos2.Line || (pos1.Line == pos2.Line && pos1.Col < pos2.Col) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if pos1.Line > pos2.Line || (pos1.Line == pos2.Line && pos1.Col > pos2.Col) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
// NewBuffer creates a new terminal buffer
|
// NewBuffer creates a new terminal buffer
|
||||||
func NewBuffer(terminalState *TerminalState) *Buffer {
|
func NewBuffer(terminalState *TerminalState) *Buffer {
|
||||||
b := &Buffer{
|
b := &Buffer{
|
||||||
lines: []Line{},
|
lines: []Line{},
|
||||||
|
selectionStart: nil,
|
||||||
|
selectionEnd: nil,
|
||||||
|
selectionMode: SelectionChar,
|
||||||
|
isSelectionComplete: true,
|
||||||
terminalState: terminalState,
|
terminalState: terminalState,
|
||||||
}
|
}
|
||||||
return b
|
return b
|
||||||
|
@ -84,50 +107,13 @@ func (buffer *Buffer) GetURLAtPosition(col uint16, viewRow uint16) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (buffer *Buffer) IsSelectionComplete() bool {
|
func (buffer *Buffer) IsSelectionComplete() bool {
|
||||||
return buffer.selectionComplete
|
return buffer.isSelectionComplete
|
||||||
}
|
}
|
||||||
|
|
||||||
func (buffer *Buffer) SelectLineAtPosition(col uint16, viewRow uint16) {
|
func (buffer *Buffer) findEndOfWord(col int, row int) int {
|
||||||
row := buffer.convertViewLineToRawLine(viewRow) - uint64(buffer.terminalState.scrollLinesFromBottom)
|
|
||||||
|
|
||||||
buffer.selectionStart = &Position {
|
|
||||||
Col: 0,
|
|
||||||
Line: int(row),
|
|
||||||
}
|
|
||||||
buffer.selectionEnd = &Position {
|
|
||||||
Col: int(buffer.ViewWidth() - 1),
|
|
||||||
Line: int(row),
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer.selectionComplete = true
|
|
||||||
buffer.emitDisplayChange()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (buffer *Buffer) SelectWordAtPosition(col uint16, viewRow uint16) {
|
|
||||||
|
|
||||||
row := buffer.convertViewLineToRawLine(viewRow) - uint64(buffer.terminalState.scrollLinesFromBottom)
|
|
||||||
|
|
||||||
cell := buffer.GetRawCell(col, row)
|
|
||||||
if cell == nil || cell.Rune() == 0x00 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
start := col
|
|
||||||
end := col
|
end := col
|
||||||
|
for i := col; i < int(buffer.terminalState.viewWidth); i++ {
|
||||||
for i := col; i >= uint16(0); i-- {
|
cell := buffer.GetRawCell(uint16(i), uint64(row))
|
||||||
cell := buffer.GetRawCell(i, row)
|
|
||||||
if cell == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if isRuneWordSelectionMarker(cell.Rune()) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
start = i
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := col; i < buffer.terminalState.viewWidth; i++ {
|
|
||||||
cell := buffer.GetRawCell(i, row)
|
|
||||||
if cell == nil {
|
if cell == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -136,18 +122,22 @@ func (buffer *Buffer) SelectWordAtPosition(col uint16, viewRow uint16) {
|
||||||
}
|
}
|
||||||
end = i
|
end = i
|
||||||
}
|
}
|
||||||
|
return end
|
||||||
|
}
|
||||||
|
|
||||||
buffer.selectionStart = &Position{
|
func (buffer *Buffer) findBeginningOfWord(col int, row int) int {
|
||||||
Col: int(start),
|
start := col
|
||||||
Line: int(row),
|
for i := col; i >= 0; i-- {
|
||||||
|
cell := buffer.GetRawCell(uint16(i), uint64(row))
|
||||||
|
if cell == nil {
|
||||||
|
break
|
||||||
}
|
}
|
||||||
buffer.selectionEnd = &Position{
|
if isRuneWordSelectionMarker(cell.Rune()) {
|
||||||
Col: int(end),
|
break
|
||||||
Line: int(row),
|
|
||||||
}
|
}
|
||||||
|
start = i
|
||||||
buffer.selectionComplete = true
|
}
|
||||||
buffer.emitDisplayChange()
|
return start
|
||||||
}
|
}
|
||||||
|
|
||||||
// bounds for word selection
|
// bounds for word selection
|
||||||
|
@ -170,29 +160,15 @@ func isRuneURLSelectionMarker(r rune) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (buffer *Buffer) GetSelectedText() string {
|
func (buffer *Buffer) GetSelectedText() string {
|
||||||
if buffer.selectionStart == nil || buffer.selectionEnd == nil {
|
start, end := buffer.getActualSelection()
|
||||||
|
if start == nil || end == nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
var x1, x2, y1, y2 int
|
|
||||||
|
|
||||||
if buffer.selectionStart.Line > buffer.selectionEnd.Line || (buffer.selectionStart.Line == buffer.selectionEnd.Line && buffer.selectionStart.Col > buffer.selectionEnd.Col) {
|
|
||||||
y2 = buffer.selectionStart.Line
|
|
||||||
y1 = buffer.selectionEnd.Line
|
|
||||||
x2 = buffer.selectionStart.Col
|
|
||||||
x1 = buffer.selectionEnd.Col
|
|
||||||
} else {
|
|
||||||
y1 = buffer.selectionStart.Line
|
|
||||||
y2 = buffer.selectionEnd.Line
|
|
||||||
x1 = buffer.selectionStart.Col
|
|
||||||
x2 = buffer.selectionEnd.Col
|
|
||||||
}
|
|
||||||
|
|
||||||
var builder strings.Builder
|
var builder strings.Builder
|
||||||
builder.Grow( int(buffer.terminalState.viewWidth) * (y2 - y1 + 1)) // reserve space to minimize allocations
|
builder.Grow( int(buffer.terminalState.viewWidth) * (end.Line - start.Line + 1)) // reserve space to minimize allocations
|
||||||
|
|
||||||
for row := y1; row <= y2; row++ {
|
|
||||||
|
|
||||||
|
for row := start.Line; row <= end.Line; row++ {
|
||||||
if row >= len(buffer.lines) {
|
if row >= len(buffer.lines) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -201,13 +177,13 @@ func (buffer *Buffer) GetSelectedText() string {
|
||||||
|
|
||||||
minX := 0
|
minX := 0
|
||||||
maxX := int(buffer.terminalState.viewWidth) - 1
|
maxX := int(buffer.terminalState.viewWidth) - 1
|
||||||
if row == y1 {
|
if row == start.Line {
|
||||||
minX = x1
|
minX = start.Col
|
||||||
} else if !line.wrapped {
|
} else if !line.wrapped {
|
||||||
builder.WriteString("\n")
|
builder.WriteString("\n")
|
||||||
}
|
}
|
||||||
if row == y2 {
|
if row == end.Line {
|
||||||
maxX = x2
|
maxX = end.Col
|
||||||
}
|
}
|
||||||
|
|
||||||
for col := minX; col <= maxX; col++ {
|
for col := minX; col <= maxX; col++ {
|
||||||
|
@ -225,25 +201,34 @@ func (buffer *Buffer) GetSelectedText() string {
|
||||||
return builder.String()
|
return builder.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (buffer *Buffer) StartSelection(col uint16, viewRow uint16) {
|
func (buffer *Buffer) StartSelection(col uint16, viewRow uint16, mode SelectionMode) {
|
||||||
row := buffer.convertViewLineToRawLine(viewRow) - uint64(buffer.terminalState.scrollLinesFromBottom)
|
row := buffer.convertViewLineToRawLine(viewRow) - uint64(buffer.terminalState.scrollLinesFromBottom)
|
||||||
buffer.selectionComplete = false
|
buffer.selectionMode = mode
|
||||||
|
|
||||||
buffer.selectionStart = &Position {
|
buffer.selectionStart = &Position {
|
||||||
Col: int(col),
|
Col: int(col),
|
||||||
Line: int(row),
|
Line: int(row),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if mode == SelectionChar {
|
||||||
buffer.selectionEnd = nil
|
buffer.selectionEnd = nil
|
||||||
}
|
} else {
|
||||||
|
buffer.selectionEnd = &Position {
|
||||||
func (buffer *Buffer) EndSelection(col uint16, viewRow uint16, complete bool) {
|
Col: int(col),
|
||||||
|
Line: int(row),
|
||||||
if buffer.selectionComplete {
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.selectionComplete = complete
|
buffer.isSelectionComplete = false
|
||||||
|
|
||||||
|
buffer.emitDisplayChange()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (buffer *Buffer) ExtendSelection(col uint16, viewRow uint16, complete bool) {
|
||||||
|
|
||||||
|
if buffer.isSelectionComplete {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
defer buffer.emitDisplayChange()
|
defer buffer.emitDisplayChange()
|
||||||
|
|
||||||
|
@ -254,47 +239,82 @@ func (buffer *Buffer) EndSelection(col uint16, viewRow uint16, complete bool) {
|
||||||
|
|
||||||
row := buffer.convertViewLineToRawLine(viewRow) - uint64(buffer.terminalState.scrollLinesFromBottom)
|
row := buffer.convertViewLineToRawLine(viewRow) - uint64(buffer.terminalState.scrollLinesFromBottom)
|
||||||
|
|
||||||
if int(col) == buffer.selectionStart.Col && int(row) == int(buffer.selectionStart.Line) && complete {
|
buffer.selectionEnd = &Position {
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer.selectionEnd = &Position{
|
|
||||||
Col: int(col),
|
Col: int(col),
|
||||||
Line: int(row),
|
Line: int(row),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if complete {
|
||||||
|
buffer.isSelectionComplete = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (buffer *Buffer) ClearSelection() {
|
func (buffer *Buffer) ClearSelection() {
|
||||||
buffer.selectionStart = nil
|
buffer.selectionStart = nil
|
||||||
buffer.selectionEnd = nil
|
buffer.selectionEnd = nil
|
||||||
buffer.selectionComplete = true
|
buffer.isSelectionComplete = true
|
||||||
|
|
||||||
buffer.emitDisplayChange()
|
buffer.emitDisplayChange()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (buffer *Buffer) InSelection(col uint16, row uint16) bool {
|
func (buffer *Buffer) getActualSelection() (*Position, *Position) {
|
||||||
|
|
||||||
if buffer.selectionStart == nil || buffer.selectionEnd == nil {
|
if buffer.selectionStart == nil || buffer.selectionEnd == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
start := &Position {}
|
||||||
|
end := &Position {}
|
||||||
|
|
||||||
|
if comparePositions(buffer.selectionStart, buffer.selectionEnd) >= 0 {
|
||||||
|
start.Col = buffer.selectionStart.Col
|
||||||
|
start.Line = buffer.selectionStart.Line
|
||||||
|
|
||||||
|
end.Col = buffer.selectionEnd.Col
|
||||||
|
end.Line = buffer.selectionEnd.Line
|
||||||
|
} else {
|
||||||
|
start.Col = buffer.selectionEnd.Col
|
||||||
|
start.Line = buffer.selectionEnd.Line
|
||||||
|
|
||||||
|
end.Col = buffer.selectionStart.Col
|
||||||
|
end.Line = buffer.selectionStart.Line
|
||||||
|
}
|
||||||
|
|
||||||
|
switch buffer.selectionMode {
|
||||||
|
case SelectionChar:
|
||||||
|
// no action
|
||||||
|
|
||||||
|
case SelectionWord:
|
||||||
|
start.Col = buffer.findBeginningOfWord(start.Col, start.Line)
|
||||||
|
end.Col = buffer.findEndOfWord(end.Col, end.Line)
|
||||||
|
|
||||||
|
case SelectionLine:
|
||||||
|
start.Col = 0
|
||||||
|
end.Col = int(buffer.ViewWidth() - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if start.Line >= len(buffer.lines) {
|
||||||
|
start.Col = 0
|
||||||
|
} else if start.Col >= len(buffer.lines[start.Line].cells) {
|
||||||
|
start.Col = len(buffer.lines[start.Line].cells)
|
||||||
|
}
|
||||||
|
|
||||||
|
if end.Line >= len(buffer.lines) || end.Col >= len(buffer.lines[end.Line].cells) {
|
||||||
|
end.Col = int(buffer.ViewWidth() - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return start, end
|
||||||
|
}
|
||||||
|
|
||||||
|
func (buffer *Buffer) InSelection(col uint16, row uint16) bool {
|
||||||
|
start, end := buffer.getActualSelection()
|
||||||
|
if start == nil || end == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
var x1, x2, y1, y2 int
|
|
||||||
|
|
||||||
// first, let's put the selection points in the correct order, earliest first
|
|
||||||
if buffer.selectionStart.Line > buffer.selectionEnd.Line || (buffer.selectionStart.Line == buffer.selectionEnd.Line && buffer.selectionStart.Col > buffer.selectionEnd.Col) {
|
|
||||||
y2 = buffer.selectionStart.Line
|
|
||||||
y1 = buffer.selectionEnd.Line
|
|
||||||
x2 = buffer.selectionStart.Col
|
|
||||||
x1 = buffer.selectionEnd.Col
|
|
||||||
} else {
|
|
||||||
y1 = buffer.selectionStart.Line
|
|
||||||
y2 = buffer.selectionEnd.Line
|
|
||||||
x1 = buffer.selectionStart.Col
|
|
||||||
x2 = buffer.selectionEnd.Col
|
|
||||||
}
|
|
||||||
|
|
||||||
rawY := int(buffer.convertViewLineToRawLine(row) - uint64(buffer.terminalState.scrollLinesFromBottom))
|
rawY := int(buffer.convertViewLineToRawLine(row) - uint64(buffer.terminalState.scrollLinesFromBottom))
|
||||||
return (rawY > y1 || (rawY == y1 && int(col) >= x1)) && (rawY < y2 || (rawY == y2 && int(col) <= x2))
|
|
||||||
|
return (rawY > start.Line || (rawY == start.Line && int(col) >= start.Col)) &&
|
||||||
|
(rawY < end.Line || (rawY == end.Line && int(col) <= end.Col))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (buffer *Buffer) IsDirty() bool {
|
func (buffer *Buffer) IsDirty() bool {
|
||||||
|
|
|
@ -639,3 +639,75 @@ func TestBufferMaxLines(t *testing.T) {
|
||||||
assert.Equal(t, "funny", b.lines[0].String())
|
assert.Equal(t, "funny", b.lines[0].String())
|
||||||
assert.Equal(t, "world", b.lines[1].String())
|
assert.Equal(t, "world", b.lines[1].String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeBufferForTestingSelection() *Buffer {
|
||||||
|
b := NewBuffer(NewTerminalState(80, 10, CellAttributes{}, 10))
|
||||||
|
b.terminalState.LineFeedMode = false
|
||||||
|
|
||||||
|
b.Write([]rune("The quick brown")...)
|
||||||
|
b.NewLine()
|
||||||
|
b.Write([]rune("fox jumps over")...)
|
||||||
|
b.NewLine()
|
||||||
|
b.Write([]rune("the lazy dog")...)
|
||||||
|
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSelectingChars(t *testing.T) {
|
||||||
|
b := makeBufferForTestingSelection()
|
||||||
|
|
||||||
|
b.StartSelection(2, 0, SelectionChar)
|
||||||
|
b.ExtendSelection(4, 1, true)
|
||||||
|
|
||||||
|
assert.Equal(t, "e quick brown\nfox j", b.GetSelectedText())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSelectingWordsDown(t *testing.T) {
|
||||||
|
b := makeBufferForTestingSelection()
|
||||||
|
|
||||||
|
b.StartSelection(6, 1, SelectionWord)
|
||||||
|
b.ExtendSelection(5, 2, true)
|
||||||
|
|
||||||
|
assert.Equal(t, "jumps over\nthe lazy", b.GetSelectedText())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSelectingWordsUp(t *testing.T) {
|
||||||
|
b := makeBufferForTestingSelection()
|
||||||
|
|
||||||
|
b.StartSelection(5, 2, SelectionWord)
|
||||||
|
b.ExtendSelection(6, 1, true)
|
||||||
|
|
||||||
|
assert.Equal(t, "jumps over\nthe lazy", b.GetSelectedText())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSelectingLinesDown(t *testing.T) {
|
||||||
|
b := makeBufferForTestingSelection()
|
||||||
|
|
||||||
|
b.StartSelection(6, 1, SelectionLine)
|
||||||
|
b.ExtendSelection(4, 2, true)
|
||||||
|
|
||||||
|
assert.Equal(t, "fox jumps over\nthe lazy dog", b.GetSelectedText())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSelectingLineUp(t *testing.T) {
|
||||||
|
b := makeBufferForTestingSelection()
|
||||||
|
|
||||||
|
b.StartSelection(8, 2, SelectionLine)
|
||||||
|
b.ExtendSelection(3, 1, true)
|
||||||
|
|
||||||
|
assert.Equal(t, "fox jumps over\nthe lazy dog", b.GetSelectedText())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSelectingAfterText(t *testing.T) {
|
||||||
|
b := makeBufferForTestingSelection()
|
||||||
|
|
||||||
|
b.StartSelection(6, 3, SelectionChar)
|
||||||
|
b.ExtendSelection(6, 3, true)
|
||||||
|
|
||||||
|
start, end := b.getActualSelection()
|
||||||
|
|
||||||
|
assert.Equal(t, start.Col, 0)
|
||||||
|
assert.Equal(t, start.Line, 3)
|
||||||
|
assert.Equal(t, end.Col, 79)
|
||||||
|
assert.Equal(t, end.Line, 3)
|
||||||
|
}
|
|
@ -54,6 +54,7 @@ type GUI struct {
|
||||||
prevLeftClickY uint16
|
prevLeftClickY uint16
|
||||||
leftClickTime time.Time
|
leftClickTime time.Time
|
||||||
leftClickCount int // number of clicks in a serie - single click, double click, or triple click
|
leftClickCount int // number of clicks in a serie - single click, double click, or triple click
|
||||||
|
mouseMovedAfterSelectionStarted bool
|
||||||
internalResize bool
|
internalResize bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
26
gui/mouse.go
26
gui/mouse.go
|
@ -7,6 +7,7 @@ import (
|
||||||
"github.com/go-gl/glfw/v3.2/glfw"
|
"github.com/go-gl/glfw/v3.2/glfw"
|
||||||
"github.com/liamg/aminal/terminal"
|
"github.com/liamg/aminal/terminal"
|
||||||
"time"
|
"time"
|
||||||
|
"github.com/liamg/aminal/buffer"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (gui *GUI) glfwScrollCallback(w *glfw.Window, xoff float64, yoff float64) {
|
func (gui *GUI) glfwScrollCallback(w *glfw.Window, xoff float64, yoff float64) {
|
||||||
|
@ -39,7 +40,7 @@ func (gui *GUI) mouseMoveCallback(w *glfw.Window, px float64, py float64) {
|
||||||
x, y := gui.convertMouseCoordinates(px, py)
|
x, y := gui.convertMouseCoordinates(px, py)
|
||||||
|
|
||||||
if gui.mouseDown {
|
if gui.mouseDown {
|
||||||
gui.terminal.ActiveBuffer().EndSelection(x, y, false)
|
gui.terminal.ActiveBuffer().ExtendSelection(x, y, false)
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
hint := gui.terminal.ActiveBuffer().GetHintAtPosition(x, y)
|
hint := gui.terminal.ActiveBuffer().GetHintAtPosition(x, y)
|
||||||
|
@ -48,7 +49,6 @@ func (gui *GUI) mouseMoveCallback(w *glfw.Window, px float64, py float64) {
|
||||||
} else {
|
} else {
|
||||||
gui.setOverlay(nil)
|
gui.setOverlay(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if url := gui.terminal.ActiveBuffer().GetURLAtPosition(x, y); url != "" {
|
if url := gui.terminal.ActiveBuffer().GetURLAtPosition(x, y); url != "" {
|
||||||
|
@ -109,20 +109,28 @@ func (gui *GUI) mouseButtonCallback(w *glfw.Window, button glfw.MouseButton, act
|
||||||
gui.mouseDown = true
|
gui.mouseDown = true
|
||||||
|
|
||||||
clickCount := gui.updateLeftClickCount(x, y)
|
clickCount := gui.updateLeftClickCount(x, y)
|
||||||
if clickCount == 1 || !activeBuffer.IsSelectionComplete() {
|
|
||||||
activeBuffer.StartSelection(x, y)
|
|
||||||
} else {
|
|
||||||
switch clickCount {
|
switch clickCount {
|
||||||
|
case 1:
|
||||||
|
activeBuffer.StartSelection(x, y, buffer.SelectionChar)
|
||||||
case 2:
|
case 2:
|
||||||
activeBuffer.SelectWordAtPosition(x, y)
|
activeBuffer.StartSelection(x, y, buffer.SelectionWord)
|
||||||
case 3:
|
case 3:
|
||||||
activeBuffer.SelectLineAtPosition(x, y)
|
activeBuffer.StartSelection(x, y, buffer.SelectionLine)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
gui.mouseMovedAfterSelectionStarted = false
|
||||||
|
|
||||||
} else if action == glfw.Release {
|
} else if action == glfw.Release {
|
||||||
gui.mouseDown = false
|
gui.mouseDown = false
|
||||||
activeBuffer.EndSelection(x, y, true)
|
|
||||||
|
|
||||||
|
if x != gui.prevLeftClickX || y != gui.prevLeftClickY {
|
||||||
|
gui.mouseMovedAfterSelectionStarted = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if gui.leftClickCount != 1 || gui.mouseMovedAfterSelectionStarted {
|
||||||
|
activeBuffer.ExtendSelection(x, y, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do copy to clipboard *or* open URL, but not both.
|
||||||
handled := false
|
handled := false
|
||||||
if gui.config.CopyAndPasteWithMouse {
|
if gui.config.CopyAndPasteWithMouse {
|
||||||
selectedText := activeBuffer.GetSelectedText()
|
selectedText := activeBuffer.GetSelectedText()
|
||||||
|
|
Loading…
Reference in New Issue