diff --git a/cmd/view-menu/main.go b/cmd/view-menu/main.go index 6861965..c093273 100644 --- a/cmd/view-menu/main.go +++ b/cmd/view-menu/main.go @@ -35,7 +35,7 @@ func main() { log.Fatalf("Couldn't load menu %s: %v", *menuName, err) } - driver, err := ui.NewDriver(menu) + driver, err := ui.NewDriver(assets, menu) if err != nil { log.Fatalf("Couldn't initialize interface: %v", err) } @@ -45,13 +45,6 @@ func main() { log.Fatal("Couldn't create window: %v", err) } - // TODO: move this into driver. It will need to be able to change cursor. - cursor, err := assets.Cursor(assetstore.UltPointer) - if err != nil { - log.Fatalf("Couldn't load cursor: %v", err) - } - win.SetCursor(cursor.Image) - if err := win.Run(); err != nil { log.Fatal(err) } diff --git a/internal/ordoor/flow/flow.go b/internal/ordoor/flow/flow.go index 0e1dbf5..3e46a36 100644 --- a/internal/ordoor/flow/flow.go +++ b/internal/ordoor/flow/flow.go @@ -114,7 +114,7 @@ func buildDriver(assets *assetstore.AssetStore, name driverName) (*ui.Driver, er return nil, err } - driver, err := ui.NewDriver(menu) + driver, err := ui.NewDriver(assets, menu) if err != nil { return nil, err } @@ -138,6 +138,10 @@ func (f *Flow) Draw(screen *ebiten.Image) error { return f.current.Draw(screen) } +func (f *Flow) Cursor() (*ebiten.Image, *ebiten.DrawImageOptions, error) { + return f.current.Cursor() +} + func (f *Flow) linkDrivers() { // linkMain f.onClick(main, "2.1", f.setDriver(newGame)) // New game diff --git a/internal/ordoor/ordoor.go b/internal/ordoor/ordoor.go index 37102ea..12059f2 100644 --- a/internal/ordoor/ordoor.go +++ b/internal/ordoor/ordoor.go @@ -93,13 +93,6 @@ func Run(configFile string, overrideX, overrideY int) error { return fmt.Errorf("Failed to create window: %v", err) } - // TODO: move this into driver. It will need to be able to change cursor. - cursor, err := assets.Cursor(assetstore.UltPointer) - if err != nil { - log.Fatalf("Couldn't load cursor: %v", err) - } - win.SetCursor(cursor.Image) - ordoor.win = win if err := ordoor.setupFlow(); err != nil { @@ -212,3 +205,11 @@ func (o *Ordoor) Draw(screen *ebiten.Image) error { return fmt.Errorf("Unknown state: %v", o.state) } } + +func (o *Ordoor) Cursor() (*ebiten.Image, *ebiten.DrawImageOptions, error) { + if o.state == StateInterface { + return o.flow.Cursor() + } + + return nil, nil, nil +} diff --git a/internal/ui/driver.go b/internal/ui/driver.go index c1eb936..19c3989 100644 --- a/internal/ui/driver.go +++ b/internal/ui/driver.go @@ -90,7 +90,8 @@ func registerBuilder(t menus.MenuType, f builderFunc) { type Driver struct { Name string - menu *assetstore.Menu + assets *assetstore.AssetStore + menu *assetstore.Menu // UI elements we need to drive clickables []clickable @@ -100,6 +101,8 @@ type Driver struct { paintables []paintable valueables []valueable + cursor assetstore.CursorName + // The cursor in two different coordinate spaces: original, and screen-scaled cursorOrig image.Point cursorScaled image.Point @@ -112,10 +115,12 @@ type Driver struct { tooltip string } -func NewDriver(menu *assetstore.Menu) (*Driver, error) { +func NewDriver(assets *assetstore.AssetStore, menu *assetstore.Menu) (*Driver, error) { driver := &Driver{ Name: menu.Name, - menu: menu, + + assets: assets, + menu: menu, } for _, record := range menu.Records() { @@ -307,6 +312,20 @@ func (d *Driver) Draw(screen *ebiten.Image) error { return nil } +func (d *Driver) Cursor() (*ebiten.Image, *ebiten.DrawImageOptions, error) { + cursor, err := d.assets.Cursor(d.cursor) + if err != nil { + return nil, nil, err + } + + op := &ebiten.DrawImageOptions{} + op.GeoM.Translate(float64(d.cursorOrig.X), float64(d.cursorOrig.Y)) + op.GeoM.Concat(d.orig2native) + op.GeoM.Translate(float64(-cursor.Hotspot.X), float64(-cursor.Hotspot.Y)) + + return cursor.Image, op, nil +} + func (d *Driver) addRecord(record *menus.Record) error { //log.Printf("Adding record: %#+v", record) children := record.Children diff --git a/internal/ui/window.go b/internal/ui/window.go index c0bf619..f15d38c 100644 --- a/internal/ui/window.go +++ b/internal/ui/window.go @@ -3,7 +3,6 @@ package ui import ( "flag" "fmt" - "image" "log" "os" "runtime/debug" @@ -19,6 +18,11 @@ type Game interface { Draw(*ebiten.Image) error } +type CustomCursor interface { + // The cursor draw operation + Cursor() (*ebiten.Image, *ebiten.DrawImageOptions, error) +} + var ( screenScale = flag.Float64("screen-scale", 1.0, "Scale the window by this factor") cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`") @@ -32,8 +36,6 @@ type Window struct { KeyUpHandlers map[ebiten.Key]func() MouseWheelHandler func(float64, float64) - cursor *ebiten.Image - // Allow the "game" to be switched out at any time game Game @@ -74,15 +76,26 @@ func (w *Window) Layout(_, _ int) (int, int) { return w.xRes, w.yRes } -func (w *Window) SetCursor(cursor *ebiten.Image) { - w.cursor = cursor +func (w *Window) drawCursor(screen *ebiten.Image) error { + cIface, ok := w.game.(CustomCursor) + if !ok { + return nil + } + + cursor, op, err := cIface.Cursor() + if err != nil { + return err + } // Hide the system cursor if we have a custom one if cursor == nil { ebiten.SetCursorMode(ebiten.CursorModeVisible) - } else { - ebiten.SetCursorMode(ebiten.CursorModeHidden) + return nil } + + ebiten.SetCursorMode(ebiten.CursorModeHidden) + + return screen.DrawImage(cursor, op) } func (w *Window) Update(screen *ebiten.Image) (outErr error) { @@ -125,23 +138,14 @@ func (w *Window) Update(screen *ebiten.Image) (outErr error) { return err } - if w.cursor != nil { - // TODO: account for scaling, including the hotspot. - pos := image.Pt(ebiten.CursorPosition()) - op := ebiten.DrawImageOptions{} - op.GeoM.Translate(float64(pos.X), float64(pos.Y)) - if err := screen.DrawImage(w.cursor, &op); err != nil { - return err - } - } - if w.debug { // Draw FPS, etc, to the screen msg := fmt.Sprintf("tps=%0.2f fps=%0.2f", ebiten.CurrentTPS(), ebiten.CurrentFPS()) ebitenutil.DebugPrint(screen, msg) } - return nil + // Draw the cursor last + return w.drawCursor(screen) } // TODO: a stop or other cancellation mechanism