unknown 5 years ago
parent
commit
90ff08ed98

File diff suppressed because it is too large
+ 9 - 0
gui/fyne_demo/data/bundled-scene.go


BIN
gui/fyne_demo/data/fyne_scene_dark.png


BIN
gui/fyne_demo/data/fyne_scene_light.png


+ 57 - 0
gui/fyne_demo/data/gen.go

@@ -0,0 +1,57 @@
+// +build ignore
+
+package main
+
+import (
+	"fmt"
+	"os"
+	"path"
+	"runtime"
+
+	"fyne.io/fyne"
+)
+
+func bundleFile(name string, filepath string, f *os.File) {
+	res, err := fyne.LoadResourceFromPath(filepath)
+	if err != nil {
+		fyne.LogError("Unable to load file "+filepath, err)
+		return
+	}
+
+	_, err = f.WriteString(fmt.Sprintf("var %s = %#v\n", name, res))
+	if err != nil {
+		fyne.LogError("Unable to write to bundled file", err)
+	}
+}
+
+func openFile(filename string) *os.File {
+	os.Remove(filename)
+	_, dirname, _, _ := runtime.Caller(0)
+	f, err := os.Create(path.Join(path.Dir(dirname), filename))
+	if err != nil {
+		fyne.LogError("Unable to open file "+filename, err)
+		return nil
+	}
+
+	_, err = f.WriteString("// **** THIS FILE IS AUTO-GENERATED, PLEASE DO NOT EDIT IT **** //\n\npackage data\n\nimport \"fyne.io/fyne\"\n\n")
+	if err != nil {
+		fyne.LogError("Unable to write file "+filename, err)
+		return nil
+	}
+
+	return f
+}
+
+func iconDir() string {
+	_, dirname, _, _ := runtime.Caller(0)
+	return path.Join(path.Dir(dirname), "icons")
+}
+
+func main() {
+	f := openFile("bundled-scene.go")
+
+	bundleFile("fynescenedark", "fyne_scene_dark.png", f)
+	bundleFile("fynescenelight", "fyne_scene_light.png", f)
+
+	f.Close()
+}

+ 40 - 0
gui/fyne_demo/data/icons.go

@@ -0,0 +1,40 @@
+package data
+
+import (
+	"fyne.io/fyne"
+)
+
+// ThemedResource is a resource wrapper that will return an appropriate resource
+// for the currently selected theme.
+type ThemedResource struct {
+	dark, light fyne.Resource
+}
+
+func isLight() bool {
+	r, g, b, _ := fyne.CurrentApp().Settings().Theme().TextColor().RGBA()
+	return r < 0xaaaa && g < 0xaaaa && b < 0xaaaa
+}
+
+// Name returns the underlying resource name (used for caching)
+func (res *ThemedResource) Name() string {
+	if isLight() {
+		return res.light.Name()
+	}
+	return res.dark.Name()
+}
+
+// Content returns the underlying content of the correct resource for the current theme
+func (res *ThemedResource) Content() []byte {
+	if isLight() {
+		return res.light.Content()
+	}
+	return res.dark.Content()
+}
+
+// NewThemedResource creates a resource that adapts to the current theme setting.
+func NewThemedResource(dark, light fyne.Resource) *ThemedResource {
+	return &ThemedResource{dark, light}
+}
+
+// FyneScene contains the full fyne logo with background design
+var FyneScene = NewThemedResource(fynescenedark, fynescenelight)

+ 76 - 0
gui/fyne_demo/main.go

@@ -0,0 +1,76 @@
+// Package main provides various examples of Fyne API capabilities
+package main
+
+import (
+	"fmt"
+	"net/url"
+
+	"fyne.io/fyne"
+	"fyne.io/fyne/app"
+	"fyne.io/fyne/canvas"
+	"fyne.io/fyne/layout"
+	"fyne.io/fyne/theme"
+	"fyne.io/fyne/widget"
+	"github.com/cnlh/nps/gui/fyne_demo/data"
+	"github.com/cnlh/nps/gui/fyne_demo/screens"
+)
+
+const preferenceCurrentTab = "currentTab"
+
+func welcomeScreen(a fyne.App) fyne.CanvasObject {
+	logo := canvas.NewImageFromResource(data.FyneScene)
+	logo.SetMinSize(fyne.NewSize(228, 167))
+
+	link, err := url.Parse("https://fyne.io/")
+	if err != nil {
+		fyne.LogError("Could not parse URL", err)
+	}
+
+	return widget.NewVBox(
+		widget.NewLabelWithStyle("Welcome to the Fyne toolkit demo app", fyne.TextAlignCenter, fyne.TextStyle{Bold: true}),
+		layout.NewSpacer(),
+		widget.NewHBox(layout.NewSpacer(), logo, layout.NewSpacer()),
+		widget.NewHyperlinkWithStyle("fyne.io", link, fyne.TextAlignCenter, fyne.TextStyle{}),
+		layout.NewSpacer(),
+
+		widget.NewGroup("Theme",
+			fyne.NewContainerWithLayout(layout.NewGridLayout(2),
+				widget.NewButton("Dark", func() {
+					a.Settings().SetTheme(theme.DarkTheme())
+				}),
+				widget.NewButton("Light", func() {
+					a.Settings().SetTheme(theme.LightTheme())
+				}),
+			),
+		),
+	)
+}
+
+func main() {
+	a := app.NewWithID("io.fyne.demo")
+	a.SetIcon(theme.FyneLogo())
+
+	w := a.NewWindow("Fyne Demo")
+	w.SetMainMenu(fyne.NewMainMenu(fyne.NewMenu("File",
+		fyne.NewMenuItem("New", func() { fmt.Println("Menu New") }),
+		// a quit item will be appended to our first menu
+	), fyne.NewMenu("Edit",
+		fyne.NewMenuItem("Cut", func() { fmt.Println("Menu Cut") }),
+		fyne.NewMenuItem("Copy", func() { fmt.Println("Menu Copy") }),
+		fyne.NewMenuItem("Paste", func() { fmt.Println("Menu Paste") }),
+	)))
+	w.SetMaster()
+
+	tabs := widget.NewTabContainer(
+		widget.NewTabItemWithIcon("Welcome", theme.HomeIcon(), welcomeScreen(a)),
+		widget.NewTabItemWithIcon("Widgets", theme.ContentCopyIcon(), screens.WidgetScreen()),
+		widget.NewTabItemWithIcon("Graphics", theme.DocumentCreateIcon(), screens.GraphicsScreen()),
+		widget.NewTabItemWithIcon("Windows", theme.ViewFullScreenIcon(), screens.DialogScreen(w)),
+		widget.NewTabItemWithIcon("Advanced", theme.SettingsIcon(), screens.AdvancedScreen(w)))
+	tabs.SetTabLocation(widget.TabLocationLeading)
+	tabs.SelectTabIndex(a.Preferences().Int(preferenceCurrentTab))
+	w.SetContent(tabs)
+
+	w.ShowAndRun()
+	a.Preferences().SetInt(preferenceCurrentTab, tabs.CurrentTabIndex())
+}

+ 65 - 0
gui/fyne_demo/screens/advanced.go

@@ -0,0 +1,65 @@
+package screens
+
+import (
+	"fmt"
+
+	"fyne.io/fyne"
+	"fyne.io/fyne/driver/desktop"
+	"fyne.io/fyne/layout"
+	"fyne.io/fyne/widget"
+)
+
+func scaleString(c fyne.Canvas) string {
+	return fmt.Sprintf("%0.2f", c.Scale())
+}
+
+func prependTo(g *widget.Group, s string) {
+	g.Prepend(widget.NewLabel(s))
+}
+
+// AdvancedScreen loads a panel that shows details and settings that are a bit
+// more detailed than normally needed.
+func AdvancedScreen(win fyne.Window) fyne.CanvasObject {
+	scale := widget.NewLabel("")
+
+	screen := widget.NewGroup("Screen", widget.NewForm(
+		&widget.FormItem{Text: "Scale", Widget: scale},
+	))
+
+	scale.SetText(scaleString(win.Canvas()))
+
+	label := widget.NewLabel("Just type...")
+	generic := widget.NewGroupWithScroller("Generic")
+	desk := widget.NewGroupWithScroller("Desktop")
+
+	win.Canvas().SetOnTypedRune(func(r rune) {
+		prependTo(generic, "Rune: "+string(r))
+	})
+	win.Canvas().SetOnTypedKey(func(ev *fyne.KeyEvent) {
+		prependTo(generic, "Key : "+string(ev.Name))
+	})
+	if deskCanvas, ok := win.Canvas().(desktop.Canvas); ok {
+		deskCanvas.SetOnKeyDown(func(ev *fyne.KeyEvent) {
+			prependTo(desk, "KeyDown: "+string(ev.Name))
+		})
+		deskCanvas.SetOnKeyUp(func(ev *fyne.KeyEvent) {
+			prependTo(desk, "KeyUp  : "+string(ev.Name))
+		})
+	}
+
+	return widget.NewHBox(widget.NewVBox(screen,
+		widget.NewButton("Custom Theme", func() {
+			fyne.CurrentApp().Settings().SetTheme(newCustomTheme())
+		}),
+		widget.NewButton("Fullscreen", func() {
+			win.SetFullScreen(!win.FullScreen())
+		}),
+	),
+
+		fyne.NewContainerWithLayout(layout.NewBorderLayout(label, nil, nil, nil),
+			label,
+			fyne.NewContainerWithLayout(layout.NewGridLayout(2),
+				generic, desk,
+			),
+		))
+}

+ 50 - 0
gui/fyne_demo/screens/graphics.go

@@ -0,0 +1,50 @@
+package screens
+
+import (
+	"image/color"
+	"time"
+
+	"fyne.io/fyne"
+	"fyne.io/fyne/canvas"
+	"fyne.io/fyne/layout"
+	"fyne.io/fyne/theme"
+)
+
+func rgbGradient(x, y, w, h int) color.Color {
+	g := int(float32(x) / float32(w) * float32(255))
+	b := int(float32(y) / float32(h) * float32(255))
+
+	return color.RGBA{uint8(255 - b), uint8(g), uint8(b), 0xff}
+}
+
+// GraphicsScreen loads a graphics example panel for the demo app
+func GraphicsScreen() fyne.CanvasObject {
+	gradient := canvas.NewHorizontalGradient(color.RGBA{0x80, 0, 0, 0xff}, color.RGBA{0, 0x80, 0, 0xff})
+	go func() {
+		for {
+			time.Sleep(time.Second)
+
+			gradient.Angle += 45
+			if gradient.Angle >= 360 {
+				gradient.Angle -= 360
+			}
+			canvas.Refresh(gradient)
+		}
+	}()
+	content := fyne.NewContainerWithLayout(layout.NewFixedGridLayout(fyne.NewSize(90, 90)),
+		&canvas.Rectangle{FillColor: color.RGBA{0x80, 0, 0, 0xff},
+			StrokeColor: color.RGBA{0xff, 0xff, 0xff, 0xff},
+			StrokeWidth: 1},
+		canvas.NewImageFromResource(theme.FyneLogo()),
+		&canvas.Line{StrokeColor: color.RGBA{0, 0, 0x80, 0xff}, StrokeWidth: 5},
+		&canvas.Circle{StrokeColor: color.RGBA{0, 0, 0x80, 0xff},
+			FillColor:   color.RGBA{0x30, 0x30, 0x30, 0x60},
+			StrokeWidth: 2},
+		canvas.NewText("Text", color.RGBA{0, 0x80, 0, 0xff}),
+		canvas.NewRasterWithPixels(rgbGradient),
+		gradient,
+		canvas.NewRadialGradient(color.RGBA{0x80, 0, 0, 0xff}, color.RGBA{0, 0x80, 0x80, 0xff}),
+	)
+
+	return fyne.NewContainerWithLayout(layout.NewAdaptiveGridLayout(2), content, IconsPanel())
+}

+ 144 - 0
gui/fyne_demo/screens/icons.go

@@ -0,0 +1,144 @@
+package screens
+
+import (
+	"image/color"
+
+	"fyne.io/fyne"
+	"fyne.io/fyne/canvas"
+	"fyne.io/fyne/layout"
+	"fyne.io/fyne/theme"
+	"fyne.io/fyne/widget"
+)
+
+type browser struct {
+	current int
+
+	name *widget.Select
+	icon *widget.Icon
+}
+
+func (b *browser) setIcon(index int) {
+	if index < 0 || index > len(icons)-1 {
+		return
+	}
+	b.current = index
+
+	b.name.SetSelected(icons[index].name)
+	b.icon.SetResource(icons[index].icon)
+}
+
+// IconsPanel loads a panel that shows the various icons available in Fyne
+func IconsPanel() fyne.CanvasObject {
+	b := &browser{}
+
+	prev := widget.NewButtonWithIcon("", theme.NavigateBackIcon(), func() {
+		b.setIcon(b.current - 1)
+	})
+	next := widget.NewButtonWithIcon("", theme.NavigateNextIcon(), func() {
+		b.setIcon(b.current + 1)
+	})
+	b.name = widget.NewSelect(iconList(), func(name string) {
+		for i, icon := range icons {
+			if icon.name == name {
+				if b.current != i {
+					b.setIcon(i)
+				}
+				break
+			}
+		}
+	})
+	b.name.SetSelected(icons[b.current].name)
+	bar := widget.NewHBox(prev, next, b.name)
+
+	background := canvas.NewRasterWithPixels(checkerPattern)
+	background.SetMinSize(fyne.NewSize(280, 280))
+	b.icon = widget.NewIcon(icons[b.current].icon)
+
+	return fyne.NewContainerWithLayout(layout.NewBorderLayout(
+		bar, nil, nil, nil), bar, background, b.icon)
+}
+
+func checkerPattern(x, y, _, _ int) color.Color {
+	x /= 20
+	y /= 20
+
+	if x%2 == y%2 {
+		return theme.BackgroundColor()
+	}
+
+	return theme.ButtonColor()
+}
+
+func iconList() []string {
+	var ret []string
+	for _, icon := range icons {
+		ret = append(ret, icon.name)
+	}
+
+	return ret
+}
+
+var icons = []struct {
+	name string
+	icon fyne.Resource
+}{
+	{"CancelIcon", theme.CancelIcon()},
+	{"ConfirmIcon", theme.ConfirmIcon()},
+	{"DeleteIcon", theme.DeleteIcon()},
+	{"SearchIcon", theme.SearchIcon()},
+	{"SearchReplaceIcon", theme.SearchReplaceIcon()},
+
+	{"CheckButtonIcon", theme.CheckButtonIcon()},
+	{"CheckButtonCheckedIcon", theme.CheckButtonCheckedIcon()},
+	{"RadioButtonIcon", theme.RadioButtonIcon()},
+	{"RadioButtonCheckedIcon", theme.RadioButtonCheckedIcon()},
+
+	{"ContentAddIcon", theme.ContentAddIcon()},
+	{"ContentRemoveIcon", theme.ContentRemoveIcon()},
+	{"ContentClearIcon", theme.ContentClearIcon()},
+	{"ContentCutIcon", theme.ContentCutIcon()},
+	{"ContentCopyIcon", theme.ContentCopyIcon()},
+	{"ContentPasteIcon", theme.ContentPasteIcon()},
+	{"ContentRedoIcon", theme.ContentRedoIcon()},
+	{"ContentUndoIcon", theme.ContentUndoIcon()},
+
+	{"InfoIcon", theme.InfoIcon()},
+	{"QuestionIcon", theme.QuestionIcon()},
+	{"WarningIcon", theme.WarningIcon()},
+
+	{"DocumentCreateIcon", theme.DocumentCreateIcon()},
+	{"DocumentPrintIcon", theme.DocumentPrintIcon()},
+	{"DocumentSaveIcon", theme.DocumentSaveIcon()},
+
+	{"FolderIcon", theme.FolderIcon()},
+	{"FolderNewIcon", theme.FolderNewIcon()},
+	{"FolderOpenIcon", theme.FolderOpenIcon()},
+	{"HomeIcon", theme.HomeIcon()},
+	{"HelpIcon", theme.HelpIcon()},
+	{"SettingsIcon", theme.SettingsIcon()},
+
+	{"ViewFullScreenIcon", theme.ViewFullScreenIcon()},
+	{"ViewRestoreIcon", theme.ViewRestoreIcon()},
+	{"ViewRefreshIcon", theme.ViewRefreshIcon()},
+	{"VisibilityIcon", theme.VisibilityIcon()},
+	{"VisibilityOffIcon", theme.VisibilityOffIcon()},
+	{"ZoomFitIcon", theme.ZoomFitIcon()},
+	{"ZoomInIcon", theme.ZoomInIcon()},
+	{"ZoomOutIcon", theme.ZoomOutIcon()},
+
+	{"MoveDownIcon", theme.MoveDownIcon()},
+	{"MoveUpIcon", theme.MoveUpIcon()},
+
+	{"NavigateBackIcon", theme.NavigateBackIcon()},
+	{"NavigateNextIcon", theme.NavigateNextIcon()},
+
+	{"MenuDropDown", theme.MenuDropDownIcon()},
+	{"MenuDropUp", theme.MenuDropUpIcon()},
+
+	{"MailAttachmentIcon", theme.MailAttachmentIcon()},
+	{"MailComposeIcon", theme.MailComposeIcon()},
+	{"MailForwardIcon", theme.MailForwardIcon()},
+	{"MailReplyIcon", theme.MailReplyIcon()},
+	{"MailReplyAllIcon", theme.MailReplyAllIcon()},
+	{"MailSendIcon", theme.MailSendIcon()},
+}

+ 72 - 0
gui/fyne_demo/screens/layout.go

@@ -0,0 +1,72 @@
+package screens
+
+import (
+	"image/color"
+
+	"fyne.io/fyne"
+	"fyne.io/fyne/canvas"
+	"fyne.io/fyne/layout"
+	"fyne.io/fyne/widget"
+)
+
+func makeCell() fyne.CanvasObject {
+	rect := canvas.NewRectangle(&color.RGBA{128, 128, 128, 255})
+	rect.SetMinSize(fyne.NewSize(30, 30))
+	return rect
+}
+
+func makeBorderLayout() *fyne.Container {
+	top := makeCell()
+	bottom := makeCell()
+	left := makeCell()
+	right := makeCell()
+	middle := widget.NewLabelWithStyle("BorderLayout", fyne.TextAlignCenter, fyne.TextStyle{})
+
+	borderLayout := layout.NewBorderLayout(top, bottom, left, right)
+	return fyne.NewContainerWithLayout(borderLayout,
+		top, bottom, left, right, middle)
+}
+
+func makeBoxLayout() *fyne.Container {
+	top := makeCell()
+	bottom := makeCell()
+	middle := widget.NewLabel("BoxLayout")
+	center := makeCell()
+	right := makeCell()
+
+	col := fyne.NewContainerWithLayout(layout.NewVBoxLayout(),
+		top, middle, bottom)
+
+	return fyne.NewContainerWithLayout(layout.NewHBoxLayout(),
+		col, center, right)
+}
+
+func makeFixedGridLayout() *fyne.Container {
+	box1 := makeCell()
+	box2 := widget.NewLabel("FixedGrid")
+	box3 := makeCell()
+	box4 := makeCell()
+
+	return fyne.NewContainerWithLayout(layout.NewFixedGridLayout(fyne.NewSize(75, 75)),
+		box1, box2, box3, box4)
+}
+
+func makeGridLayout() *fyne.Container {
+	box1 := makeCell()
+	box2 := widget.NewLabel("Grid")
+	box3 := makeCell()
+	box4 := makeCell()
+
+	return fyne.NewContainerWithLayout(layout.NewGridLayout(2),
+		box1, box2, box3, box4)
+}
+
+// LayoutPanel loads a panel that shows the layouts available for a container
+func LayoutPanel() fyne.CanvasObject {
+	return widget.NewTabContainer(
+		widget.NewTabItem("Border", makeBorderLayout()),
+		widget.NewTabItem("Box", makeBoxLayout()),
+		widget.NewTabItem("Fixed Grid", makeFixedGridLayout()),
+		widget.NewTabItem("Grid", makeGridLayout()),
+	)
+}

+ 118 - 0
gui/fyne_demo/screens/theme.go

@@ -0,0 +1,118 @@
+package screens
+
+import (
+	"image/color"
+
+	"fyne.io/fyne"
+	"fyne.io/fyne/theme"
+)
+
+var (
+	purple = &color.RGBA{R: 128, G: 0, B: 128, A: 255}
+	orange = &color.RGBA{R: 198, G: 123, B: 0, A: 255}
+	grey   = &color.Gray{Y: 123}
+)
+
+// customTheme is a simple demonstration of a bespoke theme loaded by a Fyne app.
+type customTheme struct {
+}
+
+func (customTheme) BackgroundColor() color.Color {
+	return purple
+}
+
+func (customTheme) ButtonColor() color.Color {
+	return color.Black
+}
+
+func (customTheme) DisabledButtonColor() color.Color {
+	return color.White
+}
+
+func (customTheme) HyperlinkColor() color.Color {
+	return orange
+}
+
+func (customTheme) TextColor() color.Color {
+	return color.White
+}
+
+func (customTheme) DisabledTextColor() color.Color {
+	return color.Black
+}
+
+func (customTheme) IconColor() color.Color {
+	return color.White
+}
+
+func (customTheme) DisabledIconColor() color.Color {
+	return color.Black
+}
+
+func (customTheme) PlaceHolderColor() color.Color {
+	return grey
+}
+
+func (customTheme) PrimaryColor() color.Color {
+	return orange
+}
+
+func (customTheme) HoverColor() color.Color {
+	return orange
+}
+
+func (customTheme) FocusColor() color.Color {
+	return orange
+}
+
+func (customTheme) ScrollBarColor() color.Color {
+	return grey
+}
+
+func (customTheme) ShadowColor() color.Color {
+	return &color.RGBA{0xcc, 0xcc, 0xcc, 0xcc}
+}
+
+func (customTheme) TextSize() int {
+	return 12
+}
+
+func (customTheme) TextFont() fyne.Resource {
+	return theme.DefaultTextBoldFont()
+}
+
+func (customTheme) TextBoldFont() fyne.Resource {
+	return theme.DefaultTextBoldFont()
+}
+
+func (customTheme) TextItalicFont() fyne.Resource {
+	return theme.DefaultTextBoldItalicFont()
+}
+
+func (customTheme) TextBoldItalicFont() fyne.Resource {
+	return theme.DefaultTextBoldItalicFont()
+}
+
+func (customTheme) TextMonospaceFont() fyne.Resource {
+	return theme.DefaultTextMonospaceFont()
+}
+
+func (customTheme) Padding() int {
+	return 10
+}
+
+func (customTheme) IconInlineSize() int {
+	return 20
+}
+
+func (customTheme) ScrollBarSize() int {
+	return 10
+}
+
+func (customTheme) ScrollBarSmallSize() int {
+	return 5
+}
+
+func newCustomTheme() fyne.Theme {
+	return &customTheme{}
+}

+ 157 - 0
gui/fyne_demo/screens/widget.go

@@ -0,0 +1,157 @@
+package screens
+
+import (
+	"fmt"
+	"time"
+
+	"fyne.io/fyne"
+	"fyne.io/fyne/canvas"
+	"fyne.io/fyne/layout"
+	"fyne.io/fyne/theme"
+	"fyne.io/fyne/widget"
+)
+
+func makeButtonTab() fyne.Widget {
+	disabled := widget.NewButton("Disabled", func() {})
+	disabled.Disable()
+
+	return widget.NewVBox(
+		widget.NewLabel("Text label"),
+		widget.NewButton("Text button", func() { fmt.Println("tapped text button") }),
+		widget.NewButtonWithIcon("With icon", theme.ConfirmIcon(), func() { fmt.Println("tapped icon button") }),
+		disabled,
+	)
+}
+
+func makeInputTab() fyne.Widget {
+	entry := widget.NewEntry()
+	entry.SetPlaceHolder("Entry")
+	entryReadOnly := widget.NewEntry()
+	entryReadOnly.SetText("Entry (disabled)")
+	entryReadOnly.Disable()
+
+	disabledCheck := widget.NewCheck("Disabled check", func(bool) {})
+	disabledCheck.Disable()
+	radio := widget.NewRadio([]string{"Radio Item 1", "Radio Item 2"}, func(s string) { fmt.Println("selected", s) })
+	radio.Horizontal = true
+	disabledRadio := widget.NewRadio([]string{"Disabled radio"}, func(string) {})
+	disabledRadio.Disable()
+
+	return widget.NewVBox(
+		entry,
+		entryReadOnly,
+		widget.NewSelect([]string{"Option 1", "Option 2", "Option 3"}, func(s string) { fmt.Println("selected", s) }),
+		widget.NewCheck("Check", func(on bool) { fmt.Println("checked", on) }),
+		disabledCheck,
+		radio,
+		disabledRadio,
+		widget.NewSlider(0, 100),
+	)
+}
+
+func makeProgressTab() fyne.Widget {
+	progress := widget.NewProgressBar()
+	infProgress := widget.NewProgressBarInfinite()
+
+	go func() {
+		num := 0.0
+		for num < 1.0 {
+			time.Sleep(100 * time.Millisecond)
+			progress.SetValue(num)
+			num += 0.01
+		}
+
+		progress.SetValue(1)
+	}()
+
+	return widget.NewVBox(
+		widget.NewLabel("Percent"), progress,
+		widget.NewLabel("Infinite"), infProgress)
+}
+
+func makeFormTab() fyne.Widget {
+	name := widget.NewEntry()
+	name.SetPlaceHolder("John Smith")
+	email := widget.NewEntry()
+	email.SetPlaceHolder("test@example.com")
+	password := widget.NewPasswordEntry()
+	password.SetPlaceHolder("Password")
+	largeText := widget.NewMultiLineEntry()
+
+	form := &widget.Form{
+		OnCancel: func() {
+			fmt.Println("Cancelled")
+		},
+		OnSubmit: func() {
+			fmt.Println("Form submitted")
+			fmt.Println("Name:", name.Text)
+			fmt.Println("Email:", email.Text)
+			fmt.Println("Password:", password.Text)
+			fmt.Println("Message:", largeText.Text)
+		},
+	}
+	form.Append("Name", name)
+	form.Append("Email", email)
+	form.Append("Password", password)
+	form.Append("Message", largeText)
+
+	return form
+}
+
+func makeScrollTab() fyne.CanvasObject {
+	logo := canvas.NewImageFromResource(theme.FyneLogo())
+	logo.SetMinSize(fyne.NewSize(320, 320))
+	list := widget.NewHBox()
+	list2 := widget.NewVBox()
+
+	for i := 1; i <= 20; i++ {
+		index := i // capture
+		list.Append(widget.NewButton(fmt.Sprintf("Button %d", index), func() {
+			fmt.Println("Tapped", index)
+		}))
+		list2.Append(widget.NewButton(fmt.Sprintf("Button %d", index), func() {
+			fmt.Println("Tapped", index)
+		}))
+	}
+
+	scroll := widget.NewScrollContainer(list)
+	scroll.Resize(fyne.NewSize(200, 300))
+
+	scroll2 := widget.NewScrollContainer(list2)
+	scroll2.Resize(fyne.NewSize(200, 100))
+
+	return fyne.NewContainerWithLayout(layout.NewGridLayout(1), scroll, scroll2)
+}
+
+func makeScrollBothTab() fyne.CanvasObject {
+	logo := canvas.NewImageFromResource(theme.FyneLogo())
+	logo.SetMinSize(fyne.NewSize(800, 800))
+
+	scroll := widget.NewScrollContainer(logo)
+	scroll.Resize(fyne.NewSize(400, 400))
+
+	return scroll
+}
+
+// WidgetScreen shows a panel containing widget demos
+func WidgetScreen() fyne.CanvasObject {
+	toolbar := widget.NewToolbar(widget.NewToolbarAction(theme.MailComposeIcon(), func() { fmt.Println("New") }),
+		widget.NewToolbarSeparator(),
+		widget.NewToolbarSpacer(),
+		widget.NewToolbarAction(theme.ContentCutIcon(), func() { fmt.Println("Cut") }),
+		widget.NewToolbarAction(theme.ContentCopyIcon(), func() { fmt.Println("Copy") }),
+		widget.NewToolbarAction(theme.ContentPasteIcon(), func() { fmt.Println("Paste") }),
+	)
+
+	return fyne.NewContainerWithLayout(layout.NewBorderLayout(toolbar, nil, nil, nil),
+		toolbar,
+		widget.NewTabContainer(
+			widget.NewTabItem("Buttons", makeButtonTab()),
+			widget.NewTabItem("Input", makeInputTab()),
+			widget.NewTabItem("Progress", makeProgressTab()),
+			widget.NewTabItem("Form", makeFormTab()),
+			widget.NewTabItem("Scroll", makeScrollTab()),
+			widget.NewTabItem("Full Scroll", makeScrollBothTab()),
+		),
+	)
+}

+ 84 - 0
gui/fyne_demo/screens/window.go

@@ -0,0 +1,84 @@
+package screens
+
+import (
+	"errors"
+	"fmt"
+	"time"
+
+	"fyne.io/fyne"
+	"fyne.io/fyne/dialog"
+	"fyne.io/fyne/layout"
+	"fyne.io/fyne/widget"
+)
+
+func confirmCallback(response bool) {
+	fmt.Println("Responded with", response)
+}
+
+// DialogScreen loads a panel that lists the dialog windows that can be tested.
+func DialogScreen(win fyne.Window) fyne.CanvasObject {
+	dialogs := widget.NewGroup("Dialogs",
+		widget.NewButton("Info", func() {
+			dialog.ShowInformation("Information", "You should know this thing...", win)
+		}),
+		widget.NewButton("Error", func() {
+			err := errors.New("A dummy error message")
+			dialog.ShowError(err, win)
+		}),
+		widget.NewButton("Confirm", func() {
+			cnf := dialog.NewConfirm("Confirmation", "Are you enjoying this demo?", confirmCallback, win)
+			cnf.SetDismissText("Nah")
+			cnf.SetConfirmText("Oh Yes!")
+			cnf.Show()
+		}),
+		widget.NewButton("Progress", func() {
+			prog := dialog.NewProgress("MyProgress", "Nearly there...", win)
+
+			go func() {
+				num := 0.0
+				for num < 1.0 {
+					time.Sleep(50 * time.Millisecond)
+					prog.SetValue(num)
+					num += 0.01
+				}
+
+				prog.SetValue(1)
+				prog.Hide()
+			}()
+
+			prog.Show()
+		}),
+		widget.NewButton("Custom", func() {
+			content := widget.NewEntry()
+			content.SetPlaceHolder("Type something here")
+			content.OnChanged = func(text string) {
+				fmt.Println("Entered", text)
+			}
+			dialog.ShowCustom("Custom dialog", "Done", content, win)
+		}),
+	)
+
+	windows := widget.NewVBox(dialogs, widget.NewGroup("Windows",
+		widget.NewButton("New window", func() {
+			w := fyne.CurrentApp().NewWindow("Hello")
+			w.SetContent(widget.NewLabel("Hello World!"))
+			w.Show()
+		}),
+		widget.NewButton("Fixed size window", func() {
+			w := fyne.CurrentApp().NewWindow("Fixed")
+			w.SetContent(fyne.NewContainerWithLayout(layout.NewCenterLayout(), widget.NewLabel("Hello World!")))
+
+			w.Resize(fyne.NewSize(240, 180))
+			w.SetFixedSize(true)
+			w.Show()
+		}),
+		widget.NewButton("Centered window", func() {
+			w := fyne.CurrentApp().NewWindow("Central")
+			w.SetContent(fyne.NewContainerWithLayout(layout.NewCenterLayout(), widget.NewLabel("Hello World!")))
+
+			w.CenterOnScreen()
+			w.Show()
+		})))
+
+	return fyne.NewContainerWithLayout(layout.NewAdaptiveGridLayout(2), windows, LayoutPanel())
+}

Some files were not shown because too many files changed in this diff