A game framework written with osu! in mind.
at master 431 lines 16 kB view raw
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. 2// See the LICENCE file in the repository root for full licence text. 3 4using System; 5using System.Linq; 6using NUnit.Framework; 7using osu.Framework.Graphics; 8using osu.Framework.Graphics.Containers; 9using osu.Framework.Graphics.Cursor; 10using osu.Framework.Graphics.Shapes; 11using osu.Framework.Graphics.Sprites; 12using osu.Framework.Graphics.UserInterface; 13using osu.Framework.Localisation; 14using osu.Framework.Testing; 15using osuTK; 16using osuTK.Graphics; 17 18namespace osu.Framework.Tests.Visual.UserInterface 19{ 20 public class TestSceneTooltip : ManualInputManagerTestScene 21 { 22 private TestTooltipContainer tooltipContainer; 23 24 private TooltipSpriteText tooltipText; 25 private TooltipSpriteText instantTooltipText; 26 private CustomTooltipSpriteText customTooltipTextA; 27 private CustomTooltipSpriteText customTooltipTextB; 28 private CustomTooltipSpriteTextAlt customTooltipTextAlt; 29 private TooltipSpriteText emptyTooltipText; 30 private TooltipSpriteText nullTooltipText; 31 private TooltipTextBox tooltipTextBox; 32 33 [SetUpSteps] 34 public void SetUpSteps() 35 { 36 AddToggleStep("add tooltips (cursor/cursor-less)", generateTest); 37 } 38 39 [Test] 40 public void TestTooltip() 41 { 42 ITooltip originalInstance = null; 43 44 hoverTooltipProvider(() => tooltipText); 45 46 AddStep("get tooltip instance", () => originalInstance = tooltipContainer.CurrentTooltip); 47 assertTooltipText(() => tooltipText.TooltipText); 48 49 hoverTooltipProvider(() => tooltipText); 50 51 AddAssert("tooltip reused", () => tooltipContainer.CurrentTooltip == originalInstance); 52 assertTooltipText(() => tooltipText.TooltipText); 53 } 54 55 [Test] 56 public void TestInstantTooltip() 57 { 58 hoverTooltipProvider(() => instantTooltipText, false); 59 assertTooltipText(() => instantTooltipText.TooltipText); 60 } 61 62 [Test] 63 public void TestCustomTooltip() 64 { 65 ITooltip originalInstance = null; 66 67 hoverTooltipProvider(() => customTooltipTextA); 68 69 AddStep("get tooltip instance", () => originalInstance = tooltipContainer.CurrentTooltip); 70 AddAssert("custom tooltip used", () => originalInstance.GetType() == typeof(CustomTooltip)); 71 assertTooltipText(() => ((CustomContent)customTooltipTextA.TooltipContent).Text); 72 73 hoverTooltipProvider(() => customTooltipTextB); 74 75 AddAssert("custom tooltip reused", () => tooltipContainer.CurrentTooltip == originalInstance); 76 assertTooltipText(() => ((CustomContent)customTooltipTextB.TooltipContent).Text); 77 } 78 79 [Test] 80 public void TestDifferentCustomTooltips() 81 { 82 hoverTooltipProvider(() => customTooltipTextA); 83 assertTooltipText(() => ((CustomContent)customTooltipTextA.TooltipContent).Text); 84 85 AddAssert("current tooltip type normal", () => tooltipContainer.CurrentTooltip.GetType() == typeof(CustomTooltip)); 86 87 hoverTooltipProvider(() => customTooltipTextAlt); 88 assertTooltipText(() => ((CustomContent)customTooltipTextAlt.TooltipContent).Text); 89 90 AddAssert("current tooltip type alt", () => tooltipContainer.CurrentTooltip.GetType() == typeof(CustomTooltipAlt)); 91 92 hoverTooltipProvider(() => customTooltipTextB); 93 assertTooltipText(() => ((CustomContent)customTooltipTextB.TooltipContent).Text); 94 95 AddAssert("current tooltip type normal", () => tooltipContainer.CurrentTooltip.GetType() == typeof(CustomTooltip)); 96 } 97 98 [Test] 99 public void TestEmptyTooltip() 100 { 101 AddStep("hover empty tooltip", () => InputManager.MoveMouseTo(emptyTooltipText)); 102 AddAssert("tooltip not shown", () => tooltipContainer.CurrentTooltip?.IsPresent != true); 103 } 104 105 [Test] 106 public void TestNullTooltip() 107 { 108 AddStep("hover null tooltip", () => InputManager.MoveMouseTo(nullTooltipText)); 109 AddAssert("tooltip not shown", () => tooltipContainer.CurrentTooltip?.IsPresent != true); 110 } 111 112 [Test] 113 public void TestUpdatingTooltip() 114 { 115 hoverTooltipProvider(() => tooltipTextBox); 116 assertTooltipText(() => tooltipTextBox.Text); 117 118 AddStep("update text", () => tooltipTextBox.Text = "updated!"); 119 120 assertTooltipText(() => "updated!"); 121 } 122 123 private void hoverTooltipProvider(Func<Drawable> getProvider, bool waitForDisplay = true) 124 { 125 AddStep("hover away from tooltips", () => InputManager.MoveMouseTo(Vector2.Zero)); 126 AddAssert("tooltip hidden", () => tooltipContainer.CurrentTooltip?.IsPresent != true); 127 128 AddStep("hover tooltip", () => InputManager.MoveMouseTo(getProvider())); 129 130 if (waitForDisplay) 131 AddUntilStep("wait for tooltip", () => tooltipContainer.CurrentTooltip?.IsPresent == true); 132 else 133 AddAssert("tooltip instantly displayed", () => tooltipContainer.CurrentTooltip?.IsPresent == true); 134 } 135 136 private void assertTooltipText(Func<LocalisableString> expected) 137 { 138 AddAssert("tooltip text matching", () => 139 { 140 var drawableTooltip = (Drawable)tooltipContainer.CurrentTooltip; 141 return drawableTooltip.ChildrenOfType<IHasText>().Count(t => t.Text == expected()) == 1; 142 }); 143 } 144 145 private TooltipBox makeBox(Anchor anchor) => new TooltipBox 146 { 147 RelativeSizeAxes = Axes.Both, 148 Size = new Vector2(0.2f), 149 Anchor = anchor, 150 Origin = anchor, 151 Colour = Color4.Blue, 152 TooltipText = $"{anchor}", 153 }; 154 155 private void generateTest(bool cursorlessTooltip) 156 { 157 Clear(); 158 159 CursorContainer cursor = null; 160 161 if (!cursorlessTooltip) 162 { 163 cursor = new RectangleCursorContainer(); 164 Add(cursor); 165 } 166 167 Add(tooltipContainer = new TestTooltipContainer(cursor) 168 { 169 RelativeSizeAxes = Axes.Both, 170 Children = new Drawable[] 171 { 172 new Container 173 { 174 Anchor = Anchor.Centre, 175 Origin = Anchor.Centre, 176 AutoSizeAxes = Axes.Both, 177 Children = new[] 178 { 179 new TooltipBox 180 { 181 TooltipText = "Outer Tooltip", 182 Colour = Color4.CornflowerBlue, 183 Size = new Vector2(300, 300), 184 Anchor = Anchor.Centre, 185 Origin = Anchor.Centre 186 }, 187 new TooltipBox 188 { 189 TooltipText = "Inner Tooltip", 190 Size = new Vector2(150, 150), 191 Anchor = Anchor.Centre, 192 Origin = Anchor.Centre 193 }, 194 } 195 }, 196 new FillFlowContainer 197 { 198 RelativeSizeAxes = Axes.Both, 199 Direction = FillDirection.Vertical, 200 Spacing = new Vector2(0, 10), 201 Children = new Drawable[] 202 { 203 tooltipText = new TooltipSpriteText("this text has a tooltip!"), 204 instantTooltipText = new InstantTooltipSpriteText("this text has an instant tooltip!"), 205 customTooltipTextA = new CustomTooltipSpriteText("this one is custom!"), 206 customTooltipTextB = new CustomTooltipSpriteText("this one is also!"), 207 customTooltipTextAlt = new CustomTooltipSpriteTextAlt("but this one is different."), 208 emptyTooltipText = new InstantTooltipSpriteText("this text has an empty tooltip!", string.Empty), 209 nullTooltipText = new InstantTooltipSpriteText("this text has a nulled tooltip!", null), 210 tooltipTextBox = new TooltipTextBox 211 { 212 Text = "with real time updates!", 213 Size = new Vector2(400, 30), 214 }, 215 new TooltipContainer 216 { 217 AutoSizeAxes = Axes.Both, 218 Child = new TooltipSpriteText("Nested tooltip; uses no cursor in all cases!"), 219 }, 220 new TooltipTooltipContainer("This tooltip container has a tooltip itself!") 221 { 222 AutoSizeAxes = Axes.Both, 223 Child = new Container 224 { 225 AutoSizeAxes = Axes.Both, 226 Child = new TooltipSpriteText("Nested tooltip; uses no cursor in all cases; parent TooltipContainer has a tooltip"), 227 } 228 }, 229 new Container 230 { 231 Child = new FillFlowContainer 232 { 233 Direction = FillDirection.Vertical, 234 Spacing = new Vector2(0, 8), 235 Children = new[] 236 { 237 new Container 238 { 239 Child = new Container 240 { 241 Child = new TooltipSpriteText("Tooltip within containers with zero size; i.e. parent is never hovered."), 242 } 243 }, 244 new Container 245 { 246 Child = new TooltipSpriteText("Other tooltip within containers with zero size; different nesting; overlap."), 247 } 248 } 249 } 250 } 251 }, 252 } 253 } 254 }); 255 256 tooltipContainer.Add(makeBox(Anchor.BottomLeft)); 257 tooltipContainer.Add(makeBox(Anchor.TopRight)); 258 tooltipContainer.Add(makeBox(Anchor.BottomRight)); 259 } 260 261 private class TestTooltipContainer : TooltipContainer 262 { 263 public new ITooltip CurrentTooltip => base.CurrentTooltip; 264 265 public TestTooltipContainer(CursorContainer cursor) 266 : base(cursor) 267 { 268 } 269 } 270 271 private class CustomTooltipSpriteText : Container, IHasCustomTooltip 272 { 273 public object TooltipContent { get; } 274 275 public CustomTooltipSpriteText(string displayedContent, string tooltipContent = null) 276 { 277 TooltipContent = new CustomContent(tooltipContent ?? displayedContent); 278 279 AutoSizeAxes = Axes.Both; 280 Children = new[] 281 { 282 new SpriteText 283 { 284 Text = displayedContent, 285 } 286 }; 287 } 288 289 public virtual ITooltip GetCustomTooltip() => new CustomTooltip(); 290 } 291 292 private class CustomTooltipSpriteTextAlt : CustomTooltipSpriteText 293 { 294 public CustomTooltipSpriteTextAlt(string displayedContent, string tooltipContent = null) 295 : base(displayedContent, tooltipContent) 296 { 297 } 298 299 public override ITooltip GetCustomTooltip() => new CustomTooltipAlt(); 300 } 301 302 private class CustomContent 303 { 304 public readonly LocalisableString Text; 305 306 public CustomContent(string text) 307 { 308 Text = text; 309 } 310 } 311 312 private class CustomTooltip : CompositeDrawable, ITooltip<CustomContent> 313 { 314 private static int i; 315 316 private readonly SpriteText text; 317 318 public CustomTooltip() 319 { 320 AutoSizeAxes = Axes.Both; 321 322 InternalChildren = new Drawable[] 323 { 324 new Box 325 { 326 RelativeSizeAxes = Axes.Both, 327 Colour = FrameworkColour.GreenDark, 328 }, 329 text = new SpriteText 330 { 331 Font = FrameworkFont.Regular.With(size: 16), 332 Padding = new MarginPadding(5), 333 }, 334 new SpriteText 335 { 336 Anchor = Anchor.BottomLeft, 337 Font = FontUsage.Default.With(size: 12), 338 Colour = Color4.Yellow, 339 Text = $"Custom tooltip instance {i++}" 340 }, 341 }; 342 } 343 344 public void SetContent(CustomContent content) => text.Text = content.Text; 345 346 public void Move(Vector2 pos) => Position = pos; 347 } 348 349 private class CustomTooltipAlt : CustomTooltip 350 { 351 public CustomTooltipAlt() 352 { 353 AutoSizeAxes = Axes.Both; 354 355 Colour = Color4.Red; 356 } 357 } 358 359 private class TooltipSpriteText : Container, IHasTooltip 360 { 361 public LocalisableString TooltipText { get; } 362 363 public TooltipSpriteText(string displayedContent) 364 : this(displayedContent, displayedContent) 365 { 366 } 367 368 protected TooltipSpriteText(string displayedContent, string tooltipContent) 369 { 370 TooltipText = tooltipContent; 371 372 AutoSizeAxes = Axes.Both; 373 Children = new[] 374 { 375 new SpriteText 376 { 377 Text = displayedContent, 378 } 379 }; 380 } 381 } 382 383 private class InstantTooltipSpriteText : TooltipSpriteText, IHasAppearDelay 384 { 385 public InstantTooltipSpriteText(string tooltipContent) 386 : base(tooltipContent, tooltipContent) 387 { 388 } 389 390 public InstantTooltipSpriteText(string displayedContent, string tooltipContent) 391 : base(displayedContent, tooltipContent) 392 { 393 } 394 395 public double AppearDelay => 0; 396 } 397 398 private class TooltipTooltipContainer : TooltipContainer, IHasTooltip 399 { 400 public LocalisableString TooltipText { get; set; } 401 402 public TooltipTooltipContainer(string tooltipText) 403 { 404 TooltipText = tooltipText; 405 } 406 } 407 408 private class TooltipTextBox : BasicTextBox, IHasTooltip 409 { 410 public LocalisableString TooltipText => Text; 411 } 412 413 private class TooltipBox : Box, IHasTooltip 414 { 415 public LocalisableString TooltipText { get; set; } 416 } 417 418 private class RectangleCursorContainer : CursorContainer 419 { 420 protected override Drawable CreateCursor() => new RectangleCursor(); 421 422 private class RectangleCursor : Box 423 { 424 public RectangleCursor() 425 { 426 Size = new Vector2(20, 40); 427 } 428 } 429 } 430 } 431}