88using System . Security . Principal ;
99using System . Threading . Tasks ;
1010using Windows . ApplicationModel ;
11+ using Windows . Foundation ;
12+ using Windows . Gaming . Input ;
1113using Windows . Management . Deployment ;
1214using Windows . Storage ;
1315using Windows . Storage . Pickers ;
@@ -21,6 +23,12 @@ namespace WinDurango.UI.Pages
2123{
2224 public sealed partial class AppsListPage : Page
2325 {
26+ private Gamepad gamepad ;
27+ private int currentIndex ;
28+ private Point currentPoint = new ( 0 , 0 ) ;
29+ private bool inputProcessed = true ;
30+ private long lastInput ;
31+
2432 public async Task InitAppListAsync ( )
2533 {
2634 appList . Children . Clear ( ) ;
@@ -138,14 +146,148 @@ public AppsListPage()
138146 InitializeComponent ( ) ;
139147 _ = InitAppListAsync ( ) ;
140148
141- // All this is useless now basically... because InitAppListAsync now runs asynchronously (not blocking the ui thread)
142- /*
143- Stopwatch PlatinumWatch = new Stopwatch();
144- Logger.WriteDebug("Initializing AppsListPage...");
145- PlatinumWatch.Start();
146- PlatinumWatch.Stop();
147- Logger.WriteDebug("Initialized AppsListPage in {0:D2}:{1:D2}:{2:D2}.{3:D3}", (int)PlatinumWatch.Elapsed.TotalHours, (int)PlatinumWatch.Elapsed.TotalMinutes, (int)PlatinumWatch.Elapsed.TotalSeconds, (int)PlatinumWatch.Elapsed.TotalMilliseconds);
148- */
149+ Loaded += OnAppListPage_Loaded ;
150+ }
151+
152+ private void OnAppListPage_Loaded ( object sender , RoutedEventArgs e )
153+ {
154+ Gamepad . GamepadAdded += onGamepadAdded ;
155+ Gamepad . GamepadRemoved += OnGamepadRemoved ;
156+ }
157+
158+ private void OnGamepadRemoved ( object sender , Gamepad e )
159+ {
160+ gamepad = null ;
161+ }
162+
163+ private void onGamepadAdded ( object sender , Gamepad e )
164+ {
165+ gamepad = e ;
166+ ListenGamepadInput ( ) ;
167+ this . DispatcherQueue . TryEnqueue ( ( ) =>
168+ {
169+ if ( appList . Children . Count > 0 )
170+ {
171+ appList . Children [ currentIndex ] . Focus ( FocusState . Keyboard ) ;
172+ }
173+ } ) ;
174+ }
175+
176+
177+ private async void ListenGamepadInput ( )
178+ {
179+ while ( gamepad != null )
180+ {
181+ GamepadReading gamepadInput = gamepad . GetCurrentReading ( ) ;
182+ bool moveRight = gamepadInput . LeftThumbstickX > 0.5 || ( gamepadInput . Buttons & GamepadButtons . DPadRight ) != 0 ;
183+ bool moveLeft = gamepadInput . LeftThumbstickX < - 0.5 || ( gamepadInput . Buttons & GamepadButtons . DPadLeft ) != 0 ;
184+ bool moveUp = gamepadInput . LeftThumbstickY > 0.5 || ( gamepadInput . Buttons & GamepadButtons . DPadUp ) != 0 ;
185+ bool moveDown = gamepadInput . LeftThumbstickY < - 0.5 || ( gamepadInput . Buttons & GamepadButtons . DPadDown ) != 0 ;
186+ bool actionClicked = ( gamepadInput . Buttons & GamepadButtons . A ) != 0 ;
187+
188+
189+ if ( actionClicked && inputProcessed )
190+ {
191+ inputProcessed = false ;
192+ this . DispatcherQueue . TryEnqueue ( ( ) =>
193+ {
194+ var appTile = appList . Children [ currentIndex ] as AppTile ;
195+ appTile . StartApp ( ) ;
196+ inputProcessed = true ;
197+ } ) ;
198+ }
199+
200+ if ( ( moveRight || moveLeft || moveUp || moveDown ) && inputProcessed )
201+ {
202+ inputProcessed = false ;
203+ if ( moveRight ) this . DispatcherQueue . TryEnqueue ( ( ) => MoveFocus ( 1 , 0 ) ) ;
204+ else if ( moveLeft ) this . DispatcherQueue . TryEnqueue ( ( ) => MoveFocus ( - 1 , 0 ) ) ;
205+ else if ( moveUp ) this . DispatcherQueue . TryEnqueue ( ( ) => MoveFocus ( 0 , - 1 ) ) ;
206+ else if ( moveDown ) this . DispatcherQueue . TryEnqueue ( ( ) => MoveFocus ( 0 , 1 ) ) ;
207+ }
208+
209+ await Task . Delay ( 100 ) ;
210+ }
211+ }
212+
213+ private void MoveFocus ( int xOffset , int yOffset )
214+ {
215+ bool firstInput = lastInput == 0 ;
216+ if ( lastInput > DateTimeOffset . UtcNow . ToUnixTimeMilliseconds ( ) - 200 )
217+ {
218+ inputProcessed = true ;
219+ return ;
220+ }
221+ lastInput = DateTimeOffset . UtcNow . ToUnixTimeMilliseconds ( ) ;
222+ if ( appList . Children . Count == 0 )
223+ {
224+ inputProcessed = true ;
225+ return ;
226+ }
227+
228+ // We only set focus first to the index 0 if ShowDevNotice was shown before as it cancels the initial focus done in Load event
229+ if ( firstInput && App . Settings . Settings . ShowDevNotice )
230+ {
231+ appList . Children [ 0 ] . Focus ( FocusState . Keyboard ) ;
232+ inputProcessed = true ;
233+ return ;
234+ }
235+
236+ int columns = GetColumnCount ( ) ;
237+ int rows = appList . Children . Count / columns ;
238+
239+ int newX = ( int ) ( currentPoint . X + xOffset ) ;
240+ int newY = ( int ) ( currentPoint . Y + yOffset ) ;
241+
242+ newX = Math . Clamp ( newX , 0 , columns - 1 ) ;
243+ newY = Math . Clamp ( newY , 0 , rows - 1 ) ;
244+
245+ if ( newX != currentPoint . X || newY != currentPoint . Y )
246+ {
247+ currentPoint = new Point ( newX , newY ) ;
248+ currentIndex = newY * columns + newX ;
249+
250+ if ( currentIndex < appList . Children . Count )
251+ {
252+ appList . Children [ currentIndex ] . Focus ( FocusState . Keyboard ) ;
253+ }
254+ }
255+
256+ inputProcessed = true ;
257+ }
258+
259+ // We need do this our self as WrapPanel doesn't have internal field or function to get current column amount
260+ private int GetColumnCount ( )
261+ {
262+ if ( appList . Children . Count == 0 ) return 1 ;
263+
264+
265+ FrameworkElement firstItem = appList . Children [ 0 ] as FrameworkElement ;
266+ if ( firstItem == null ) return 1 ;
267+
268+
269+ double firstItemTop = firstItem . TransformToVisual ( appList ) . TransformPoint ( new Point ( 0 , 0 ) ) . Y ;
270+ int columnCount = 1 ;
271+
272+ for ( int i = 1 ; i < appList . Children . Count ; i ++ )
273+ {
274+ var item = appList . Children [ i ] as FrameworkElement ;
275+ if ( item == null )
276+ continue ;
277+
278+ double itemTop = item . TransformToVisual ( appList ) . TransformPoint ( new Point ( 0 , 0 ) ) . Y ;
279+
280+ if ( Math . Abs ( itemTop - firstItemTop ) < 1 )
281+ {
282+ columnCount ++ ;
283+ }
284+ else
285+ {
286+ break ;
287+ }
288+ }
289+
290+ return columnCount ;
149291 }
150292
151293 private void SearchBox_TextChanged ( AutoSuggestBox sender , AutoSuggestBoxTextChangedEventArgs args )
0 commit comments