A game framework written with osu! in mind.
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.Collections.Generic;
5using System.Linq;
6using NUnit.Framework;
7using osu.Framework.Graphics;
8using osu.Framework.Graphics.Containers;
9using osu.Framework.Graphics.Sprites;
10using osu.Framework.Graphics.UserInterface;
11using osuTK;
12
13namespace osu.Framework.Tests.Visual.UserInterface
14{
15 public class TestSceneSearchContainer : FrameworkTestScene
16 {
17 private SearchContainer search;
18 private BasicTextBox textBox;
19
20 [SetUp]
21 public void SetUp() => Schedule(() =>
22 {
23 Children = new Drawable[]
24 {
25 textBox = new BasicTextBox
26 {
27 Size = new Vector2(300, 40),
28 },
29 search = new SearchContainer
30 {
31 AutoSizeAxes = Axes.Both,
32 Margin = new MarginPadding { Top = 40 },
33 Children = new[]
34 {
35 new HeaderContainer
36 {
37 AutoSizeAxes = Axes.Both,
38 Children = new[]
39 {
40 new HeaderContainer("Subsection 1")
41 {
42 AutoSizeAxes = Axes.Both,
43 Children = new Drawable[]
44 {
45 new SearchableText { Text = "test", },
46 new SearchableText { Text = "TEST", },
47 new SearchableText { Text = "123", },
48 new SearchableText { Text = "444", },
49 new FilterableFlowContainer
50 {
51 Direction = FillDirection.Horizontal,
52 AutoSizeAxes = Axes.Both,
53 Children = new[]
54 {
55 new SpriteText { Text = "multi", },
56 new SpriteText { Text = "piece", },
57 new SpriteText { Text = "container", },
58 }
59 },
60 new SearchableText { Text = "öüäéèêáàâ", }
61 }
62 },
63 new HeaderContainer("Subsection 2")
64 {
65 AutoSizeAxes = Axes.Both,
66 Children = new[]
67 {
68 new SearchableText { Text = "?!()[]{}" },
69 new SearchableText { Text = "@€$" },
70 },
71 },
72 },
73 }
74 }
75 }
76 };
77
78 textBox.Current.ValueChanged += e => search.SearchTerm = e.NewValue;
79 });
80
81 [TestCase("test", 2)]
82 [TestCase("sUbSeCtIoN 1", 6)]
83 [TestCase("€", 1)]
84 [TestCase("èê", 1)]
85 [TestCase("321", 0)]
86 [TestCase("mul pi", 1)]
87 [TestCase("header", 8)]
88 public void TestFiltering(string term, int count)
89 {
90 setTerm(term);
91 checkCount(count);
92 }
93
94 [TestCase]
95 public void TestRefilterAfterNewChild()
96 {
97 setTerm("multi");
98 checkCount(1);
99 AddStep("Add new filtered item", () => search.Add(new SearchableText { Text = "not visible" }));
100 checkCount(1);
101 AddStep("Add new unfiltered item", () => search.Add(new SearchableText { Text = "multi visible" }));
102 checkCount(2);
103 }
104
105 private void checkCount(int count)
106 {
107 AddAssert("Visible children: " + count, () => count == countSearchableText(search));
108 }
109
110 private int countSearchableText(CompositeDrawable container)
111 {
112 return container.InternalChildren.Where(t => t is SearchableText || t is FilterableFlowContainer).Count(c => c.IsPresent)
113 + container.InternalChildren.Where(c => c.IsPresent).OfType<CompositeDrawable>().Sum(countSearchableText);
114 }
115
116 private void setTerm(string term)
117 {
118 AddStep("Search term: " + term, () => textBox.Text = term);
119 }
120
121 private class HeaderContainer : Container, IHasFilterableChildren
122 {
123 public IEnumerable<string> FilterTerms => header.FilterTerms;
124
125 public bool MatchingFilter
126 {
127 set
128 {
129 if (value)
130 this.FadeIn();
131 else
132 this.FadeOut();
133 }
134 }
135
136 public bool FilteringActive
137 {
138 set { }
139 }
140
141 public IEnumerable<IFilterable> FilterableChildren => Children.OfType<IFilterable>();
142
143 protected override Container<Drawable> Content => flowContainer;
144
145 private readonly HeaderText header;
146 private readonly FillFlowContainer flowContainer;
147
148 public HeaderContainer(string headerText = "Header")
149 {
150 AddInternal(header = new HeaderText
151 {
152 Text = headerText,
153 });
154 AddInternal(flowContainer = new FillFlowContainer
155 {
156 Margin = new MarginPadding { Top = header.Font.Size, Left = 30 },
157 AutoSizeAxes = Axes.Both,
158 Direction = FillDirection.Vertical,
159 });
160 }
161 }
162
163 private class FilterableFlowContainer : FillFlowContainer, IFilterable
164 {
165 public IEnumerable<string> FilterTerms => Children.OfType<IHasFilterTerms>().SelectMany(d => d.FilterTerms);
166
167 public bool MatchingFilter
168 {
169 set
170 {
171 if (value)
172 Show();
173 else
174 Hide();
175 }
176 }
177
178 public bool FilteringActive
179 {
180 set { }
181 }
182 }
183
184 private class HeaderText : SpriteText, IFilterable
185 {
186 public bool MatchingFilter
187 {
188 set
189 {
190 if (value)
191 Show();
192 else
193 Hide();
194 }
195 }
196
197 public bool FilteringActive
198 {
199 set { }
200 }
201 }
202
203 private class SearchableText : SpriteText, IFilterable
204 {
205 public bool MatchingFilter
206 {
207 set
208 {
209 if (value)
210 Show();
211 else
212 Hide();
213 }
214 }
215
216 public bool FilteringActive
217 {
218 set { }
219 }
220 }
221 }
222}