1
+ using System ;
2
+ using System . Collections . Generic ;
3
+ using RSG ;
4
+ using Unity . UIWidgets . animation ;
5
+ using Unity . UIWidgets . foundation ;
6
+ using Unity . UIWidgets . service ;
7
+ using Unity . UIWidgets . widgets ;
8
+ using UnityEngine ;
9
+ using Color = Unity . UIWidgets . ui . Color ;
10
+
11
+ namespace Unity . UIWidgets . material {
12
+ public class SearchUtils {
13
+ public static IPromise < object > showSearch (
14
+ BuildContext context ,
15
+ SearchDelegate del ,
16
+ string query = ""
17
+ ) {
18
+ D . assert ( del != null ) ;
19
+ D . assert ( context != null ) ;
20
+
21
+ del . query = query ?? del . query ;
22
+ del . _currentBody = _SearchBody . suggestions ;
23
+ return Navigator . of ( context ) . push ( new _SearchPageRoute (
24
+ del : del
25
+ ) ) ;
26
+ }
27
+ }
28
+
29
+ public abstract class SearchDelegate {
30
+ public abstract Widget buildSuggestions ( BuildContext context ) ;
31
+ public abstract Widget buildResults ( BuildContext context ) ;
32
+ public abstract Widget buildLeading ( BuildContext context ) ;
33
+ public abstract List < Widget > buildActions ( BuildContext context ) ;
34
+
35
+ public virtual ThemeData appBarTheme ( BuildContext context ) {
36
+ D . assert ( context != null ) ;
37
+ ThemeData theme = Theme . of ( context ) ;
38
+ D . assert ( theme != null ) ;
39
+ return theme . copyWith (
40
+ primaryColor : Colors . white ,
41
+ primaryIconTheme : theme . primaryIconTheme . copyWith ( color : Colors . grey ) ,
42
+ primaryColorBrightness : Brightness . light ,
43
+ primaryTextTheme : theme . textTheme
44
+ ) ;
45
+ }
46
+
47
+ public virtual string query {
48
+ get { return this . _queryTextController . text ; }
49
+ set {
50
+ D . assert ( this . query != null ) ;
51
+ this . _queryTextController . text = value ;
52
+ }
53
+ }
54
+
55
+ public virtual void showResults ( BuildContext context ) {
56
+ this . _focusNode . unfocus ( ) ;
57
+ this . _currentBody = _SearchBody . results ;
58
+ }
59
+
60
+ public virtual void showSuggestions ( BuildContext context ) {
61
+ FocusScope . of ( context ) . requestFocus ( this . _focusNode ) ;
62
+ this . _currentBody = _SearchBody . suggestions ;
63
+ }
64
+
65
+ public virtual void close ( BuildContext context , object result ) {
66
+ this . _currentBody = null ;
67
+ this . _focusNode . unfocus ( ) ;
68
+ var state = Navigator . of ( context ) ;
69
+ state . popUntil ( ( Route route ) => route == this . _route ) ;
70
+ state . pop ( result ) ;
71
+ }
72
+
73
+
74
+ public virtual Animation < float > transitionAnimation {
75
+ get { return this . _proxyAnimation ; }
76
+ }
77
+
78
+ readonly internal FocusNode _focusNode = new FocusNode ( ) ;
79
+
80
+ readonly internal TextEditingController _queryTextController = new TextEditingController ( ) ;
81
+
82
+ readonly internal ProxyAnimation _proxyAnimation = new ProxyAnimation ( Animations . kAlwaysDismissedAnimation ) ;
83
+
84
+ readonly internal ValueNotifier < _SearchBody ? > _currentBodyNotifier = new ValueNotifier < _SearchBody ? > ( null ) ;
85
+
86
+ internal _SearchBody ? _currentBody {
87
+ get { return this . _currentBodyNotifier . value ; }
88
+ set { this . _currentBodyNotifier . value = value ; }
89
+ }
90
+
91
+ internal _SearchPageRoute _route ;
92
+ }
93
+
94
+ enum _SearchBody {
95
+ suggestions ,
96
+ results
97
+ }
98
+
99
+ class _SearchPageRoute : PageRoute {
100
+ public _SearchPageRoute ( SearchDelegate del ) {
101
+ D . assert ( del != null ) ;
102
+ D . assert ( del . _route == null ,
103
+ ( ) => $ "The { this . del . GetType ( ) } instance is currently used by another active " +
104
+ "search. Please close that search by calling close() on the SearchDelegate " +
105
+ "before openening another search with the same delegate instance."
106
+ ) ;
107
+ this . del = del ;
108
+ this . del . _route = this ;
109
+ }
110
+
111
+ public readonly SearchDelegate del ;
112
+
113
+ public override Color barrierColor {
114
+ get { return null ; }
115
+ }
116
+
117
+ public override TimeSpan transitionDuration {
118
+ get { return new TimeSpan ( 0 , 0 , 0 , 0 , 300 ) ; }
119
+ }
120
+
121
+ public override bool maintainState {
122
+ get { return false ; }
123
+ }
124
+
125
+ public override Widget buildTransitions (
126
+ BuildContext context ,
127
+ Animation < float > animation ,
128
+ Animation < float > secondaryAnimation ,
129
+ Widget child
130
+ ) {
131
+ return new FadeTransition (
132
+ opacity : animation ,
133
+ child : child
134
+ ) ;
135
+ }
136
+
137
+ public override Animation < float > createAnimation ( ) {
138
+ Animation < float > animation = base . createAnimation ( ) ;
139
+ this . del . _proxyAnimation . parent = animation ;
140
+ return animation ;
141
+ }
142
+
143
+ public override Widget buildPage (
144
+ BuildContext context ,
145
+ Animation < float > animation ,
146
+ Animation < float > secondaryAnimation
147
+ ) {
148
+ return new _SearchPage (
149
+ del : this . del ,
150
+ animation : animation
151
+ ) ;
152
+ }
153
+
154
+ protected internal override void didComplete ( object result ) {
155
+ base . didComplete ( result ) ;
156
+ D . assert ( this . del . _route == this ) ;
157
+ this . del . _route = null ;
158
+ this . del . _currentBody = null ;
159
+ }
160
+ }
161
+
162
+ class _SearchPage : StatefulWidget {
163
+ public _SearchPage (
164
+ SearchDelegate del ,
165
+ Animation < float > animation
166
+ ) {
167
+ this . del = del ;
168
+ this . animation = animation ;
169
+ }
170
+
171
+ public readonly SearchDelegate del ;
172
+
173
+ public readonly Animation < float > animation ;
174
+
175
+ public override State createState ( ) {
176
+ return new _SearchPageState ( ) ;
177
+ }
178
+ }
179
+
180
+ class _SearchPageState : State < _SearchPage > {
181
+ public override void initState ( ) {
182
+ base . initState ( ) ;
183
+ this . queryTextController . addListener ( this . _onQueryChanged ) ;
184
+ this . widget . animation . addStatusListener ( this . _onAnimationStatusChanged ) ;
185
+ this . widget . del . _currentBodyNotifier . addListener ( this . _onSearchBodyChanged ) ;
186
+ this . widget . del . _focusNode . addListener ( this . _onFocusChanged ) ;
187
+ }
188
+
189
+ public override void dispose ( ) {
190
+ base . dispose ( ) ;
191
+ this . queryTextController . removeListener ( this . _onQueryChanged ) ;
192
+ this . widget . animation . removeStatusListener ( this . _onAnimationStatusChanged ) ;
193
+ this . widget . del . _currentBodyNotifier . removeListener ( this . _onSearchBodyChanged ) ;
194
+ this . widget . del . _focusNode . removeListener ( this . _onFocusChanged ) ;
195
+ }
196
+
197
+ void _onAnimationStatusChanged ( AnimationStatus status ) {
198
+ if ( status != AnimationStatus . completed ) {
199
+ return ;
200
+ }
201
+
202
+ this . widget . animation . removeStatusListener ( this . _onAnimationStatusChanged ) ;
203
+ if ( this . widget . del . _currentBody == _SearchBody . suggestions ) {
204
+ FocusScope . of ( this . context ) . requestFocus ( this . widget . del . _focusNode ) ;
205
+ }
206
+ }
207
+
208
+ void _onFocusChanged ( ) {
209
+ if ( this . widget . del . _focusNode . hasFocus && this . widget . del . _currentBody != _SearchBody . suggestions ) {
210
+ this . widget . del . showSuggestions ( this . context ) ;
211
+ }
212
+ }
213
+
214
+ void _onQueryChanged ( ) {
215
+ this . setState ( ( ) => { } ) ;
216
+ }
217
+
218
+ void _onSearchBodyChanged ( ) {
219
+ this . setState ( ( ) => { } ) ;
220
+ }
221
+
222
+ public override Widget build ( BuildContext context ) {
223
+ MaterialD . debugCheckHasMaterialLocalizations ( context ) ;
224
+
225
+ ThemeData theme = this . widget . del . appBarTheme ( context ) ;
226
+ string searchFieldLabel = MaterialLocalizations . of ( context ) . searchFieldLabel ;
227
+ Widget body = null ;
228
+ switch ( this . widget . del . _currentBody ) {
229
+ case _SearchBody . suggestions :
230
+ body = new KeyedSubtree (
231
+ key : new ValueKey < _SearchBody > ( _SearchBody . suggestions ) ,
232
+ child : this . widget . del . buildSuggestions ( context )
233
+ ) ;
234
+ break ;
235
+ case _SearchBody . results :
236
+ body = new KeyedSubtree (
237
+ key : new ValueKey < _SearchBody > ( _SearchBody . results ) ,
238
+ child : this . widget . del . buildResults ( context )
239
+ ) ;
240
+ break ;
241
+ }
242
+
243
+ string routeName ;
244
+ switch ( Theme . of ( this . context ) . platform ) {
245
+ case RuntimePlatform . IPhonePlayer :
246
+ routeName = "" ;
247
+ break ;
248
+ case RuntimePlatform . Android :
249
+ routeName = searchFieldLabel ;
250
+ break ;
251
+ }
252
+
253
+ return new Scaffold (
254
+ appBar : new AppBar (
255
+ backgroundColor : theme . primaryColor ,
256
+ iconTheme : theme . primaryIconTheme ,
257
+ textTheme : theme . primaryTextTheme ,
258
+ brightness : theme . primaryColorBrightness ,
259
+ leading : this . widget . del . buildLeading ( context ) ,
260
+ title : new TextField (
261
+ controller : this . queryTextController ,
262
+ focusNode : this . widget . del . _focusNode ,
263
+ style : theme . textTheme . title ,
264
+ textInputAction : TextInputAction . search ,
265
+ onSubmitted : ( string _ ) => { this . widget . del . showResults ( context ) ; } ,
266
+ decoration : new InputDecoration (
267
+ border : InputBorder . none ,
268
+ hintText : searchFieldLabel
269
+ )
270
+ ) ,
271
+ actions : this . widget . del . buildActions ( context )
272
+ ) ,
273
+ body : new AnimatedSwitcher (
274
+ duration : new TimeSpan ( 0 , 0 , 0 , 0 , 300 ) ,
275
+ child : body
276
+ )
277
+ ) ;
278
+ }
279
+
280
+ TextEditingController queryTextController {
281
+ get { return this . widget . del . _queryTextController ; }
282
+ }
283
+ }
284
+ }
0 commit comments