github.com/gop9/olt@v0.0.0-20200202132135-d956aad50b08/gio/widget/material/button.go (about)

     1  // SPDX-License-Identifier: Unlicense OR MIT
     2  
     3  package material
     4  
     5  import (
     6  	"image"
     7  	"image/color"
     8  
     9  	"github.com/gop9/olt/gio/f32"
    10  	"github.com/gop9/olt/gio/io/pointer"
    11  	"github.com/gop9/olt/gio/layout"
    12  	"github.com/gop9/olt/gio/op"
    13  	"github.com/gop9/olt/gio/op/clip"
    14  	"github.com/gop9/olt/gio/op/paint"
    15  	"github.com/gop9/olt/gio/text"
    16  	"github.com/gop9/olt/gio/unit"
    17  	"github.com/gop9/olt/gio/widget"
    18  )
    19  
    20  type Button struct {
    21  	Text string
    22  	// Color is the text color.
    23  	Color        color.RGBA
    24  	Font         text.Font
    25  	Background   color.RGBA
    26  	CornerRadius unit.Value
    27  	shaper       *text.Shaper
    28  }
    29  
    30  type IconButton struct {
    31  	Background color.RGBA
    32  	Color      color.RGBA
    33  	Icon       *Icon
    34  	Size       unit.Value
    35  	Padding    unit.Value
    36  }
    37  
    38  func (t *Theme) Button(txt string) Button {
    39  	return Button{
    40  		Text:       txt,
    41  		Color:      rgb(0xffffff),
    42  		Background: t.Color.Primary,
    43  		Font: text.Font{
    44  			Size: t.TextSize.Scale(14.0 / 16.0),
    45  		},
    46  		shaper: t.Shaper,
    47  	}
    48  }
    49  
    50  func (t *Theme) IconButton(icon *Icon) IconButton {
    51  	return IconButton{
    52  		Background: t.Color.Primary,
    53  		Color:      t.Color.InvText,
    54  		Icon:       icon,
    55  		Size:       unit.Dp(56),
    56  		Padding:    unit.Dp(16),
    57  	}
    58  }
    59  
    60  func (b Button) Layout(gtx *layout.Context, button *widget.Button) {
    61  	col := b.Color
    62  	bgcol := b.Background
    63  	hmin := gtx.Constraints.Width.Min
    64  	vmin := gtx.Constraints.Height.Min
    65  	layout.Stack{Alignment: layout.Center}.Layout(gtx,
    66  		layout.Expanded(func() {
    67  			rr := float32(gtx.Px(unit.Dp(4)))
    68  			clip.Rect{
    69  				Rect: f32.Rectangle{Max: f32.Point{
    70  					X: float32(gtx.Constraints.Width.Min),
    71  					Y: float32(gtx.Constraints.Height.Min),
    72  				}},
    73  				NE: rr, NW: rr, SE: rr, SW: rr,
    74  			}.Op(gtx.Ops).Add(gtx.Ops)
    75  			fill(gtx, bgcol)
    76  			for _, c := range button.History() {
    77  				drawInk(gtx, c)
    78  			}
    79  		}),
    80  		layout.Stacked(func() {
    81  			gtx.Constraints.Width.Min = hmin
    82  			gtx.Constraints.Height.Min = vmin
    83  			layout.Align(layout.Center).Layout(gtx, func() {
    84  				layout.Inset{Top: unit.Dp(10), Bottom: unit.Dp(10), Left: unit.Dp(12), Right: unit.Dp(12)}.Layout(gtx, func() {
    85  					paint.ColorOp{Color: col}.Add(gtx.Ops)
    86  					widget.Label{}.Layout(gtx, b.shaper, b.Font, b.Text)
    87  				})
    88  			})
    89  			pointer.Rect(image.Rectangle{Max: gtx.Dimensions.Size}).Add(gtx.Ops)
    90  			button.Layout(gtx)
    91  		}),
    92  	)
    93  }
    94  
    95  func (b IconButton) Layout(gtx *layout.Context, button *widget.Button) {
    96  	layout.Stack{}.Layout(gtx,
    97  		layout.Expanded(func() {
    98  			size := float32(gtx.Constraints.Width.Min)
    99  			rr := float32(size) * .5
   100  			clip.Rect{
   101  				Rect: f32.Rectangle{Max: f32.Point{X: size, Y: size}},
   102  				NE:   rr, NW: rr, SE: rr, SW: rr,
   103  			}.Op(gtx.Ops).Add(gtx.Ops)
   104  			fill(gtx, b.Background)
   105  			for _, c := range button.History() {
   106  				drawInk(gtx, c)
   107  			}
   108  		}),
   109  		layout.Stacked(func() {
   110  			layout.UniformInset(b.Padding).Layout(gtx, func() {
   111  				size := gtx.Px(b.Size) - 2*gtx.Px(b.Padding)
   112  				if b.Icon != nil {
   113  					b.Icon.Color = b.Color
   114  					b.Icon.Layout(gtx, unit.Px(float32(size)))
   115  				}
   116  				gtx.Dimensions = layout.Dimensions{
   117  					Size: image.Point{X: size, Y: size},
   118  				}
   119  			})
   120  			pointer.Ellipse(image.Rectangle{Max: gtx.Dimensions.Size}).Add(gtx.Ops)
   121  			button.Layout(gtx)
   122  		}),
   123  	)
   124  }
   125  
   126  func toPointF(p image.Point) f32.Point {
   127  	return f32.Point{X: float32(p.X), Y: float32(p.Y)}
   128  }
   129  
   130  func toRectF(r image.Rectangle) f32.Rectangle {
   131  	return f32.Rectangle{
   132  		Min: toPointF(r.Min),
   133  		Max: toPointF(r.Max),
   134  	}
   135  }
   136  
   137  func drawInk(gtx *layout.Context, c widget.Click) {
   138  	d := gtx.Now().Sub(c.Time)
   139  	t := float32(d.Seconds())
   140  	const duration = 0.5
   141  	if t > duration {
   142  		return
   143  	}
   144  	t = t / duration
   145  	var stack op.StackOp
   146  	stack.Push(gtx.Ops)
   147  	size := float32(gtx.Px(unit.Dp(700))) * t
   148  	rr := size * .5
   149  	col := byte(0xaa * (1 - t*t))
   150  	ink := paint.ColorOp{Color: color.RGBA{A: col, R: col, G: col, B: col}}
   151  	ink.Add(gtx.Ops)
   152  	op.TransformOp{}.Offset(c.Position).Offset(f32.Point{
   153  		X: -rr,
   154  		Y: -rr,
   155  	}).Add(gtx.Ops)
   156  	clip.Rect{
   157  		Rect: f32.Rectangle{Max: f32.Point{
   158  			X: float32(size),
   159  			Y: float32(size),
   160  		}},
   161  		NE: rr, NW: rr, SE: rr, SW: rr,
   162  	}.Op(gtx.Ops).Add(gtx.Ops)
   163  	paint.PaintOp{Rect: f32.Rectangle{Max: f32.Point{X: float32(size), Y: float32(size)}}}.Add(gtx.Ops)
   164  	stack.Pop()
   165  	op.InvalidateOp{}.Add(gtx.Ops)
   166  }