Skip to content

Commit e0a11dd

Browse files
authored
feat: add mock UI for file syncing listing (#60)
1 parent 13ce6b9 commit e0a11dd

21 files changed

+1337
-74
lines changed

App/App.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.1" />
6666
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.1" />
6767
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.6.250108002" />
68+
<PackageReference Include="WinUIEx" Version="2.5.1" />
6869
</ItemGroup>
6970

7071
<ItemGroup>

App/App.xaml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,18 @@
33
<Application
44
x:Class="Coder.Desktop.App.App"
55
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
6-
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
6+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
7+
xmlns:converters="using:Coder.Desktop.App.Converters">
78
<Application.Resources>
89
<ResourceDictionary>
910
<ResourceDictionary.MergedDictionaries>
1011
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
1112
</ResourceDictionary.MergedDictionaries>
13+
14+
<converters:InverseBoolConverter x:Key="InverseBoolConverter" />
15+
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
16+
<converters:InverseBoolToVisibilityConverter x:Key="InverseBoolToVisibilityConverter" />
17+
<converters:FriendlyByteConverter x:Key="FriendlyByteConverter" />
1218
</ResourceDictionary>
1319
</Application.Resources>
1420
</Application>

App/App.xaml.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ public App()
4747
services.AddTransient<SignInViewModel>();
4848
services.AddTransient<SignInWindow>();
4949

50+
// FileSyncListWindow views and view models
51+
services.AddTransient<FileSyncListViewModel>();
52+
// FileSyncListMainPage is created by FileSyncListWindow.
53+
services.AddTransient<FileSyncListWindow>();
54+
5055
// TrayWindow views and view models
5156
services.AddTransient<TrayWindowLoadingPage>();
5257
services.AddTransient<TrayWindowDisconnectedViewModel>();

App/Controls/SizedFrame.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,8 @@ public class SizedFrameEventArgs : EventArgs
1212

1313
/// <summary>
1414
/// SizedFrame extends Frame by adding a SizeChanged event, which will be triggered when:
15-
/// - The contained Page's content's size changes
16-
/// - We switch to a different page.
17-
///
15+
/// - The contained Page's content's size changes
16+
/// - We switch to a different page.
1817
/// Sadly this is necessary because Window.Content.SizeChanged doesn't trigger when the Page's content changes.
1918
/// </summary>
2019
public class SizedFrame : Frame

App/Converters/AgentStatusToColorConverter.cs

Lines changed: 0 additions & 33 deletions
This file was deleted.
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
using System;
2+
using System.Linq;
3+
using Windows.Foundation.Collections;
4+
using Windows.UI.Xaml.Markup;
5+
using Microsoft.UI.Xaml;
6+
using Microsoft.UI.Xaml.Data;
7+
using Microsoft.UI.Xaml.Media;
8+
9+
namespace Coder.Desktop.App.Converters;
10+
11+
// This file uses manual DependencyProperty properties rather than
12+
// DependencyPropertyGenerator since it doesn't seem to work properly with
13+
// generics.
14+
15+
/// <summary>
16+
/// An item in a DependencyObjectSelector. Each item has a key and a value.
17+
/// The default item in a DependencyObjectSelector will be the only item
18+
/// with a null key.
19+
/// </summary>
20+
/// <typeparam name="TK">Key type</typeparam>
21+
/// <typeparam name="TV">Value type</typeparam>
22+
public class DependencyObjectSelectorItem<TK, TV> : DependencyObject
23+
where TK : IEquatable<TK>
24+
{
25+
public static readonly DependencyProperty KeyProperty =
26+
DependencyProperty.Register(nameof(Key),
27+
typeof(TK?),
28+
typeof(DependencyObjectSelectorItem<TK, TV>),
29+
new PropertyMetadata(null));
30+
31+
public static readonly DependencyProperty ValueProperty =
32+
DependencyProperty.Register(nameof(Value),
33+
typeof(TV?),
34+
typeof(DependencyObjectSelectorItem<TK, TV>),
35+
new PropertyMetadata(null));
36+
37+
public TK? Key
38+
{
39+
get => (TK?)GetValue(KeyProperty);
40+
set => SetValue(KeyProperty, value);
41+
}
42+
43+
public TV? Value
44+
{
45+
get => (TV?)GetValue(ValueProperty);
46+
set => SetValue(ValueProperty, value);
47+
}
48+
}
49+
50+
/// <summary>
51+
/// Allows selecting between multiple value references based on a selected
52+
/// key. This allows for dynamic mapping of model values to other objects.
53+
/// The main use case is for selecting between other bound values, which
54+
/// you cannot do with a simple ValueConverter.
55+
/// </summary>
56+
/// <typeparam name="TK">Key type</typeparam>
57+
/// <typeparam name="TV">Value type</typeparam>
58+
[ContentProperty(Name = nameof(References))]
59+
public class DependencyObjectSelector<TK, TV> : DependencyObject
60+
where TK : IEquatable<TK>
61+
{
62+
public static readonly DependencyProperty ReferencesProperty =
63+
DependencyProperty.Register(nameof(References),
64+
typeof(DependencyObjectCollection),
65+
typeof(DependencyObjectSelector<TK, TV>),
66+
new PropertyMetadata(null, ReferencesPropertyChanged));
67+
68+
public static readonly DependencyProperty SelectedKeyProperty =
69+
DependencyProperty.Register(nameof(SelectedKey),
70+
typeof(TK?),
71+
typeof(DependencyObjectSelector<TK, TV>),
72+
new PropertyMetadata(null, SelectedKeyPropertyChanged));
73+
74+
public static readonly DependencyProperty SelectedObjectProperty =
75+
DependencyProperty.Register(nameof(SelectedObject),
76+
typeof(TV?),
77+
typeof(DependencyObjectSelector<TK, TV>),
78+
new PropertyMetadata(null));
79+
80+
public DependencyObjectCollection? References
81+
{
82+
get => (DependencyObjectCollection?)GetValue(ReferencesProperty);
83+
set
84+
{
85+
// Ensure unique keys and that the values are DependencyObjectSelectorItem<K, V>.
86+
if (value != null)
87+
{
88+
var items = value.OfType<DependencyObjectSelectorItem<TK, TV>>().ToArray();
89+
var keys = items.Select(i => i.Key).Distinct().ToArray();
90+
if (keys.Length != value.Count)
91+
throw new ArgumentException("ObservableCollection Keys must be unique.");
92+
}
93+
94+
SetValue(ReferencesProperty, value);
95+
}
96+
}
97+
98+
/// <summary>
99+
/// The key of the selected item. This should be bound to a property on
100+
/// the model.
101+
/// </summary>
102+
public TK? SelectedKey
103+
{
104+
get => (TK?)GetValue(SelectedKeyProperty);
105+
set => SetValue(SelectedKeyProperty, value);
106+
}
107+
108+
/// <summary>
109+
/// The selected object. This can be read from to get the matching
110+
/// object for the selected key. If the selected key doesn't match any
111+
/// object, this will be the value of the null key. If there is no null
112+
/// key, this will be null.
113+
/// </summary>
114+
public TV? SelectedObject
115+
{
116+
get => (TV?)GetValue(SelectedObjectProperty);
117+
set => SetValue(SelectedObjectProperty, value);
118+
}
119+
120+
public DependencyObjectSelector()
121+
{
122+
References = [];
123+
}
124+
125+
private void UpdateSelectedObject()
126+
{
127+
if (References != null)
128+
{
129+
// Look for a matching item a matching key, or fallback to the null
130+
// key.
131+
var references = References.OfType<DependencyObjectSelectorItem<TK, TV>>().ToArray();
132+
var item = references
133+
.FirstOrDefault(i =>
134+
(i.Key == null && SelectedKey == null) ||
135+
(i.Key != null && SelectedKey != null && i.Key!.Equals(SelectedKey!)))
136+
?? references.FirstOrDefault(i => i.Key == null);
137+
if (item is not null)
138+
{
139+
// Bind the SelectedObject property to the reference's Value.
140+
// If the underlying Value changes, it will propagate to the
141+
// SelectedObject.
142+
BindingOperations.SetBinding
143+
(
144+
this,
145+
SelectedObjectProperty,
146+
new Binding
147+
{
148+
Source = item,
149+
Path = new PropertyPath(nameof(DependencyObjectSelectorItem<TK, TV>.Value)),
150+
}
151+
);
152+
return;
153+
}
154+
}
155+
156+
ClearValue(SelectedObjectProperty);
157+
}
158+
159+
// Called when the References property is replaced.
160+
private static void ReferencesPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
161+
{
162+
var self = obj as DependencyObjectSelector<TK, TV>;
163+
if (self == null) return;
164+
var oldValue = args.OldValue as DependencyObjectCollection;
165+
if (oldValue != null)
166+
oldValue.VectorChanged -= self.OnVectorChangedReferences;
167+
var newValue = args.NewValue as DependencyObjectCollection;
168+
if (newValue != null)
169+
newValue.VectorChanged += self.OnVectorChangedReferences;
170+
}
171+
172+
// Called when the References collection changes without being replaced.
173+
private void OnVectorChangedReferences(IObservableVector<DependencyObject> sender, IVectorChangedEventArgs args)
174+
{
175+
UpdateSelectedObject();
176+
}
177+
178+
// Called when SelectedKey changes.
179+
private static void SelectedKeyPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
180+
{
181+
var self = obj as DependencyObjectSelector<TK, TV>;
182+
self?.UpdateSelectedObject();
183+
}
184+
}
185+
186+
public sealed class StringToBrushSelectorItem : DependencyObjectSelectorItem<string, Brush>;
187+
188+
public sealed class StringToBrushSelector : DependencyObjectSelector<string, Brush>;
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using System;
2+
using Microsoft.UI.Xaml.Data;
3+
4+
namespace Coder.Desktop.App.Converters;
5+
6+
public class FriendlyByteConverter : IValueConverter
7+
{
8+
private static readonly string[] Suffixes = ["B", "KB", "MB", "GB", "TB", "PB", "EB"];
9+
10+
public object Convert(object value, Type targetType, object parameter, string language)
11+
{
12+
switch (value)
13+
{
14+
case int i:
15+
if (i < 0) i = 0;
16+
return FriendlyBytes((ulong)i);
17+
case uint ui:
18+
return FriendlyBytes(ui);
19+
case long l:
20+
if (l < 0) l = 0;
21+
return FriendlyBytes((ulong)l);
22+
case ulong ul:
23+
return FriendlyBytes(ul);
24+
default:
25+
return FriendlyBytes(0);
26+
}
27+
}
28+
29+
public object ConvertBack(object value, Type targetType, object parameter, string language)
30+
{
31+
throw new NotImplementedException();
32+
}
33+
34+
public static string FriendlyBytes(ulong bytes)
35+
{
36+
if (bytes == 0)
37+
return $"0 {Suffixes[0]}";
38+
39+
var place = System.Convert.ToInt32(Math.Floor(Math.Log(bytes, 1024)));
40+
var num = Math.Round(bytes / Math.Pow(1024, place), 1);
41+
return $"{num} {Suffixes[place]}";
42+
}
43+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System;
2+
using Microsoft.UI.Xaml.Data;
3+
4+
namespace Coder.Desktop.App.Converters;
5+
6+
public class InverseBoolConverter : IValueConverter
7+
{
8+
public object Convert(object value, Type targetType, object parameter, string language)
9+
{
10+
return value is false;
11+
}
12+
13+
public object ConvertBack(object value, Type targetType, object parameter, string language)
14+
{
15+
throw new NotImplementedException();
16+
}
17+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using Microsoft.UI.Xaml;
2+
3+
namespace Coder.Desktop.App.Converters;
4+
5+
public partial class InverseBoolToVisibilityConverter : BoolToObjectConverter
6+
{
7+
public InverseBoolToVisibilityConverter()
8+
{
9+
TrueValue = Visibility.Collapsed;
10+
FalseValue = Visibility.Visible;
11+
}
12+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy