Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions App/App.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.1" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.6.250108002" />
<PackageReference Include="WinUIEx" Version="2.5.1" />
</ItemGroup>

<ItemGroup>
Expand Down
8 changes: 7 additions & 1 deletion App/App.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,18 @@
<Application
x:Class="Coder.Desktop.App.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="using:Coder.Desktop.App.Converters">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
</ResourceDictionary.MergedDictionaries>

<converters:InverseBoolConverter x:Key="InverseBoolConverter" />
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
<converters:InverseBoolToVisibilityConverter x:Key="InverseBoolToVisibilityConverter" />
<converters:FriendlyByteConverter x:Key="FriendlyByteConverter" />
</ResourceDictionary>
</Application.Resources>
</Application>
5 changes: 2 additions & 3 deletions App/Controls/SizedFrame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ public class SizedFrameEventArgs : EventArgs

/// <summary>
/// SizedFrame extends Frame by adding a SizeChanged event, which will be triggered when:
/// - The contained Page's content's size changes
/// - We switch to a different page.
///
/// - The contained Page's content's size changes
/// - We switch to a different page.
/// Sadly this is necessary because Window.Content.SizeChanged doesn't trigger when the Page's content changes.
/// </summary>
public class SizedFrame : Frame
Expand Down
33 changes: 0 additions & 33 deletions App/Converters/AgentStatusToColorConverter.cs

This file was deleted.

155 changes: 155 additions & 0 deletions App/Converters/DependencyObjectSelector.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
using System;
using System.Linq;
using Windows.Foundation.Collections;
using Windows.UI.Xaml.Markup;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Media;

namespace Coder.Desktop.App.Converters;

// This file uses manual DependencyProperty properties rather than
// DependencyPropertyGenerator since it doesn't seem to work properly with
// generics.

public class DependencyObjectSelectorItem<TK, TV> : DependencyObject
where TK : IEquatable<TK>
{
public static readonly DependencyProperty KeyProperty =
DependencyProperty.Register(nameof(Key),
typeof(TK?),
typeof(DependencyObjectSelectorItem<TK, TV>),
new PropertyMetadata(null));

public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register(nameof(Value),
typeof(TV?),
typeof(DependencyObjectSelectorItem<TK, TV>),
new PropertyMetadata(null));

public TK? Key
{
get => (TK?)GetValue(KeyProperty);
set => SetValue(KeyProperty, value);
}

public TV? Value
{
get => (TV?)GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
}
}

[ContentProperty(Name = nameof(References))]
public class DependencyObjectSelector<TK, TV> : DependencyObject
where TK : IEquatable<TK>
{
public static readonly DependencyProperty ReferencesProperty =
DependencyProperty.Register(nameof(References),
typeof(DependencyObjectCollection),
typeof(DependencyObjectSelector<TK, TV>),
new PropertyMetadata(null, ReferencesPropertyChanged));

public static readonly DependencyProperty SelectedKeyProperty =
DependencyProperty.Register(nameof(SelectedKey),
typeof(TK?),
typeof(DependencyObjectSelector<TK, TV>),
new PropertyMetadata(null, SelectedPropertyChanged));

public static readonly DependencyProperty SelectedObjectProperty =
DependencyProperty.Register(nameof(SelectedObject),
typeof(TV?),
typeof(DependencyObjectSelector<TK, TV>),
new PropertyMetadata(null));

public DependencyObjectCollection? References
{
get => (DependencyObjectCollection?)GetValue(ReferencesProperty);
set
{
// Ensure unique keys and that the values are DependencyObjectSelectorItem<K, V>.
if (value != null)
{
var items = value.OfType<DependencyObjectSelectorItem<TK, TV>>().ToArray();
var keys = items.Select(i => i.Key).Distinct().ToArray();
if (keys.Length != value.Count)
throw new ArgumentException("ObservableCollection Keys must be unique.");
}

SetValue(ReferencesProperty, value);
}
}

public TK? SelectedKey
{
get => (TK?)GetValue(SelectedKeyProperty);
set => SetValue(SelectedKeyProperty, value);
}

public TV? SelectedObject
{
get => (TV?)GetValue(SelectedObjectProperty);
set => SetValue(SelectedObjectProperty, value);
}

public DependencyObjectSelector()
{
References = [];
}

private void OnVectorChangedReferences(IObservableVector<DependencyObject> sender, IVectorChangedEventArgs args)
{
UpdateSelectedObject();
}

private void UpdateSelectedObject()
{
if (References != null)
{
var references = References.OfType<DependencyObjectSelectorItem<TK, TV>>().ToArray();
var item = references
.FirstOrDefault(i =>
(i.Key == null && SelectedKey == null) ||
(i.Key != null && SelectedKey != null && i.Key!.Equals(SelectedKey!)))
?? references.FirstOrDefault(i => i.Key == null);
if (item is not null)
{
BindingOperations.SetBinding
(
this,
SelectedObjectProperty,
new Binding
{
Source = item,
Path = new PropertyPath(nameof(DependencyObjectSelectorItem<TK, TV>.Value)),
}
);
return;
}
}

ClearValue(SelectedObjectProperty);
}

private static void ReferencesPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
var self = obj as DependencyObjectSelector<TK, TV>;
if (self == null) return;
var oldValue = args.OldValue as DependencyObjectCollection;
if (oldValue != null)
oldValue.VectorChanged -= self.OnVectorChangedReferences;
var newValue = args.NewValue as DependencyObjectCollection;
if (newValue != null)
newValue.VectorChanged += self.OnVectorChangedReferences;
}

private static void SelectedPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
var self = obj as DependencyObjectSelector<TK, TV>;
self?.UpdateSelectedObject();
}
}

public sealed class StringToBrushSelectorItem : DependencyObjectSelectorItem<string, Brush>;

public sealed class StringToBrushSelector : DependencyObjectSelector<string, Brush>;
43 changes: 43 additions & 0 deletions App/Converters/FriendlyByteConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System;
using Microsoft.UI.Xaml.Data;

namespace Coder.Desktop.App.Converters;

public class FriendlyByteConverter : IValueConverter
{
private static readonly string[] Suffixes = ["B", "KB", "MB", "GB", "TB", "PB", "EB"];

public object Convert(object value, Type targetType, object parameter, string language)
{
switch (value)
{
case int i:
if (i < 0) i = 0;
return FriendlyBytes((ulong)i);
case uint ui:
return FriendlyBytes(ui);
case long l:
if (l < 0) l = 0;
return FriendlyBytes((ulong)l);
case ulong ul:
return FriendlyBytes(ul);
default:
return FriendlyBytes(0);
}
}

public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}

public static string FriendlyBytes(ulong bytes)
{
if (bytes == 0)
return $"0 {Suffixes[0]}";

var place = System.Convert.ToInt32(Math.Floor(Math.Log(bytes, 1024)));
var num = Math.Round(bytes / Math.Pow(1024, place), 1);
return $"{num} {Suffixes[place]}";
}
}
17 changes: 17 additions & 0 deletions App/Converters/InverseBoolConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System;
using Microsoft.UI.Xaml.Data;

namespace Coder.Desktop.App.Converters;

public class InverseBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
return value is false;
}

public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
12 changes: 12 additions & 0 deletions App/Converters/InverseBoolToVisibilityConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Microsoft.UI.Xaml;

namespace Coder.Desktop.App.Converters;

public partial class InverseBoolToVisibilityConverter : BoolToObjectConverter
{
public InverseBoolToVisibilityConverter()
{
TrueValue = Visibility.Collapsed;
FalseValue = Visibility.Visible;
}
}
Loading
Loading