diff --git a/button.go b/button.go index 6f470b0..f3ca2dc 100644 --- a/button.go +++ b/button.go @@ -14,11 +14,14 @@ type Button struct { } // NewButton creates a new button with the specified text. -func NewButton (text string) Button { - box := Button { TextBox: tomo.NewTextBox() } +func NewButton (text string) *Button { + box := &Button { TextBox: tomo.NewTextBox() } theme.Apply(box, theme.R("objects", "Button")) box.SetText(text) + box.SetAlign(tomo.AlignMiddle, tomo.AlignMiddle) box.OnMouseUp(box.handleMouseUp) + box.OnKeyUp(box.handleKeyUp) + box.SetFocusable(true) return box } @@ -27,6 +30,11 @@ func (this *Button) OnClick (callback func ()) event.Cookie { return this.on.click.Connect(callback) } +func (this *Button) handleKeyUp (key input.Key, numberPad bool) { + if key != input.KeyEnter && key != input.Key(' ') { return } + this.on.click.Broadcast() +} + func (this *Button) handleMouseUp (button input.Button) { if button != input.ButtonLeft { return } if this.MousePosition().In(this.Bounds()) { diff --git a/input.go b/input.go new file mode 100644 index 0000000..a24210b --- /dev/null +++ b/input.go @@ -0,0 +1,90 @@ +package objects + +import "git.tebibyte.media/tomo/tomo" +import "git.tebibyte.media/tomo/tomo/text" +import "git.tebibyte.media/tomo/tomo/theme" +import "git.tebibyte.media/tomo/tomo/input" +import "git.tebibyte.media/tomo/tomo/event" + +// TextInput is a single-line editable text box. +type TextInput struct { + tomo.TextBox + text []rune + on struct { + enter event.FuncBroadcaster + } +} + +// NewTextInput creates a new text input containing the specified text. +func NewTextInput (text string) *TextInput { + this := &TextInput { TextBox: tomo.NewTextBox() } + theme.Apply(this, theme.R("objects", "TextInput")) + this.SetText(text) + this.SetFocusable(true) + this.SetSelectable(true) + this.OnKeyDown(this.handleKeyDown) + return this +} + +// SetText sets the text content of the input. +func (this *TextInput) SetText (text string) { + this.text = []rune(text) + this.TextBox.SetText(text) +} + +// Text returns the text content of the input. +func (this *TextInput) Text () string { + return string(this.text) +} + +// OnEnter specifies a function to be called when the user presses enter within +// the text input. +func (this *TextInput) OnEnter (callback func ()) event.Cookie { + return this.on.enter.Connect(callback) +} + +func (this *TextInput) handleKeyDown (key input.Key, numpad bool) { + dot := this.Dot() + modifiers := this.Modifiers() + word := modifiers.Control + sel := modifiers.Shift + changed := false + + switch { + case key == input.KeyEnter: + this.on.enter.Broadcast() + case key == input.KeyHome || (modifiers.Alt && key == input.KeyLeft): + dot.End = 0 + if !sel { dot.Start = dot.End } + case key == input.KeyEnd || (modifiers.Alt && key == input.KeyRight): + dot.End = len(this.text) + if !sel { dot.Start = dot.End } + case key == input.KeyLeft: + if sel { + dot = text.SelectLeft(this.text, dot, word) + } else { + dot = text.MoveLeft(this.text, dot, word) + } + case key == input.KeyRight: + if sel { + dot = text.SelectRight(this.text, dot, word) + } else { + dot = text.MoveRight(this.text, dot, word) + } + case key == input.KeyBackspace: + this.text, dot = text.Backspace(this.text, dot, word) + changed = true + case key == input.KeyDelete: + this.text, dot = text.Delete(this.text, dot, word) + changed = true + case key == input.Key('a') && modifiers.Control: + dot.Start = 0 + dot.End = len(this.text) + case key.Printable(): + this.text, dot = text.Type(this.text, dot, rune(key)) + changed = true + } + + this.Select(dot) + if changed { this.SetText(string(this.text)) } +} diff --git a/label.go b/label.go index 52ed594..9a06817 100644 --- a/label.go +++ b/label.go @@ -4,13 +4,15 @@ import "git.tebibyte.media/tomo/tomo" import "git.tebibyte.media/tomo/tomo/theme" // Label is a simple text label. -type Label tomo.TextBox - -// NewLabel creates a new text label. -func NewLabel (text string) Label { - box := tomo.NewTextBox() - theme.Apply(box, theme.R("objects", "Label")) - box.SetText(text) - return box +type Label struct { + tomo.TextBox +} + +// NewLabel creates a new text label. +func NewLabel (text string) *Label { + this := &Label { TextBox: tomo.NewTextBox() } + theme.Apply(this, theme.R("objects", "Label")) + this.SetText(text) + return this }