Resources only exposes OpenResource (and Destroy).

PhysicalResources derives from Resources and exposes FetchResource.
Made dependency specific resource addons.
Extended the available resource options (fallback, path, refactored copy).
NewRenderer provides DefaultResources to the created renderer.
This commit is contained in:
Sander Schobers 2020-05-25 22:24:06 +02:00
parent 869f87dd4f
commit 67e73a8671
11 changed files with 192 additions and 96 deletions

23
addons/aferores/afero.go Normal file
View File

@ -0,0 +1,23 @@
package aferores
import (
"io"
"github.com/spf13/afero"
"opslag.de/schobers/zntg/ui"
)
type aferoResources struct {
afero.Fs
}
var _ ui.Resources = &aferoResources{}
// New provides resources from a afero file system.
func New(fs afero.Fs) ui.Resources {
return &aferoResources{fs}
}
func (r *aferoResources) Destroy() error { return nil }
func (r *aferoResources) OpenResource(name string) (io.ReadCloser, error) { return r.Fs.Open(name) }

View File

@ -1,76 +0,0 @@
package res
import (
"io"
"os"
"path/filepath"
"github.com/spf13/afero"
"opslag.de/schobers/zntg"
"opslag.de/schobers/zntg/ui"
)
type aferoResources struct {
dir string
fs afero.Fs
copy *zntg.Dir
}
var _ ui.Resources = &aferoResources{}
// NewAferoResources provides resources from a afero file system. The prefix is used as a prefix of the temporary directory.
func NewAferoResources(fs afero.Fs, prefix string) (ui.Resources, error) {
return NewAferoFallbackResources("", fs, prefix)
}
// NewAferoFallbackResources provides resources from the directory first and uses afero file system as a fallback if the resource in the directory doesn't exist. The prefix is used as a prefix of the temporary directory.
func NewAferoFallbackResources(dir string, fs afero.Fs, prefix string) (ui.Resources, error) {
copy, err := zntg.NewTempDir(prefix)
if err != nil {
return nil, err
}
return &aferoResources{dir, fs, copy}, nil
}
func (r *aferoResources) fetchAferoResource(name string) (string, error) {
path := r.copy.FilePath(name)
if !zntg.FileExists(path) {
src, err := r.fs.Open(name)
if err != nil {
return "", err
}
defer src.Close()
err = r.copy.Write(name, src)
if err != nil {
return "", err
}
}
return path, nil
}
func (r *aferoResources) openAferoResource(name string) (io.ReadCloser, error) { return r.fs.Open(name) }
func (r *aferoResources) Destroy() error { return r.copy.Destroy() }
func (r *aferoResources) FetchResource(name string) (string, error) {
if r.dir == "" {
return r.fetchAferoResource(name)
}
path := filepath.Join(r.dir, name)
if zntg.FileExists(path) {
return path, nil
}
return r.fetchAferoResource(name)
}
func (r *aferoResources) OpenResource(name string) (io.ReadCloser, error) {
if r.dir == "" {
return r.openAferoResource(name)
}
path := filepath.Join(r.dir, name)
if zntg.FileExists(path) {
return os.Open(path)
}
return r.openAferoResource(name)
}

23
addons/riceres/rice.go Normal file
View File

@ -0,0 +1,23 @@
package riceres
import (
"io"
rice "github.com/GeertJohan/go.rice"
"opslag.de/schobers/zntg/ui"
)
type riceResources struct {
*rice.Box
}
var _ ui.Resources = &riceResources{}
// New provides resources from a rice Box.
func New(box *rice.Box) ui.Resources {
return &riceResources{box}
}
func (r *riceResources) Destroy() error { return nil }
func (r *riceResources) OpenResource(name string) (io.ReadCloser, error) { return r.Box.Open(name) }

View File

@ -56,7 +56,7 @@ type Renderer struct {
eq *allg5.EventQueue
unh func(allg5.Event)
user *allg5.UserEventSource
res ui.Resources
res ui.PhysicalResources
dispPos geom.Point
keys ui.KeyState
@ -335,7 +335,11 @@ func (r *Renderer) SetResourceProvider(factory func() ui.Resources) {
if r.res != nil {
r.res.Destroy()
}
r.res = factory()
res, err := ui.NewCopyResources("allg5ui", factory(), false)
if err != nil {
return
}
r.res = res
}
func (r *Renderer) SetUnhandledEventHandler(handler func(allg5.Event)) {

View File

@ -22,7 +22,7 @@ type Renderer struct {
window *sdl.Window
renderer *sdl.Renderer
refresh uint32
resources ui.Resources
resources ui.PhysicalResources
mouse geom.PointF32
cursor ui.MouseCursor
@ -477,7 +477,11 @@ func (r *Renderer) SetResourceProvider(factory func() ui.Resources) {
if r.resources != nil {
r.resources.Destroy()
}
r.resources = factory()
resources, err := ui.NewCopyResources("sdlui", factory(), false)
if err != nil {
return
}
r.resources = resources
}
// Texture

View File

@ -7,22 +7,28 @@ import (
"opslag.de/schobers/zntg"
)
var _ Resources = &CopyResources{}
var _ PhysicalResources = &CopyResources{}
// CopyResources copies and opens resources to a temporary directory.
// CopyResources copies the resource to a temporary directory when fetched. Optionally the resource is fetched as well before opening.
type CopyResources struct {
Source Resources
copy *zntg.Dir
mustCopyBeforeOpen bool
}
// NewCopyResource creates a proxy that copied resources first to disk.
func NewCopyResource(prefix string, source Resources) (*CopyResources, error) {
func newCopyResources(prefix string, source Resources, mustCopyBeforeOpen bool) (*CopyResources, error) {
copy, err := zntg.NewTempDir(prefix)
if nil != err {
return nil, err
}
return &CopyResources{source, copy}, nil
return &CopyResources{source, copy, mustCopyBeforeOpen}, nil
}
// NewCopyResources creates a proxy that copies resources first to disk. Copy on OpenResource only happens when mustCopyBeforeOpen is set to true.
func NewCopyResources(prefix string, source Resources, mustCopyBeforeOpen bool) (*CopyResources, error) {
return newCopyResources(prefix, source, false)
}
// FetchResource copies the file from the source to disk and returns the path to it.
@ -43,11 +49,16 @@ func (r *CopyResources) FetchResource(name string) (string, error) {
return path, nil
}
// OpenResource opens the (copied) resource on disk.
// OpenResource opens the (optionally copied) resource.
func (r *CopyResources) OpenResource(name string) (io.ReadCloser, error) {
path := r.copy.FilePath(name)
src, err := os.Open(path)
return src, err
if r.mustCopyBeforeOpen {
path, err := r.FetchResource(name)
if err != nil {
return nil, err
}
return os.Open(path)
}
return r.Source.OpenResource(name)
}
// Destroy destroy the copy of the resources.

34
ui/fallbackresources.go Normal file
View File

@ -0,0 +1,34 @@
package ui
import "io"
var _ Resources = &fallbackResources{}
type fallbackResources struct {
resources Resources
fallback Resources
}
// NewFallbackResources creates a Resources that first will try to access resources and on failure will try to access the fallback resources. Will take ownership of both resources (Destroy).
func NewFallbackResources(resources, fallback Resources) Resources {
return &fallbackResources{resources, fallback}
}
func (r *fallbackResources) OpenResource(name string) (io.ReadCloser, error) {
if reader, err := r.resources.OpenResource(name); err == nil {
return reader, nil
}
return r.fallback.OpenResource(name)
}
func (r *fallbackResources) Destroy() error {
errResources := r.resources.Destroy()
errFallback := r.fallback.Destroy()
if errResources != nil {
return errResources
}
if errFallback != nil {
return errFallback
}
return nil
}

View File

@ -5,10 +5,10 @@ import (
"os"
)
var _ Resources = &OSResources{}
var _ PhysicalResources = &OSResources{}
// DefaultResources returns the default Resources implementation (OSResources).
func DefaultResources() Resources {
func DefaultResources() PhysicalResources {
return &OSResources{}
}

62
ui/pathresources.go Normal file
View File

@ -0,0 +1,62 @@
package ui
import (
"io"
"path/filepath"
)
var _ Resources = &PathResources{}
// PathResources implements Resources by adding a prefix to the requested source name before proxying it to its source.
type PathResources struct {
Source Resources
Prefix string
}
// NewPathResources creates a new Resources by adding a prefix to the requested source name before proxying it to its source. If source is nil it will use the OS file system for its resources.
func NewPathResources(source Resources, prefix string) *PathResources {
if source == nil {
source = &OSResources{}
}
return &PathResources{source, prefix}
}
// Destroy destroys the source.
func (r *PathResources) Destroy() error { return r.Source.Destroy() }
// OpenResource opens the resource with the prefixed name.
func (r *PathResources) OpenResource(name string) (io.ReadCloser, error) {
path := filepath.Join(r.Prefix, name)
return r.Source.OpenResource(path)
}
var _ PhysicalResources = &PathPhysicalResources{}
// PathPhysicalResources implements PhysicalResources by adding a prefix to the requested source name before proxying it to its source.
type PathPhysicalResources struct {
Source PhysicalResources
Prefix string
}
// NewPathPhysicalResources creates a new PhysicalResources by adding a prefix to the requested source name before proxying it to its source. If source is nil it will use the OS file system for its resources.
func NewPathPhysicalResources(source PhysicalResources, prefix string) *PathPhysicalResources {
if source == nil {
source = &OSResources{}
}
return &PathPhysicalResources{source, prefix}
}
// Destroy destroys the source.
func (r *PathPhysicalResources) Destroy() error { return r.Source.Destroy() }
// FetchResource fetches the resource with the prefixed name.
func (r *PathPhysicalResources) FetchResource(name string) (string, error) {
path := filepath.Join(r.Prefix, name)
return r.Source.FetchResource(path)
}
// OpenResource opens the resource with the prefixed name.
func (r *PathPhysicalResources) OpenResource(name string) (io.ReadCloser, error) {
path := filepath.Join(r.Prefix, name)
return r.Source.OpenResource(path)
}

View File

@ -18,12 +18,17 @@ func NewRenderer(title string, width, height int, opts NewRendererOptions) (Rend
if rendererFactory == nil {
return nil, errors.New("no renderer factory registered")
}
return rendererFactory.New(title, width, height, opts)
renderer, err := rendererFactory.New(title, width, height, opts)
if err != nil {
return nil, err
}
renderer.SetResourceProvider(func() Resources { return DefaultResources() })
return renderer, nil
}
// NewRendererDefault creates a new renderer with default options set based on the registered renderer factory.
func NewRendererDefault(title string, width, height int) (Renderer, error) {
return rendererFactory.New(title, width, height, NewRendererOptions{
return NewRenderer(title, width, height, NewRendererOptions{
Resizable: true,
})
}

View File

@ -2,12 +2,18 @@ package ui
import "io"
// Resources is an abstraction on resources.
// Resources is an abstraction for opening resources.
type Resources interface {
// FetchResource should fetch the resource with the specified name and return a path (on disk) where the resource can be accessed.
FetchResource(name string) (string, error)
// OpenResource should open the resource with the specified name. The user is responsible for closing the resource.
OpenResource(name string) (io.ReadCloser, error)
// Destroy can be used for cleaning up at the end of the applications lifetime.
Destroy() error
}
// PhysicalResources is an abstraction for opening and fetching (to disk) resources.
type PhysicalResources interface {
Resources
// FetchResource should fetch the resource with the specified name and return a path (on disk) where the resource can be accessed.
FetchResource(name string) (string, error)
}