TextBox supports copy/paste with keyboard commands

This commit is contained in:
Sasha Koshka 2023-03-31 03:25:46 -04:00
parent ab78bc640d
commit c1e2bf46a6
8 changed files with 82 additions and 5 deletions

View File

@ -122,6 +122,10 @@ func (window *window) NotifyMinimumSizeChange (child tomo.Element) {
window.childMinimumSizeChangeCallback(child.MinimumSize())
}
func (window *window) Window () tomo.Window {
return window
}
func (window *window) RequestFocus (
child tomo.Focusable,
) (

View File

@ -204,6 +204,10 @@ func (element *Container) redoAll () {
}
}
func (element *Container) Window () tomo.Window {
return element.core.Window()
}
// NotifyMinimumSizeChange notifies the container that the minimum size of a
// child element has changed.
func (element *Container) NotifyMinimumSizeChange (child tomo.Element) {

View File

@ -202,6 +202,10 @@ func (element *DocumentContainer) partition () {
}
}
func (element *DocumentContainer) Window () tomo.Window {
return element.core.Window()
}
// NotifyMinimumSizeChange notifies the container that the minimum size of a
// child element has changed.
func (element *DocumentContainer) NotifyMinimumSizeChange (child tomo.Element) {

View File

@ -110,6 +110,10 @@ func (element *ScrollContainer) disownChild (child tomo.Scrollable) {
}
}
func (element *ScrollContainer) Window () tomo.Window {
return element.core.Window()
}
// NotifyMinimumSizeChange notifies the container that the minimum size of a
// child element has changed.
func (element *ScrollContainer) NotifyMinimumSizeChange (child tomo.Element) {

View File

@ -122,6 +122,16 @@ func (control CoreControl) Parent () tomo.Parent {
return control.core.parent
}
// Window returns the window containing the element.
func (control CoreControl) Window () tomo.Window {
parent := control.Parent()
if parent == nil {
return nil
} else {
return parent.Window()
}
}
// Outer returns the outer element given when the control was constructed.
func (control CoreControl) Outer () tomo.Element {
return control.core.outer

View File

@ -1,7 +1,9 @@
package elements
import "io"
import "image"
import "git.tebibyte.media/sashakoshka/tomo"
import "git.tebibyte.media/sashakoshka/tomo/data"
import "git.tebibyte.media/sashakoshka/tomo/input"
import "git.tebibyte.media/sashakoshka/tomo/artist"
import "git.tebibyte.media/sashakoshka/tomo/canvas"
@ -181,6 +183,34 @@ func (element *TextBox) HandleKeyDown(key input.Key, modifiers input.Modifiers)
case key == 'a' && modifiers.Control:
element.dot.Start = 0
element.dot.End = len(element.text)
case key == 'x' && modifiers.Control:
var lifted []rune
element.text, element.dot, lifted = textmanip.Lift (
element.text,
element.dot)
if lifted != nil {
element.clipboardPut(lifted)
textChanged = true
}
case key == 'c' && modifiers.Control:
element.clipboardPut(element.dot.Slice(element.text))
case key == 'v' && modifiers.Control:
window := element.core.Window()
if window == nil { break }
window.Paste (func (d data.Data, err error) {
if err != nil { return }
reader, ok := d[data.MimePlain]
if !ok { return }
bytes, _ := io.ReadAll(reader)
element.text, element.dot = textmanip.Type (
element.text,
element.dot,
[]rune(string(bytes))...)
element.notifyAsyncTextChange()
})
case key.Printable():
element.text, element.dot = textmanip.Type (
@ -213,6 +243,13 @@ func (element *TextBox) HandleKeyDown(key input.Key, modifiers input.Modifiers)
}
}
func (element *TextBox) clipboardPut (text []rune) {
window := element.core.Window()
if window != nil {
window.Copy(data.Bytes(data.MimePlain, []byte(string(text))))
}
}
func (element *TextBox) HandleKeyUp(key input.Key, modifiers input.Modifiers) { }
func (element *TextBox) SetPlaceholder (placeholder string) {
@ -366,6 +403,16 @@ func (element *TextBox) updateMinimumSize () {
element.placeholderDrawer.LineHeight().Round())
}
func (element *TextBox) notifyAsyncTextChange () {
element.runOnChange()
element.valueDrawer.SetText(element.text)
element.scrollToCursor()
if parent, ok := element.core.Parent().(tomo.ScrollableParent); ok {
parent.NotifyScrollBoundsChange(element)
}
element.redo()
}
func (element *TextBox) redo () {
if element.core.HasImage () {
element.draw()

View File

@ -6,6 +6,9 @@ type Parent interface {
// minimum size has changed. This method is expected to be called by
// child elements when their minimum size changes.
NotifyMinimumSizeChange (child Element)
// Window returns the window containing the parent.
Window () Window
}
// FocusableParent represents a parent with keyboard navigation support.

View File

@ -48,6 +48,7 @@ func (dot Dot) Width () int {
}
func (dot Dot) Slice (text []rune) []rune {
dot = dot.Canon().Constrain(len(text))
return text[dot.Start:dot.End]
}
@ -146,22 +147,22 @@ func Lift (text []rune, dot Dot) (result []rune, moved Dot, lifted []rune) {
return
}
func Type (text []rune, dot Dot, character rune) (result []rune, moved Dot) {
func Type (text []rune, dot Dot, characters ...rune) (result []rune, moved Dot) {
dot = dot.Constrain(len(text))
if dot.Empty() {
result = append(result, text[:dot.End]...)
result = append(result, character)
result = append(result, characters...)
if dot.End < len(text) {
result = append(result, text[dot.End:]...)
}
moved = EmptyDot(dot.Add(1).End)
moved = EmptyDot(dot.Add(len(characters)).End)
return
} else {
dot = dot.Canon()
result = append(result, text[:dot.Start]...)
result = append(result, character)
result = append(result, characters...)
result = append(result, text[dot.End:]...)
moved = EmptyDot(dot.Add(1).Start)
moved = EmptyDot(dot.Add(len(characters)).Start)
return
}
}