123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209 |
- package material
- import (
- "image"
- "image/color"
- "gioui.org/f32"
- "gioui.org/io/semantic"
- "gioui.org/io/system"
- "gioui.org/layout"
- "gioui.org/op"
- "gioui.org/op/clip"
- "gioui.org/op/paint"
- "gioui.org/unit"
- "gioui.org/widget"
- )
- // DecorationsStyle provides the style elements for Decorations.
- type DecorationsStyle struct {
- Decorations *widget.Decorations
- Actions system.Action
- Title LabelStyle
- Background color.NRGBA
- Foreground color.NRGBA
- }
- // Decorations returns the style to decorate a window.
- func Decorations(th *Theme, deco *widget.Decorations, actions system.Action, title string) DecorationsStyle {
- titleStyle := Body1(th, title)
- titleStyle.Color = th.Palette.ContrastFg
- return DecorationsStyle{
- Decorations: deco,
- Actions: actions,
- Title: titleStyle,
- Background: th.Palette.ContrastBg,
- Foreground: th.Palette.ContrastFg,
- }
- }
- // Layout a window with its title and action buttons.
- func (d DecorationsStyle) Layout(gtx layout.Context) layout.Dimensions {
- rec := op.Record(gtx.Ops)
- dims := d.layoutDecorations(gtx)
- decos := rec.Stop()
- r := clip.Rect{Max: dims.Size}
- paint.FillShape(gtx.Ops, d.Background, r.Op())
- decos.Add(gtx.Ops)
- return dims
- }
- func (d DecorationsStyle) layoutDecorations(gtx layout.Context) layout.Dimensions {
- gtx.Constraints.Min.Y = 0
- inset := layout.UniformInset(10)
- return layout.Flex{
- Axis: layout.Horizontal,
- Alignment: layout.Middle,
- Spacing: layout.SpaceBetween,
- }.Layout(gtx,
- layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
- return d.Decorations.LayoutMove(gtx, func(gtx layout.Context) layout.Dimensions {
- return inset.Layout(gtx, d.Title.Layout)
- })
- }),
- layout.Rigid(func(gtx layout.Context) layout.Dimensions {
- // Remove the unmaximize action as it is taken care of by maximize.
- actions := d.Actions &^ system.ActionUnmaximize
- var size image.Point
- for a := system.Action(1); actions != 0; a <<= 1 {
- if a&actions == 0 {
- continue
- }
- actions &^= a
- var w layout.Widget
- switch a {
- case system.ActionMinimize:
- w = minimizeWindow
- case system.ActionMaximize:
- if d.Decorations.Maximized {
- w = maximizedWindow
- } else {
- w = maximizeWindow
- }
- case system.ActionClose:
- w = closeWindow
- default:
- continue
- }
- cl := d.Decorations.Clickable(a)
- dims := cl.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
- semantic.Button.Add(gtx.Ops)
- return layout.Background{}.Layout(gtx,
- func(gtx layout.Context) layout.Dimensions {
- defer clip.Rect{Max: gtx.Constraints.Min}.Push(gtx.Ops).Pop()
- for _, c := range cl.History() {
- drawInk(gtx, c)
- }
- return layout.Dimensions{Size: gtx.Constraints.Min}
- },
- func(gtx layout.Context) layout.Dimensions {
- paint.ColorOp{Color: d.Foreground}.Add(gtx.Ops)
- return inset.Layout(gtx, w)
- },
- )
- })
- size.X += dims.Size.X
- if size.Y < dims.Size.Y {
- size.Y = dims.Size.Y
- }
- op.Offset(image.Pt(dims.Size.X, 0)).Add(gtx.Ops)
- }
- return layout.Dimensions{Size: size}
- }),
- )
- }
- const (
- winIconSize = unit.Dp(20)
- winIconMargin = unit.Dp(4)
- winIconStroke = unit.Dp(2)
- )
- // minimizeWindows draws a line icon representing the minimize action.
- func minimizeWindow(gtx layout.Context) layout.Dimensions {
- size := gtx.Dp(winIconSize)
- size32 := float32(size)
- margin := float32(gtx.Dp(winIconMargin))
- width := float32(gtx.Dp(winIconStroke))
- var p clip.Path
- p.Begin(gtx.Ops)
- p.MoveTo(f32.Point{X: margin, Y: size32 - margin})
- p.LineTo(f32.Point{X: size32 - 2*margin, Y: size32 - margin})
- st := clip.Stroke{
- Path: p.End(),
- Width: width,
- }.Op().Push(gtx.Ops)
- paint.PaintOp{}.Add(gtx.Ops)
- st.Pop()
- return layout.Dimensions{Size: image.Pt(size, size)}
- }
- // maximizeWindow draws a rectangle representing the maximize action.
- func maximizeWindow(gtx layout.Context) layout.Dimensions {
- size := gtx.Dp(winIconSize)
- margin := gtx.Dp(winIconMargin)
- width := gtx.Dp(winIconStroke)
- r := clip.RRect{
- Rect: image.Rect(margin, margin, size-margin, size-margin),
- }
- st := clip.Stroke{
- Path: r.Path(gtx.Ops),
- Width: float32(width),
- }.Op().Push(gtx.Ops)
- paint.PaintOp{}.Add(gtx.Ops)
- st.Pop()
- r.Rect.Max = image.Pt(size-margin, 2*margin)
- st = clip.Outline{
- Path: r.Path(gtx.Ops),
- }.Op().Push(gtx.Ops)
- paint.PaintOp{}.Add(gtx.Ops)
- st.Pop()
- return layout.Dimensions{Size: image.Pt(size, size)}
- }
- // maximizedWindow draws interleaved rectangles representing the un-maximize action.
- func maximizedWindow(gtx layout.Context) layout.Dimensions {
- size := gtx.Dp(winIconSize)
- margin := gtx.Dp(winIconMargin)
- width := gtx.Dp(winIconStroke)
- r := clip.RRect{
- Rect: image.Rect(margin, margin, size-2*margin, size-2*margin),
- }
- st := clip.Stroke{
- Path: r.Path(gtx.Ops),
- Width: float32(width),
- }.Op().Push(gtx.Ops)
- paint.PaintOp{}.Add(gtx.Ops)
- st.Pop()
- r = clip.RRect{
- Rect: image.Rect(2*margin, 2*margin, size-margin, size-margin),
- }
- st = clip.Stroke{
- Path: r.Path(gtx.Ops),
- Width: float32(width),
- }.Op().Push(gtx.Ops)
- paint.PaintOp{}.Add(gtx.Ops)
- st.Pop()
- return layout.Dimensions{Size: image.Pt(size, size)}
- }
- // closeWindow draws a cross representing the close action.
- func closeWindow(gtx layout.Context) layout.Dimensions {
- size := gtx.Dp(winIconSize)
- size32 := float32(size)
- margin := float32(gtx.Dp(winIconMargin))
- width := float32(gtx.Dp(winIconStroke))
- var p clip.Path
- p.Begin(gtx.Ops)
- p.MoveTo(f32.Point{X: margin, Y: margin})
- p.LineTo(f32.Point{X: size32 - margin, Y: size32 - margin})
- p.MoveTo(f32.Point{X: size32 - margin, Y: margin})
- p.LineTo(f32.Point{X: margin, Y: size32 - margin})
- st := clip.Stroke{
- Path: p.End(),
- Width: width,
- }.Op().Push(gtx.Ops)
- paint.PaintOp{}.Add(gtx.Ops)
- st.Pop()
- return layout.Dimensions{Size: image.Pt(size, size)}
- }
|