TextBox supports copy/paste with keyboard commands
This commit is contained in:
parent
ab78bc640d
commit
c1e2bf46a6
@ -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,
|
||||
) (
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user