// Copyright 2022 wanderer // SPDX-License-Identifier: GPL-3.0-or-later package xkcdreader import ( "fmt" "log" "os" "fyne.io/fyne/v2" "fyne.io/fyne/v2/app" "fyne.io/fyne/v2/canvas" "fyne.io/fyne/v2/container" "fyne.io/fyne/v2/layout" "fyne.io/fyne/v2/storage" "fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/widget" "git.dotya.ml/wanderer/go-xkcdreader/cmd" "git.dotya.ml/wanderer/go-xkcdreader/xkcdreader/xkcd" ) const appGreeting = "welcome to go-xkcdreader" var ( a fyne.App latestComic = xkcd.GetLatest() ) // RunApp performs sets up and runs the main application. func RunApp() { // initialize the fyne application newApp() goxkcdreader := getApp() w := goxkcdreader.NewWindow(cmd.GetAppName()) // mark this window as the main window w.SetMaster() centered := container.New( layout.NewHBoxLayout(), layout.NewSpacer(), makeGreeting(), layout.NewSpacer(), ) w.SetContent(container.New( layout.NewVBoxLayout(), makeToolbar(), centered, makeTabs(), )) w.Resize(fyne.NewSize(400, 400)) w.ShowAndRun() } func newApp() { a = app.New() log.Println("Created a new fyne application") } func getApp() fyne.App { return a } // makeGreeting creates a greeting label. func makeGreeting() *widget.Label { w := widget.NewLabel(appGreeting) w.TextStyle.Monospace = true return w } func makeToolbar() *widget.Toolbar { toolbar := widget.NewToolbar( widget.NewToolbarSpacer(), widget.NewToolbarAction(theme.HelpIcon(), func() { log.Println("Display help") }), widget.NewToolbarAction(theme.SettingsIcon(), func() { log.Println("Display settings") }), widget.NewToolbarAction(theme.InfoIcon(), aboutWindow), ) return toolbar } func makeTabs() *container.AppTabs { tabs := container.NewAppTabs( container.NewTabItem("browse", makeBrowseUI()), container.NewTabItem("what if?", widget.NewLabel("serious answers to absurd questions and absurd advice for common concerns from xkcd's Randall Munroe")), container.NewTabItemWithIcon("search", theme.SearchIcon(), makeSearchTab()), ) return tabs } func makeBrowseUI() *fyne.Container { // container for the image and surrounding elements imgC := container.New( layout.NewHBoxLayout(), widget.NewButtonWithIcon("", theme.NavigateBackIcon(), func() { log.Println("Previous comic") }), layout.NewSpacer(), container.NewCenter( // TODO(me): dynamically replace placeholder text with image once // fetched... // widget.NewLabel("img placeholder"), getImg(latestComic.Img, false), ), layout.NewSpacer(), widget.NewButtonWithIcon("", theme.NavigateNextIcon(), func() { log.Println("Next comic") }), ) browseUI := container.New( layout.NewVBoxLayout(), layout.NewSpacer(), container.NewCenter( container.NewVBox( &widget.Label{ Text: "published on " + fmt.Sprint( latestComic.Year, "-", latestComic.Month, "-", latestComic.Day, ), TextStyle: fyne.TextStyle{Italic: true}, }, container.NewHBox( &widget.Label{ Text: latestComic.Title, TextStyle: fyne.TextStyle{Bold: true}, }, &widget.Label{ Text: "(#" + fmt.Sprint(latestComic.Num) + ")", TextStyle: fyne.TextStyle{Monospace: true}, }, ), ), ), imgC, layout.NewSpacer(), container.NewCenter( // comic "alt text" &widget.Label{ Text: latestComic.Alt, TextStyle: fyne.TextStyle{Italic: true}, }, ), ) return browseUI } // getComicLatest fetches latest comic, updates latestComic if necessary. func getComicLatest() { comic := xkcd.GetLatest() if comic == nil { // TODO(me): properly handle the situation (i.e. bail early) log.Println("error getting latest comic") } // update latesComic, if necessary if latestComic != comic { latestComic = comic } } // get img from filesystem, resize it and return as *canvas.Image. func getImg(imgURI string, local bool) *canvas.Image { if local { _, err := os.Stat(imgURI) // properly handle error, perhaps panic?...don't panic, I know.. if err != nil { log.Println("failed to read file " + imgURI) return canvas.NewImageFromFile("") } img := canvas.NewImageFromFile(imgURI) img.SetMinSize( fyne.Size{ Height: 383, Width: 273, }, ) img.ScaleMode = canvas.ImageScaleSmooth img.FillMode = canvas.ImageFillContain return img } preImg, err := storage.ParseURI(imgURI) if err != nil { log.Printf("error parsing comic URI: %q", err.Error()) } // get the actual image img := canvas.NewImageFromURI(preImg) if img.Resource == nil { log.Println("error fetching the image file of the latest comic") } // set basic img properties img.SetMinSize( fyne.Size{ // subject to change, this is what I consider to be sane defaults // atm... Height: 650, Width: 750, }, ) img.ScaleMode = canvas.ImageScaleSmooth img.FillMode = canvas.ImageFillContain return img }