From 9641719579701dc68708bd2b0d87c2dfe0fdcbae Mon Sep 17 00:00:00 2001 From: Sander Schobers Date: Mon, 11 May 2020 11:44:50 +0200 Subject: [PATCH] Added intro dialog. Refactored event handling to be able to "handle" events so no other controls will handle the same event again. --- buttonbar.go | 6 +- buyflowerbutton.go | 11 +- cmd/tins2020/res/images/checkmark.png | Bin 0 -> 15290 bytes cmd/tins2020/res/images/cross.png | Bin 0 -> 15195 bytes .../res/images/game_control_hover.png | Bin 297 -> 0 bytes cmd/tins2020/res/images/information.png | Bin 0 -> 15121 bytes cmd/tins2020/res/images/return.png | Bin 0 -> 15313 bytes cmd/tins2020/res/textures.txt | 6 +- cmd/tins2020/tins2020.go | 28 +++-- color.go | 13 +++ container.go | 13 ++- content.go | 28 +++++ control.go | 9 +- dialogs.go | 53 +++++++++ fpsrenderer.go | 5 + game.go | 4 +- gamecontrols.go | 26 +++-- iconbutton.go | 36 ++++-- intro.go | 27 +++++ io.go | 2 +- label.go | 108 ++++++++++++++++++ largedialog.go | 95 +++++++++++++++ proxy.go | 37 ++++++ terrainrenderer.go | 3 +- textures.go | 4 + 25 files changed, 467 insertions(+), 47 deletions(-) create mode 100644 cmd/tins2020/res/images/checkmark.png create mode 100644 cmd/tins2020/res/images/cross.png delete mode 100644 cmd/tins2020/res/images/game_control_hover.png create mode 100644 cmd/tins2020/res/images/information.png create mode 100644 cmd/tins2020/res/images/return.png create mode 100644 content.go create mode 100644 dialogs.go create mode 100644 intro.go create mode 100644 label.go create mode 100644 largedialog.go create mode 100644 proxy.go 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 0000000000000000000000000000000000000000..34ec4380ccd12e75fc22088ad20c273509a59f2f GIT binary patch literal 15290 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 literal 0 HcmV?d00001 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 f6ee4b278eceadd6c31769d0f675edcb58c0832f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 297 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7OGoA3R+gLn`LHxfsaFpdfO>$e`T|IJFve&Q4N?3PpLF*A&i!L*!sA@h z-mf#?-*;v{^PB0u?EZniP0dXYHvs^ed%DFz^zF8P8keHqkG1dn1br<_c8{3=EMH;& z+y&lxXB7a!1$B7T8jbb}vYv2Biar5dnM4w)0SL8cl9Id=TFeBTRJAt8&C_QbjHBkL=m*luPWOjcs; zY;$zj12Uu00Vbjwkny>EPMLG_Od#muecm9?Kh3x~-ox@7>kc}3UhoD4-p}Mej&Ku- zLWU9(2F0%YaOkf#$CPCy1(r>x)2_6~r5lqh9}EUr&ds{rPNZ;}Gnyr3oSL~NXHxJH zp(z_`(o%Jev3;cpJ!Q2y9QHuPPkvpAWN{$P%(Fuj*^HEAc^AjlNs2~qpO#3}%rvb* zSXx=l0nOo=BxDDnsizDXBKewHQ!uU0X|$v~qD2^WPD9PAX{E?TpJd92XF)463*#_> zQcOgFuep}wly2$flwJp5vAZrtOO4fGVfWNcgBi6>n%$F2%hptGt3#-eg@^$qOE-pf zJs!@L>p)@lOeDgr-==Dco;IHdv6bpl&6JFwC|NL!YTApc*~z(w-Mrx81Bkg|&`Pp`5Tbvb3%P~KWW*6xUx^NXEc zzdO>!^PS$HH^O&x@m(8RyCPmb(owL*QmbvDp-w}k%g__q<*kK|xVp%VDQ*wC%>+rdZ|txsq0J4k*&D9V zR+>~U!yYInsIjMuE8nX0`eUT0pjLL=D~@W;Dyyn*V(Kv~Eg7(55}gRbC6qHPP# zPf6M&RKjclTA`sbCB)jh7FWwRE!SGUmxcPP-usXu3o+eDNLE-)NRyCFYLg+h)L0rb zRQwMhMD)0Bpvw$~Jt4MMGnQrF{dQ?1N>a8S_z+vwR;eGg4}LtA)d$=h`k9# z5S#`vrnrcRZ~?`I(;&ta7ZDLIptx`v#F*kDBEkg}7fyp1Q(QzuxPaoqX%J(Ii--sp zP+T|-VoY%n5#a)g3#UPhDJ~)+TtIQ*G>9?9MMQ)PC@!1^F{ZePh;RYLh0`F$6c-T@ zE}*z@8pN35A|k>C6ch90CO(`@Xx>K`#JzA4uGG>0T2!Yuu?y; zDK0!p(6)w zZhh{uxqoCKI5)KAV#C>QKOBE@e)*ibrT3okOR4!Q@AqxL(a*f`Z141ZO1yZC{q=I- zvmN?&c5P$#LoGM%ofEDdV@>h&h&G=%{3!p^s_)kwVn+IJyxsB$IR5zD)QROS8!umD zuI~DCQ+5I((=g*){l$fK$M+s-=l}Te%vb4ipFZ!5_w5#$zjpm_sk8mP57wfIip*=L b?gy*)etz_Y&2#q4ub`*1PyA@(_}>2j7iw}Q literal 0 HcmV?d00001 diff --git a/cmd/tins2020/res/images/return.png b/cmd/tins2020/res/images/return.png new file mode 100644 index 0000000000000000000000000000000000000000..5f3bed332f5e2e785a47573a7e62addfb8d6595f GIT binary patch literal 15313 zcmeI3d2ka|9LHa@A}v$J17XBtCmf=Z?vbQzmNcD`G`45}OQo$BU3NFgLb99gZb@2| z>BtOeiM(8~o<1zp-QB=S7XT?}A^IiIy${fN|$oe>Sl3 z{z(9M)++6tW@qRsUew|>f}|~$Yf|w9R0H5`OeF+yscce<<%ps-*nfC_ubon)278A) z#Do%lIjXeu>+*vBx$R>AQqd#X8+}#Y6b}vJvMErhcuY0;RD(V3%fr}ertMT(VlHj4 zH(3KxouT=ZU(;pEUE_9$498M+o*LHe^04eoiep$O%`!CSaj-1!s^i&OD*Lkgs$k^R zr7+(X2xfa2S26DWn{#F(qS?^9jg? zs4EFm(NxOv6&7o~W`o^s4U~Un=M_)n2U3kJJ4lgE2??65VdyeRq0oub;_;H1hS??; zW>#`QqrER7(`~Y$_3EMwPYZOJ| zopnxUZM{cyFiyeksN-B=hsVh|9bu=$I_txt;9~32lN^~o$~vHly;conttDv9`I&%= z3p#5#e~@LHT^^U8Z3?o%+4Vuci}g3b&8ls=4EAT`YVhAg1 z1^)}?5*ElsKsPu{i(-N~_A77+Tm&3cB1Bfja_o2xSaM zW|VgpqkSU{Z?s1N10EUZX0ekIRl$)Qn_VKTsJM3*b$e?5lOVoo+8(++3V7UrEWa z&VT~XQp4IIZaA-WPH{)c#IACQNJlm?QwqrKQ@TcO7fJ&g6I@tCsDR)?X<%c53yTO95L_q?Y)o)r5upNt3#EaL z2`(%mR6uZ{G_Wzjg++u42riTcHYT{Rh)@B+h0?&r1Q!+&Dj>K}8rYcN!XiQi1Q$vJ z8xveuM5ut^LTO-Qf(wfX6%bq~4QxzsVG*GMf(xaAjR`I+B2++dp){~D!G%SH3J5Ng z1~w+Ru!v9r!G+Sm#sn7@5h@_KP#V~n;KCw81q2sL0~-@uSVX9R;6iC&V}c8d2o(@q zC=G0ULR?jaM=fO)K3|!H4^n=4*Zm#vAxuhaX$t|+KMjC^6#)GH5B&ZHfL;cGZ@U5D z*8?z7yJyk9AON=At%0WY)bMxfzmLDdj9S%i6rv2+#YWmdVFCW}0eH{5@;n4D}j_vP1 z|Nh)*M~_W(9()~)-BR)H-M|Je+;!i+)q~=?u2mX6>Bfie-1OX@g&*AVW8J{KO%oTK z?s;~&cG{Y!|2k^(zELsjyhHnKFWh$5Sr<>3`sBE6!@&)TdS>-=uX%21;8jXS2Tc+v=MTfO7xLl%uI}0wue@l_t|va)7n-oN zWBS7S_LrkoTc7&&=CxZ$mhGsEv}zA`x6FLCQ$ z4ua*eMQ8LK8-Mnfrw+~h>fYBb8xvSUP2Q$Qd@DDN+3+CPG;Zk5BV8Xl#K62SCIp(! gh3~1Yf1e4K%=$dKc}B%E)= 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 // }