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/tomo/canvas" import "git.tebibyte.media/tomo/objects/layouts" import "git.tebibyte.media/tomo/objects/internal" // HSVAColorPicker allows the user to pick a color by controlling its HSVA // parameters. type HSVAColorPicker struct { tomo.ContainerBox value internal.HSVA pickerMap *hsvaColorPickerMap hueSlider *Slider alphaSlider *Slider on struct { valueChange event.FuncBroadcaster } } // NewHSVAColorPicker creates a new color picker with the specified color. func NewHSVAColorPicker (value color.Color) *HSVAColorPicker { picker := &HSVAColorPicker { ContainerBox: tomo.NewContainerBox(), } picker.SetRole(tomo.R("objects", "ColorPicker")) picker.SetAttr(tomo.ALayout(layouts.Row { true, false, false })) picker.pickerMap = newHsvaColorPickerMap(picker) picker.Add(picker.pickerMap) picker.hueSlider = NewVerticalSlider(0.0) picker.Add(picker.hueSlider) picker.hueSlider.OnValueChange(func () { picker.value.H = picker.hueSlider.Value() picker.on.valueChange.Broadcast() picker.pickerMap.Invalidate() }) picker.alphaSlider = NewVerticalSlider(0.0) picker.Add(picker.alphaSlider) picker.alphaSlider.OnValueChange(func () { picker.value.A = uint16(picker.alphaSlider.Value() * 0xFFFF) picker.on.valueChange.Broadcast() picker.pickerMap.Invalidate() }) if value == nil { value = color.Transparent } picker.SetValue(value) return picker } // Value returns the color of the picker. func (this *HSVAColorPicker) Value () color.Color { return this.value } // SetValue sets the color of the picker. func (this *HSVAColorPicker) SetValue (value color.Color) { if value == nil { value = color.Transparent } this.value = internal.HSVAModel.Convert(value).(internal.HSVA) this.hueSlider.SetValue(this.value.H) this.alphaSlider.SetValue(float64(this.value.A) / 0xFFFF) } // OnValueChange specifies a function to be called when the user changes the // swatch's color. func (this *HSVAColorPicker) OnValueChange (callback func ()) event.Cookie { return this.on.valueChange.Connect(callback) } // RGBA satisfies the color.Color interface func (this *HSVAColorPicker) RGBA () (r, g, b, a uint32) { return this.value.RGBA() } type hsvaColorPickerMap struct { tomo.CanvasBox dragging bool parent *HSVAColorPicker } func newHsvaColorPickerMap (parent *HSVAColorPicker) *hsvaColorPickerMap { picker := &hsvaColorPickerMap { CanvasBox: tomo.NewCanvasBox(), parent: parent, } picker.SetDrawer(picker) picker.SetRole(tomo.R("objects", "ColorPickerMap")) picker.OnButtonUp(picker.handleButtonUp) picker.OnButtonDown(picker.handleButtonDown) picker.OnMouseMove(picker.handleMouseMove) return picker } func (this *hsvaColorPickerMap) handleButtonDown (button input.Button) bool { if button != input.ButtonLeft { return false } this.dragging = true this.drag() return true } func (this *hsvaColorPickerMap) handleButtonUp (button input.Button) bool { if button != input.ButtonLeft { return false } this.dragging = false return true } func (this *hsvaColorPickerMap) handleMouseMove () bool { if !this.dragging { return false } this.drag() return true } func (this *hsvaColorPickerMap) drag () { pointer := this.Window().MousePosition() bounds := this.InnerBounds() this.parent.value.S = float64(pointer.X - bounds.Min.X) / float64(bounds.Dx()) this.parent.value.V = 1 - float64(pointer.Y - bounds.Min.Y) / float64(bounds.Dy()) this.parent.value = this.parent.value.Canon() this.parent.on.valueChange.Broadcast() this.Invalidate() } func (this *hsvaColorPickerMap) Draw (can canvas.Canvas) { bounds := can.Bounds() for y := bounds.Min.Y; y < bounds.Max.Y; y ++ { for x := bounds.Min.X; x < bounds.Max.X; x ++ { xx := x - bounds.Min.X yy := y - bounds.Min.Y pixel := internal.HSVA { H: this.parent.value.H, S: float64(xx) / float64(bounds.Dx()), V: 1 - float64(yy) / float64(bounds.Dy()), A: 0xFFFF, } sPos := int( this.parent.value.S * float64(bounds.Dx())) vPos := int((1 - this.parent.value.V) * float64(bounds.Dy())) sDist := sPos - xx vDist := vPos - yy crosshair := (sDist == 0 || vDist == 0) && -8 < sDist && sDist < 8 && -8 < vDist && vDist < 8 if crosshair { pixel.S = 1 - pixel.S pixel.V = 1 - pixel.V } can.Set(x, y, pixel) }} }