Rudimentary text selection with keybaord keys

This commit is contained in:
Sasha Koshka 2023-02-13 12:55:51 -05:00
parent 4bc8566820
commit 21abd147bf
2 changed files with 129 additions and 82 deletions

View File

@ -97,16 +97,30 @@ func (element *TextBox) HandleKeyDown(key input.Key, modifiers input.Modifiers)
textChanged = true textChanged = true
case key == input.KeyLeft: case key == input.KeyLeft:
element.dot = textmanip.MoveLeft ( if modifiers.Shift {
element.text, element.dot = textmanip.SelectLeft (
element.dot, element.text,
modifiers.Control) element.dot,
modifiers.Control)
} else {
element.dot = textmanip.MoveLeft (
element.text,
element.dot,
modifiers.Control)
}
case key == input.KeyRight: case key == input.KeyRight:
element.dot = textmanip.MoveRight ( if modifiers.Shift {
element.text, element.dot = textmanip.SelectRight (
element.dot, element.text,
modifiers.Control) element.dot,
modifiers.Control)
} else {
element.dot = textmanip.MoveRight (
element.text,
element.dot,
modifiers.Control)
}
case key.Printable(): case key.Printable():
element.text, element.dot = textmanip.Type ( element.text, element.dot = textmanip.Type (
@ -299,14 +313,28 @@ func (element *TextBox) draw () {
} }
pattern := element.theme.Pattern(theme.PatternSunken, state) pattern := element.theme.Pattern(theme.PatternSunken, state)
artist.FillRectangle(element.core, pattern, bounds) artist.FillRectangle(element.core, pattern, bounds)
offset := bounds.Min.Add (image.Point {
X: element.config.Padding() - element.scroll,
Y: element.config.Padding(),
})
if len(element.text) == 0 && !element.Focused() { if element.Focused() && !element.dot.Empty() {
// draw selection bounds
accent := element.theme.Pattern (
theme.PatternAccent, state)
canon := element.dot.Canon()
start := element.valueDrawer.PositionOf(canon.Start).Add(offset)
end := element.valueDrawer.PositionOf(canon.End).Add(offset)
end.Y += element.valueDrawer.LineHeight().Round()
artist.FillRectangle (
element.core,
accent,
image.Rectangle { start, end })
}
if len(element.text) == 0 {
// draw placeholder // draw placeholder
textBounds := element.placeholderDrawer.LayoutBounds() textBounds := element.placeholderDrawer.LayoutBounds()
offset := bounds.Min.Add (image.Point {
X: element.config.Padding(),
Y: element.config.Padding(),
})
foreground := element.theme.Pattern ( foreground := element.theme.Pattern (
theme.PatternForeground, theme.PatternForeground,
theme.PatternState { Disabled: true }) theme.PatternState { Disabled: true })
@ -317,30 +345,27 @@ func (element *TextBox) draw () {
} else { } else {
// draw input value // draw input value
textBounds := element.valueDrawer.LayoutBounds() textBounds := element.valueDrawer.LayoutBounds()
offset := bounds.Min.Add (image.Point {
X: element.config.Padding() - element.scroll,
Y: element.config.Padding(),
})
foreground := element.theme.Pattern ( foreground := element.theme.Pattern (
theme.PatternForeground, state) theme.PatternForeground, state)
element.valueDrawer.Draw ( element.valueDrawer.Draw (
element.core, element.core,
foreground, foreground,
offset.Sub(textBounds.Min)) offset.Sub(textBounds.Min))
}
if element.Focused() { if element.Focused() && element.dot.Empty() {
// cursor // draw cursor
// TODO: draw selection if exists foreground := element.theme.Pattern (
cursorPosition := element.valueDrawer.PositionOf ( theme.PatternForeground, state)
element.dot.End) cursorPosition := element.valueDrawer.PositionOf (
artist.Line ( element.dot.End)
element.core, artist.Line (
foreground, 1, element.core,
cursorPosition.Add(offset), foreground, 1,
image.Pt ( cursorPosition.Add(offset),
cursorPosition.X, image.Pt (
cursorPosition.Y + element.valueDrawer. cursorPosition.X,
LineHeight().Round()).Add(offset)) cursorPosition.Y + element.valueDrawer.
} LineHeight().Round()).Add(offset))
} }
} }

View File

@ -34,12 +34,19 @@ func (dot Dot) Sub (delta int) Dot {
} }
} }
func WordToLeft (text []rune, dot Dot) (length int) { func (dot Dot) Constrain (length int) Dot {
cursor := dot.End if dot.Start < 0 { dot.Start = 0 }
if cursor < 1 { return } if dot.Start > length { dot.Start = length}
if cursor > len(text) { cursor = len(text) } if dot.End < 0 { dot.End = 0 }
if dot.End > length { dot.End = length}
return dot
}
index := cursor - 1 func WordToLeft (text []rune, position int) (length int) {
if position < 1 { return }
if position > len(text) { position = len(text) }
index := position - 1
for index >= 0 && unicode.IsSpace(text[index]) { for index >= 0 && unicode.IsSpace(text[index]) {
length ++ length ++
index -- index --
@ -51,12 +58,11 @@ func WordToLeft (text []rune, dot Dot) (length int) {
return return
} }
func WordToRight (text []rune, dot Dot) (length int) { func WordToRight (text []rune, position int) (length int) {
cursor := dot.End if position < 0 { return }
if cursor < 0 { return } if position > len(text) { position = len(text) }
if cursor > len(text) { cursor = len(text) }
index := cursor index := position
for index < len(text) && unicode.IsSpace(text[index]) { for index < len(text) && unicode.IsSpace(text[index]) {
length ++ length ++
index ++ index ++
@ -68,41 +74,46 @@ func WordToRight (text []rune, dot Dot) (length int) {
return return
} }
func Backspace (text []rune, dot Dot, word bool) (result []rune, moved Dot) { func WordAround (text []rune, position int) (around Dot) {
if dot.Empty() { return Dot {
cursor := dot.End WordToLeft(text, position),
if cursor < 1 { return text, dot } WordToRight(text, position),
if cursor > len(text) { cursor = len(text) } }
}
func Backspace (text []rune, dot Dot, word bool) (result []rune, moved Dot) {
dot = dot.Constrain(len(text))
if dot.Empty() {
distance := 1 distance := 1
if word { if word {
distance = WordToLeft(text, dot) distance = WordToLeft(text, dot.End)
} }
result = append(result, text[:cursor - distance]...) result = append (
result = append(result, text[cursor:]...) result,
moved = EmptyDot(cursor - distance) text[:dot.Sub(distance).Constrain(len(text)).End]...)
result = append(result, text[dot.End:]...)
moved = EmptyDot(dot.Sub(distance).Start)
return
} else { } else {
return Delete(text, dot, word) return Delete(text, dot, word)
} }
return
} }
func Delete (text []rune, dot Dot, word bool) (result []rune, moved Dot) { func Delete (text []rune, dot Dot, word bool) (result []rune, moved Dot) {
dot = dot.Constrain(len(text))
if dot.Empty() { if dot.Empty() {
cursor := dot.End
if cursor < 0 { return text, dot }
if cursor > len(text) { cursor = len(text) }
distance := 1 distance := 1
if word { if word {
distance = WordToRight(text, dot) distance = WordToRight(text, dot.End)
} }
result = append(result, text[:cursor]...) result = append(result, text[:dot.End]...)
result = append(result, text[cursor + distance:]...) result = append (
result,
text[dot.Add(distance).Constrain(len(text)).End:]...)
moved = dot moved = dot
return return
} else { } else {
dot = dot.Canon()
result = append(result, text[:dot.Start]...) result = append(result, text[:dot.Start]...)
result = append(result, text[dot.End:]...) result = append(result, text[dot.End:]...)
moved = EmptyDot(dot.Start) moved = EmptyDot(dot.Start)
@ -111,50 +122,61 @@ func Delete (text []rune, dot Dot, word bool) (result []rune, moved Dot) {
} }
func Type (text []rune, dot Dot, character rune) (result []rune, moved Dot) { func Type (text []rune, dot Dot, character rune) (result []rune, moved Dot) {
dot = dot.Constrain(len(text))
if dot.Empty() { if dot.Empty() {
cursor := dot.End result = append(result, text[:dot.End]...)
if cursor < 0 { cursor = 0 }
if cursor > len(text) { cursor = len(text) }
result = append(result, text[:cursor]...)
result = append(result, character) result = append(result, character)
if cursor < len(text) { if dot.End < len(text) {
result = append(result, text[cursor:]...) result = append(result, text[dot.End:]...)
} }
moved = EmptyDot(cursor + 1) moved = EmptyDot(dot.Add(1).End)
return return
} else { } else {
dot = dot.Canon()
result = append(result, text[:dot.Start]...) result = append(result, text[:dot.Start]...)
result = append(result, character) result = append(result, character)
result = append(result, text[dot.End:]...) result = append(result, text[dot.End:]...)
moved = EmptyDot(dot.Start) moved = EmptyDot(dot.Add(1).Start)
return return
} }
} }
func MoveLeft (text []rune, dot Dot, word bool) (moved Dot) { func MoveLeft (text []rune, dot Dot, word bool) (moved Dot) {
cursor := dot.Start dot = dot.Constrain(len(text))
if cursor < 1 { return EmptyDot(cursor) }
if cursor > len(text) { cursor = len(text) }
distance := 1 distance := 1
if word { if word {
distance = WordToLeft(text, dot) distance = WordToLeft(text, dot.Start)
} }
moved = EmptyDot(cursor - distance) moved = EmptyDot(dot.Sub(distance).Start)
return return
} }
func MoveRight (text []rune, dot Dot, word bool) (moved Dot) { func MoveRight (text []rune, dot Dot, word bool) (moved Dot) {
cursor := dot.End dot = dot.Constrain(len(text))
if cursor < 0 { return EmptyDot(cursor) }
if cursor > len(text) { cursor = len(text) }
distance := 1 distance := 1
if word { if word {
distance = WordToRight(text, dot) distance = WordToRight(text, dot.End)
} }
moved = EmptyDot(cursor + distance) moved = EmptyDot(dot.Add(distance).End)
return return
} }
func SelectLeft (text []rune, dot Dot, word bool) (moved Dot) {
dot = dot.Constrain(len(text))
distance := 1
if word {
distance = WordToLeft(text, dot.Start)
}
dot.Start -= distance
return dot
}
func SelectRight (text []rune, dot Dot, word bool) (moved Dot) {
dot = dot.Constrain(len(text))
distance := 1
if word {
distance = WordToRight(text, dot.End)
}
dot.Start += distance
return dot
}