1 Hello World Again

Hello world again#

showing words “hello world”#

After the 0-hello-world , now we are going to work on a window with some static content(showing words “Hello world”), a window with button in material theme, then we will complete this first program and move on to other examples.

From now on, I will only show the code, and put comments in the code lines, only show extra instructions when needed.

Move on 1, put code in a go routine:#

package main

import (
	"gioui.org/app"
	"gioui.org/op"
)

func main() {
	go func() { // put code in a go routine
		w := app.NewWindow()
		var ops op.Ops

		for {
			switch e := w.NextEvent().(type) {
			case app.FrameEvent:
				e.Frame(&ops)
			}
		}
	}() // this line close the go routine code, 
        // with () at the end means it will run immediatelly

	app.Main() // gio need this line to hand over the routine to main program
}
go run ./0-hello-world.go

Move on 2, put the run(w *app.Window) code in a seperate function:#

package main

import (
	"log"
	"os"

	"gioui.org/app"
	"gioui.org/op"
)

func main() {
	go func() {
		w := app.NewWindow()

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

	app.Main()
}

func run(w *app.Window) error {
	var ops op.Ops

	for {
		switch e := w.NextEvent().(type) {
		case app.FrameEvent:
			e.Frame(&ops)
		}
	}
}
go run ./0-hello-world.go

Move on 3, draw words “Hello world” in the window:#

(This is a long way moving forward, so please be patient in this step.
The good news is, after this step, the learning curve will not be so steep.)

package main

import ( // added many dependencies, this seems trivial,
         // but if you added "Go" plugin (by Go Team at Google) in vscode,
         // you are no need to add these manually, just write the code,
         // save the code, vscode will add these dependencies for you 
         // automatically. But be noticed sometimes it will add some wrong
         // dependencies, so also keep an eye here.
	"log"
	"os"

	"gioui.org/app"
	"gioui.org/font"
	"gioui.org/layout"
	"gioui.org/op"
	"gioui.org/text"
	"gioui.org/unit"
	"gioui.org/widget"  // if this is been added as 
                        // "golang.org/x/exp/shiny/widget"
                        // you need to correct it manually
)

func main() {
	go func() {
		w := app.NewWindow()

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

	app.Main()
}

func run(w *app.Window) error {
	var ops op.Ops

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

			gtx := app.NewContext(&ops, e) 
            // define a new context variable,
            // context is very important in gio, 
            // it is used almost everywhere in layout

			greeting_txt := "Hello world" // define the words to show

			layout.Flex{}.Layout(gtx,      
            // use layout package to layout our content
                // layout Flex source: https://github.com/gioui/gio/blob/main/layout/flex.go#L13
                // check though Flex source at this move on.
                // layout Flex.Layout: func (f Flex) Layout(gtx Context, children ...FlexChild) Dimensions {...}

				layout.Rigid(  
                // func Rigid(widget Widget) FlexChild {...}
                    // type Widget func(gtx Context) Dimensions

					func(gtx layout.Context) layout.Dimensions {
                    // this is a widget: func(gtx Context) Dimensions
                    
						return widget.Label{}.Layout(
                        // widget Label is used for static text.
                        // widget source: https://github.com/gioui/gio/blob/main/widget/label.go
                        // the widget Label Layout func:
                        // func (l Label) Layout(gtx layout.Context, lt *text.Shaper, font font.Font, size unit.Sp, txt string, textMaterial op.CallOp) layout.Dimensions {...}

							gtx,              // these parameters to fit the widget.Label{}.Layout() request
							&text.Shaper{},
							font.Font{},
							unit.Sp(14),
							greeting_txt,     // this is the actual text to show
							op.CallOp{},
						)
					},
				),
			)

			e.Frame(gtx.Ops)   // use our defined gtx.Ops here

		case app.DestroyEvent: // The case means window was closed.
			return e.Err       // With this case section, 
			                   // and the os.Exit(0) in the
							   // go routine, close the window
							   // will not give an error now.
		}
	}
}
go mod tidy
go run ./0-hello-world.go

You will get a window which now actually showing “Hello world”. Hooray!

But we still have to move on a bit at this “Most simple” hello world sample.

Move on 4: put the text in Material theme as a button.#

package main

import (
	"log"
	"os"

	"gioui.org/app"
	"gioui.org/op"
	"gioui.org/widget/material"
)

func main() {
	go func() {
		w := app.NewWindow()

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

	app.Main()
}

func run(w *app.Window) error {
	var ops op.Ops

	th := material.NewTheme() // set a new material theme variable

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

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

			greeting_txt := material.H1(th, "Hello world")
			// define the words to show
			// as a material theme label, text size H1

			greeting_txt.Layout(gtx)
			// the layout now seems simple, right?
			// because the theme did the trivial things for us

			e.Frame(gtx.Ops)

		case app.DestroyEvent:
			return e.Err
		}
	}
}
go run ./0-hello-world.go

Make the text to be a button:

package main

import (
	"log"
	"os"

	"gioui.org/app"
	"gioui.org/op"
	"gioui.org/widget"
	"gioui.org/widget/material"
)

func main() {
	go func() {
		w := app.NewWindow()

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

	app.Main()
}

func run(w *app.Window) error {
	var ops op.Ops

	th := material.NewTheme()

	var greeting_btn widget.Clickable
	// set a widget.Clickable varialbe
	// for using in the Material.Button later

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

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

			greeting_button := material.Button(th, &greeting_btn, "Hello world")
			// put text and clickable in material.Button
			// to form a button

			greeting_button.Layout(gtx)
			// and then layout the button

			e.Frame(gtx.Ops)

		case app.DestroyEvent:
			return e.Err
		}
	}
}
go run ./0-hello-world.go

Now when you click on the button, you will see a click animation.

Move on 5, set the window title and size:#

package main

import (
	"log"
	"os"

	"gioui.org/app"
	"gioui.org/op"
	"gioui.org/unit" // if this is been added as 
                     // "golang.org/x/exp/shiny/unit"
                     // you need to correct it manually
	"gioui.org/widget"
	"gioui.org/widget/material"
)

func main() {
	go func() {
		w := app.NewWindow(
			// ref: https://github.com/gioui/gio/blob/main/app/window.go#L145
			// check the default value of this NewWindow function

			app.Title("Hello"),                   // default is "gio"
			app.Size(unit.Dp(200), unit.Dp(200)), // default is 800 x 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

	th := material.NewTheme()

	var greeting_btn widget.Clickable

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

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

			greeting_button := material.Button(th, &greeting_btn, "Hello world")

			greeting_button.Layout(gtx)

			e.Frame(gtx.Ops)

		case app.DestroyEvent:
			return e.Err
		}
	}
}
go run ./0-hello-world.go

Congratulations! You have complete this “Most simple” gio program, almostly.

Bonus. Move on 6, add button click function:#

package main

import (
	"log"
	"os"

	"gioui.org/app"
	"gioui.org/op"
	"gioui.org/unit"
	"gioui.org/widget"
	"gioui.org/widget/material"
)

func main() {
	go func() {
		w := app.NewWindow(
			app.Title("Hello"),
			app.Size(unit.Dp(200), unit.Dp(200)),
		)

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

	app.Main()
}

func run(w *app.Window) error {
	var ops op.Ops

	th := material.NewTheme()

	var greeting_btn widget.Clickable 
    // this is the button variable to be clicked

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

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

			if greeting_btn.Clicked(gtx) {
				// set action on clicking on the button
				// notice the varible name here, do not missspell it.

				println("greeting_btn clicked")
			}

			greeting_button := material.Button(th, &greeting_btn, "Hello world")

			greeting_button.Layout(gtx)

			e.Frame(gtx.Ops)

		case app.DestroyEvent:
			return e.Err
		}
	}
}
go run ./0-hello-world.go

Notice the terminal will output something now when clicking on the button.

That’s all for this “Most simple” gio hello world practice.

Now you can check the Official hello world in the “Get Started” page.
Hope you can ACTUALLY get started with gio now!

Remember to come back here when you finished that page.

» Official Get Started Page

Egg timer#

When you come back, we will checking Jon Strand’s tutorial “Implementing an egg timer”, that is a really good gio tutorial.

» 2-egg-timer