package gel import ( "image" "image/color" "math" "strings" "gioui.org/font" l "gioui.org/layout" "gioui.org/op" "gioui.org/op/clip" "gioui.org/op/paint" "gioui.org/text" "gioui.org/unit" "git.mleku.dev/mleku/prevara/f32color" ) // Button is a material text label icon with options to change all features type Button struct { *Window background color.NRGBA color color.NRGBA cornerRadius unit.Dp font font.Font inset *l.Inset text string textSize unit.Sp button *Clickable shaper *text.Shaper } // Button is a regular material text button where all the dimensions, colors, corners and font can be changed func (w *Window) Button(btn *Clickable) *Button { var fnt font.Font var e error if fnt, e = w.collection.Font("plan9"); E.Chk(e) { } insetVal := unit.Dp(float32(w.TextSize) * 0.5) return &Button{ Window: w, text: strings.ToUpper("text unset"), // default sets font: fnt, color: w.Colors.GetNRGBAFromName("DocBg"), cornerRadius: unit.Dp(float32(w.TextSize) * 0.25), background: w.Colors.GetNRGBAFromName("Primary"), textSize: w.TextSize, inset: &l.Inset{ Top: insetVal, Bottom: insetVal, Left: insetVal, Right: insetVal, }, button: btn, shaper: w.shaper, } } // Background sets the background color func (b *Button) Background(background string) *Button { b.background = b.Theme.Colors.GetNRGBAFromName(background) return b } // Color sets the text color func (b *Button) Color(color string) *Button { b.color = b.Theme.Colors.GetNRGBAFromName(color) return b } // CornerRadius sets the corner radius (all measurements are scaled from the base text size) func (b *Button) CornerRadius(cornerRadius float32) *Button { b.cornerRadius = unit.Dp(float32(b.TextSize) * cornerRadius) return b } // Font sets the font style. If font not found, keeps current font. func (b *Button) Font(fontName string) *Button { if fon, e := b.collection.Font(fontName); e == nil { b.font = fon } else { D.Ln("font not found:", fontName, "- keeping current font") } return b } // Inset sets the inset between the button border and the text func (b *Button) Inset(scale float32) *Button { insetVal := unit.Dp(float32(b.TextSize) * scale) b.inset = &l.Inset{ Top: insetVal, Right: insetVal, Bottom: insetVal, Left: insetVal, } return b } // Text sets the text on the button func (b *Button) Text(text string) *Button { b.text = text return b } // TextScale sets the dimensions of the text as a fraction of the base text size func (b *Button) TextScale(scale float32) *Button { b.textSize = unit.Sp(float32(b.Theme.TextSize) * scale) return b } // SetClick defines the callback to run on a click (mouse up) event func (b *Button) SetClick(fn func()) *Button { b.button.SetClick(fn) return b } // SetCancel sets the callback to run when the user presses down over the button // but then moves out of its hitbox before release (click) func (b *Button) SetCancel(fn func()) *Button { b.button.SetCancel(fn) return b } func (b *Button) SetPress(fn func()) *Button { b.button.SetPress(fn) return b } // Fn renders the button func (b *Button) Fn(gtx l.Context) l.Dimensions { bl := &ButtonLayout{ Window: b.Window, background: b.background, cornerRadius: b.cornerRadius, button: b.button, } fn := func(gtx l.Context) l.Dimensions { return b.inset.Layout( gtx, func(gtx l.Context) l.Dimensions { // paint.ColorOp{Color: b.color}.Add(gtx.Ops) return b.Flex().Rigid(b.Label().Text(b.text).TextScale(float32(b.textSize) / float32(b.TextSize)).Fn).Fn(gtx) // b.Window.Text(). // Alignment(text.Middle). // Fn(gtx, b.shaper, b.font, b.textSize, b.text) }, ) } bl.Embed(fn) return bl.Fn(gtx) } func drawInk(c l.Context, p press) { // duration is the number of seconds for the completed animation: expand while // fading in, then out. const ( expandDuration = float32(0.5) fadeDuration = float32(0.9) ) now := c.Now t := float32(now.Sub(p.Start).Seconds()) end := p.End if end.IsZero() { // If the press hasn't ended, don't fade-out. end = now } endt := float32(end.Sub(p.Start).Seconds()) // Compute the fade-in/out position in [0;1]. var alphat float32 { var haste float32 if p.Cancelled { // If the press was cancelled before the inkwell was fully faded in, fast // forward the animation to match the fade-out. if h := 0.5 - endt/fadeDuration; h > 0 { haste = h } } // Fade in. half1 := t/fadeDuration + haste if half1 > 0.5 { half1 = 0.5 } // Fade out. half2 := float32(now.Sub(end).Seconds()) half2 /= fadeDuration half2 += haste if half2 > 0.5 { // Too old. return } alphat = half1 + half2 } // Compute the expand position in [0;1]. sizet := t if p.Cancelled { // Freeze expansion of cancelled presses. sizet = endt } sizet /= expandDuration // Animate only ended presses, and presses that are fading in. if !p.End.IsZero() || sizet <= 1.0 { c.Execute(op.InvalidateCmd{}) } if sizet > 1.0 { sizet = 1.0 } if alphat > .5 { // Start fadeout after half the animation. alphat = 1.0 - alphat } // Twice the speed to attain fully faded in at 0.5. t2 := alphat * 2 // BeziƩr ease-in curve. alphaBezier := t2 * t2 * (3.0 - 2.0*t2) sizeBezier := sizet * sizet * (3.0 - 2.0*sizet) size := c.Constraints.Min.X if h := c.Constraints.Min.Y; h > size { size = h } // Cover the entire constraints min rectangle and // apply curve values to size and color. size = int(float32(size) * 2 * float32(math.Sqrt(2)) * sizeBezier) alpha := 0.7 * alphaBezier const col = 0.8 ba, bc := byte(alpha*0xff), byte(col*0xff) rgba := f32color.MulAlpha(color.NRGBA{A: 0xff, R: bc, G: bc, B: bc}, ba) ink := paint.ColorOp{Color: rgba} ink.Add(c.Ops) rr := size / 2 defer op.Offset(p.Position.Add(image.Point{ X: -rr, Y: -rr, })).Push(c.Ops).Pop() defer clip.UniformRRect(image.Rectangle{Max: image.Pt(size, size)}, rr).Push(c.Ops).Pop() paint.PaintOp{}.Add(c.Ops) }