diff --git a/buttonbar.go b/buttonbar.go index 6990526..f494d34 100644 --- a/buttonbar.go +++ b/buttonbar.go @@ -45,12 +45,8 @@ func (b *ButtonBar) Arrange(ctx *Context, bounds Rectangle) { } } -func (b *ButtonBar) Handle(ctx *Context, event sdl.Event) { - b.Container.Handle(ctx, event) -} - func (b *ButtonBar) Render(ctx *Context) { - ctx.Renderer.SetDrawColor(b.Background.R, b.Background.G, b.Background.B, b.Background.A) + SetDrawColor(ctx.Renderer, b.Background) ctx.Renderer.FillRect(b.Bounds.SDLPtr()) b.Container.Render(ctx) } diff --git a/buyflowerbutton.go b/buyflowerbutton.go index 08f79f4..7aecec2 100644 --- a/buyflowerbutton.go +++ b/buyflowerbutton.go @@ -71,24 +71,27 @@ func (b *BuyFlowerButton) Init(ctx *Context) error { return b.updateTexts(ctx) } -func (b *BuyFlowerButton) Handle(ctx *Context, event sdl.Event) { - b.IconButton.Handle(ctx, event) +func (b *BuyFlowerButton) Handle(ctx *Context, event sdl.Event) bool { + if b.IconButton.Handle(ctx, event) { + return true + } if b.IsMouseOver && b.hoverAnimation == nil { b.hoverAnimation = NewAnimationPtr(10 * time.Millisecond) b.hoverOffset = b.priceTexture.Size().X } else if !b.IsMouseOver { b.hoverAnimation = nil } + return false } func (b *BuyFlowerButton) Render(ctx *Context) { iconTexture := b.activeTexture(ctx) - mouseOverTexture := ctx.Textures.Texture("control-hover") pos := Pt(b.Bounds.X, b.Bounds.Y) iconTexture.CopyResize(ctx.Renderer, RectSize(pos.X, pos.Y-60, b.Bounds.W, 120)) if (b.IsMouseOver && !b.IsDisabled) || b.IsActive { - mouseOverTexture.CopyResize(ctx.Renderer, b.Bounds) + SetDrawColor(ctx.Renderer, TransparentWhite) + ctx.Renderer.FillRect(b.Bounds.SDLPtr()) } if b.hoverAnimation != nil { diff --git a/cmd/tins2020/res/images/checkmark.png b/cmd/tins2020/res/images/checkmark.png new file mode 100644 index 0000000..34ec438 Binary files /dev/null and b/cmd/tins2020/res/images/checkmark.png differ diff --git a/cmd/tins2020/res/images/cross.png b/cmd/tins2020/res/images/cross.png new file mode 100644 index 0000000..3ace8b9 Binary files /dev/null and b/cmd/tins2020/res/images/cross.png differ diff --git a/cmd/tins2020/res/images/game_control_hover.png b/cmd/tins2020/res/images/game_control_hover.png deleted file mode 100644 index f6ee4b2..0000000 Binary files a/cmd/tins2020/res/images/game_control_hover.png and /dev/null differ diff --git a/cmd/tins2020/res/images/information.png b/cmd/tins2020/res/images/information.png new file mode 100644 index 0000000..c5b42f6 Binary files /dev/null and b/cmd/tins2020/res/images/information.png differ diff --git a/cmd/tins2020/res/images/return.png b/cmd/tins2020/res/images/return.png new file mode 100644 index 0000000..5f3bed3 Binary files /dev/null and b/cmd/tins2020/res/images/return.png differ diff --git a/cmd/tins2020/res/textures.txt b/cmd/tins2020/res/textures.txt index 919220b..2291d90 100644 --- a/cmd/tins2020/res/textures.txt +++ b/cmd/tins2020/res/textures.txt @@ -1,10 +1,11 @@ -control-hover: images/game_control_hover.png control-shovel: images/genericItem_color_022.png control-research: images/genericItem_color_111.png control-settings: images/gear.png control-save: images/save.png control-load: images/import.png +control-new: images/return.png +control-information: images/information.png control-quit: images/power.png control-pause: images/pause.png @@ -14,6 +15,9 @@ control-run-disabled: images/forward_disabled.png control-run-fast: images/fastForward.png control-run-fast-disabled: images/fastForward_disabled.png +control-cancel: images/cross.png +control-confirm: images/checkmark.png + tile-dirt: images/tile_dirt.png tile-grass: images/tile_grass.png tile-snow: images/tile_snow.png diff --git a/cmd/tins2020/tins2020.go b/cmd/tins2020/tins2020.go index 71de9b8..1890eda 100644 --- a/cmd/tins2020/tins2020.go +++ b/cmd/tins2020/tins2020.go @@ -28,7 +28,7 @@ func run() error { } defer sdl.Quit() - // logSDLVersion() + logSDLVersion() if err := ttf.Init(); err != nil { return err @@ -56,7 +56,7 @@ func run() error { sdl.SetHint(sdl.HINT_RENDER_VSYNC, "1") } sdl.SetHint(sdl.HINT_RENDER_SCALE_QUALITY, "1") - window, err := sdl.CreateWindow("TINS 2020", + window, err := sdl.CreateWindow("Botanim - TINS 2020", ctx.Settings.Window.Location.X, ctx.Settings.Window.Location.Y, ctx.Settings.Window.Size.X, ctx.Settings.Window.Size.Y, sdl.WINDOW_SHOWN|sdl.WINDOW_RESIZABLE) @@ -74,10 +74,11 @@ func run() error { ctx.Init(renderer) err = ctx.Fonts.LoadDesc( - tins2020.FontDescriptor{Name: "debug", Path: "fonts/FiraMono-Regular.ttf", Size: 12}, - tins2020.FontDescriptor{Name: "default", Path: "fonts/OpenSans-Regular.ttf", Size: 16}, - tins2020.FontDescriptor{Name: "small", Path: "fonts/OpenSans-Regular.ttf", Size: 12}, tins2020.FontDescriptor{Name: "balance", Path: "fonts/OpenSans-Bold.ttf", Size: 40}, + tins2020.FontDescriptor{Name: "debug", Path: "fonts/FiraMono-Regular.ttf", Size: 12}, + tins2020.FontDescriptor{Name: "default", Path: "fonts/OpenSans-Regular.ttf", Size: 14}, + tins2020.FontDescriptor{Name: "small", Path: "fonts/OpenSans-Regular.ttf", Size: 12}, + tins2020.FontDescriptor{Name: "title", Path: "fonts/OpenSans-Bold.ttf", Size: 40}, ) if err != nil { return err @@ -93,18 +94,25 @@ func run() error { game := tins2020.NewGame() app := tins2020.NewContainer() + overlays := tins2020.NewContainer() - gameControls := tins2020.NewGameControls(game) - overlays.AddChild(gameControls) - overlays.AddChild(&tins2020.FPS{}) - content := tins2020.NewContainer() + dialogs := tins2020.NewDialogs() + + overlays.AddChild(dialogs) + overlays.AddChild(&tins2020.FPS{Show: &game.Debug}) + + content := tins2020.NewContent(dialogs) + content.AddChild(tins2020.NewTerrainRenderer(game)) + content.AddChild(tins2020.NewGameControls(game, dialogs)) + app.AddChild(content) app.AddChild(overlays) - content.AddChild(tins2020.NewTerrainRenderer(game)) + err = app.Init(ctx) if err != nil { return err } + dialogs.ShowIntro() w, h := window.GetSize() app.Arrange(ctx, tins2020.Rect(0, 0, w, h)) diff --git a/color.go b/color.go index 3f26260..fc953d1 100644 --- a/color.go +++ b/color.go @@ -6,4 +6,17 @@ import ( "github.com/veandco/go-sdl2/sdl" ) +var Black = sdl.Color{R: 0, G: 0, B: 0, A: 255} +var Transparent = sdl.Color{R: 0, G: 0, B: 0, A: 0} +var TransparentWhite = sdl.Color{R: 255, G: 255, B: 255, A: 31} +var White = sdl.Color{R: 255, G: 255, B: 255, A: 255} + func MustHexColor(s string) sdl.Color { return sdl.Color(img.MustHexColor(s)) } + +func SetDrawColor(renderer *sdl.Renderer, color sdl.Color) { + renderer.SetDrawColor(color.R, color.G, color.B, color.A) +} + +func SetDrawColorHex(renderer *sdl.Renderer, s string) { + SetDrawColor(renderer, MustHexColor(s)) +} diff --git a/container.go b/container.go index 4ca9673..981b37b 100644 --- a/container.go +++ b/container.go @@ -25,11 +25,16 @@ func (c *Container) Arrange(ctx *Context, bounds Rectangle) { } } -func (c *Container) Handle(ctx *Context, event sdl.Event) { - c.ControlBase.Handle(ctx, event) - for _, child := range c.Children { - child.Handle(ctx, event) +func (c *Container) Handle(ctx *Context, event sdl.Event) bool { + if c.ControlBase.Handle(ctx, event) { + return true } + for _, child := range c.Children { + if child.Handle(ctx, event) { + return true + } + } + return false } func (c *Container) Init(ctx *Context) error { diff --git a/content.go b/content.go new file mode 100644 index 0000000..eec93c4 --- /dev/null +++ b/content.go @@ -0,0 +1,28 @@ +package tins2020 + +import "github.com/veandco/go-sdl2/sdl" + +// Content shortcuts events when a dialog is opened. +type Content struct { + Container + + dialogOverlayed bool +} + +func NewContent(dialogs *Dialogs) *Content { + content := &Content{} + dialogs.DialogOpened().Register(func() { + content.dialogOverlayed = true + }) + dialogs.DialogClosed().Register(func() { + content.dialogOverlayed = false + }) + return content +} + +func (c *Content) Handle(ctx *Context, event sdl.Event) bool { + if c.dialogOverlayed { + return false + } + return c.Container.Handle(ctx, event) +} diff --git a/control.go b/control.go index 3f2f513..713ce9e 100644 --- a/control.go +++ b/control.go @@ -5,7 +5,7 @@ import "github.com/veandco/go-sdl2/sdl" type Control interface { Init(*Context) error Arrange(*Context, Rectangle) - Handle(*Context, sdl.Event) + Handle(*Context, sdl.Event) bool Render(*Context) } @@ -31,7 +31,12 @@ func (b *ControlBase) Arrange(ctx *Context, bounds Rectangle) { b.Bounds = bound func (b *ControlBase) Init(*Context) error { return nil } -func (b *ControlBase) Handle(ctx *Context, event sdl.Event) { +func (b *ControlBase) Handle(ctx *Context, event sdl.Event) bool { + b.HandleNoFeedback(ctx, event) + return false +} + +func (b *ControlBase) HandleNoFeedback(ctx *Context, event sdl.Event) { switch e := event.(type) { case *sdl.MouseMotionEvent: b.IsMouseOver = b.Bounds.IsPointInside(e.X, e.Y) diff --git a/dialogs.go b/dialogs.go new file mode 100644 index 0000000..0afc78b --- /dev/null +++ b/dialogs.go @@ -0,0 +1,53 @@ +package tins2020 + +type Dialogs struct { + Proxy + + intro Control + settings Control + + dialogClosed *Events + dialogOpened *Events +} + +func NewDialogs() *Dialogs { + return &Dialogs{ + dialogClosed: NewEvents(), + dialogOpened: NewEvents(), + } +} + +func (d *Dialogs) DialogClosed() EventHandler { return d.dialogClosed } +func (d *Dialogs) DialogOpened() EventHandler { return d.dialogOpened } + +func (d *Dialogs) Init(ctx *Context) error { + d.intro = &Intro{} + d.settings = &DialogBase{} + + err := d.intro.Init(ctx) + if err != nil { + return err + } + err = d.settings.Init(ctx) + if err != nil { + return err + } + return nil +} + +func (d *Dialogs) Close() { + d.Proxied = nil + d.dialogClosed.Notify(nil) +} + +func (d *Dialogs) ShowIntro() { + d.Proxied = d.intro + d.intro.(Dialog).ShowDialog(d.Close) + d.dialogOpened.Notify(nil) +} + +func (d *Dialogs) ShowSettings() { + d.Proxied = d.settings + d.settings.(Dialog).ShowDialog(d.Close) + d.dialogOpened.Notify(nil) +} diff --git a/fpsrenderer.go b/fpsrenderer.go index ddacc9a..57d48d2 100644 --- a/fpsrenderer.go +++ b/fpsrenderer.go @@ -10,6 +10,7 @@ import ( type FPS struct { ControlBase + Show *bool start time.Time stamp time.Duration slot int @@ -25,6 +26,10 @@ func (f *FPS) Init(*Context) error { } func (f *FPS) Render(ctx *Context) { + if f.Show == nil || !*f.Show { + return + } + elapsed := time.Since(f.start) stamp := elapsed / (20 * time.Millisecond) for f.stamp < stamp { diff --git a/game.go b/game.go index 1d746f0..fbe62d9 100644 --- a/game.go +++ b/game.go @@ -7,6 +7,8 @@ import ( ) type Game struct { + Debug bool + Balance int Speed GameSpeed SpeedBeforePause GameSpeed @@ -29,7 +31,7 @@ const ( ) const simulationInterval = 120 * time.Millisecond -const fastSimulationInterval = 40 * time.Millisecond +const fastSimulationInterval = 20 * time.Millisecond func NewGame() *Game { terrain := &Map{ diff --git a/gamecontrols.go b/gamecontrols.go index ddd39c2..3f16f41 100644 --- a/gamecontrols.go +++ b/gamecontrols.go @@ -7,7 +7,8 @@ import ( type GameControls struct { Container - game *Game + game *Game + dialogs *Dialogs menu ButtonBar top ButtonBar @@ -22,8 +23,8 @@ type GameControls struct { research *IconButton } -func NewGameControls(game *Game) *GameControls { - return &GameControls{game: game} +func NewGameControls(game *Game, dialogs *Dialogs) *GameControls { + return &GameControls{game: game, dialogs: dialogs} } func (c *GameControls) createBuyFlowerButton(id string) *BuyFlowerButton { @@ -75,6 +76,8 @@ func (c *GameControls) Arrange(ctx *Context, bounds Rectangle) { func (c *GameControls) Init(ctx *Context) error { c.game.SpeedChanged().RegisterItf(c.speedChanged) c.game.ToolChanged().RegisterItf(c.toolChanged) + c.dialogs.DialogOpened().Register(func() { c.game.Pause() }) + c.dialogs.DialogClosed().Register(func() { c.game.Resume() }) c.flowers.Background = MustHexColor("#356dad") c.flowers.ButtonLength = 64 @@ -104,7 +107,7 @@ func (c *GameControls) Init(ctx *Context) error { c.menu.Background = MustHexColor("#356dad") c.menu.Buttons = []Control{ - NewIconButton("control-settings", func(*Context) {}), + NewIconButton("control-settings", func(*Context) { c.dialogs.ShowSettings() }), NewIconButton("control-save", func(*Context) { c.game.Save() }), NewIconButton("control-load", func(ctx *Context) { c.game.Load() @@ -126,11 +129,14 @@ func (c *GameControls) Init(ctx *Context) error { c.Container.AddChild(&c.top) c.Container.AddChild(&c.flowers) c.Container.AddChild(&c.otherTools) + return c.Container.Init(ctx) } -func (c *GameControls) Handle(ctx *Context, event sdl.Event) { - c.Container.Handle(ctx, event) +func (c *GameControls) Handle(ctx *Context, event sdl.Event) bool { + if c.Container.Handle(ctx, event) { + return true + } switch e := event.(type) { case *sdl.KeyboardEvent: @@ -148,18 +154,22 @@ func (c *GameControls) Handle(ctx *Context, event sdl.Event) { c.game.SelectResearch() case sdl.K_ESCAPE: if c.game.Tool() == nil { - // TODO: display menu + c.dialogs.ShowIntro() } else { c.game.CancelTool() } + return true + case sdl.K_F3: + c.game.Debug = !c.game.Debug } } } + return false } func (c *GameControls) Render(ctx *Context) { topBar := MustHexColor("#0000007f") - ctx.Renderer.SetDrawColor(topBar.R, topBar.G, topBar.B, topBar.A) + SetDrawColor(ctx.Renderer, topBar) ctx.Renderer.FillRect(Rect(c.menu.Bounds.Right(), 0, c.flowers.Bounds.X, 64).SDLPtr()) ctx.Fonts.Font("balance").RenderCopyAlign(ctx.Renderer, FmtMoney(c.game.Balance), Pt(c.top.Bounds.X-8, 58), MustHexColor("#4AC69A"), TextAlignmentRight) diff --git a/iconbutton.go b/iconbutton.go index 4c20bfd..32703f6 100644 --- a/iconbutton.go +++ b/iconbutton.go @@ -1,5 +1,12 @@ package tins2020 +type HoverEffect int + +const ( + HoverEffectLigthen HoverEffect = iota + HoverEffectColor +) + type IconButton struct { ControlBase @@ -9,17 +16,13 @@ type IconButton struct { IconScale Scale IconWidth int32 + IconActive HoverEffect + IconHover HoverEffect + IsActive bool IsDisabled bool } -type Scale int - -const ( - ScaleCenter Scale = iota - ScaleStretch -) - func NewIconButton(icon string, onClick EventContextFn) *IconButton { return &IconButton{ ControlBase: ControlBase{ @@ -47,7 +50,11 @@ func (b *IconButton) activeTexture(ctx *Context) *Texture { func (b *IconButton) Render(ctx *Context) { iconTexture := b.activeTexture(ctx) - mouseOverTexture := ctx.Textures.Texture("control-hover") + + hover := b.IsMouseOver && !b.IsDisabled + if (hover && b.IconHover == HoverEffectColor) || (b.IsActive && b.IconActive == HoverEffectColor) { + iconTexture.SetColor(MustHexColor("#15569F")) + } if b.IconScale == ScaleCenter { size := iconTexture.Size() @@ -60,7 +67,16 @@ func (b *IconButton) Render(ctx *Context) { } else { iconTexture.CopyResize(ctx.Renderer, b.Bounds) } - if (b.IsMouseOver && !b.IsDisabled) || b.IsActive { - mouseOverTexture.CopyResize(ctx.Renderer, b.Bounds) + if (hover && b.IconHover == HoverEffectLigthen) || (b.IsActive && b.IconActive == HoverEffectLigthen) { + SetDrawColor(ctx.Renderer, TransparentWhite) + ctx.Renderer.FillRect(b.Bounds.SDLPtr()) } + iconTexture.SetColor(White) } + +type Scale int + +const ( + ScaleCenter Scale = iota + ScaleStretch +) diff --git a/intro.go b/intro.go new file mode 100644 index 0000000..f88ed34 --- /dev/null +++ b/intro.go @@ -0,0 +1,27 @@ +package tins2020 + +type Intro struct { + LargeDialog + + welcome Paragraph +} + +func (i *Intro) Init(ctx *Context) error { + i.welcome.Text = + "Welcome to Botanim!\n\n" + + "In Botanim you play the role of botanist and your goal is to cultivate flowers in an open landscape.\n\n" + + "Flowers can only grow (well) in certain climates based on two properties: humidity and temperature. Watch out for existing vegetation to get an idea how humid the land is and check the appearance of the tile to see how hot it is. When well placed your planted flower will spread soon but an odd choice might kill your flower almost instantly. So choose carefully. When the flower spread significantly you can dig up flowers again to collect more money.\n\n" + + "Controls:\n" + + " - D: Selects shovel\n" + + " - R: Selects research\n" + + " - Spacebar: pauses game\n" + + " - 1: runs game at normal speed\n" + + " - 2: runs game extra fast\n" + + " - Mouse wheel or plus/minus: zooms landscape\n" + + " - CTRL + left mouse button or middle mouse button: pans landscape\n" + + "\n" + + "Have fun playing!" + i.SetContent(&i.welcome) + + return i.LargeDialog.Init(ctx) +} diff --git a/io.go b/io.go index 2bbeb0d..388ab9c 100644 --- a/io.go +++ b/io.go @@ -33,7 +33,7 @@ func UserDir() (string, error) { if err != nil { return "", err } - dir := filepath.Join(config, "tins2020_flowers_sim") + dir := filepath.Join(config, "tins2020_botanim") err = os.MkdirAll(dir, 0777) if err != nil { return "", err diff --git a/label.go b/label.go new file mode 100644 index 0000000..f622790 --- /dev/null +++ b/label.go @@ -0,0 +1,108 @@ +package tins2020 + +import ( + "strings" + + "github.com/veandco/go-sdl2/sdl" +) + +type Label struct { + ControlBase + + FontColor sdl.Color + FontName string + Text string + Alignment TextAlignment +} + +func (l *Label) fontColor() sdl.Color { + var none sdl.Color + if l.FontColor == none { + return MustHexColor("#ffffff") + } + return l.FontColor +} + +func (l *Label) fontName() string { + if len(l.FontName) == 0 { + return "default" + } + return l.FontName +} + +func (l *Label) Render(ctx *Context) { + font := ctx.Fonts.Font(l.fontName()) + color := l.fontColor() + bottom := l.Bounds.Y + l.Bounds.H + switch l.Alignment { + case TextAlignmentCenter: + font.RenderCopyAlign(ctx.Renderer, l.Text, Pt(l.Bounds.X+l.Bounds.W/2, bottom), color, TextAlignmentCenter) + case TextAlignmentLeft: + font.RenderCopyAlign(ctx.Renderer, l.Text, Pt(l.Bounds.X, bottom), color, TextAlignmentLeft) + case TextAlignmentRight: + font.RenderCopyAlign(ctx.Renderer, l.Text, Pt(l.Bounds.X+l.Bounds.W, bottom), color, TextAlignmentRight) + } +} + +type Paragraph struct { + Label +} + +// func (p *Paragraph) Arrange(ctx *Context, bounds Rectangle) { +// p.Label.Arrange(ctx, bounds) +// } + +func (p *Paragraph) Render(ctx *Context) { + font := ctx.Fonts.Font(p.fontName()) + color := p.fontColor() + fontHeight := int32(font.Height()) + lines := strings.Split(p.Text, "\n") + + measure := func(s string) int32 { + w, _, _ := font.SizeUTF8(s) + return int32(w) + } + + spaces := func(s string) []int { + var spaces []int + offset := 0 + for { + space := strings.Index(s[offset:], " ") + if space == -1 { + return spaces + } + offset += space + spaces = append(spaces, offset) + offset++ + } + } + + fit := func(s string) string { + if measure(s) < p.Bounds.W { + return s + } + spaces := spaces(s) + for split := len(spaces) - 1; split >= 0; split-- { + clipped := s[:spaces[split]] + if measure(clipped) < p.Bounds.W { + return clipped + } + } + return s + } + + offset := p.Bounds.Y + for _, line := range lines { + if len(line) == 0 { + offset += fontHeight + continue + } + + for len(line) > 0 { + offset += fontHeight + clipped := fit(line) + line = strings.TrimLeft(line[len(clipped):], " ") + font.RenderCopy(ctx.Renderer, clipped, Pt(p.Bounds.X, offset), color) + } + } +} diff --git a/largedialog.go b/largedialog.go new file mode 100644 index 0000000..c257b4e --- /dev/null +++ b/largedialog.go @@ -0,0 +1,95 @@ +package tins2020 + +import "github.com/veandco/go-sdl2/sdl" + +type DialogBase struct { + Container + + content Proxy + close EventFn +} + +type Dialog interface { + CloseDialog() + ShowDialog(EventFn) +} + +func (d *DialogBase) CloseDialog() { + close := d.close + if close != nil { + close() + } +} + +func (d *DialogBase) SetContent(control Control) { + d.content.Proxied = control +} + +func (d *DialogBase) ShowDialog(close EventFn) { + d.close = close +} + +func (d *DialogBase) Init(ctx *Context) error { + d.AddChild(&d.content) + return d.Container.Init(ctx) +} + +type LargeDialog struct { + DialogBase + + title Label + close IconButton +} + +func (d *LargeDialog) Arrange(ctx *Context, bounds Rectangle) { + const titleHeight = 64 + d.ControlBase.Arrange(ctx, bounds) + d.title.Arrange(ctx, RectSize(bounds.X, bounds.Y, bounds.W, titleHeight)) + d.close.Arrange(ctx, RectSize(bounds.W-64, 0, 64, 64)) + d.content.Arrange(ctx, RectSize(bounds.X+titleHeight, 96, bounds.W-2*titleHeight, bounds.H-titleHeight)) +} + +func (d *LargeDialog) Init(ctx *Context) error { + d.title = Label{ + Text: "Botanim", + FontName: "title", + Alignment: TextAlignmentCenter, + } + d.close = IconButton{ + Icon: "control-cancel", + IconHover: HoverEffectColor, + IconWidth: 32, + } + d.close.OnLeftMouseButtonClick = EmptyEvent(d.CloseDialog) + d.AddChild(&d.title) + d.AddChild(&d.close) + return d.DialogBase.Init(ctx) +} + +func (d *LargeDialog) Handle(ctx *Context, event sdl.Event) bool { + if d.DialogBase.Handle(ctx, event) { + return true + } + + switch e := event.(type) { + case *sdl.KeyboardEvent: + if e.Type == sdl.KEYDOWN { + switch e.Keysym.Sym { + case sdl.K_ESCAPE: + d.CloseDialog() + return true + case sdl.K_RETURN: + d.CloseDialog() + return true + } + } + } + return false +} + +func (d *LargeDialog) Render(ctx *Context) { + SetDrawColor(ctx.Renderer, MustHexColor("#356DAD")) + ctx.Renderer.FillRect(d.Bounds.SDLPtr()) + + d.DialogBase.Render(ctx) +} diff --git a/proxy.go b/proxy.go new file mode 100644 index 0000000..5ee5a92 --- /dev/null +++ b/proxy.go @@ -0,0 +1,37 @@ +package tins2020 + +import "github.com/veandco/go-sdl2/sdl" + +var _ Control = &Proxy{} + +type Proxy struct { + Proxied Control +} + +func (p *Proxy) Arrange(ctx *Context, bounds Rectangle) { + if p.Proxied == nil { + return + } + p.Proxied.Arrange(ctx, bounds) +} + +func (p *Proxy) Handle(ctx *Context, event sdl.Event) bool { + if p.Proxied == nil { + return false + } + return p.Proxied.Handle(ctx, event) +} + +func (p *Proxy) Init(ctx *Context) error { + if p.Proxied == nil { + return nil + } + return p.Proxied.Init(ctx) +} + +func (p *Proxy) Render(ctx *Context) { + if p.Proxied == nil { + return + } + p.Proxied.Render(ctx) +} diff --git a/terrainrenderer.go b/terrainrenderer.go index 84495b7..3df1b89 100644 --- a/terrainrenderer.go +++ b/terrainrenderer.go @@ -37,7 +37,7 @@ func isControlKeyDown() bool { return state[sdl.SCANCODE_LCTRL] == 1 || state[sdl.SCANCODE_RCTRL] == 1 } -func (r *terrainRenderer) Handle(ctx *Context, event sdl.Event) { +func (r *terrainRenderer) Handle(ctx *Context, event sdl.Event) bool { switch e := event.(type) { case *sdl.MouseButtonEvent: if r.project.windowInteractRect.IsPointInside(e.X, e.Y) { @@ -97,6 +97,7 @@ func (r *terrainRenderer) Handle(ctx *Context, event sdl.Event) { r.project.update(ctx.Renderer) } } + return false } func (r *terrainRenderer) Render(ctx *Context) { diff --git a/textures.go b/textures.go index 9128eb1..4d0eb7b 100644 --- a/textures.go +++ b/textures.go @@ -44,6 +44,10 @@ func (t *Texture) CopyResize(renderer *sdl.Renderer, dst Rectangle) { t.CopyPartResize(renderer, Rect(0, 0, t.size.X, t.size.Y), dst) } +func (t *Texture) SetColor(color sdl.Color) { + t.texture.SetColorMod(color.R, color.G, color.B) +} + // func (t *Texture) CopyF(renderer *sdl.Renderer, dst *sdl.FRect) { // renderer.CopyF(t.texture, t.rect, dst) // Depends on SDL >=2.0.10 // }