Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add about dialog and window for helping apps make a pretty about screen #87

Merged
merged 8 commits into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,23 @@ m := NewMap()

![](img/map.png)

## Dialogs

### About

A cool parallax about dialog that pulls data from the app metadata and includes
some markup content and links at the bottom of the window/dialog.

```go
docURL, _ := url.Parse("https://docs.fyne.io")
links := []*widget.Hyperlink{
widget.NewHyperlink("Docs", docURL),
}
dialog.ShowAboutWindow("Some **cool** stuff", links, a)
```

![](img/about.png)

## Data Binding

Community contributed data sources for binding.
Expand Down
4 changes: 4 additions & 0 deletions cmd/fyne-x/FyneApp.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[Details]
Icon = "Icon.png"
Version = "0.1.0"
Build = 5
Binary file added cmd/fyne-x/Icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 30 additions & 0 deletions cmd/fyne-x/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package main

import (
"net/url"

"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
"fyne.io/x/fyne/dialog"
)

func main() {
a := app.New()
w := a.NewWindow("Fyne-x demo")

docURL, _ := url.Parse("https://docs.fyne.io")
links := []*widget.Hyperlink{
widget.NewHyperlink("Docs", docURL),
}
w.SetContent(container.NewGridWithColumns(1,
widget.NewButton("About", func() {
dialog.ShowAbout("Some **cool** stuff", links, a, w)
}),
widget.NewButton("About window", func() {
dialog.ShowAboutWindow("Some **cool** stuff", links, a)
}),
))

w.ShowAndRun()
}
175 changes: 175 additions & 0 deletions dialog/about.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
package dialog

import (
"image/color"

"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
)

// NewAbout creates a parallax about dialog using the app metadata along with the
// markdown content and links passed into this method.
// You should call Show on the returned dialog to display it.
func NewAbout(content string, links []*widget.Hyperlink, a fyne.App, w fyne.Window) dialog.Dialog {
d := dialog.NewCustom("About", "OK", aboutContent(content, links, a), w)
d.Resize(fyne.NewSize(400, 360))

return d
}

// NewAboutWindow creates a parallax about window using the app metadata along with the
// markdown content and links passed into this method.
// You should call Show on the returned window to display it.
func NewAboutWindow(content string, links []*widget.Hyperlink, a fyne.App) fyne.Window {
w := a.NewWindow("About")
w.SetContent(aboutContent(content, links, a))
w.Resize(fyne.NewSize(360, 300))

return w
}

// ShowAbout opens a parallax about dialog using the app metadata along with the
// markdown content and links passed into this method.
func ShowAbout(content string, links []*widget.Hyperlink, a fyne.App, w fyne.Window) {
Jacalz marked this conversation as resolved.
Show resolved Hide resolved
d := NewAbout(content, links, a, w)
d.Show()
}

// ShowAboutWindow opens a parallax about window using the app metadata along with the
// markdown content and links passed into this method.
func ShowAboutWindow(content string, links []*widget.Hyperlink, a fyne.App) {
w := NewAboutWindow(content, links, a)
w.Show()
}

func aboutContent(content string, links []*widget.Hyperlink, a fyne.App) fyne.CanvasObject {
Jacalz marked this conversation as resolved.
Show resolved Hide resolved
rich := widget.NewRichTextFromMarkdown(content)
footer := aboutFooter(links)

logo := canvas.NewImageFromResource(a.Metadata().Icon)
logo.FillMode = canvas.ImageFillContain
logo.SetMinSize(fyne.NewSize(128, 128))

appData := widget.NewRichTextFromMarkdown(
"## " + a.Metadata().Name + "\n**Version:** " + a.Metadata().Version)
centerText(appData)
space := canvas.NewRectangle(color.Transparent)
space.SetMinSize(fyne.NewSquareSize(theme.Padding() * 4))
body := container.NewVBox(
space,
logo,
appData,
container.NewCenter(rich))
scroll := container.NewScroll(body)

bgColor := withAlpha(theme.BackgroundColor(), 0xe0)
shadowColor := withAlpha(theme.BackgroundColor(), 0x33)

underlay := canvas.NewImageFromResource(a.Metadata().Icon)
bg := canvas.NewRectangle(bgColor)
underlayer := underLayout{}
slideBG := container.New(underlayer, underlay)
footerBG := canvas.NewRectangle(shadowColor)
watchTheme(bg, footerBG, a)

underlay.Resize(fyne.NewSize(512, 512))
scroll.OnScrolled = func(p fyne.Position) {
underlayer.offset = -p.Y / 3
underlayer.Layout(slideBG.Objects, slideBG.Size())
}

bgClip := container.NewScroll(slideBG)
bgClip.Direction = container.ScrollNone
return container.NewStack(container.New(unpad{top: true}, bgClip, bg),
container.NewBorder(nil,
container.NewStack(footerBG, footer), nil, nil,
container.New(unpad{top: true, bottom: true}, scroll)))
}

func aboutFooter(links []*widget.Hyperlink) fyne.CanvasObject {
footer := container.NewHBox(layout.NewSpacer())
for i, a := range links {
footer.Add(a)
if i < len(links)-1 {
footer.Add(widget.NewLabel("-"))
}
}
footer.Add(layout.NewSpacer())

return footer
}

func centerText(rich *widget.RichText) {
for _, s := range rich.Segments {
if text, ok := s.(*widget.TextSegment); ok {
text.Style.Alignment = fyne.TextAlignCenter
}
}
}

func watchTheme(bg, footer *canvas.Rectangle, a fyne.App) {
listen := make(chan fyne.Settings)
fyne.CurrentApp().Settings().AddChangeListener(listen)
go func() {
for range listen {
bgColor := withAlpha(theme.BackgroundColor(), 0xe0)
bg.FillColor = bgColor
bg.Refresh()

shadowColor := withAlpha(theme.BackgroundColor(), 0x33)
footer.FillColor = shadowColor
footer.Refresh()
}
}()
}

func withAlpha(c color.Color, alpha uint8) color.Color {
r, g, b, _ := c.RGBA()
return color.NRGBA{R: uint8(r >> 8), G: uint8(g >> 8), B: uint8(b >> 8), A: alpha}
}

type underLayout struct {
offset float32
}

func (u underLayout) Layout(objs []fyne.CanvasObject, size fyne.Size) {
under := objs[0]
left := size.Width/2 - under.Size().Width/2
under.Move(fyne.NewPos(left, u.offset-50))
}

func (u underLayout) MinSize(_ []fyne.CanvasObject) fyne.Size {
return fyne.Size{}
}

type unpad struct {
top, bottom bool
}

func (u unpad) Layout(objs []fyne.CanvasObject, s fyne.Size) {
pad := theme.Padding()
var pos fyne.Position
if u.top {
pos.Y = -pad
}
size := s
if u.top {
size.Height += pad
}
if u.bottom {
size.Height += pad
}
for _, o := range objs {
o.Move(pos)
o.Resize(size)
}
}

func (u unpad) MinSize(_ []fyne.CanvasObject) fyne.Size {
return fyne.NewSize(100, 100)
}
Binary file added img/about-dialog.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/about.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.