271 lines
6.0 KiB
Go
271 lines
6.0 KiB
Go
// Copyright 2022 wanderer <a_mirre at utb dot cz>
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package xkcdreader
|
|
|
|
import (
|
|
"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/data/binding"
|
|
"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()
|
|
currentComic = latestComic
|
|
cc xkcdcomic
|
|
)
|
|
|
|
// 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 {
|
|
err := setCurrentComicBinding()
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
|
|
cc.pic = getImg(currentComic.Img, false)
|
|
|
|
// container for the image and surrounding elements
|
|
imgC := container.New(
|
|
layout.NewHBoxLayout(),
|
|
widget.NewButtonWithIcon("", theme.NavigateBackIcon(), func() {
|
|
log.Println("Previous comic")
|
|
showPrevious()
|
|
cc.refreshComicPic(getImg(currentComic.Img, false))
|
|
}),
|
|
layout.NewSpacer(),
|
|
container.NewCenter(
|
|
// TODO(me): dynamically replace placeholder text with image once
|
|
// fetched...
|
|
// widget.NewLabel("img placeholder"),
|
|
cc.pic,
|
|
),
|
|
layout.NewSpacer(),
|
|
widget.NewButtonWithIcon("", theme.NavigateNextIcon(), func() {
|
|
log.Println("Next comic")
|
|
showNext()
|
|
}),
|
|
)
|
|
|
|
year := widget.NewLabelWithData(comicCurrent.year)
|
|
month := widget.NewLabelWithData(comicCurrent.month)
|
|
day := widget.NewLabelWithData(comicCurrent.day)
|
|
title := widget.NewLabelWithData(comicCurrent.title)
|
|
num := widget.NewLabelWithData(
|
|
binding.IntToString(comicCurrent.num),
|
|
)
|
|
altText := widget.NewLabelWithData(comicCurrent.alt)
|
|
|
|
title.TextStyle.Bold = true
|
|
num.TextStyle.Monospace = true
|
|
altText.TextStyle.Italic = true
|
|
altText.Wrapping = fyne.TextTruncate
|
|
altText.Alignment = fyne.TextAlignCenter
|
|
|
|
browseUI := container.New(
|
|
layout.NewVBoxLayout(),
|
|
// layout.NewSpacer(),
|
|
container.NewCenter(
|
|
container.NewVBox(
|
|
///&widget.Label{
|
|
/// Text: "published on " +
|
|
/// fmt.Sprint(
|
|
/// currentComic.Year, "-",
|
|
/// currentComic.Month, "-",
|
|
/// currentComic.Day,
|
|
/// ),
|
|
/// TextStyle: fyne.TextStyle{Italic: true},
|
|
///},
|
|
container.NewHBox(
|
|
year, month, day,
|
|
),
|
|
container.NewHBox(
|
|
///&widget.Label{
|
|
/// Text: currentComic.Title,
|
|
/// TextStyle: fyne.TextStyle{Bold: true},
|
|
///},
|
|
///&widget.Label{
|
|
/// Text: "(#" + fmt.Sprint(currentComic.Num) + ")",
|
|
/// TextStyle: fyne.TextStyle{Monospace: true},
|
|
///},
|
|
title,
|
|
num,
|
|
),
|
|
),
|
|
),
|
|
imgC,
|
|
// layout.NewSpacer(),
|
|
container.NewCenter(
|
|
// comic "alt text"
|
|
///&widget.Label{
|
|
/// Text: currentComic.Alt,
|
|
/// TextStyle: fyne.TextStyle{Italic: true},
|
|
///},
|
|
// container.NewCenter(
|
|
// container.NewAdaptiveGrid(
|
|
// 1,
|
|
// layout.NewSpacer(),
|
|
altText,
|
|
layout.NewSpacer(),
|
|
// ),
|
|
),
|
|
layout.NewSpacer(),
|
|
)
|
|
|
|
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)
|
|
|
|
log.Println("getImg: img name: ", img.Resource.Name())
|
|
|
|
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
|
|
}
|