Make the mouse selection code re-useable
This commit is contained in:
parent
277dea647e
commit
00d4841304
@ -4,30 +4,17 @@ package diff
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"kitty"
|
"kitty"
|
||||||
"kitty/tools/config"
|
"kitty/tools/config"
|
||||||
"kitty/tools/tui/loop"
|
"kitty/tools/tui/loop"
|
||||||
"kitty/tools/utils"
|
"kitty/tools/utils"
|
||||||
"path/filepath"
|
|
||||||
"strconv"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = fmt.Print
|
var _ = fmt.Print
|
||||||
|
|
||||||
type SelectionBoundary struct {
|
|
||||||
line ScrollPos
|
|
||||||
x int
|
|
||||||
in_first_half_of_cell bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type MouseSelection struct {
|
|
||||||
start, end SelectionBoundary
|
|
||||||
is_active bool
|
|
||||||
min_x, max_x int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *MouseSelection) IsEmpty() bool { return self.start == self.end }
|
|
||||||
|
|
||||||
type KittyOpts struct {
|
type KittyOpts struct {
|
||||||
Wheel_scroll_multiplier int
|
Wheel_scroll_multiplier int
|
||||||
}
|
}
|
||||||
@ -62,8 +49,6 @@ func (self *Handler) handle_wheel_event(up bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *Handler) start_mouse_selection(ev *loop.MouseEvent) {
|
func (self *Handler) start_mouse_selection(ev *loop.MouseEvent) {
|
||||||
self.mouse_selection = MouseSelection{}
|
|
||||||
ms := &self.mouse_selection
|
|
||||||
available_cols := self.logical_lines.columns / 2
|
available_cols := self.logical_lines.columns / 2
|
||||||
if ev.Cell.Y >= self.screen_size.num_lines || ev.Cell.X < self.logical_lines.margin_size || (ev.Cell.X >= available_cols && ev.Cell.X < available_cols+self.logical_lines.margin_size) {
|
if ev.Cell.Y >= self.screen_size.num_lines || ev.Cell.X < self.logical_lines.margin_size || (ev.Cell.X >= available_cols && ev.Cell.X < available_cols+self.logical_lines.margin_size) {
|
||||||
return
|
return
|
||||||
@ -74,51 +59,38 @@ func (self *Handler) start_mouse_selection(ev *loop.MouseEvent) {
|
|||||||
if ll.line_type == EMPTY_LINE || ll.line_type == IMAGE_LINE {
|
if ll.line_type == EMPTY_LINE || ll.line_type == IMAGE_LINE {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ms.start.line = pos
|
|
||||||
|
|
||||||
ms.start.x = ev.Cell.X
|
min_x := self.logical_lines.margin_size
|
||||||
ms.min_x = self.logical_lines.margin_size
|
max_x := available_cols - 1
|
||||||
ms.max_x = available_cols - 1
|
if ev.Cell.X >= available_cols {
|
||||||
if ms.start.x >= available_cols {
|
min_x += available_cols
|
||||||
ms.min_x += available_cols
|
max_x += available_cols
|
||||||
ms.max_x += available_cols
|
|
||||||
}
|
}
|
||||||
ms.start.x = utils.Max(ms.min_x, utils.Min(ms.start.x, ms.max_x))
|
self.mouse_selection.StartNewSelection(ev, &pos, min_x, max_x, 0, self.screen_size.num_lines-1, self.screen_size.cell_width, self.screen_size.cell_height)
|
||||||
cell_start := self.screen_size.cell_width * ev.Cell.X
|
|
||||||
ms.start.in_first_half_of_cell = ev.Pixel.X <= cell_start+self.screen_size.cell_width/2
|
|
||||||
|
|
||||||
ms.end = ms.start
|
|
||||||
ms.is_active = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Handler) update_mouse_selection(ev *loop.MouseEvent) {
|
func (self *Handler) update_mouse_selection(ev *loop.MouseEvent) {
|
||||||
ms := &self.mouse_selection
|
if !self.mouse_selection.IsActive() {
|
||||||
if !self.mouse_selection.is_active {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
pos := self.scroll_pos
|
pos := self.scroll_pos
|
||||||
y := ev.Cell.Y
|
y := ev.Cell.Y
|
||||||
y = utils.Max(0, utils.Min(y, self.screen_size.num_lines-1))
|
y = utils.Max(0, utils.Min(y, self.screen_size.num_lines-1))
|
||||||
self.logical_lines.IncrementScrollPosBy(&pos, y)
|
self.logical_lines.IncrementScrollPosBy(&pos, y)
|
||||||
ms.end.x = ev.Cell.X
|
self.mouse_selection.Update(ev, &pos)
|
||||||
ms.end.x = utils.Max(ms.min_x, utils.Min(ms.end.x, ms.max_x))
|
|
||||||
cell_start := self.screen_size.cell_width * ms.end.x
|
|
||||||
ms.end.in_first_half_of_cell = ev.Pixel.X <= cell_start+self.screen_size.cell_width/2
|
|
||||||
ms.end.line = pos
|
|
||||||
self.draw_screen()
|
self.draw_screen()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Handler) clear_mouse_selection() {
|
func (self *Handler) clear_mouse_selection() {
|
||||||
self.mouse_selection = MouseSelection{}
|
self.mouse_selection.Clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Handler) finish_mouse_selection(ev *loop.MouseEvent) {
|
func (self *Handler) finish_mouse_selection(ev *loop.MouseEvent) {
|
||||||
self.update_mouse_selection(ev)
|
if !self.mouse_selection.IsActive() {
|
||||||
ms := &self.mouse_selection
|
|
||||||
if !self.mouse_selection.is_active {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ms.is_active = false
|
self.update_mouse_selection(ev)
|
||||||
|
self.mouse_selection.Finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
func format_part_of_line(sgr string, start_x, end_x, y int) string {
|
func format_part_of_line(sgr string, start_x, end_x, y int) string {
|
||||||
@ -131,24 +103,9 @@ func (self *Handler) add_mouse_selection_to_line(line string, line_pos ScrollPos
|
|||||||
if ms.IsEmpty() {
|
if ms.IsEmpty() {
|
||||||
return line
|
return line
|
||||||
}
|
}
|
||||||
a, b := ms.start.line, ms.end.line
|
x_start, x_end := self.mouse_selection.LineBounds(&line_pos)
|
||||||
ax, bx := ms.start.x, ms.end.x
|
if x_start > -1 {
|
||||||
if b.Less(a) {
|
line += format_part_of_line(selection_sgr, x_start, x_end, y)
|
||||||
a, b = b, a
|
|
||||||
ax, bx = bx, ax
|
|
||||||
}
|
|
||||||
if a.Less(line_pos) {
|
|
||||||
if line_pos.Less(b) {
|
|
||||||
line += format_part_of_line(selection_sgr, 0, ms.max_x, y)
|
|
||||||
} else if b == line_pos {
|
|
||||||
line += format_part_of_line(selection_sgr, 0, bx, y)
|
|
||||||
}
|
|
||||||
} else if a == line_pos {
|
|
||||||
if line_pos.Less(b) {
|
|
||||||
line += format_part_of_line(selection_sgr, ax, ms.max_x, y)
|
|
||||||
} else if b == line_pos {
|
|
||||||
line += format_part_of_line(selection_sgr, ax, bx, y)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return line
|
return line
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import (
|
|||||||
|
|
||||||
"kitty/tools/config"
|
"kitty/tools/config"
|
||||||
"kitty/tools/tty"
|
"kitty/tools/tty"
|
||||||
|
"kitty/tools/tui"
|
||||||
"kitty/tools/tui/graphics"
|
"kitty/tools/tui/graphics"
|
||||||
"kitty/tools/tui/loop"
|
"kitty/tools/tui/loop"
|
||||||
"kitty/tools/tui/readline"
|
"kitty/tools/tui/readline"
|
||||||
@ -37,6 +38,20 @@ func (self ScrollPos) Less(other ScrollPos) bool {
|
|||||||
return self.logical_line < other.logical_line || (self.logical_line == other.logical_line && self.screen_line < other.screen_line)
|
return self.logical_line < other.logical_line || (self.logical_line == other.logical_line && self.screen_line < other.screen_line)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *ScrollPos) Equal(other tui.LinePos) bool {
|
||||||
|
if o, ok := other.(*ScrollPos); ok {
|
||||||
|
return *self == *o
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ScrollPos) LessThan(other tui.LinePos) bool {
|
||||||
|
if o, ok := other.(*ScrollPos); ok {
|
||||||
|
return self.Less(*o)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (self ScrollPos) Add(other ScrollPos) ScrollPos {
|
func (self ScrollPos) Add(other ScrollPos) ScrollPos {
|
||||||
return ScrollPos{self.logical_line + other.logical_line, self.screen_line + other.screen_line}
|
return ScrollPos{self.logical_line + other.logical_line, self.screen_line + other.screen_line}
|
||||||
}
|
}
|
||||||
@ -54,7 +69,7 @@ var image_collection *graphics.ImageCollection
|
|||||||
type screen_size struct{ rows, columns, num_lines, cell_width, cell_height int }
|
type screen_size struct{ rows, columns, num_lines, cell_width, cell_height int }
|
||||||
type Handler struct {
|
type Handler struct {
|
||||||
async_results chan AsyncResult
|
async_results chan AsyncResult
|
||||||
mouse_selection MouseSelection
|
mouse_selection tui.MouseSelection
|
||||||
shortcut_tracker config.ShortcutTracker
|
shortcut_tracker config.ShortcutTracker
|
||||||
left, right string
|
left, right string
|
||||||
collection *Collection
|
collection *Collection
|
||||||
|
|||||||
93
tools/tui/mouse.go
Normal file
93
tools/tui/mouse.go
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
package tui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"kitty/tools/tty"
|
||||||
|
"kitty/tools/tui/loop"
|
||||||
|
"kitty/tools/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = fmt.Print
|
||||||
|
|
||||||
|
type LinePos interface {
|
||||||
|
LessThan(other LinePos) bool
|
||||||
|
Equal(other LinePos) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type SelectionBoundary struct {
|
||||||
|
line LinePos
|
||||||
|
x int
|
||||||
|
in_first_half_of_cell bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *SelectionBoundary) Equal(other SelectionBoundary) bool {
|
||||||
|
if self.x != other.x || self.in_first_half_of_cell != other.in_first_half_of_cell {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if self.line == nil {
|
||||||
|
return other.line == nil
|
||||||
|
}
|
||||||
|
return self.line.Equal(other.line)
|
||||||
|
}
|
||||||
|
|
||||||
|
type MouseSelection struct {
|
||||||
|
start, end SelectionBoundary
|
||||||
|
is_active bool
|
||||||
|
min_x, max_x int
|
||||||
|
min_y, max_y int
|
||||||
|
cell_width, cell_height int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *MouseSelection) IsEmpty() bool { return self.start.Equal(self.end) }
|
||||||
|
func (self *MouseSelection) IsActive() bool { return self.is_active }
|
||||||
|
func (self *MouseSelection) Finish() { self.is_active = false }
|
||||||
|
func (self *MouseSelection) Clear() { *self = MouseSelection{} }
|
||||||
|
|
||||||
|
func (ms *MouseSelection) StartNewSelection(ev *loop.MouseEvent, line LinePos, min_x, max_x, min_y, max_y, cell_width, cell_height int) {
|
||||||
|
*ms = MouseSelection{min_x: min_x, max_x: max_x, cell_width: cell_width, cell_height: cell_height, min_y: min_y, max_y: max_y}
|
||||||
|
ms.start.line = line
|
||||||
|
ms.start.x = utils.Max(ms.min_x, utils.Min(ev.Cell.X, ms.max_x))
|
||||||
|
cell_start := cell_width * ev.Cell.X
|
||||||
|
ms.start.in_first_half_of_cell = ev.Pixel.X <= cell_start+cell_width/2
|
||||||
|
ms.end = ms.start
|
||||||
|
ms.is_active = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *MouseSelection) Update(ev *loop.MouseEvent, line LinePos) {
|
||||||
|
if ms.is_active {
|
||||||
|
ms.end.x = utils.Max(ms.min_x, utils.Min(ev.Cell.X, ms.max_x))
|
||||||
|
cell_start := ms.cell_width * ms.end.x
|
||||||
|
ms.end.in_first_half_of_cell = ev.Pixel.X <= cell_start+ms.cell_width/2
|
||||||
|
ms.end.line = line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var DebugPrintln = tty.DebugPrintln
|
||||||
|
|
||||||
|
func (ms *MouseSelection) LineBounds(line_pos LinePos) (start_x, end_x int) {
|
||||||
|
if ms.IsEmpty() {
|
||||||
|
return -1, -1
|
||||||
|
}
|
||||||
|
a, b := ms.start.line, ms.end.line
|
||||||
|
ax, bx := ms.start.x, ms.end.x
|
||||||
|
if b.LessThan(a) {
|
||||||
|
a, b = b, a
|
||||||
|
ax, bx = bx, ax
|
||||||
|
}
|
||||||
|
if a.LessThan(line_pos) {
|
||||||
|
if line_pos.LessThan(b) {
|
||||||
|
return ms.min_x, ms.max_x
|
||||||
|
} else if b.Equal(line_pos) {
|
||||||
|
return ms.min_x, bx
|
||||||
|
}
|
||||||
|
} else if a.Equal(line_pos) {
|
||||||
|
if line_pos.LessThan(b) {
|
||||||
|
return ax, ms.max_x
|
||||||
|
} else if b.Equal(line_pos) {
|
||||||
|
return ax, bx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1, -1
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user