From 67e73a8671ed7477dcb59b534a7681d43ddb9fac Mon Sep 17 00:00:00 2001 From: Sander Schobers Date: Mon, 25 May 2020 22:24:06 +0200 Subject: [PATCH] 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. --- addons/aferores/afero.go | 23 ++++++++++++ addons/res/afero.go | 76 ---------------------------------------- addons/riceres/rice.go | 23 ++++++++++++ allg5ui/renderer.go | 8 +++-- sdlui/renderer.go | 8 +++-- ui/copyresources.go | 29 ++++++++++----- ui/fallbackresources.go | 34 ++++++++++++++++++ ui/osresources.go | 4 +-- ui/pathresources.go | 62 ++++++++++++++++++++++++++++++++ ui/rendererfactory.go | 9 +++-- ui/resources.go | 12 +++++-- 11 files changed, 192 insertions(+), 96 deletions(-) create mode 100644 addons/aferores/afero.go delete mode 100644 addons/res/afero.go create mode 100644 addons/riceres/rice.go create mode 100644 ui/fallbackresources.go create mode 100644 ui/pathresources.go diff --git a/addons/aferores/afero.go b/addons/aferores/afero.go new file mode 100644 index 0000000..e9cd5a4 --- /dev/null +++ b/addons/aferores/afero.go @@ -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) } diff --git a/addons/res/afero.go b/addons/res/afero.go deleted file mode 100644 index 237a86a..0000000 --- a/addons/res/afero.go +++ /dev/null @@ -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) -} diff --git a/addons/riceres/rice.go b/addons/riceres/rice.go new file mode 100644 index 0000000..73bc770 --- /dev/null +++ b/addons/riceres/rice.go @@ -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) } diff --git a/allg5ui/renderer.go b/allg5ui/renderer.go index 3b9a43e..046f7a1 100644 --- a/allg5ui/renderer.go +++ b/allg5ui/renderer.go @@ -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)) { diff --git a/sdlui/renderer.go b/sdlui/renderer.go index 8d39454..5d23c8e 100644 --- a/sdlui/renderer.go +++ b/sdlui/renderer.go @@ -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 diff --git a/ui/copyresources.go b/ui/copyresources.go index 07e11a3..58deaa9 100644 --- a/ui/copyresources.go +++ b/ui/copyresources.go @@ -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. diff --git a/ui/fallbackresources.go b/ui/fallbackresources.go new file mode 100644 index 0000000..cf8b83e --- /dev/null +++ b/ui/fallbackresources.go @@ -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 +} diff --git a/ui/osresources.go b/ui/osresources.go index 6572644..a4e48f1 100644 --- a/ui/osresources.go +++ b/ui/osresources.go @@ -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{} } diff --git a/ui/pathresources.go b/ui/pathresources.go new file mode 100644 index 0000000..19500f1 --- /dev/null +++ b/ui/pathresources.go @@ -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) +} diff --git a/ui/rendererfactory.go b/ui/rendererfactory.go index d89154a..6c3217a 100644 --- a/ui/rendererfactory.go +++ b/ui/rendererfactory.go @@ -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, }) } diff --git a/ui/resources.go b/ui/resources.go index c8da887..0a9d474 100644 --- a/ui/resources.go +++ b/ui/resources.go @@ -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) +}