Compare commits
11 Commits
2b354979aa
...
v0.23.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 2727972c30 | |||
| e48933385e | |||
| b9c4e3c003 | |||
| 92e4eb970d | |||
| c7887c5ea4 | |||
| 30d4e208b1 | |||
| a688e2dc24 | |||
| e1cef9bb37 | |||
| 6089dd3ff1 | |||
| d4e8847908 | |||
| 82cf822602 |
25
README.md
25
README.md
@@ -2,5 +2,26 @@
|
|||||||
|
|
||||||
[](https://pkg.go.dev/git.tebibyte.media/tomo/objects)
|
[](https://pkg.go.dev/git.tebibyte.media/tomo/objects)
|
||||||
|
|
||||||
Objects contains a standard collection of re-usable objects. All objects in this
|
Objects contains a standard collection of re-usable objects. It should be viewed
|
||||||
module visually conform to whatever the theme is set to.
|
also as a reference for how to create custom objects in Tomo.
|
||||||
|
|
||||||
|
## Styling
|
||||||
|
All objects in this module have roles of the form:
|
||||||
|
|
||||||
|
```
|
||||||
|
objects.TypeName
|
||||||
|
```
|
||||||
|
|
||||||
|
Where `TypeName` is the exact Go type name of the object in question. Objects
|
||||||
|
may also have different tags to indicate variations, states, etc. If applicable,
|
||||||
|
they are listed and described in the doc comment for the object's type. More
|
||||||
|
complex objects may have sub-components that are not accessible from the API.
|
||||||
|
These are listed alongside the tags.
|
||||||
|
|
||||||
|
## Setting Attributes
|
||||||
|
It is generally not recommended to set attributes on these objects. However, if
|
||||||
|
you must, they can be set by obtaining the object's underlying box through the
|
||||||
|
`GetBox` method. Be aware that the exact type of box that is returned here is
|
||||||
|
not part of the API, and may change unexpectedly even after v1.0. This caveat
|
||||||
|
also applies to boxes/sub-components making up the internal composition of the
|
||||||
|
objects.
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ var iconButtonLayout = layouts.Row { true }
|
|||||||
var bothButtonLayout = layouts.Row { false, true }
|
var bothButtonLayout = layouts.Row { false, true }
|
||||||
|
|
||||||
// Button is a clickable button.
|
// Button is a clickable button.
|
||||||
|
//
|
||||||
|
// Tags:
|
||||||
|
// - [icon] The button has an icon.
|
||||||
type Button struct {
|
type Button struct {
|
||||||
box tomo.ContainerBox
|
box tomo.ContainerBox
|
||||||
|
|
||||||
|
|||||||
10
calendar.go
10
calendar.go
@@ -10,6 +10,16 @@ var _ tomo.Object = new(Calendar)
|
|||||||
|
|
||||||
// Calendar is an object that can display a date and allow the user to change
|
// Calendar is an object that can display a date and allow the user to change
|
||||||
// it. It can display one month at a time.
|
// it. It can display one month at a time.
|
||||||
|
//
|
||||||
|
// Sub-components:
|
||||||
|
// - CalendarGrid organizes the days into a grid.
|
||||||
|
// - CalendarWeekdayHeader appears at the top of each grid column, and shows
|
||||||
|
// the day of the week that column represents.
|
||||||
|
// - CalendarDay appears within the grid for each day of the current month.
|
||||||
|
//
|
||||||
|
// CalendarDay tags:
|
||||||
|
// - [weekend] The day is a weekend.
|
||||||
|
// - [weekday] The day is a weekday.
|
||||||
type Calendar struct {
|
type Calendar struct {
|
||||||
box tomo.ContainerBox
|
box tomo.ContainerBox
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,10 @@ import "git.tebibyte.media/tomo/tomo/event"
|
|||||||
var _ tomo.Object = new(Checkbox)
|
var _ tomo.Object = new(Checkbox)
|
||||||
|
|
||||||
// Checkbox is a control that can be toggled.
|
// Checkbox is a control that can be toggled.
|
||||||
|
//
|
||||||
|
// Tags:
|
||||||
|
// - [checked] The checkbox's value is true.
|
||||||
|
// - [unchecked] The checkbox's value is false.
|
||||||
type Checkbox struct {
|
type Checkbox struct {
|
||||||
box tomo.Box
|
box tomo.Box
|
||||||
value bool
|
value bool
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ var _ tomo.Object = new(HSVAColorPicker)
|
|||||||
|
|
||||||
// HSVAColorPicker allows the user to pick a color by controlling its HSVA
|
// HSVAColorPicker allows the user to pick a color by controlling its HSVA
|
||||||
// parameters.
|
// parameters.
|
||||||
|
//
|
||||||
|
// Sub-components:
|
||||||
|
// - ColorPickerMap is a recangular control where the X axis controls
|
||||||
|
// saturation and the Y axis controls value.
|
||||||
type HSVAColorPicker struct {
|
type HSVAColorPicker struct {
|
||||||
box tomo.ContainerBox
|
box tomo.ContainerBox
|
||||||
value internal.HSVA
|
value internal.HSVA
|
||||||
|
|||||||
11
container.go
11
container.go
@@ -10,9 +10,14 @@ var _ tomo.ContentObject = new(Container)
|
|||||||
// primitive for building more complex layouts. It has two main variants: an
|
// primitive for building more complex layouts. It has two main variants: an
|
||||||
// outer container, and an inner container. The outer container has padding
|
// outer container, and an inner container. The outer container has padding
|
||||||
// around its edges, whereas the inner container does not. It also has a
|
// around its edges, whereas the inner container does not. It also has a
|
||||||
// "sunken" variation designed to hold a scrolled list of items. The container
|
// "sunken" variation designed to hold a scrolled list of items.
|
||||||
// will have a corresponding object role variation of either "outer", "inner",
|
//
|
||||||
// or "sunken".
|
// Tags:
|
||||||
|
// - [outer] The container is the root of a window.
|
||||||
|
// - [inner] The container is within another container, and is part of a
|
||||||
|
// larger layout.
|
||||||
|
// - [sunken] The container holds a visually grouped, usually scrolled, list
|
||||||
|
// of items.
|
||||||
type Container struct {
|
type Container struct {
|
||||||
box tomo.ContainerBox
|
box tomo.ContainerBox
|
||||||
}
|
}
|
||||||
|
|||||||
129
file.go
Normal file
129
file.go
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
import "unicode"
|
||||||
|
import "git.tebibyte.media/tomo/tomo"
|
||||||
|
import "git.tebibyte.media/tomo/tomo/data"
|
||||||
|
import "git.tebibyte.media/tomo/tomo/input"
|
||||||
|
import "git.tebibyte.media/tomo/tomo/event"
|
||||||
|
import "git.tebibyte.media/tomo/objects/layouts"
|
||||||
|
|
||||||
|
var _ tomo.Object = new(File)
|
||||||
|
|
||||||
|
// File is a representation of a file or directory.
|
||||||
|
type File struct {
|
||||||
|
box tomo.ContainerBox
|
||||||
|
|
||||||
|
label *Label
|
||||||
|
icon *MimeIcon
|
||||||
|
mime data.Mime
|
||||||
|
|
||||||
|
lastClick time.Time
|
||||||
|
|
||||||
|
on struct {
|
||||||
|
doubleClick event.FuncBroadcaster
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFile creates a new file icon with the given name and MIME type.
|
||||||
|
func NewFile (name string, mime data.Mime) *File {
|
||||||
|
file := &File {
|
||||||
|
box: tomo.NewContainerBox(),
|
||||||
|
label: NewLabel(""),
|
||||||
|
icon: NewMimeIcon(mime, tomo.IconSizeLarge),
|
||||||
|
}
|
||||||
|
file.box.SetRole(tomo.R("objects", "File"))
|
||||||
|
file.box.SetAttr(tomo.ALayout(layouts.ContractVertical))
|
||||||
|
|
||||||
|
file.box.Add(file.icon)
|
||||||
|
file.box.Add(file.label)
|
||||||
|
file.box.SetAttr(tomo.AAlign(tomo.AlignMiddle, tomo.AlignStart))
|
||||||
|
file.label.SetAlign(tomo.AlignMiddle, tomo.AlignStart)
|
||||||
|
// file.label.SetOverflow(false, true)
|
||||||
|
// file.label.SetWrap(true)
|
||||||
|
file.SetType(mime)
|
||||||
|
file.SetName(name)
|
||||||
|
|
||||||
|
file.box.SetInputMask(true)
|
||||||
|
file.box.SetFocusable(true)
|
||||||
|
file.box.OnButtonDown(file.handleButtonDown)
|
||||||
|
file.box.OnButtonUp(file.handleButtonUp)
|
||||||
|
file.box.OnKeyDown(file.handleKeyDown)
|
||||||
|
file.box.OnKeyUp(file.handleKeyUp)
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBox returns the underlying box.
|
||||||
|
func (this *File) GetBox () tomo.Box {
|
||||||
|
return this.box
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetFocused sets whether or not this file has keyboard focus. If set to
|
||||||
|
// true, this method will steal focus away from whichever object currently has
|
||||||
|
// focus.
|
||||||
|
func (this *File) SetFocused (focused bool) {
|
||||||
|
this.box.SetFocused(focused)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetName sets the text of the file's label.
|
||||||
|
func (this *File) SetName (text string) {
|
||||||
|
this.label.SetText(truncateText(text, 16))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetType sets the MIME type of the file.
|
||||||
|
func (this *File) SetType (mime data.Mime) {
|
||||||
|
this.mime = mime
|
||||||
|
this.icon.SetIcon(mime, tomo.IconSizeLarge)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnDoubleClick specifies a function to be called when the file is
|
||||||
|
// double-clicked.
|
||||||
|
func (this *File) OnDoubleClick (callback func ()) event.Cookie {
|
||||||
|
return this.on.doubleClick.Connect(callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *File) handleKeyDown (key input.Key, numberPad bool) bool {
|
||||||
|
if key != input.KeyEnter && key != input.Key(' ') { return false }
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *File) handleKeyUp (key input.Key, numberPad bool) bool {
|
||||||
|
if key != input.KeyEnter && key != input.Key(' ') { return false }
|
||||||
|
this.on.doubleClick.Broadcast()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *File) handleButtonDown (button input.Button) bool {
|
||||||
|
if button != input.ButtonLeft { return false }
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *File) handleButtonUp (button input.Button) bool {
|
||||||
|
if button != input.ButtonLeft { return false }
|
||||||
|
if this.box.Window().MousePosition().In(this.box.Bounds()) {
|
||||||
|
// TODO double click delay should be configurable
|
||||||
|
if time.Since(this.lastClick) < time.Second {
|
||||||
|
this.on.doubleClick.Broadcast()
|
||||||
|
}
|
||||||
|
this.lastClick = time.Now()
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func truncateText (text string, max int) string {
|
||||||
|
lastChar := -1
|
||||||
|
len := 0
|
||||||
|
for index, char := range text {
|
||||||
|
if !unicode.IsSpace(char) {
|
||||||
|
lastChar = index
|
||||||
|
}
|
||||||
|
len ++
|
||||||
|
if len >= max {
|
||||||
|
if lastChar != -1 {
|
||||||
|
return text[:lastChar] + "..."
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return text
|
||||||
|
}
|
||||||
@@ -9,7 +9,12 @@ var _ tomo.Object = new(Heading)
|
|||||||
|
|
||||||
// Heading is a label that denotes the start of some section of content. It can
|
// Heading is a label that denotes the start of some section of content. It can
|
||||||
// have a level from 0 to 2, with 0 being the most prominent and 2 being the
|
// have a level from 0 to 2, with 0 being the most prominent and 2 being the
|
||||||
// most subtle. The level is described in the role variation.
|
// most subtle.
|
||||||
|
//
|
||||||
|
// Tags:
|
||||||
|
// - [0] The heading has a level of 0 (most prominent).
|
||||||
|
// - [1] The heading has a level of 1.
|
||||||
|
// - [2] The heading has a level of 2 (least prominent).
|
||||||
type Heading struct {
|
type Heading struct {
|
||||||
box tomo.TextBox
|
box tomo.TextBox
|
||||||
}
|
}
|
||||||
|
|||||||
5
icon.go
5
icon.go
@@ -6,6 +6,11 @@ import "git.tebibyte.media/tomo/tomo/canvas"
|
|||||||
var _ tomo.Object = new(Icon)
|
var _ tomo.Object = new(Icon)
|
||||||
|
|
||||||
// Icon displays a single icon.
|
// Icon displays a single icon.
|
||||||
|
//
|
||||||
|
// Tags:
|
||||||
|
// - [large] The icon is large sized.
|
||||||
|
// - [medium] The icon is medium sized.
|
||||||
|
// - [small] The icon is small sized.
|
||||||
type Icon struct {
|
type Icon struct {
|
||||||
box tomo.Box
|
box tomo.Box
|
||||||
icon tomo.Icon
|
icon tomo.Icon
|
||||||
|
|||||||
12
label.go
12
label.go
@@ -33,7 +33,7 @@ func (this *Label) SetFocused (focused bool) {
|
|||||||
this.box.SetFocused(focused)
|
this.box.SetFocused(focused)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetText sets the text content of the heading.
|
// SetText sets the text content of the label.
|
||||||
func (this *Label) SetText (text string) {
|
func (this *Label) SetText (text string) {
|
||||||
this.box.SetText(text)
|
this.box.SetText(text)
|
||||||
}
|
}
|
||||||
@@ -48,13 +48,13 @@ func (this *Label) Dot () text.Dot {
|
|||||||
return this.box.Dot()
|
return this.box.Dot()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetAlign sets the X and Y alignment of the label.
|
|
||||||
func (this *Label) SetAlign (x, y tomo.Align) {
|
|
||||||
this.box.SetAttr(tomo.AAlign(x, y))
|
|
||||||
}
|
|
||||||
|
|
||||||
// OnDotChange specifies a function to be called when the text cursor or
|
// OnDotChange specifies a function to be called when the text cursor or
|
||||||
// selection changes.
|
// selection changes.
|
||||||
func (this *Label) OnDotChange (callback func ()) event.Cookie {
|
func (this *Label) OnDotChange (callback func ()) event.Cookie {
|
||||||
return this.box.OnDotChange(callback)
|
return this.box.OnDotChange(callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetAlign sets the X and Y alignment of the label.
|
||||||
|
func (this *Label) SetAlign (x, y tomo.Align) {
|
||||||
|
this.box.SetAttr(tomo.AAlign(x, y))
|
||||||
|
}
|
||||||
|
|||||||
4
menu.go
4
menu.go
@@ -7,6 +7,10 @@ import "git.tebibyte.media/tomo/tomo/input"
|
|||||||
import "git.tebibyte.media/tomo/objects/layouts"
|
import "git.tebibyte.media/tomo/objects/layouts"
|
||||||
|
|
||||||
// Menu is a menu window.
|
// Menu is a menu window.
|
||||||
|
//
|
||||||
|
// Sub-components:
|
||||||
|
// - TearLine is a horizontal line at the top of the menu that, when clicked,
|
||||||
|
// causes the menu to be "torn off" into a movable window.
|
||||||
type Menu struct {
|
type Menu struct {
|
||||||
tomo.Window
|
tomo.Window
|
||||||
|
|
||||||
|
|||||||
@@ -75,13 +75,13 @@ func (this *NumberInput) Dot () text.Dot {
|
|||||||
|
|
||||||
// Value returns the value of the input.
|
// Value returns the value of the input.
|
||||||
func (this *NumberInput) Value () float64 {
|
func (this *NumberInput) Value () float64 {
|
||||||
value, _ := strconv.ParseFloat(this.input.Text(), 64)
|
value, _ := strconv.ParseFloat(this.input.Value(), 64)
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetValue sets the value of the input.
|
// SetValue sets the value of the input.
|
||||||
func (this *NumberInput) SetValue (value float64) {
|
func (this *NumberInput) SetValue (value float64) {
|
||||||
this.input.SetText(strconv.FormatFloat(value, 'g', -1, 64))
|
this.input.SetValue(strconv.FormatFloat(value, 'g', -1, 64))
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnValueChange specifies a function to be called when the user edits the input
|
// OnValueChange specifies a function to be called when the user edits the input
|
||||||
|
|||||||
11
scrollbar.go
11
scrollbar.go
@@ -9,6 +9,17 @@ var _ tomo.Object = new(Scrollbar)
|
|||||||
|
|
||||||
// Scrollbar is a special type of slider that can control the scroll of any
|
// Scrollbar is a special type of slider that can control the scroll of any
|
||||||
// overflowing ContainerObject.
|
// overflowing ContainerObject.
|
||||||
|
//
|
||||||
|
// Sub-components:
|
||||||
|
// - ScrollbarHandle is the grabbable handle of the scrollbar.
|
||||||
|
//
|
||||||
|
// Tags:
|
||||||
|
// - [vertical] The scrollbar is oriented vertically.
|
||||||
|
// - [horizontall] The scrollbar is oriented horizontally.
|
||||||
|
//
|
||||||
|
// ScrollbarHandle tags:
|
||||||
|
// - [vertical] The handle is oriented vertically.
|
||||||
|
// - [horizontall] The handle is oriented horizontally.
|
||||||
type Scrollbar struct {
|
type Scrollbar struct {
|
||||||
box tomo.ContainerBox
|
box tomo.ContainerBox
|
||||||
handle *sliderHandle
|
handle *sliderHandle
|
||||||
|
|||||||
11
slider.go
11
slider.go
@@ -9,6 +9,17 @@ import "git.tebibyte.media/tomo/tomo/event"
|
|||||||
var _ tomo.Object = new(Slider)
|
var _ tomo.Object = new(Slider)
|
||||||
|
|
||||||
// Slider is a control that selects a numeric value between 0 and 1.
|
// Slider is a control that selects a numeric value between 0 and 1.
|
||||||
|
//
|
||||||
|
// Sub-components:
|
||||||
|
// - SliderHandle is the grabbable handle of the slider.
|
||||||
|
//
|
||||||
|
// Tags:
|
||||||
|
// - [vertical] The slider is oriented vertically.
|
||||||
|
// - [horizontall] The slider is oriented horizontally.
|
||||||
|
//
|
||||||
|
// SliderHandle tags:
|
||||||
|
// - [vertical] The handle is oriented vertically.
|
||||||
|
// - [horizontall] The handle is oriented horizontally.
|
||||||
type Slider struct {
|
type Slider struct {
|
||||||
box tomo.ContainerBox
|
box tomo.ContainerBox
|
||||||
handle *sliderHandle
|
handle *sliderHandle
|
||||||
|
|||||||
@@ -8,6 +8,19 @@ var _ tomo.Object = new(TabbedContainer)
|
|||||||
|
|
||||||
// TabbedContainer holds multiple objects, each in their own tab. The user can
|
// TabbedContainer holds multiple objects, each in their own tab. The user can
|
||||||
// click the tab bar at the top to choose which one is activated.
|
// click the tab bar at the top to choose which one is activated.
|
||||||
|
//
|
||||||
|
// Sub-components:
|
||||||
|
// - TabRow sits at the top of the container and contains a row of tabs.
|
||||||
|
// - TabSpacer sits at either end of the tab row, bookending the list of tabs.
|
||||||
|
// - Tab appears in the tab row for each tab in the container. The user can
|
||||||
|
// click on the tab to switch to it.
|
||||||
|
//
|
||||||
|
// TabSpacer tags:
|
||||||
|
// - [left] The spacer is on the left.
|
||||||
|
// - [right] The spacer is on the right.
|
||||||
|
//
|
||||||
|
// Tab tags:
|
||||||
|
// - [active] The tab is currently active and its contents are visible.
|
||||||
type TabbedContainer struct {
|
type TabbedContainer struct {
|
||||||
box tomo.ContainerBox
|
box tomo.ContainerBox
|
||||||
|
|
||||||
@@ -35,7 +48,7 @@ func NewTabbedContainer () *TabbedContainer {
|
|||||||
tabbedContainer.leftSpacer.SetTag("left", true)
|
tabbedContainer.leftSpacer.SetTag("left", true)
|
||||||
tabbedContainer.rightSpacer = tomo.NewBox()
|
tabbedContainer.rightSpacer = tomo.NewBox()
|
||||||
tabbedContainer.rightSpacer.SetRole(tomo.R("objects", "TabSpacer"))
|
tabbedContainer.rightSpacer.SetRole(tomo.R("objects", "TabSpacer"))
|
||||||
tabbedContainer.rightSpacer.SetTag("left", true)
|
tabbedContainer.rightSpacer.SetTag("right", true)
|
||||||
|
|
||||||
tabbedContainer.ClearTabs()
|
tabbedContainer.ClearTabs()
|
||||||
tabbedContainer.setTabRowLayout()
|
tabbedContainer.setTabRowLayout()
|
||||||
|
|||||||
63
textinput.go
63
textinput.go
@@ -12,18 +12,28 @@ var _ tomo.ContentObject = new(TextInput)
|
|||||||
type TextInput struct {
|
type TextInput struct {
|
||||||
box tomo.TextBox
|
box tomo.TextBox
|
||||||
text []rune
|
text []rune
|
||||||
|
multiline bool
|
||||||
on struct {
|
on struct {
|
||||||
valueChange event.FuncBroadcaster
|
valueChange event.FuncBroadcaster
|
||||||
confirm event.FuncBroadcaster
|
confirm event.FuncBroadcaster
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTextInput creates a new text input containing the specified text.
|
func newTextInput (text string, multiline bool) *TextInput {
|
||||||
func NewTextInput (text string) *TextInput {
|
textInput := &TextInput {
|
||||||
textInput := &TextInput { box: tomo.NewTextBox() }
|
box: tomo.NewTextBox(),
|
||||||
|
multiline: multiline,
|
||||||
|
}
|
||||||
textInput.box.SetRole(tomo.R("objects", "TextInput"))
|
textInput.box.SetRole(tomo.R("objects", "TextInput"))
|
||||||
textInput.box.SetAttr(tomo.AAlign(tomo.AlignStart, tomo.AlignMiddle))
|
textInput.box.SetTag("multiline", multiline)
|
||||||
|
if multiline {
|
||||||
|
textInput.box.SetAttr(tomo.AOverflow(false, true))
|
||||||
|
textInput.box.SetAttr(tomo.AWrap(true))
|
||||||
|
textInput.box.SetAttr(tomo.AAlign(tomo.AlignStart, tomo.AlignStart))
|
||||||
|
} else {
|
||||||
textInput.box.SetAttr(tomo.AOverflow(true, false))
|
textInput.box.SetAttr(tomo.AOverflow(true, false))
|
||||||
|
textInput.box.SetAttr(tomo.AAlign(tomo.AlignStart, tomo.AlignMiddle))
|
||||||
|
}
|
||||||
textInput.SetValue(text)
|
textInput.SetValue(text)
|
||||||
textInput.box.SetFocusable(true)
|
textInput.box.SetFocusable(true)
|
||||||
textInput.box.SetSelectable(true)
|
textInput.box.SetSelectable(true)
|
||||||
@@ -33,6 +43,17 @@ func NewTextInput (text string) *TextInput {
|
|||||||
return textInput
|
return textInput
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewTextInput creates a new text input containing the specified text.
|
||||||
|
func NewTextInput (text string) *TextInput {
|
||||||
|
return newTextInput(text, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMultilineTextInput creates a new multiline text input containing the
|
||||||
|
// specified text.
|
||||||
|
func NewMultilineTextInput (text string) *TextInput {
|
||||||
|
return newTextInput(text, true)
|
||||||
|
}
|
||||||
|
|
||||||
// GetBox returns the underlying box.
|
// GetBox returns the underlying box.
|
||||||
func (this *TextInput) GetBox () tomo.Box {
|
func (this *TextInput) GetBox () tomo.Box {
|
||||||
return this.box
|
return this.box
|
||||||
@@ -55,7 +76,13 @@ func (this *TextInput) Dot () text.Dot {
|
|||||||
return this.box.Dot()
|
return this.box.Dot()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetAlign sets the X and Y alignment of the label.
|
// OnDotChange specifies a function to be called when the text cursor or
|
||||||
|
// selection changes.
|
||||||
|
func (this *TextInput) OnDotChange (callback func ()) event.Cookie {
|
||||||
|
return this.box.OnDotChange(callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAlign sets the X and Y alignment of the text input.
|
||||||
func (this *TextInput) SetAlign (x, y tomo.Align) {
|
func (this *TextInput) SetAlign (x, y tomo.Align) {
|
||||||
this.box.SetAttr(tomo.AAlign(x, y))
|
this.box.SetAttr(tomo.AAlign(x, y))
|
||||||
}
|
}
|
||||||
@@ -109,6 +136,8 @@ func (this *TextInput) Type (char rune) {
|
|||||||
this.box.SetText(string(this.text))
|
this.box.SetText(string(this.text))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: add up/down controls if this is a multiline input
|
||||||
|
|
||||||
func (this *TextInput) handleKeyDown (key input.Key, numpad bool) bool {
|
func (this *TextInput) handleKeyDown (key input.Key, numpad bool) bool {
|
||||||
dot := this.Dot()
|
dot := this.Dot()
|
||||||
modifiers := this.box.Window().Modifiers()
|
modifiers := this.box.Window().Modifiers()
|
||||||
@@ -123,6 +152,19 @@ func (this *TextInput) handleKeyDown (key input.Key, numpad bool) bool {
|
|||||||
}
|
}
|
||||||
} ()
|
} ()
|
||||||
|
|
||||||
|
typ := func () {
|
||||||
|
this.text, dot = text.Type(this.text, dot, rune(key))
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if this.multiline && !modifiers.Control {
|
||||||
|
switch {
|
||||||
|
case key == '\n', key == '\t':
|
||||||
|
typ()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case isConfirmationKey(key):
|
case isConfirmationKey(key):
|
||||||
this.on.confirm.Broadcast()
|
this.on.confirm.Broadcast()
|
||||||
@@ -136,8 +178,7 @@ func (this *TextInput) handleKeyDown (key input.Key, numpad bool) bool {
|
|||||||
changed = true
|
changed = true
|
||||||
return true
|
return true
|
||||||
case key.Printable() && !modifiers.Control:
|
case key.Printable() && !modifiers.Control:
|
||||||
this.text, dot = text.Type(this.text, dot, rune(key))
|
typ()
|
||||||
changed = true
|
|
||||||
return true
|
return true
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
@@ -146,6 +187,14 @@ func (this *TextInput) handleKeyDown (key input.Key, numpad bool) bool {
|
|||||||
|
|
||||||
func (this *TextInput) handleKeyUp (key input.Key, numpad bool) bool {
|
func (this *TextInput) handleKeyUp (key input.Key, numpad bool) bool {
|
||||||
modifiers := this.box.Window().Modifiers()
|
modifiers := this.box.Window().Modifiers()
|
||||||
|
|
||||||
|
if this.multiline && !modifiers.Control {
|
||||||
|
switch {
|
||||||
|
case key == '\n', key == '\t':
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case isConfirmationKey(key):
|
case isConfirmationKey(key):
|
||||||
return true
|
return true
|
||||||
|
|||||||
80
textview.go
80
textview.go
@@ -2,23 +2,83 @@ package objects
|
|||||||
|
|
||||||
import "image"
|
import "image"
|
||||||
import "git.tebibyte.media/tomo/tomo"
|
import "git.tebibyte.media/tomo/tomo"
|
||||||
|
import "git.tebibyte.media/tomo/tomo/text"
|
||||||
|
import "git.tebibyte.media/tomo/tomo/event"
|
||||||
|
|
||||||
|
var _ tomo.Object = new(TabbedContainer)
|
||||||
|
|
||||||
// TextView is an area for displaying a large amount of multi-line text.
|
// TextView is an area for displaying a large amount of multi-line text.
|
||||||
type TextView struct {
|
type TextView struct {
|
||||||
tomo.TextBox
|
box tomo.TextBox
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTextView creates a new text view.
|
// NewTextView creates a new text view.
|
||||||
func NewTextView (text string) *TextView {
|
func NewTextView (text string) *TextView {
|
||||||
this := &TextView { TextBox: tomo.NewTextBox() }
|
textView := &TextView { box: tomo.NewTextBox() }
|
||||||
this.SetRole(tomo.R("objects", "TextView"))
|
textView.box.SetRole(tomo.R("objects", "TextView"))
|
||||||
this.SetFocusable(true)
|
textView.box.SetFocusable(true)
|
||||||
this.SetSelectable(true)
|
textView.box.SetSelectable(true)
|
||||||
this.SetText(text)
|
textView.SetText(text)
|
||||||
this.SetAttr(tomo.AOverflow(false, true))
|
textView.box.SetAttr(tomo.AOverflow(false, true))
|
||||||
this.SetAttr(tomo.AWrap(true))
|
textView.box.SetAttr(tomo.AWrap(true))
|
||||||
this.OnScroll(this.handleScroll)
|
textView.box.OnScroll(textView.handleScroll)
|
||||||
return this
|
return textView
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBox returns the underlying box.
|
||||||
|
func (this *TextView) GetBox () tomo.Box {
|
||||||
|
return this.box
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetFocused sets whether or not this text view has keyboard focus. If set to
|
||||||
|
// true, this method will steal focus away from whichever object currently has
|
||||||
|
// focus.
|
||||||
|
func (this *TextView) SetFocused (focused bool) {
|
||||||
|
this.box.SetFocused(focused)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select sets the text cursor or selection.
|
||||||
|
func (this *TextView) Select (dot text.Dot) {
|
||||||
|
this.box.Select(dot)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dot returns the text cursor or selection.
|
||||||
|
func (this *TextView) Dot () text.Dot {
|
||||||
|
return this.box.Dot()
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnDotChange specifies a function to be called when the text cursor or
|
||||||
|
// selection changes.
|
||||||
|
func (this *TextView) OnDotChange (callback func ()) event.Cookie {
|
||||||
|
return this.box.OnDotChange(callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAlign sets the X and Y alignment of the text view.
|
||||||
|
func (this *TextView) SetAlign (x, y tomo.Align) {
|
||||||
|
this.box.SetAttr(tomo.AAlign(x, y))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContentBounds returns the bounds of the inner content of the text view
|
||||||
|
// relative to the text view's InnerBounds.
|
||||||
|
func (this *TextView) ContentBounds () image.Rectangle {
|
||||||
|
return this.box.ContentBounds()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScrollTo shifts the origin of the text view's content to the origin of the
|
||||||
|
// text view's InnerBounds, offset by the given point.
|
||||||
|
func (this *TextView) ScrollTo (position image.Point) {
|
||||||
|
this.box.ScrollTo(position)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnContentBoundsChange specifies a function to be called when the text view's
|
||||||
|
// ContentBounds or InnerBounds changes.
|
||||||
|
func (this *TextView) OnContentBoundsChange (callback func ()) event.Cookie {
|
||||||
|
return this.box.OnContentBoundsChange(callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetText sets the text content of the view.
|
||||||
|
func (this *TextView) SetText (text string) {
|
||||||
|
this.box.SetText(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *TextView) handleScroll (x, y float64) bool {
|
func (this *TextView) handleScroll (x, y float64) bool {
|
||||||
|
|||||||
Reference in New Issue
Block a user