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/sashakoshka/goutil/image/color" var _ tomo.Object = new(HSVAColorPicker) // HSVAColorPicker allows the user to pick a color by controlling its HSVA // parameters. // // Sub-components: // - ColorPickerMap is a rectangular control where the X axis controls // saturation and the Y axis controls value. type HSVAColorPicker struct { box tomo.ContainerBox value ucolor.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 { box: tomo.NewContainerBox(), } picker.box.SetRole(tomo.R("objects", "ColorPicker")) picker.box.SetAttr(tomo.ALayout(layouts.Row { true, false, false })) picker.pickerMap = newHsvaColorPickerMap(picker) picker.box.Add(picker.pickerMap) picker.hueSlider = NewVerticalSlider(0.0) picker.box.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.box.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 } // GetBox returns the underlying box. func (this *HSVAColorPicker) GetBox () tomo.Box { return this.box } // SetFocused sets whether or not this color picker has keyboard focus. If set // to true, this method will steal focus away from whichever object currently // has focus. func (this *HSVAColorPicker) SetFocused (focused bool) { this.box.SetFocused(focused) } // 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 = ucolor.HSVAModel.Convert(value).(ucolor.HSVA) this.hueSlider.SetValue(this.value.H) this.alphaSlider.SetValue(float64(this.value.A) / 0xFFFF) this.pickerMap.Invalidate() } // 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 := ucolor.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) }} }