5 Further practices

Further practices#

window options
// ref: https://github.com/gioui/gio/blob/main/app/os.go#L23
// ref: https://github.com/gioui/gio/blob/main/app/window.go#L135
// ref: https://github.com/gioui/gio/blob/main/io/system/decoration.go#L17

package main

import (
	"fmt"
	"log"
	"os"

	"gioui.org/app"
	"gioui.org/io/system"
	"gioui.org/layout"
	"gioui.org/op"
	"gioui.org/unit"
	"gioui.org/widget"
	"gioui.org/widget/material"
)

type (
	C = layout.Context
	D = layout.Dimensions
)

func main() {
	go func() {
		w := app.NewWindow(
			app.Title("Window options"),
			app.Size(unit.Dp(400), unit.Dp(600)),
			// app.MaxSize(unit.Dp(500), unit.Dp(700)),
			// app.MinSize(unit.Dp(300), unit.Dp(500)),
			app.Decorated(true),
			// app.Maximized.Option(),
			// app.Minimized.Option(),
			// app.Fullscreen.Option(),
		)

		if err := run(w); err != nil {
			log.Fatal(err)
		}
		os.Exit(0)
	}()

	app.Main()
}

func run(w *app.Window) error {
	var ops op.Ops
	var button_maximize widget.Clickable
	var button_unMaximize widget.Clickable
	var button_minimize widget.Clickable
	var button_800x600 widget.Clickable
	var button_notDecorated widget.Clickable
	var button_centerMe widget.Clickable
	var button_close widget.Clickable
	th := material.NewTheme()

	for {
		switch e := w.NextEvent().(type) {

		case app.FrameEvent:
			gtx := app.NewContext(&ops, e)

			// BUG: if clicked maximize and then click other buttons, the window will return to the last not-max size
			// eg.: when click on 800x600 -> maximize -> 500x500 (it will return to 800x600 window),
			// if click on 500x500 again, it will resize to correct 500x500 size.
			if button_maximize.Clicked(gtx) {
				fmt.Println("Maximize button clicked!")
				// w.Option(app.Decorated(true))
				// w.Option(app.Maximized.Option())
				w.Perform(system.ActionMaximize)
			}
			if button_unMaximize.Clicked(gtx) {
				fmt.Println("Unmaximize button clicked!")
				// w.Option(app.Windowed.Option())
				w.Perform(system.ActionUnmaximize)
			}
			if button_minimize.Clicked(gtx) {
				fmt.Println("Miniimize button clicked!")
				// w.Option(app.Minimized.Option())
				w.Perform(system.ActionMinimize)
			}
			if button_800x600.Clicked(gtx) {
				fmt.Println("800x600 button clicked!")
				w.Option(app.Decorated(true))
				w.Option(app.Size(unit.Dp(800), unit.Dp(600)))
			}
			if button_notDecorated.Clicked(gtx) {
				fmt.Println("NotDecorated button clicked!")
				w.Option(app.Size(unit.Dp(500), unit.Dp(500)))
				w.Option(app.Decorated(false))
			}
			if button_centerMe.Clicked(gtx) {
				fmt.Println("CenterMe button clicked!")
				w.Perform(system.ActionCenter)
			}
			if button_close.Clicked(gtx) {
				fmt.Println("Close button clicked!")
				w.Perform(system.ActionClose)
			}

			btn1 := material.Button(th, &button_maximize, "Maximize")
			btn2 := material.Button(th, &button_unMaximize, "UnMaximize")
			btn3 := material.Button(th, &button_minimize, "Minimize")
			btn4 := material.Button(th, &button_800x600, "800x600")
			btn5 := material.Button(th, &button_notDecorated, "NotDecorated(500x500)")
			btn6 := material.Button(th, &button_centerMe, "CenterMe")
			btn7 := material.Button(th, &button_close, "Close")

			inset := layout.Inset{
				Top:    unit.Dp(0),
				Right:  unit.Dp(10),
				Bottom: unit.Dp(10),
				Left:   unit.Dp(10),
			}
			layout.Flex{
				Axis:    layout.Vertical,
				Spacing: layout.SpaceStart,
			}.Layout(gtx,
				layout.Rigid(
					func(gtx C) D {
						return inset.Layout(gtx, func(gtx C) D {
							return btn1.Layout(gtx)
						})
					},
				),
				layout.Rigid(
					func(gtx C) D {
						return inset.Layout(gtx, func(gtx C) D {
							return btn2.Layout(gtx)
						})
					},
				),
				layout.Rigid(
					func(gtx C) D {
						return inset.Layout(gtx, func(gtx C) D {
							return btn3.Layout(gtx)
						})
					},
				),
				layout.Rigid(
					func(gtx C) D {
						return inset.Layout(gtx, func(gtx C) D {
							return btn4.Layout(gtx)
						})
					},
				),
				layout.Rigid(
					func(gtx C) D {
						return inset.Layout(gtx, func(gtx C) D {
							return btn5.Layout(gtx)
						})
					},
				),
				layout.Rigid(
					func(gtx C) D {
						return inset.Layout(gtx, func(gtx C) D {
							return btn6.Layout(gtx)
						})
					},
				),
				layout.Rigid(
					func(gtx C) D {
						return inset.Layout(gtx, func(gtx C) D {
							return btn7.Layout(gtx)
						})
					},
				),
			)

			e.Frame(&ops)

		case app.ConfigEvent:
			fmt.Println(e.Config.Mode.String())
		case app.DestroyEvent:
			return e.Err
		}
	}
}
Multi window
// ref: https://github.com/gioui/gio-example/tree/main/multiwindow

package main

import (
	"fmt"
	"log"
	"os"

	"gioui.org/app"
	"gioui.org/io/system"
	"gioui.org/layout"
	"gioui.org/op"
	"gioui.org/unit"
	"gioui.org/widget"
	"gioui.org/widget/material"
)

type (
	C = layout.Context
	D = layout.Dimensions
)

func main() {
	go func() {
		w := app.NewWindow(
			app.Title("Multi Window"),
			app.Size(unit.Dp(400), unit.Dp(600)),
		)

		if err := run(w); err != nil {
			log.Fatal(err)
		}
		os.Exit(0)
	}()

	app.Main()
}

func run(w *app.Window) error {
	var ops op.Ops
	var button_newWindow widget.Clickable
	var button_close widget.Clickable
	th := material.NewTheme()

	var newWinNumber int

	for {
		switch e := w.NextEvent().(type) {

		case app.FrameEvent:
			gtx := app.NewContext(&ops, e)

			if button_newWindow.Clicked(gtx) {
				// fmt.Println("NewWindow button clicked!")
				fmt.Printf("Openning new window, number %d\n", newWinNumber)
				if err := newWin(newWinNumber); err != nil {
					return err
				}
				newWinNumber += 1
			}
			if button_close.Clicked(gtx) {
				fmt.Println("Close button clicked!")
				w.Perform(system.ActionClose)
			}

			btn1 := material.Button(th, &button_newWindow, "NewWindow")
			btn2 := material.Button(th, &button_close, "Close")

			inset := layout.Inset{
				Top:    unit.Dp(0),
				Right:  unit.Dp(10),
				Bottom: unit.Dp(10),
				Left:   unit.Dp(10),
			}
			layout.Flex{
				Axis:    layout.Vertical,
				Spacing: layout.SpaceStart,
			}.Layout(gtx,
				layout.Rigid(
					func(gtx C) D {
						return inset.Layout(gtx, func(gtx C) D {
							return btn1.Layout(gtx)
						})
					},
				),
				layout.Rigid(
					func(gtx C) D {
						return inset.Layout(gtx, func(gtx C) D {
							return btn2.Layout(gtx)
						})
					},
				),
			)

			e.Frame(&ops)

		case app.ConfigEvent:
			fmt.Println(e.Config.Mode.String())
		case app.DestroyEvent:
			return e.Err
		}
	}
}

func newWin(winNumber int) error {
	go func() error {
		w := app.NewWindow(
			app.Title(fmt.Sprintf("New Window Number %d", winNumber)),
			app.Size(unit.Dp(200), unit.Dp(200)),
		)
		var ops op.Ops
		th := material.NewTheme()

		for {
			switch e := w.NextEvent().(type) {
			case app.DestroyEvent:
				return e.Err
			case app.FrameEvent:
				gtx := app.NewContext(&ops, e)

				label := material.Label(th, unit.Sp(14), fmt.Sprintf("New Window Number %d", winNumber))
				label.Layout(gtx)

				e.Frame(gtx.Ops)
			}
		}
	}()

	return nil
}
Layouts
stack
  • stack
  • stacked
  • expanded
  • background
// ref: https://github.com/gioui/gio/blob/main/layout/layout_test.go
// ref: https://github.com/gioui/gio/blob/main/layout/stack.go

package main

import (
	"fmt"
	"image"
	"image/color"
	"log"
	"os"

	"gioui.org/app"
	"gioui.org/io/system"
	"gioui.org/layout"
	"gioui.org/op"
	"gioui.org/op/clip"
	"gioui.org/op/paint"
	"gioui.org/unit"
	"gioui.org/widget"
	"gioui.org/widget/material"
)

type (
	C = layout.Context
	D = layout.Dimensions
)

func main() {
	go func() {
		w := app.NewWindow(
			app.Title("Layout"),
			app.Size(unit.Dp(640), unit.Dp(480)),
		)

		if err := run(w); err != nil {
			log.Fatal(err)
		}
		os.Exit(0)
	}()

	app.Main()
}

func run(w *app.Window) error {
	var ops op.Ops
	var button_stack widget.Clickable
	var button_close widget.Clickable
	th := material.NewTheme()

	for {
		switch e := w.NextEvent().(type) {

		case app.FrameEvent:
			gtx := app.NewContext(&ops, e)

			if button_stack.Clicked(gtx) {
				if err := runStackLayoutWindow(); err != nil {
					return err
				}
			}

			if button_close.Clicked(gtx) {
				fmt.Println("Close button clicked!")
				w.Perform(system.ActionClose)
			}

			btn1 := material.Button(th, &button_stack, "Stack")
			btn2 := material.Button(th, &button_close, "Close")

			inset := layout.Inset{
				Top:    unit.Dp(0),
				Right:  unit.Dp(10),
				Bottom: unit.Dp(10),
				Left:   unit.Dp(10),
			}
			layout.Flex{
				Axis:    layout.Vertical,
				Spacing: layout.SpaceStart,
			}.Layout(gtx,
				layout.Rigid(
					func(gtx C) D {
						return inset.Layout(gtx, func(gtx C) D {
							return btn1.Layout(gtx)
						})
					},
				),
				layout.Rigid(
					func(gtx C) D {
						return inset.Layout(gtx, func(gtx C) D {
							return btn2.Layout(gtx)
						})
					},
				),
			)

			e.Frame(&ops)

		case app.ConfigEvent:
			fmt.Println(e.Config.Mode.String())
		case app.DestroyEvent:
			return e.Err
		}
	}
}

func runStackLayoutWindow() error {
	go func() {
		newW := app.NewWindow(
			app.Title("Stack layout"),
			app.Size(unit.Dp(400), unit.Dp(400)),
		)
		var ops op.Ops

		for {
			switch e := newW.NextEvent().(type) {
			case app.DestroyEvent:
				fmt.Println("Stack layout closed.")
			case app.FrameEvent:
				// gtx := app.NewContext(&ops, e)
				gtx := layout.Context{
					Ops:    &ops,
					Now:    e.Now,
					Source: e.Source,
					Metric: e.Metric,
					Constraints: layout.Constraints{
						Min: image.Pt(0, 0),
						Max: image.Pt(300, 300),
					},
				}

				layout.Stack{Alignment: layout.Center}.Layout(gtx,
					layout.Stacked(func(gtx C) D {
						fmt.Println("1.", gtx.Constraints)
						paint.FillShape(gtx.Ops, color.NRGBA{R: 150, B: 200, A: 200}, clip.Rect{Min: gtx.Constraints.Min, Max: gtx.Constraints.Max}.Op())
						return layout.Dimensions{Size: gtx.Constraints.Max}
					}),
					layout.Stacked(func(gtx C) D {
						return layout.UniformInset(unit.Dp(30)).Layout(gtx, func(gtx C) D {
							fmt.Println("2.", gtx.Constraints)
							paint.FillShape(gtx.Ops, color.NRGBA{R: 100, B: 200, A: 200}, clip.Rect{Min: gtx.Constraints.Min, Max: gtx.Constraints.Max}.Op())
							return layout.Dimensions{Size: gtx.Constraints.Max}
						})
					}),
					layout.Expanded(func(gtx C) D {
						return layout.Background{}.Layout(gtx,
							func(gtx C) D {
								fmt.Println("3.", gtx.Constraints)
								paint.Fill(gtx.Ops, color.NRGBA{R: 200, B: 200, A: 100})

								return emptyWidget{
									Size: image.Point{X: 60, Y: 60},
								}.Layout(gtx)
							},
							func(gtx C) D {
								return layoutWidget(gtx, 60, 60)
							},
						)
						// paint.FillShape(gtx.Ops, color.NRGBA{R: 200, B: 200, A: 200}, clip.Rect{Min: gtx.Constraints.Min, Max: gtx.Constraints.Max}.Op())
						// return layout.Dimensions{Size: gtx.Constraints.Max}
					}),
				)

				e.Frame(gtx.Ops)
			}
		}
	}()

	return nil
}

type emptyWidget struct {
	Size image.Point
}

func (w emptyWidget) Layout(gtx C) D {
	return layout.Dimensions{Size: w.Size}
}

func layoutWidget(ctx C, width, height int) D {
	return layout.Dimensions{
		Size: image.Point{
			X: width,
			Y: height,
		},
	}
}
showing border for debug
// ref: https://github.com/gioui/gio/blob/main/widget/border.go
// ref: https://github.com/gioui/gio-example/blob/main/fps-table/table.go#L99

package main

import (
	"fmt"
	"image"
	"image/color"
	"log"
	"os"

	"gioui.org/app"
	"gioui.org/io/system"
	"gioui.org/layout"
	"gioui.org/op"
	"gioui.org/op/clip"
	"gioui.org/op/paint"
	"gioui.org/unit"
	"gioui.org/widget"
	"gioui.org/widget/material"
)

type (
	C = layout.Context
	D = layout.Dimensions
)

func main() {
	go func() {
		w := app.NewWindow(
			app.Title("Layout"),
			app.Size(unit.Dp(640), unit.Dp(480)),
		)

		if err := run(w); err != nil {
			log.Fatal(err)
		}
		os.Exit(0)
	}()

	app.Main()
}

func run(w *app.Window) error {
	var ops op.Ops
	var button_stack widget.Clickable
	var button_border widget.Clickable
	var button_close widget.Clickable
	th := material.NewTheme()

	for {
		switch e := w.NextEvent().(type) {

		case app.FrameEvent:
			gtx := app.NewContext(&ops, e)

			if button_stack.Clicked(gtx) {
				if err := runStackLayoutWindow(); err != nil {
					return err
				}
			}

			if button_border.Clicked(gtx) {
				if err := runBorderLayoutWindow(); err != nil {
					return err
				}
			}

			if button_close.Clicked(gtx) {
				fmt.Println("Close button clicked!")
				w.Perform(system.ActionClose)
			}

			btn1 := material.Button(th, &button_stack, "Stack")
			btn2 := material.Button(th, &button_border, "Border")
			btn3 := material.Button(th, &button_close, "Close")

			inset := layout.Inset{
				Top:    unit.Dp(0),
				Right:  unit.Dp(10),
				Bottom: unit.Dp(10),
				Left:   unit.Dp(10),
			}
			layout.Flex{
				Axis:    layout.Vertical,
				Spacing: layout.SpaceStart,
			}.Layout(gtx,
				layout.Rigid(
					func(gtx C) D {
						return inset.Layout(gtx, func(gtx C) D {
							return btn1.Layout(gtx)
						})
					},
				),
				layout.Rigid(
					func(gtx C) D {
						return inset.Layout(gtx, func(gtx C) D {
							return btn2.Layout(gtx)
						})
					},
				),
				layout.Rigid(
					func(gtx C) D {
						return inset.Layout(gtx, func(gtx C) D {
							return btn3.Layout(gtx)
						})
					},
				),
			)

			e.Frame(&ops)

		case app.ConfigEvent:
			fmt.Println(e.Config.Mode.String())
		case app.DestroyEvent:
			return e.Err
		}
	}
}

func runStackLayoutWindow() error {
	go func() {
		newW := app.NewWindow(
			app.Title("Stack layout"),
			app.Size(unit.Dp(400), unit.Dp(400)),
		)
		var ops op.Ops

		for {
			switch e := newW.NextEvent().(type) {
			case app.DestroyEvent:
				fmt.Println("Stack layout closed.")
			case app.FrameEvent:
				// gtx := app.NewContext(&ops, e)
				gtx := layout.Context{
					Ops:    &ops,
					Now:    e.Now,
					Source: e.Source,
					Metric: e.Metric,
					Constraints: layout.Constraints{
						Min: image.Pt(0, 0),
						Max: image.Pt(300, 300),
					},
				}

				layout.Stack{Alignment: layout.Center}.Layout(gtx,
					layout.Stacked(func(gtx C) D {
						fmt.Println("3.", gtx.Constraints)
						paint.FillShape(gtx.Ops, color.NRGBA{R: 150, B: 200, A: 200}, clip.Rect{Min: gtx.Constraints.Min, Max: gtx.Constraints.Max}.Op())
						return layout.Dimensions{Size: gtx.Constraints.Max}
					}),
					layout.Stacked(func(gtx C) D {
						return layout.UniformInset(unit.Dp(30)).Layout(gtx, func(gtx C) D {
							fmt.Println("2.", gtx.Constraints)
							paint.FillShape(gtx.Ops, color.NRGBA{R: 100, B: 200, A: 200}, clip.Rect{Min: gtx.Constraints.Min, Max: gtx.Constraints.Max}.Op())
							return layout.Dimensions{Size: gtx.Constraints.Max}
						})
					}),
					layout.Expanded(func(gtx C) D {
						return layout.Background{}.Layout(gtx,
							func(gtx C) D {
								fmt.Println("1.", gtx.Constraints)
								paint.Fill(gtx.Ops, color.NRGBA{R: 200, B: 200, A: 100})

								return emptyWidget{
									Size: image.Point{X: 60, Y: 60},
								}.Layout(gtx)
							},
							func(gtx C) D {
								return layoutWidget(gtx, 60, 60)
							},
						)
						// paint.FillShape(gtx.Ops, color.NRGBA{R: 200, B: 200, A: 200}, clip.Rect{Min: gtx.Constraints.Min, Max: gtx.Constraints.Max}.Op())
						return layout.Dimensions{Size: gtx.Constraints.Max}
					}),
				)

				e.Frame(gtx.Ops)
			}
		}
	}()

	return nil
}

func runBorderLayoutWindow() error {
	go func() {
		newW := app.NewWindow(
			app.Title("Border layout"),
			app.Size(unit.Dp(400), unit.Dp(400)),
		)
		var ops op.Ops

		for {
			switch e := newW.NextEvent().(type) {
			case app.DestroyEvent:
				fmt.Println("Border layout closed.")
			case app.FrameEvent:
				// gtx := app.NewContext(&ops, e)
				gtx := layout.Context{
					Ops:    &ops,
					Now:    e.Now,
					Source: e.Source,
					Metric: e.Metric,
					Constraints: layout.Constraints{
						Min: image.Pt(0, 0),
						Max: image.Pt(300, 300),
					},
				}

				border := widget.Border{
					Color: color.NRGBA{R: 255, A: 255},
					Width: unit.Dp(1),
				}

				border.Layout(gtx, func(gtx C) D {
					return layout.Stack{Alignment: layout.Center}.Layout(gtx,
						layout.Stacked(func(gtx C) D {
							fmt.Println("1.", gtx.Constraints)
							paint.FillShape(gtx.Ops, color.NRGBA{R: 150, B: 200, A: 200}, clip.Rect{Min: gtx.Constraints.Min, Max: gtx.Constraints.Max}.Op())
							return border.Layout(gtx, func(gtx C) D {
								return layout.Dimensions{Size: gtx.Constraints.Max}
							})
						}),
						layout.Stacked(func(gtx C) D {
							return layout.UniformInset(unit.Dp(30)).Layout(gtx, func(gtx C) D {
								fmt.Println("2.", gtx.Constraints)
								paint.FillShape(gtx.Ops, color.NRGBA{R: 100, B: 200, A: 200}, clip.Rect{Min: gtx.Constraints.Min, Max: gtx.Constraints.Max}.Op())
								return border.Layout(gtx, func(gtx C) D {
									return layout.Dimensions{Size: gtx.Constraints.Max}
								})
							})
						}),
						// layout.Expanded(func(gtx C) D {
						// 	return border.Layout(gtx, func(gtx C) D {
						// 		return layout.Background{}.Layout(gtx,
						// 			func(gtx C) D {
						// 				fmt.Println("3.", gtx.Constraints)
						// 				paint.Fill(gtx.Ops, color.NRGBA{R: 200, B: 200, A: 100})

						// 				return emptyWidget{
						// 					Size: image.Point{X: 60, Y: 60},
						// 				}.Layout(gtx)
						// 			},
						// 			func(gtx C) D {
						// 				return layoutWidget(gtx, 60, 60)
						// 			},
						// 		)
						// 		// paint.FillShape(gtx.Ops, color.NRGBA{R: 200, B: 200, A: 200}, clip.Rect{Min: gtx.Constraints.Min, Max: gtx.Constraints.Max}.Op())
						// 		// return layout.Dimensions{Size: gtx.Constraints.Max}
						// 	})
						// }),
					)
				})

				e.Frame(gtx.Ops)
			}
		}
	}()

	return nil
}

type emptyWidget struct {
	Size image.Point
}

func (w emptyWidget) Layout(gtx C) D {
	return layout.Dimensions{Size: w.Size}
}

func layoutWidget(ctx C, width, height int) D {
	return layout.Dimensions{
		Size: image.Point{
			X: width,
			Y: height,
		},
	}
}
Flex Flex put first level child elements in a row, from left to right.
Flex-vertical Flex-vertical put first level child elements in a column, from top to bottom.
holy-grail
package main

import (
	"fmt"
	"image"
	"image/color"
	"log"
	"os"

	"gioui.org/app"
	"gioui.org/io/system"
	"gioui.org/layout"
	"gioui.org/op"
	"gioui.org/op/clip"
	"gioui.org/op/paint"
	"gioui.org/unit"
	"gioui.org/widget"
	"gioui.org/widget/material"
)

type (
	C = layout.Context
	D = layout.Dimensions
)

func main() {
	go func() {
		w := app.NewWindow(
			app.Title("Layout"),
			app.Size(unit.Dp(640), unit.Dp(480)),
		)

		if err := run(w); err != nil {
			log.Fatal(err)
		}
		os.Exit(0)
	}()

	app.Main()
}

func run(w *app.Window) error {
	var ops op.Ops
	var button_stack widget.Clickable
	var button_border widget.Clickable
	var button_holygrail widget.Clickable
	var button_close widget.Clickable
	th := material.NewTheme()

	for {
		switch e := w.NextEvent().(type) {

		case app.FrameEvent:
			gtx := app.NewContext(&ops, e)

			if button_stack.Clicked(gtx) {
				if err := runStackLayoutWindow(); err != nil {
					return err
				}
			}

			if button_border.Clicked(gtx) {
				if err := runBorderLayoutWindow(); err != nil {
					return err
				}
			}

			if button_holygrail.Clicked(gtx) {
				if err := runHolygrailLayoutWindow(); err != nil {
					return err
				}
			}

			if button_close.Clicked(gtx) {
				fmt.Println("Close button clicked!")
				w.Perform(system.ActionClose)
			}

			btn1 := material.Button(th, &button_stack, "Stack")
			btn2 := material.Button(th, &button_border, "Border")
			btn3 := material.Button(th, &button_holygrail, "Holy Grail")
			btn4 := material.Button(th, &button_close, "Close")

			inset := layout.Inset{
				Top:    unit.Dp(0),
				Right:  unit.Dp(10),
				Bottom: unit.Dp(10),
				Left:   unit.Dp(10),
			}
			layout.Flex{
				Axis:    layout.Vertical,
				Spacing: layout.SpaceStart,
			}.Layout(gtx,
				layout.Rigid(
					func(gtx C) D {
						return inset.Layout(gtx, func(gtx C) D {
							return btn1.Layout(gtx)
						})
					},
				),
				layout.Rigid(
					func(gtx C) D {
						return inset.Layout(gtx, func(gtx C) D {
							return btn2.Layout(gtx)
						})
					},
				),
				layout.Rigid(
					func(gtx C) D {
						return inset.Layout(gtx, func(gtx C) D {
							return btn3.Layout(gtx)
						})
					},
				),
				layout.Rigid(
					func(gtx C) D {
						return inset.Layout(gtx, func(gtx C) D {
							return btn4.Layout(gtx)
						})
					},
				),
			)

			e.Frame(&ops)

		case app.ConfigEvent:
			fmt.Println(e.Config.Mode.String())
		case app.DestroyEvent:
			return e.Err
		}
	}
}

func runStackLayoutWindow() error {
	go func() {
		newW := app.NewWindow(
			app.Title("Stack layout"),
			app.Size(unit.Dp(400), unit.Dp(400)),
		)
		var ops op.Ops

		for {
			switch e := newW.NextEvent().(type) {
			case app.DestroyEvent:
				fmt.Println("Stack layout closed.")
			case app.FrameEvent:
				// gtx := app.NewContext(&ops, e)
				gtx := layout.Context{
					Ops:    &ops,
					Now:    e.Now,
					Source: e.Source,
					Metric: e.Metric,
					Constraints: layout.Constraints{
						Min: image.Pt(0, 0),
						Max: image.Pt(300, 300),
					},
				}

				layout.Stack{Alignment: layout.Center}.Layout(gtx,
					layout.Stacked(func(gtx C) D {
						fmt.Println("3.", gtx.Constraints)
						paint.FillShape(gtx.Ops, color.NRGBA{R: 150, B: 200, A: 200}, clip.Rect{Min: gtx.Constraints.Min, Max: gtx.Constraints.Max}.Op())
						return layout.Dimensions{Size: gtx.Constraints.Max}
					}),
					layout.Stacked(func(gtx C) D {
						return layout.UniformInset(unit.Dp(30)).Layout(gtx, func(gtx C) D {
							fmt.Println("2.", gtx.Constraints)
							paint.FillShape(gtx.Ops, color.NRGBA{R: 100, B: 200, A: 200}, clip.Rect{Min: gtx.Constraints.Min, Max: gtx.Constraints.Max}.Op())
							return layout.Dimensions{Size: gtx.Constraints.Max}
						})
					}),
					layout.Expanded(func(gtx C) D {
						return layout.Background{}.Layout(gtx,
							func(gtx C) D {
								fmt.Println("1.", gtx.Constraints)
								paint.Fill(gtx.Ops, color.NRGBA{R: 200, B: 200, A: 100})

								return emptyWidget{
									Size: image.Point{X: 60, Y: 60},
								}.Layout(gtx)
							},
							func(gtx C) D {
								return layoutWidget(gtx, 60, 60)
							},
						)
						// paint.FillShape(gtx.Ops, color.NRGBA{R: 200, B: 200, A: 200}, clip.Rect{Min: gtx.Constraints.Min, Max: gtx.Constraints.Max}.Op())
						return layout.Dimensions{Size: gtx.Constraints.Max}
					}),
				)

				e.Frame(gtx.Ops)
			}
		}
	}()

	return nil
}

func runBorderLayoutWindow() error {
	go func() {
		newW := app.NewWindow(
			app.Title("Border layout"),
			app.Size(unit.Dp(400), unit.Dp(400)),
		)
		var ops op.Ops

		for {
			switch e := newW.NextEvent().(type) {
			case app.DestroyEvent:
				fmt.Println("Border layout closed.")
			case app.FrameEvent:
				// gtx := app.NewContext(&ops, e)
				gtx := layout.Context{
					Ops:    &ops,
					Now:    e.Now,
					Source: e.Source,
					Metric: e.Metric,
					Constraints: layout.Constraints{
						Min: image.Pt(0, 0),
						Max: image.Pt(300, 300),
					},
				}

				border := widget.Border{
					Color: color.NRGBA{R: 255, A: 255},
					Width: unit.Dp(1),
				}

				border.Layout(gtx, func(gtx C) D {
					return layout.Stack{Alignment: layout.Center}.Layout(gtx,
						layout.Stacked(func(gtx C) D {
							fmt.Println("1.", gtx.Constraints)
							paint.FillShape(gtx.Ops, color.NRGBA{R: 150, B: 200, A: 200}, clip.Rect{Min: gtx.Constraints.Min, Max: gtx.Constraints.Max}.Op())
							return border.Layout(gtx, func(gtx C) D {
								return layout.Dimensions{Size: gtx.Constraints.Max}
							})
						}),
						layout.Stacked(func(gtx C) D {
							return layout.UniformInset(unit.Dp(30)).Layout(gtx, func(gtx C) D {
								fmt.Println("2.", gtx.Constraints)
								paint.FillShape(gtx.Ops, color.NRGBA{R: 100, B: 200, A: 200}, clip.Rect{Min: gtx.Constraints.Min, Max: gtx.Constraints.Max}.Op())
								return border.Layout(gtx, func(gtx C) D {
									return layout.Dimensions{Size: gtx.Constraints.Max}
								})
							})
						}),
						// layout.Expanded(func(gtx C) D {
						// 	return border.Layout(gtx, func(gtx C) D {
						// 		return layout.Background{}.Layout(gtx,
						// 			func(gtx C) D {
						// 				fmt.Println("3.", gtx.Constraints)
						// 				paint.Fill(gtx.Ops, color.NRGBA{R: 200, B: 200, A: 100})

						// 				return emptyWidget{
						// 					Size: image.Point{X: 60, Y: 60},
						// 				}.Layout(gtx)
						// 			},
						// 			func(gtx C) D {
						// 				return layoutWidget(gtx, 60, 60)
						// 			},
						// 		)
						// 		// paint.FillShape(gtx.Ops, color.NRGBA{R: 200, B: 200, A: 200}, clip.Rect{Min: gtx.Constraints.Min, Max: gtx.Constraints.Max}.Op())
						// 		// return layout.Dimensions{Size: gtx.Constraints.Max}
						// 	})
						// }),
					)
				})

				e.Frame(gtx.Ops)
			}
		}
	}()

	return nil
}
func runHolygrailLayoutWindow() error {
	go func() {
		newW := app.NewWindow(
			app.Title("Holy grail layout"),
			app.Size(unit.Dp(640), unit.Dp(480)),
		)
		var ops op.Ops
		th := material.NewTheme()

		for {
			switch e := newW.NextEvent().(type) {
			case app.DestroyEvent:
				fmt.Println("Holy grail layout closed.")
			case app.FrameEvent:
				// gtx := app.NewContext(&ops, e)
				gtx := layout.Context{
					Ops:    &ops,
					Now:    e.Now,
					Source: e.Source,
					Metric: e.Metric,
					Constraints: layout.Constraints{
						Min: image.Pt(0, 0),
						Max: image.Pt(640, 480),
					},
				}

				border := widget.Border{
					Color: color.NRGBA{R: 255, A: 255},
					Width: unit.Dp(1),
				}

				layout.Flex{Axis: layout.Vertical}.Layout(gtx,
					layout.Rigid(func(gtx C) D {
						return border.Layout(gtx, func(gtx C) D {
							material.Label(th, unit.Sp(14), "header").Layout(gtx)
							return layoutWidget(gtx, 640, 100)
						})
					}),
					layout.Flexed(1, func(gtx C) D {
						// return layoutWidget(gtx, 640, 280)
						return layout.Flex{}.Layout(gtx,
							layout.Rigid(func(gtx C) D {
								return border.Layout(gtx, func(gtx C) D {
									material.Label(th, unit.Sp(14), "left sidebar").Layout(gtx)
									return layoutWidget(gtx, 100, 280)
								})
							}),

							layout.Rigid(func(gtx C) D {
								return border.Layout(gtx, func(gtx C) D {
									material.Label(th, unit.Sp(14), "content").Layout(gtx)
									return layoutWidget(gtx, 440, 280)
								})
							}),

							layout.Rigid(func(gtx C) D {
								return border.Layout(gtx, func(gtx C) D {
									material.Label(th, unit.Sp(14), "right sidebar").Layout(gtx)
									return layoutWidget(gtx, 100, 280)
								})
							}),
						)
					}),
					layout.Rigid(func(gtx C) D {
						return border.Layout(gtx, func(gtx C) D {
							material.Label(th, unit.Sp(14), "footer").Layout(gtx)
							return layoutWidget(gtx, 640, 100)
						})
					}),
				)

				e.Frame(gtx.Ops)
			}
		}
	}()

	return nil
}

type emptyWidget struct {
	Size image.Point
}

func (w emptyWidget) Layout(gtx C) D {
	return layout.Dimensions{Size: w.Size}
}

func layoutWidget(ctx C, width, height int) D {
	return layout.Dimensions{
		Size: image.Point{
			X: width,
			Y: height,
		},
	}
}
holy-grail-extend
package main

import (
	"fmt"
	"image"
	"image/color"
	"log"
	"os"

	"gioui.org/app"
	"gioui.org/io/system"
	"gioui.org/layout"
	"gioui.org/op"
	"gioui.org/op/clip"
	"gioui.org/op/paint"
	"gioui.org/unit"
	"gioui.org/widget"
	"gioui.org/widget/material"
)

type (
	C = layout.Context
	D = layout.Dimensions
)

func main() {
	go func() {
		w := app.NewWindow(
			app.Title("Layout"),
			app.Size(unit.Dp(640), unit.Dp(480)),
		)

		if err := run(w); err != nil {
			log.Fatal(err)
		}
		os.Exit(0)
	}()

	app.Main()
}

func run(w *app.Window) error {
	var ops op.Ops
	var button_stack widget.Clickable
	var button_border widget.Clickable
	var button_holygrail widget.Clickable
	var button_holygrailExtend widget.Clickable
	var button_close widget.Clickable
	th := material.NewTheme()

	for {
		switch e := w.NextEvent().(type) {

		case app.FrameEvent:
			gtx := app.NewContext(&ops, e)

			if button_stack.Clicked(gtx) {
				if err := runStackLayoutWindow(); err != nil {
					return err
				}
			}

			if button_border.Clicked(gtx) {
				if err := runBorderLayoutWindow(); err != nil {
					return err
				}
			}

			if button_holygrail.Clicked(gtx) {
				if err := runHolygrailLayoutWindow(); err != nil {
					return err
				}
			}

			if button_holygrailExtend.Clicked(gtx) {
				if err := runHolygrailExtendWindow(); err != nil {
					return err
				}
			}

			if button_close.Clicked(gtx) {
				fmt.Println("Close button clicked!")
				w.Perform(system.ActionClose)
			}

			btn1 := material.Button(th, &button_stack, "Stack")
			btn2 := material.Button(th, &button_border, "Border")
			btn3 := material.Button(th, &button_holygrail, "Holy Grail")
			btn4 := material.Button(th, &button_holygrailExtend, "Holy Grail Extend")
			btn5 := material.Button(th, &button_close, "Close")

			inset := layout.Inset{
				Top:    unit.Dp(0),
				Right:  unit.Dp(10),
				Bottom: unit.Dp(10),
				Left:   unit.Dp(10),
			}
			layout.Flex{
				Axis:    layout.Vertical,
				Spacing: layout.SpaceStart,
			}.Layout(gtx,
				layout.Rigid(
					func(gtx C) D {
						return inset.Layout(gtx, func(gtx C) D {
							return btn1.Layout(gtx)
						})
					},
				),
				layout.Rigid(
					func(gtx C) D {
						return inset.Layout(gtx, func(gtx C) D {
							return btn2.Layout(gtx)
						})
					},
				),
				layout.Rigid(
					func(gtx C) D {
						return inset.Layout(gtx, func(gtx C) D {
							return btn3.Layout(gtx)
						})
					},
				),
				layout.Rigid(
					func(gtx C) D {
						return inset.Layout(gtx, func(gtx C) D {
							return btn4.Layout(gtx)
						})
					},
				),
				layout.Rigid(
					func(gtx C) D {
						return inset.Layout(gtx, func(gtx C) D {
							return btn5.Layout(gtx)
						})
					},
				),
			)

			e.Frame(&ops)

		case app.ConfigEvent:
			fmt.Println(e.Config.Mode.String())
		case app.DestroyEvent:
			return e.Err
		}
	}
}

func runStackLayoutWindow() error {
	go func() {
		newW := app.NewWindow(
			app.Title("Stack layout"),
			app.Size(unit.Dp(400), unit.Dp(400)),
		)
		var ops op.Ops

		for {
			switch e := newW.NextEvent().(type) {
			case app.DestroyEvent:
				fmt.Println("Stack layout closed.")
			case app.FrameEvent:
				// gtx := app.NewContext(&ops, e)
				gtx := layout.Context{
					Ops:    &ops,
					Now:    e.Now,
					Source: e.Source,
					Metric: e.Metric,
					Constraints: layout.Constraints{
						Min: image.Pt(0, 0),
						Max: image.Pt(300, 300),
					},
				}

				layout.Stack{Alignment: layout.Center}.Layout(gtx,
					layout.Stacked(func(gtx C) D {
						fmt.Println("3.", gtx.Constraints)
						paint.FillShape(gtx.Ops, color.NRGBA{R: 150, B: 200, A: 200}, clip.Rect{Min: gtx.Constraints.Min, Max: gtx.Constraints.Max}.Op())
						return layout.Dimensions{Size: gtx.Constraints.Max}
					}),
					layout.Stacked(func(gtx C) D {
						return layout.UniformInset(unit.Dp(30)).Layout(gtx, func(gtx C) D {
							fmt.Println("2.", gtx.Constraints)
							paint.FillShape(gtx.Ops, color.NRGBA{R: 100, B: 200, A: 200}, clip.Rect{Min: gtx.Constraints.Min, Max: gtx.Constraints.Max}.Op())
							return layout.Dimensions{Size: gtx.Constraints.Max}
						})
					}),
					layout.Expanded(func(gtx C) D {
						return layout.Background{}.Layout(gtx,
							func(gtx C) D {
								fmt.Println("1.", gtx.Constraints)
								paint.Fill(gtx.Ops, color.NRGBA{R: 200, B: 200, A: 100})

								return emptyWidget{
									Size: image.Point{X: 60, Y: 60},
								}.Layout(gtx)
							},
							func(gtx C) D {
								return layoutWidget(gtx, 60, 60)
							},
						)
						// paint.FillShape(gtx.Ops, color.NRGBA{R: 200, B: 200, A: 200}, clip.Rect{Min: gtx.Constraints.Min, Max: gtx.Constraints.Max}.Op())
						return layout.Dimensions{Size: gtx.Constraints.Max}
					}),
				)

				e.Frame(gtx.Ops)
			}
		}
	}()

	return nil
}

func runBorderLayoutWindow() error {
	go func() {
		newW := app.NewWindow(
			app.Title("Border layout"),
			app.Size(unit.Dp(400), unit.Dp(400)),
		)
		var ops op.Ops

		for {
			switch e := newW.NextEvent().(type) {
			case app.DestroyEvent:
				fmt.Println("Border layout closed.")
			case app.FrameEvent:
				// gtx := app.NewContext(&ops, e)
				gtx := layout.Context{
					Ops:    &ops,
					Now:    e.Now,
					Source: e.Source,
					Metric: e.Metric,
					Constraints: layout.Constraints{
						Min: image.Pt(0, 0),
						Max: image.Pt(300, 300),
					},
				}

				border := widget.Border{
					Color: color.NRGBA{R: 255, A: 255},
					Width: unit.Dp(1),
				}

				border.Layout(gtx, func(gtx C) D {
					return layout.Stack{Alignment: layout.Center}.Layout(gtx,
						layout.Stacked(func(gtx C) D {
							fmt.Println("1.", gtx.Constraints)
							paint.FillShape(gtx.Ops, color.NRGBA{R: 150, B: 200, A: 200}, clip.Rect{Min: gtx.Constraints.Min, Max: gtx.Constraints.Max}.Op())
							return border.Layout(gtx, func(gtx C) D {
								return layout.Dimensions{Size: gtx.Constraints.Max}
							})
						}),
						layout.Stacked(func(gtx C) D {
							return layout.UniformInset(unit.Dp(30)).Layout(gtx, func(gtx C) D {
								fmt.Println("2.", gtx.Constraints)
								paint.FillShape(gtx.Ops, color.NRGBA{R: 100, B: 200, A: 200}, clip.Rect{Min: gtx.Constraints.Min, Max: gtx.Constraints.Max}.Op())
								return border.Layout(gtx, func(gtx C) D {
									return layout.Dimensions{Size: gtx.Constraints.Max}
								})
							})
						}),
						// layout.Expanded(func(gtx C) D {
						// 	return border.Layout(gtx, func(gtx C) D {
						// 		return layout.Background{}.Layout(gtx,
						// 			func(gtx C) D {
						// 				fmt.Println("3.", gtx.Constraints)
						// 				paint.Fill(gtx.Ops, color.NRGBA{R: 200, B: 200, A: 100})

						// 				return emptyWidget{
						// 					Size: image.Point{X: 60, Y: 60},
						// 				}.Layout(gtx)
						// 			},
						// 			func(gtx C) D {
						// 				return layoutWidget(gtx, 60, 60)
						// 			},
						// 		)
						// 		// paint.FillShape(gtx.Ops, color.NRGBA{R: 200, B: 200, A: 200}, clip.Rect{Min: gtx.Constraints.Min, Max: gtx.Constraints.Max}.Op())
						// 		// return layout.Dimensions{Size: gtx.Constraints.Max}
						// 	})
						// }),
					)
				})

				e.Frame(gtx.Ops)
			}
		}
	}()

	return nil
}

func runHolygrailLayoutWindow() error {
	go func() {
		newW := app.NewWindow(
			app.Title("Holy grail layout"),
			app.Size(unit.Dp(640), unit.Dp(480)),
		)
		var ops op.Ops
		th := material.NewTheme()

		for {
			switch e := newW.NextEvent().(type) {
			case app.DestroyEvent:
				fmt.Println("Holy grail layout closed.")
			case app.FrameEvent:
				// gtx := app.NewContext(&ops, e)
				gtx := layout.Context{
					Ops:    &ops,
					Now:    e.Now,
					Source: e.Source,
					Metric: e.Metric,
					Constraints: layout.Constraints{
						Min: image.Pt(0, 0),
						Max: image.Pt(640, 480),
					},
				}

				border := widget.Border{
					Color: color.NRGBA{R: 255, A: 255},
					Width: unit.Dp(1),
				}

				layout.Flex{Axis: layout.Vertical}.Layout(gtx,
					layout.Rigid(func(gtx C) D {
						return border.Layout(gtx, func(gtx C) D {
							material.Label(th, unit.Sp(14), "header").Layout(gtx)
							return layoutWidget(gtx, 640, 100)
						})
					}),
					layout.Flexed(1, func(gtx C) D {
						// return layoutWidget(gtx, 640, 280)
						return layout.Flex{}.Layout(gtx,
							layout.Rigid(func(gtx C) D {
								return border.Layout(gtx, func(gtx C) D {
									material.Label(th, unit.Sp(14), "left sidebar").Layout(gtx)
									return layoutWidget(gtx, 100, 280)
								})
							}),

							layout.Rigid(func(gtx C) D {
								return border.Layout(gtx, func(gtx C) D {
									material.Label(th, unit.Sp(14), "content").Layout(gtx)
									return layoutWidget(gtx, 440, 280)
								})
							}),

							layout.Rigid(func(gtx C) D {
								return border.Layout(gtx, func(gtx C) D {
									material.Label(th, unit.Sp(14), "right sidebar").Layout(gtx)
									return layoutWidget(gtx, 100, 280)
								})
							}),
						)
					}),
					layout.Rigid(func(gtx C) D {
						return border.Layout(gtx, func(gtx C) D {
							material.Label(th, unit.Sp(14), "footer").Layout(gtx)
							return layoutWidget(gtx, 640, 100)
						})
					}),
				)

				e.Frame(gtx.Ops)
			}
		}
	}()

	return nil
}

func runHolygrailExtendWindow() error {
	go func() {
		newW := app.NewWindow(
			app.Title("Holy grail extend layout"),
			app.Size(unit.Dp(640), unit.Dp(480)),
		)
		var ops op.Ops
		th := material.NewTheme()

		for {
			switch e := newW.NextEvent().(type) {
			case app.DestroyEvent:
				fmt.Println("Holy grail extend layout closed.")
			case app.FrameEvent:
				gtx := app.NewContext(&ops, e)
				// gtx := layout.Context{
				// 	Ops:    &ops,
				// 	Now:    e.Now,
				// 	Source: e.Source,
				// 	Metric: e.Metric,
				// 	// Constraints: layout.Constraints{
				// 	// 	Min: image.Pt(0, 0),
				// 	// 	Max: image.Pt(640, 480),
				// 	// },
				// }

				// fmt.Println(gtx.Constraints)
				// fmt.Println(gtx)
				// fmt.Println(gtx.Metric)
				// fmt.Println(gtx.Now)
				// fmt.Println(gtx.Locale)
				// fmt.Println(gtx.Source)
				// fmt.Println(gtx.Ops)

				// fmt.Printf("e.Size type: %T\n", e.Size)
				fmt.Println(e.Size)
				// fmt.Println(e.Size.X)
				// fmt.Println(e.Size.Y)

				border := widget.Border{
					Color: color.NRGBA{R: 255, A: 255},
					Width: unit.Dp(1),
				}

				layout.Flex{Axis: layout.Vertical}.Layout(gtx,
					layout.Rigid(func(gtx C) D {
						return border.Layout(gtx, func(gtx C) D {
							material.Label(th, unit.Sp(14), "header").Layout(gtx)
							return layoutWidget(gtx, e.Size.X, 100)
						})
					}),
					layout.Flexed(1, func(gtx C) D {
						// return layoutWidget(gtx, 640, 280)
						return layout.Flex{}.Layout(gtx,
							layout.Rigid(func(gtx C) D {
								return border.Layout(gtx, func(gtx C) D {
									material.Label(th, unit.Sp(14), "left sidebar").Layout(gtx)
									return layoutWidget(gtx, 100, e.Size.Y-200)
								})
							}),

							layout.Rigid(func(gtx C) D {
								return border.Layout(gtx, func(gtx C) D {
									material.Label(th, unit.Sp(14), "content").Layout(gtx)
									return layoutWidget(gtx, e.Size.X-200, e.Size.Y-200)
								})
							}),

							layout.Rigid(func(gtx C) D {
								return border.Layout(gtx, func(gtx C) D {
									material.Label(th, unit.Sp(14), "right sidebar").Layout(gtx)
									return layoutWidget(gtx, 100, e.Size.Y-200)
								})
							}),
						)
					}),
					layout.Rigid(func(gtx C) D {
						return border.Layout(gtx, func(gtx C) D {
							material.Label(th, unit.Sp(14), "footer").Layout(gtx)
							return layoutWidget(gtx, e.Size.X, 100)
						})
					}),
				)

				e.Frame(gtx.Ops)
			}
		}
	}()

	return nil
}

type emptyWidget struct {
	Size image.Point
}

func (w emptyWidget) Layout(gtx C) D {
	return layout.Dimensions{Size: w.Size}
}

func layoutWidget(ctx C, width, height int) D {
	return layout.Dimensions{
		Size: image.Point{
			X: width,
			Y: height,
		},
	}
}
others centered / background 2 col 3 col

holy-grail-extend-responsive

wrap ? ref: gio-x/outlay/flow.go , flow is renamed from grid
RAM (Repeat, Auto, Minmax) ?

grid / gio-x/outlay/flow
12-span-grid

layout reference website:

Layout details
repro: examples / components / widget
component menu
package main

import (
	"fmt"
	"image"
	"image/color"
	"log"
	"os"

	"gioui.org/app"
	"gioui.org/io/pointer"
	"gioui.org/io/system"
	"gioui.org/layout"
	"gioui.org/op"
	"gioui.org/unit"
	"gioui.org/widget"
	"gioui.org/widget/material"
	"gioui.org/x/component"
)

type (
	C = layout.Context
	D = layout.Dimensions
)

func main() {
	go func() {
		w := app.NewWindow(
			app.Title("Layout"),
			app.Size(unit.Dp(640), unit.Dp(480)),
		)

		if err := run(w); err != nil {
			log.Fatal(err)
		}
		os.Exit(0)
	}()

	app.Main()
}

func run(w *app.Window) error {
	var ops op.Ops
	var button_menu widget.Clickable
	var button_close widget.Clickable
	th := material.NewTheme()

	for {
		switch e := w.NextEvent().(type) {

		case app.FrameEvent:
			gtx := app.NewContext(&ops, e)

			if button_menu.Clicked(gtx) {
				if err := runMenuWindow(); err != nil {
					return err
				}
			}

			if button_close.Clicked(gtx) {
				fmt.Println("Close button clicked!")
				w.Perform(system.ActionClose)
			}

			btn1 := material.Button(th, &button_menu, "Menu")
			btn2 := material.Button(th, &button_close, "Close")

			inset := layout.Inset{
				Top:    unit.Dp(0),
				Right:  unit.Dp(10),
				Bottom: unit.Dp(10),
				Left:   unit.Dp(10),
			}
			layout.Flex{
				Axis:    layout.Vertical,
				Spacing: layout.SpaceStart,
			}.Layout(gtx,
				layout.Rigid(
					func(gtx C) D {
						return inset.Layout(gtx, func(gtx C) D {
							return btn1.Layout(gtx)
						})
					},
				),
				layout.Rigid(
					func(gtx C) D {
						return inset.Layout(gtx, func(gtx C) D {
							return btn2.Layout(gtx)
						})
					},
				),
			)

			e.Frame(&ops)

		case app.ConfigEvent:
			fmt.Println(e.Config.Mode.String())
		case app.DestroyEvent:
			return e.Err
		}
	}
}

func runMenuWindow() error {
	go func() {
		newW := app.NewWindow(
			app.Title("Menu layout"),
			app.Size(unit.Dp(640), unit.Dp(480)),
		)
		var ops op.Ops
		th := material.NewTheme()

		var newfileButton, exitButton, copyButton, pasteButton widget.Clickable
		menufileContextArea := component.ContextArea{Activation: pointer.ButtonPrimary, PositionHint: layout.Center}
		menueditContextArea := component.ContextArea{Activation: pointer.ButtonPrimary, PositionHint: layout.Center}

		for {
			switch e := newW.NextEvent().(type) {
			case app.DestroyEvent:
				fmt.Println("Menu layout closed.")
			case app.FrameEvent:
				gtx := app.NewContext(&ops, e)
				// fmt.Println(e.Size)

				if exitButton.Clicked(gtx) {
					fmt.Println("exitButton in menu layout clicked.")
				}

				// could add more menu click actions

				border := widget.Border{
					Color: color.NRGBA{R: 255, A: 255},
					Width: unit.Dp(1),
				}

				layout.Flex{Axis: layout.Vertical}.Layout(gtx,
					// custom menu
					layout.Rigid(func(gtx C) D {
						menu_File := component.MenuState{
							Options: []func(gtx C) D{
								component.MenuItem(th, &newfileButton, "New File").Layout,
								component.Divider(th).Layout,
								component.MenuItem(th, &exitButton, "Exit").Layout,
							},
						}
						menu_Edit := component.MenuState{
							Options: []func(gtx C) D{
								component.MenuItem(th, &copyButton, "Copy").Layout,
								component.Divider(th).Layout,
								component.MenuItem(th, &pasteButton, "Paste").Layout,
							},
						}

						return widget.Border{
							Color: color.NRGBA{A: 255},
							Width: unit.Dp(0),
						}.Layout(gtx, func(gtx C) D {
							return layout.Flex{}.Layout(gtx,
								layout.Rigid(func(gtx C) D {
									return layout.Stack{}.Layout(gtx,
										layout.Stacked(func(gtx C) D {
											return layout.UniformInset(unit.Dp(4)).Layout(gtx, func(gtx C) D {
												return component.SurfaceStyle{Theme: th, ShadowStyle: component.Shadow(unit.Dp(1), unit.Dp(2))}.Layout(gtx, func(gtx C) D {
													return layout.UniformInset(unit.Dp(4)).Layout(gtx, material.Body1(th, "File").Layout)
												})
											})
										}),
										layout.Expanded(func(gtx C) D {
											return menufileContextArea.Layout(gtx, func(gtx C) D {
												gtx.Constraints.Min = image.Point{}
												return component.Menu(th, &menu_File).Layout(gtx)
											})
										}),
									)
								}),
								layout.Rigid(func(gtx C) D {
									return layout.Stack{}.Layout(gtx,
										layout.Stacked(func(gtx C) D {
											return layout.UniformInset(unit.Dp(4)).Layout(gtx, func(gtx C) D {
												return component.SurfaceStyle{Theme: th, ShadowStyle: component.Shadow(unit.Dp(1), unit.Dp(2))}.Layout(gtx, func(gtx C) D {
													return layout.UniformInset(unit.Dp(4)).Layout(gtx, material.Body1(th, "Edit").Layout)
												})
											})
										}),
										layout.Expanded(func(gtx C) D {
											return menueditContextArea.Layout(gtx, func(gtx C) D {
												gtx.Constraints.Min = image.Point{}
												return component.Menu(th, &menu_Edit).Layout(gtx)
											})
										}),
									)
								}),
							)
						})
					}),

					layout.Rigid(func(gtx C) D {
						return border.Layout(gtx, func(gtx C) D {
							material.Label(th, unit.Sp(14), "header").Layout(gtx)
							return layoutWidget(gtx, e.Size.X, 54)
						})
					}),
					layout.Flexed(1, func(gtx C) D {
						return layout.Flex{}.Layout(gtx,
							layout.Rigid(func(gtx C) D {
								return border.Layout(gtx, func(gtx C) D {
									material.Label(th, unit.Sp(14), "left sidebar").Layout(gtx)
									return layoutWidget(gtx, 100, e.Size.Y-200)
								})
							}),

							layout.Rigid(func(gtx C) D {
								return border.Layout(gtx, func(gtx C) D {
									material.Label(th, unit.Sp(14), "content").Layout(gtx)
									return layoutWidget(gtx, e.Size.X-200, e.Size.Y-200)
								})
							}),

							layout.Rigid(func(gtx C) D {
								return border.Layout(gtx, func(gtx C) D {
									material.Label(th, unit.Sp(14), "right sidebar").Layout(gtx)
									return layoutWidget(gtx, 100, e.Size.Y-200)
								})
							}),
						)
					}),
					layout.Rigid(func(gtx C) D {
						return border.Layout(gtx, func(gtx C) D {
							material.Label(th, unit.Sp(14), "footer").Layout(gtx)
							return layoutWidget(gtx, e.Size.X, 100)
						})
					}),
				)

				e.Frame(gtx.Ops)
			}
		}
	}()

	return nil
}

type emptyWidget struct {
	Size image.Point
}

func (w emptyWidget) Layout(gtx C) D {
	return layout.Dimensions{Size: w.Size}
}

func layoutWidget(ctx C, width, height int) D {
	return layout.Dimensions{
		Size: image.Point{
			X: width,
			Y: height,
		},
	}
}
others
  • gio example

    • 7 gui: counter, temperature, timer
    • bidi: word selection
    • kitchen: material widgets
    • multiwindow
    • outlay(grid)
    • tabs
  • layout

  • component

    • menu
    • tabs
    • carousels
  • webview

  • scrollbar

  • list rendering with scrollbar

  • “github.com/getlantern/systray”

  • “github.com/go-co-op/gocron”

  • caculator

  • todo list

  • calender

  • ref: https://github.com/gioui/gio/blob/main/widget/example_test.go

    • draggable
OSC: Open Source Collaboration
  • idiomatic go project structure
On this page: