package gel import ( "image" "image/color" l "gioui.org/layout" "gioui.org/op" "gioui.org/op/clip" "gioui.org/op/paint" "gioui.org/unit" "git.mleku.dev/mleku/prevara/f32color" ) type Switch struct { *Window color struct { enabled color.NRGBA disabled color.NRGBA } swtch *Bool } // Switch creates a boolean switch widget (basically a checkbox but looks like a switch) func (w *Window) Switch(swtch *Bool) *Switch { sw := &Switch{ Window: w, swtch: swtch, } sw.color.enabled = w.Colors.GetNRGBAFromName("Primary") sw.color.disabled = w.Colors.GetNRGBAFromName("scrim") return sw } // EnabledColor sets the color to draw for the enabled state func (s *Switch) EnabledColor(color string) *Switch { s.color.enabled = s.Theme.Colors.GetNRGBAFromName(color) return s } // DisabledColor sets the color to draw for the disabled state func (s *Switch) DisabledColor(color string) *Switch { s.color.disabled = s.Theme.Colors.GetNRGBAFromName(color) return s } func (s *Switch) SetHook(fn func(b bool)) *Switch { s.swtch.SetOnChange(fn) return s } // Fn updates the switch and displays it. func (s *Switch) Fn(gtx l.Context) l.Dimensions { return s.Inset(0.25, func(gtx l.Context) l.Dimensions { trackWidth := gtx.Dp(unit.Dp(float32(s.Theme.TextSize) * 2.5)) trackHeight := gtx.Dp(unit.Dp(16)) thumbSize := gtx.Dp(unit.Dp(20)) trackOff := (thumbSize - trackHeight) / 2 // Draw track. trackCorner := trackHeight / 2 trackRect := image.Rectangle{Max: image.Pt(trackWidth, trackHeight)} col := s.color.disabled if s.swtch.value { col = s.color.enabled } if !gtx.Source.Enabled() { col = f32color.MulAlpha(col, 150) } trackColor := f32color.MulAlpha(col, 200) stack := op.Offset(image.Pt(0, trackOff)).Push(gtx.Ops) clip.UniformRRect(trackRect, trackCorner).Push(gtx.Ops).Pop() paint.ColorOp{Color: trackColor}.Add(gtx.Ops) paint.PaintOp{}.Add(gtx.Ops) stack.Pop() // Draw thumb ink. inkSize := gtx.Dp(unit.Dp(44)) rr := inkSize / 2 inkOff := image.Pt( trackWidth/2-rr, -rr+trackHeight/2+trackOff, ) stack2 := op.Offset(inkOff).Push(gtx.Ops) gtx.Constraints.Min = image.Pt(inkSize, inkSize) clip.UniformRRect(image.Rectangle{Max: gtx.Constraints.Min}, rr).Push(gtx.Ops).Pop() for _, p := range s.swtch.History() { drawInk(gtx, p) } stack2.Pop() // Compute thumb offset and color. var stack3 op.TransformStack if s.swtch.value { off := trackWidth - thumbSize stack3 = op.Offset(image.Pt(off, 0)).Push(gtx.Ops) } else { stack3 = op.Offset(image.Pt(0, 0)).Push(gtx.Ops) } // Draw thumb shadow, a translucent disc slightly larger than the // thumb itself. shadowSize := 2 // Center shadow horizontally and slightly adjust its Y. shadowStack := op.Offset(image.Pt(-shadowSize/2, -1)).Push(gtx.Ops) drawDisc(gtx.Ops, thumbSize+shadowSize, color.NRGBA(argb(0x55000000))) shadowStack.Pop() // Draw thumb. drawDisc(gtx.Ops, thumbSize, col) stack3.Pop() // Set up click area. clickSize := gtx.Dp(unit.Dp(40)) clickOff := image.Pt( (trackWidth-clickSize)/2, (trackHeight-clickSize)/2+trackOff, ) stack4 := op.Offset(clickOff).Push(gtx.Ops) sz := image.Pt(clickSize, clickSize) defer clip.Ellipse(image.Rectangle{Max: sz}).Push(gtx.Ops).Pop() gtx.Constraints.Min = sz s.swtch.Fn(gtx) stack4.Pop() dims := image.Point{X: trackWidth, Y: thumbSize} return l.Dimensions{Size: dims} }).Fn(gtx) } func drawDisc(ops *op.Ops, sz int, col color.NRGBA) { rr := sz / 2 r := image.Rectangle{Max: image.Pt(sz, sz)} defer clip.UniformRRect(r, rr).Push(ops).Pop() paint.ColorOp{Color: col}.Add(ops) paint.PaintOp{}.Add(ops) }