Add swatch

This commit is contained in:
Sasha Koshka 2024-06-22 15:44:37 -04:00
parent 0b7e5392f4
commit ae1e62c1f2
2 changed files with 205 additions and 0 deletions

62
labelswatch.go Normal file
View File

@ -0,0 +1,62 @@
package objects
import "image/color"
import "git.tebibyte.media/tomo/tomo"
import "git.tebibyte.media/tomo/tomo/input"
import "git.tebibyte.media/tomo/tomo/event"
import "git.tebibyte.media/tomo/objects/layouts"
// LabelSwatch is a swatch with a label.
type LabelSwatch struct {
tomo.ContainerBox
swatch *Swatch
label *Label
}
// NewLabelSwatch creates a new labeled swatch with the specified color and
// label text.
func NewLabelSwatch (value color.Color, text string) *LabelSwatch {
box := &LabelSwatch {
ContainerBox: tomo.NewContainerBox(),
swatch: NewSwatch(value),
label: NewLabel(text),
}
box.SetRole(tomo.R("objects", "LabelSwatch", ""))
box.label.SetAlign(tomo.AlignStart, tomo.AlignMiddle)
box.Add(box.swatch)
box.Add(box.label)
box.SetLayout(layouts.NewGrid([]bool { false, true }, []bool { false }))
box.OnMouseUp(box.handleMouseUp)
box.label.OnMouseUp(box.handleMouseUp)
return box
}
// SetValue sets the color of the swatch.
func (this *LabelSwatch) SetValue (value color.Color) {
this.swatch.SetValue(value)
}
// Value returns the color of the swatch.
func (this *LabelSwatch) Value () color.Color {
return this.swatch.Value()
}
// RGBA satisfies the color.Color interface
func (this *LabelSwatch) RGBA () (r, g, b, a uint32) {
return this.swatch.RGBA()
}
// OnValueChange specifies a function to be called when the swatch's color
// changes.
func (this *LabelSwatch) OnValueChange (callback func ()) event.Cookie {
return this.swatch.OnValueChange(callback)
}
func (this *LabelSwatch) handleMouseUp (button input.Button) {
if button != input.ButtonLeft { return }
if this.MousePosition().In(this.Bounds()) {
this.swatch.SetFocused(true)
this.swatch.Choose()
}
}

143
swatch.go Normal file
View File

@ -0,0 +1,143 @@
package objects
import "log"
import "image"
import "image/color"
import "git.tebibyte.media/tomo/tomo"
import "git.tebibyte.media/tomo/tomo/input"
import "git.tebibyte.media/tomo/tomo/event"
import "git.tebibyte.media/tomo/tomo/canvas"
import "git.tebibyte.media/tomo/objects/layouts"
// Swatch displays a color, allowing the user to edit it by clicking on it.
type Swatch struct {
tomo.CanvasBox
value color.Color
editing bool
on struct {
valueChange event.FuncBroadcaster
}
}
// NewSwatch creates a new swatch with the given color.
func NewSwatch (value color.Color) *Swatch {
swatch := &Swatch {
CanvasBox: tomo.NewCanvasBox(),
}
swatch.SetRole(tomo.R("objects", "Swatch", ""))
swatch.SetDrawer(swatch)
swatch.SetValue(value)
swatch.OnMouseUp(swatch.handleMouseUp)
swatch.OnKeyUp(swatch.handleKeyUp)
swatch.SetFocusable(true)
return swatch
}
// SetValue sets the color of the swatch.
func (this *Swatch) SetValue (value color.Color) {
this.value = value
if value == nil { value = color.Transparent }
this.Invalidate()
}
// Value returns the color of the swatch.
func (this *Swatch) Value () color.Color {
return this.value
}
// RGBA satisfies the color.Color interface
func (this *Swatch) RGBA () (r, g, b, a uint32) {
return this.value.RGBA()
}
// OnValueChange specifies a function to be called when the swatch's color
// changes.
func (this *Swatch) OnValueChange (callback func ()) event.Cookie {
return this.on.valueChange.Connect(callback)
}
// Choose creates a modal that allows the user to edit the color of the swatch.
func (this *Swatch) Choose () {
if this.editing { return }
var err error
var window tomo.Window
if parent := this.Window(); parent != nil {
window, err = parent.NewModal(image.Rectangle { })
} else {
window, err = tomo.NewWindow(image.Rectangle { })
}
if err != nil {
log.Println("objects: could not create swatch modal:", err)
return
}
window.SetTitle("Select Color")
colorPicker := NewColorPicker(this.Value())
colorPicker.OnValueChange(func () {
this.userSetValue(colorPicker.Value())
})
colorMemory := this.value
cancelButton := NewButton("Cancel")
cancelButton.SetIcon(tomo.IconDialogCancel)
cancelButton.OnClick(func () {
this.userSetValue(colorMemory)
window.Close()
})
okButton := NewButton("OK")
okButton.SetFocused(true)
okButton.SetIcon(tomo.IconDialogOkay)
okButton.OnClick(func () {
window.Close()
})
controlRow := NewInnerContainer (
layouts.ContractHorizontal,
cancelButton,
okButton)
controlRow.SetAlign(tomo.AlignEnd, tomo.AlignMiddle)
window.SetRoot(NewOuterContainer (
layouts.Column { true, false },
colorPicker,
controlRow))
window.OnClose(func () {
this.editing = false
})
this.editing = true
window.SetVisible(true)
}
func (this *Swatch) Draw (can canvas.Canvas) {
pen := can.Pen()
// transparency slash
pen.Stroke(color.RGBA { R: 255, A: 255 })
pen.StrokeWeight(1)
pen.Path(this.Bounds().Min, this.Bounds().Max)
// color
if this.value != nil {
pen.StrokeWeight(0)
pen.Fill(this.value)
pen.Rectangle(this.Bounds())
}
}
func (this *Swatch) userSetValue (value color.Color) {
this.SetValue(value)
this.on.valueChange.Broadcast()
}
func (this *Swatch) handleKeyUp (key input.Key, numberPad bool) {
if key != input.KeyEnter && key != input.Key(' ') { return }
this.Choose()
}
func (this *Swatch) handleMouseUp (button input.Button) {
if button != input.ButtonLeft { return }
if this.MousePosition().In(this.Bounds()) {
this.Choose()
}
}