Skip to content

Commit 7d23b41

Browse files
Merge pull request #13 from atomsk-0/controllerSupport
Added basic controller navigation support
2 parents 7b40d2a + 4de454c commit 7d23b41

5 files changed

Lines changed: 191 additions & 32 deletions

File tree

Controls/AppTile.xaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
mc:Ignorable="d"
99
Margin="10, 10, 10, 10"
1010
>
11-
<Button Width="320" Height="180" Padding="0" Name="startButton" CornerRadius="5, 5, 0, 0" BorderThickness="0" Background="{ThemeResource SystemControlAltLowAcrylicElementBrush}" >
11+
<Button Width="320" Height="180" Padding="0" x:Name="startButton" CornerRadius="5, 5, 0, 0" BorderThickness="0" Background="{ThemeResource SystemControlAltLowAcrylicElementBrush}" >
1212
<Image x:Name="appLogo"/>
1313
</Button>
1414
<Expander CornerRadius="0, 0, 5, 5" Width="320" Name="infoExpander" Background="{ThemeResource DesktopAcrylicTransparentBrush}">

Controls/AppTile.xaml.cs

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ public sealed partial class AppTile
2626
private string _Publisher;
2727
private string _Version;
2828
private Uri _Logo;
29+
30+
private AppListEntry appListEntry;
2931

3032
private async void HandleUnregister(object sender, SplitButtonClickEventArgs e)
3133
{
@@ -200,19 +202,19 @@ public AppTile(string familyName)
200202
catch
201203
{
202204
Logger.WriteWarning($"Could not get the applist entries of \"{_Name}\"");
203-
}
204-
AppListEntry firstAppListEntry = appListEntries?.FirstOrDefault() ?? null;
205+
}
206+
appListEntry = appListEntries?.FirstOrDefault() ?? null;
205207

206-
if (firstAppListEntry == null)
208+
if (appListEntry == null)
207209
Logger.WriteWarning($"Could not get the applist entry of \"{_Name}\"");
208210

209211
if (String.IsNullOrEmpty(ss) || !File.Exists(ss))
210212
{
211213
try
212214
{
213-
if (firstAppListEntry != null)
215+
if (appListEntry != null)
214216
{
215-
RandomAccessStreamReference logoStream = firstAppListEntry.DisplayInfo.GetLogo(new Size(320, 180));
217+
RandomAccessStreamReference logoStream = appListEntry.DisplayInfo.GetLogo(new Size(320, 180));
216218
BitmapImage logoImage = new();
217219
using IRandomAccessStream stream = logoStream.OpenReadAsync().GetAwaiter().GetResult();
218220
logoImage.SetSource(stream);
@@ -275,24 +277,28 @@ public AppTile(string familyName)
275277
rcFlyout.ShowAt(sender as FrameworkElement, e.GetPosition(sender as UIElement));
276278
};
277279

278-
startButton.Tapped += async (s, e) =>
280+
startButton.Tapped += (_, _) => StartApp();
281+
}
282+
283+
284+
public async void StartApp()
285+
{
286+
if (_package.Status.LicenseIssue)
279287
{
280-
if (_package.Status.LicenseIssue)
281-
{
282-
Logger.WriteError($"Could not launch {_Name} due to licensing issue.");
283-
_ = new NoticeDialog($"There is a licensing issue... Do you own this package?", $"Could not launch {_Name}").ShowAsync();
284-
return;
285-
}
288+
Logger.WriteError($"Could not launch {_Name} due to licensing issue.");
289+
_ = new NoticeDialog($"There is a licensing issue... Do you own this package?", $"Could not launch {_Name}").ShowAsync();
290+
return;
291+
}
286292

287-
if (firstAppListEntry == null)
288-
{
289-
_ = new NoticeDialog($"Could not get the applist entry of \"{_Name}\"", $"Could not launch {_Name}").ShowAsync();
290-
return;
291-
}
292-
Logger.WriteInformation($"Launching {_Name}");
293-
if (await firstAppListEntry.LaunchAsync() == false)
294-
_ = new NoticeDialog($"Failed to launch \"{_Name}\"!", $"Could not launch {_Name}").ShowAsync();
295-
};
293+
if (appListEntry == null)
294+
{
295+
_ = new NoticeDialog($"Could not get the applist entry of \"{_Name}\"", $"Could not launch {_Name}").ShowAsync();
296+
return;
297+
}
298+
Logger.WriteInformation($"Launching {_Name}");
299+
if (await appListEntry.LaunchAsync() == false)
300+
_ = new NoticeDialog($"Failed to launch \"{_Name}\"!", $"Could not launch {_Name}").ShowAsync();
296301
}
297302
}
303+
298304
}

MainWindow.xaml.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,18 @@ private async void appTitleBar_Loaded(object sender, RoutedEventArgs e)
102102
if (String.IsNullOrEmpty(FSHelper.FindFileOnPath("vcruntime140d.dll")))
103103
missing.Add("Microsoft Visual C++ Redistributable", null);
104104

105-
var devNotice = new NoticeDialog($"This UI is very early in development, and mainly developed by a C# learner... There WILL be bugs, and some things will NOT work...\n\nDevelopers, check Readme.md in the repo for the todolist.", "Important");
106-
await devNotice.ShowAsync();
107105

106+
if (App.Settings.Settings.ShowDevNotice)
107+
{
108+
var devNotice = new NoticeDialog("This UI is very early in development, and mainly developed by a C# learner... There WILL be bugs, and some things will NOT work...\n\nDevelopers, check Readme.md in the repo for the todolist.", "Important");
109+
await devNotice.ShowAsync();
110+
111+
// We only show this notification once from now on
112+
Settings.Set("ShowDevNotice", false);
113+
Settings.Save();
114+
}
115+
116+
108117
if (missing.Count != 0)
109118
{
110119
// todo: properly provide download link

Pages/AppsListPage.xaml.cs

Lines changed: 150 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
using System.Security.Principal;
99
using System.Threading.Tasks;
1010
using Windows.ApplicationModel;
11+
using Windows.Foundation;
12+
using Windows.Gaming.Input;
1113
using Windows.Management.Deployment;
1214
using Windows.Storage;
1315
using 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)

Settings/UiConfig.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ public enum PatchSource
3232

3333
public string DownloadedWdVer { get; set; } = String.Empty;
3434
public PatchSource DownloadSource { get; set; } = PatchSource.Release;
35+
36+
public bool ShowDevNotice { get; set; } = true;
3537
}
3638

3739
// TODO: fix type init exception

0 commit comments

Comments
 (0)