From 4bc8566820ec9db5e230b3bf60e36044a4131578 Mon Sep 17 00:00:00 2001 From: Sasha Koshka Date: Mon, 13 Feb 2023 01:52:31 -0500 Subject: [PATCH] Textmanip now operates on a dot instead of a cursor --- elements/basic/textbox.go | 33 ++++---- textmanip/textmanip.go | 153 ++++++++++++++++++++++++++------------ 2 files changed, 125 insertions(+), 61 deletions(-) diff --git a/elements/basic/textbox.go b/elements/basic/textbox.go index aab07ff..e69d414 100644 --- a/elements/basic/textbox.go +++ b/elements/basic/textbox.go @@ -15,7 +15,7 @@ type TextBox struct { core core.CoreControl focusableControl core.FocusableCoreControl - cursor int + dot textmanip.Dot scroll int placeholder string text []rune @@ -74,42 +74,44 @@ func (element *TextBox) HandleKeyDown(key input.Key, modifiers input.Modifiers) return } + // TODO: text selection with shift + scrollMemory := element.scroll altered := true textChanged := false switch { case key == input.KeyBackspace: if len(element.text) < 1 { break } - element.text, element.cursor = textmanip.Backspace ( + element.text, element.dot = textmanip.Backspace ( element.text, - element.cursor, + element.dot, modifiers.Control) textChanged = true case key == input.KeyDelete: if len(element.text) < 1 { break } - element.text, element.cursor = textmanip.Delete ( + element.text, element.dot = textmanip.Delete ( element.text, - element.cursor, + element.dot, modifiers.Control) textChanged = true case key == input.KeyLeft: - element.cursor = textmanip.MoveLeft ( + element.dot = textmanip.MoveLeft ( element.text, - element.cursor, + element.dot, modifiers.Control) case key == input.KeyRight: - element.cursor = textmanip.MoveRight ( + element.dot = textmanip.MoveRight ( element.text, - element.cursor, + element.dot, modifiers.Control) case key.Printable(): - element.text, element.cursor = textmanip.Type ( + element.text, element.dot = textmanip.Type ( element.text, - element.cursor, + element.dot, rune(key)) textChanged = true @@ -154,8 +156,8 @@ func (element *TextBox) SetValue (text string) { element.text = []rune(text) element.runOnChange() element.valueDrawer.SetText(element.text) - if element.cursor > element.valueDrawer.Length() { - element.cursor = element.valueDrawer.Length() + if element.dot.End > element.valueDrawer.Length() { + element.dot = textmanip.EmptyDot(element.valueDrawer.Length()) } element.scrollToCursor() element.redo() @@ -238,7 +240,7 @@ func (element *TextBox) scrollToCursor () { bounds := element.Bounds().Inset(element.config.Padding()) bounds = bounds.Sub(bounds.Min) bounds.Max.X -= element.valueDrawer.Em().Round() - cursorPosition := element.valueDrawer.PositionOf(element.cursor) + cursorPosition := element.valueDrawer.PositionOf(element.dot.End) cursorPosition.X -= element.scroll maxX := bounds.Max.X minX := maxX @@ -328,8 +330,9 @@ func (element *TextBox) draw () { if element.Focused() { // cursor + // TODO: draw selection if exists cursorPosition := element.valueDrawer.PositionOf ( - element.cursor) + element.dot.End) artist.Line ( element.core, foreground, 1, diff --git a/textmanip/textmanip.go b/textmanip/textmanip.go index d4b8685..c22037a 100644 --- a/textmanip/textmanip.go +++ b/textmanip/textmanip.go @@ -2,7 +2,40 @@ package textmanip import "unicode" -func WordToLeft (text []rune, cursor int) (length int) { +type Dot struct { Start, End int } + +func EmptyDot (position int) Dot { + return Dot { position, position } +} + +func (dot Dot) Canon () Dot { + if dot.Start > dot.End { + return Dot { dot.End, dot.Start } + } else { + return dot + } +} + +func (dot Dot) Empty () bool { + return dot.Start == dot.End +} + +func (dot Dot) Add (delta int) Dot { + return Dot { + dot.Start + delta, + dot.End + delta, + } +} + +func (dot Dot) Sub (delta int) Dot { + return Dot { + dot.Start - delta, + dot.End - delta, + } +} + +func WordToLeft (text []rune, dot Dot) (length int) { + cursor := dot.End if cursor < 1 { return } if cursor > len(text) { cursor = len(text) } @@ -18,7 +51,8 @@ func WordToLeft (text []rune, cursor int) (length int) { return } -func WordToRight (text []rune, cursor int) (length int) { +func WordToRight (text []rune, dot Dot) (length int) { + cursor := dot.End if cursor < 0 { return } if cursor > len(text) { cursor = len(text) } @@ -34,66 +68,93 @@ func WordToRight (text []rune, cursor int) (length int) { return } -func Backspace (text []rune, cursor int, word bool) (result []rune, moved int) { - if cursor < 1 { return text, cursor } - if cursor > len(text) { cursor = len(text) } +func Backspace (text []rune, dot Dot, word bool) (result []rune, moved Dot) { + if dot.Empty() { + cursor := dot.End + if cursor < 1 { return text, dot } + if cursor > len(text) { cursor = len(text) } - moved = 1 - if word { - moved = WordToLeft(text, cursor) - } - result = append(result, text[:cursor - moved]...) - result = append(result, text[cursor:]...) - moved = cursor - moved - return -} - -func Delete (text []rune, cursor int, word bool) (result []rune, moved int) { - if cursor < 0 { return text, cursor } - if cursor > len(text) { cursor = len(text) } - - moved = 1 - if word { - moved = WordToRight(text, cursor) - } - result = append(result, text[:cursor]...) - result = append(result, text[cursor + moved:]...) - moved = cursor - return -} - -func Type (text []rune, cursor int, character rune) (result []rune, moved int) { - if cursor < 0 { cursor = 0 } - if cursor > len(text) { cursor = len(text) } - result = append(result, text[:cursor]...) - result = append(result, character) - if cursor < len(text) { + distance := 1 + if word { + distance = WordToLeft(text, dot) + } + result = append(result, text[:cursor - distance]...) result = append(result, text[cursor:]...) + moved = EmptyDot(cursor - distance) + } else { + return Delete(text, dot, word) } - moved = cursor + 1 + return } -func MoveLeft (text []rune, cursor int, word bool) (moved int) { - if cursor < 1 { return cursor } +func Delete (text []rune, dot Dot, word bool) (result []rune, moved Dot) { + if dot.Empty() { + cursor := dot.End + if cursor < 0 { return text, dot } + if cursor > len(text) { cursor = len(text) } + + distance := 1 + if word { + distance = WordToRight(text, dot) + } + result = append(result, text[:cursor]...) + result = append(result, text[cursor + distance:]...) + moved = dot + return + } else { + result = append(result, text[:dot.Start]...) + result = append(result, text[dot.End:]...) + moved = EmptyDot(dot.Start) + return + } +} + +func Type (text []rune, dot Dot, character rune) (result []rune, moved Dot) { + if dot.Empty() { + cursor := dot.End + if cursor < 0 { cursor = 0 } + if cursor > len(text) { cursor = len(text) } + result = append(result, text[:cursor]...) + result = append(result, character) + if cursor < len(text) { + result = append(result, text[cursor:]...) + } + moved = EmptyDot(cursor + 1) + return + } else { + result = append(result, text[:dot.Start]...) + result = append(result, character) + result = append(result, text[dot.End:]...) + moved = EmptyDot(dot.Start) + return + } +} + +func MoveLeft (text []rune, dot Dot, word bool) (moved Dot) { + cursor := dot.Start + + if cursor < 1 { return EmptyDot(cursor) } if cursor > len(text) { cursor = len(text) } - moved = 1 + distance := 1 if word { - moved = WordToLeft(text, cursor) + distance = WordToLeft(text, dot) } - moved = cursor - moved + moved = EmptyDot(cursor - distance) return } -func MoveRight (text []rune, cursor int, word bool) (moved int) { - if cursor < 0 { return cursor } +func MoveRight (text []rune, dot Dot, word bool) (moved Dot) { + cursor := dot.End + + if cursor < 0 { return EmptyDot(cursor) } if cursor > len(text) { cursor = len(text) } - moved = 1 + distance := 1 if word { - moved = WordToRight(text, cursor) + distance = WordToRight(text, dot) } - moved = cursor + moved + moved = EmptyDot(cursor + distance) return }