A game framework written with osu! in mind.
at master 231 lines 8.6 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.Globalization; 6using osu.Framework.Bindables; 7using osu.Framework.Graphics; 8using osu.Framework.Graphics.Containers; 9using osu.Framework.Graphics.Sprites; 10 11namespace osu.Framework.Tests.Visual.Bindables 12{ 13 public class TestSceneBindableNumbers : FrameworkTestScene 14 { 15 private readonly BindableInt bindableInt = new BindableInt(); 16 private readonly BindableLong bindableLong = new BindableLong(); 17 private readonly BindableDouble bindableDouble = new BindableDouble(); 18 private readonly BindableFloat bindableFloat = new BindableFloat(); 19 20 public TestSceneBindableNumbers() 21 { 22 AddStep("Reset", () => 23 { 24 setValue(0); 25 setPrecision(1); 26 }); 27 28 testBasic(); 29 testPrecision3(); 30 testPrecision10(); 31 testMinMaxWithoutPrecision(); 32 testMinMaxWithPrecision(); 33 testInvalidPrecision(); 34 testFractionalPrecision(); 35 36 AddSliderStep("Min value", -100, 100, -100, setMin); 37 AddSliderStep("Max value", -100, 100, 100, setMax); 38 AddSliderStep("Value", -100, 100, 0, setValue); 39 AddSliderStep("Precision", 1, 10, 1, setPrecision); 40 41 Child = new GridContainer 42 { 43 RelativeSizeAxes = Axes.Both, 44 Content = new[] 45 { 46 new Drawable[] 47 { 48 new BindableDisplayContainer<int>(bindableInt), 49 new BindableDisplayContainer<long>(bindableLong), 50 }, 51 new Drawable[] 52 { 53 new BindableDisplayContainer<float>(bindableFloat), 54 new BindableDisplayContainer<double>(bindableDouble), 55 } 56 } 57 }; 58 } 59 60 /// <summary> 61 /// Tests basic setting of values. 62 /// </summary> 63 private void testBasic() 64 { 65 AddStep("Value = 10", () => setValue(10)); 66 AddAssert("Check = 10", () => checkExact(10)); 67 } 68 69 /// <summary> 70 /// Tests that midpoint values are correctly rounded with a precision of 3. 71 /// </summary> 72 private void testPrecision3() 73 { 74 AddStep("Precision = 3", () => setPrecision(3)); 75 AddStep("Value = 4", () => setValue(3)); 76 AddAssert("Check = 3", () => checkExact(3)); 77 AddStep("Value = 5", () => setValue(5)); 78 AddAssert("Check = 6", () => checkExact(6)); 79 AddStep("Value = 59", () => setValue(59)); 80 AddAssert("Check = 60", () => checkExact(60)); 81 } 82 83 /// <summary> 84 /// Tests that midpoint values are correctly rounded with a precision of 10. 85 /// </summary> 86 private void testPrecision10() 87 { 88 AddStep("Precision = 10", () => setPrecision(10)); 89 AddStep("Value = 6", () => setValue(6)); 90 AddAssert("Check = 10", () => checkExact(10)); 91 } 92 93 /// <summary> 94 /// Tests that values are correctly clamped to min/max values. 95 /// </summary> 96 private void testMinMaxWithoutPrecision() 97 { 98 AddStep("Precision = 1", () => setPrecision(1)); 99 AddStep("Min = -30", () => setMin(-30)); 100 AddStep("Max = 30", () => setMax(30)); 101 AddStep("Value = -50", () => setValue(-50)); 102 AddAssert("Check = -30", () => checkExact(-30)); 103 AddStep("Value = 50", () => setValue(50)); 104 AddAssert("Check = 30", () => checkExact(30)); 105 } 106 107 /// <summary> 108 /// Tests that values are correctly clamped to min/max values when precision is involved. 109 /// In this case, precision is preferred over min/max values. 110 /// </summary> 111 private void testMinMaxWithPrecision() 112 { 113 AddStep("Precision = 5", () => setPrecision(5)); 114 AddStep("Min = -27", () => setMin(-27)); 115 AddStep("Max = 27", () => setMax(27)); 116 AddStep("Value = -30", () => setValue(-30)); 117 AddAssert("Check = -25", () => checkExact(-25)); 118 AddStep("Value = 30", () => setValue(30)); 119 AddAssert("Check = 25", () => checkExact(25)); 120 } 121 122 /// <summary> 123 /// Tests that invalid precisions cause exceptions. 124 /// </summary> 125 private void testInvalidPrecision() 126 { 127 AddAssert("Precision = 0 throws", () => 128 { 129 try 130 { 131 setPrecision(0); 132 return false; 133 } 134 catch (Exception) 135 { 136 return true; 137 } 138 }); 139 140 AddAssert("Precision = -1 throws", () => 141 { 142 try 143 { 144 setPrecision(-1); 145 return false; 146 } 147 catch (Exception) 148 { 149 return true; 150 } 151 }); 152 } 153 154 /// <summary> 155 /// Tests that fractional precisions are obeyed. 156 /// Note that int bindables are assigned int precisions/values, so their results will differ. 157 /// </summary> 158 private void testFractionalPrecision() 159 { 160 AddStep("Precision = 2.25/2", () => setPrecision(2.25)); 161 AddStep("Value = 3.3/3", () => setValue(3.3)); 162 AddAssert("Check = 2.25/4", () => checkExact(2.25m, 4)); 163 AddStep("Value = 4.17/4", () => setValue(4.17)); 164 AddAssert("Check = 4.5/4", () => checkExact(4.5m, 4)); 165 } 166 167 private bool checkExact(decimal value) => checkExact(value, value); 168 169 private bool checkExact(decimal floatValue, decimal intValue) 170 => bindableInt.Value == Convert.ToInt32(intValue) 171 && bindableLong.Value == Convert.ToInt64(intValue) 172 && bindableFloat.Value == Convert.ToSingle(floatValue) 173 && bindableDouble.Value == Convert.ToDouble(floatValue); 174 175 private void setMin<T>(T value) 176 { 177 bindableInt.MinValue = Convert.ToInt32(value); 178 bindableLong.MinValue = Convert.ToInt64(value); 179 bindableFloat.MinValue = Convert.ToSingle(value); 180 bindableDouble.MinValue = Convert.ToDouble(value); 181 } 182 183 private void setMax<T>(T value) 184 { 185 bindableInt.MaxValue = Convert.ToInt32(value); 186 bindableLong.MaxValue = Convert.ToInt64(value); 187 bindableFloat.MaxValue = Convert.ToSingle(value); 188 bindableDouble.MaxValue = Convert.ToDouble(value); 189 } 190 191 private void setValue<T>(T value) 192 { 193 bindableInt.Value = Convert.ToInt32(value); 194 bindableLong.Value = Convert.ToInt64(value); 195 bindableFloat.Value = Convert.ToSingle(value); 196 bindableDouble.Value = Convert.ToDouble(value); 197 } 198 199 private void setPrecision<T>(T precision) 200 { 201 bindableInt.Precision = Convert.ToInt32(precision); 202 bindableLong.Precision = Convert.ToInt64(precision); 203 bindableFloat.Precision = Convert.ToSingle(precision); 204 bindableDouble.Precision = Convert.ToDouble(precision); 205 } 206 207 private class BindableDisplayContainer<T> : CompositeDrawable 208 where T : struct, IComparable<T>, IConvertible, IEquatable<T> 209 { 210 public BindableDisplayContainer(BindableNumber<T> bindable) 211 { 212 Anchor = Anchor.Centre; 213 Origin = Anchor.Centre; 214 215 SpriteText valueText; 216 InternalChild = new FillFlowContainer 217 { 218 AutoSizeAxes = Axes.Both, 219 Direction = FillDirection.Vertical, 220 Children = new Drawable[] 221 { 222 new SpriteText { Text = $"{typeof(T).Name} value:" }, 223 valueText = new SpriteText { Text = bindable.Value.ToString(CultureInfo.InvariantCulture) } 224 } 225 }; 226 227 bindable.ValueChanged += e => valueText.Text = e.NewValue.ToString(CultureInfo.InvariantCulture); 228 } 229 } 230 } 231}