Skip to content

Commit

Permalink
Fixed library settings loading and saving, updated grid view, and more.
Browse files Browse the repository at this point in the history
  • Loading branch information
syntax-tm committed Jul 11, 2024
1 parent cab6b82 commit cad015e
Show file tree
Hide file tree
Showing 28 changed files with 938 additions and 322 deletions.
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
## 0.7.0-beta

#### General

- Added grid view to library
- Added ability to group apps in library
- Added automatic saving and loading of library settings

## 0.7.0-alpha

#### General

- **[BUG]** Fixed application crash caused by large libraries

## 0.6.0

#### General
Expand Down
7 changes: 0 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,6 @@ A special thank you to [JetBrains](https://www.jetbrains.com/) for their continu
<a href="https://github.com/DevExpress/DevExpress.Mvvm.Free">DevExpress</a> • <a href="https://github.com/RudeySH/SteamCountries">SteamCountries</a> • <a href="https://github.com/lepoco/wpfui">WPF UI</a>
</p>

## Resources

- [DevExpress MVVM](https://docs.devexpress.com/WPF/15112/mvvm-framework)
- [Steamworks Overview](https://partner.steamgames.com/doc/sdk/api)
- [Steamworks API](https://partner.steamgames.com/doc/api)
- [Steamworks Web API](https://partner.steamgames.com/doc/webapi)

---

[^1]: For non-commercial development
5 changes: 5 additions & 0 deletions src/SAM.ChildProcessDbgSettings
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<ChildProcessDebuggingSettings IsEnabled="true" xmlns="http://schemas.microsoft.com/vstudio/ChildProcessDebuggingSettings/2014">
<DefaultRule EngineFilter="[inherit]" />
<Rule IsEnabled="true" ProcessName="SAM.exe" CommandLine="" EngineFilter="[inherit]" />
</ChildProcessDebuggingSettings>
18 changes: 14 additions & 4 deletions src/SAM.Core/Storage/CacheManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,12 @@ public static void CacheObject(ICacheKey key, object target, bool overwrite = tr

if (string.IsNullOrEmpty(filePath)) throw new ArgumentNullException(nameof(key));

var targetObjectJson = JsonConvert.SerializeObject(target, Formatting.Indented);
var settings = new JsonSerializerSettings()
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
Formatting = Formatting.Indented
};
var targetObjectJson = JsonConvert.SerializeObject(target, settings);

StorageManager.SaveText(filePath, targetObjectJson, overwrite);

Expand All @@ -62,8 +67,13 @@ public static Task CacheObjectAsync(ICacheKey key, object target, bool overwrite
var filePath = key?.GetFullPath();

if (string.IsNullOrEmpty(filePath)) throw new ArgumentNullException(nameof(key));

var targetObjectJson = JsonConvert.SerializeObject(target, Formatting.Indented);

var settings = new JsonSerializerSettings()
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
Formatting = Formatting.Indented
};
var targetObjectJson = JsonConvert.SerializeObject(target, settings);

var task = StorageManager.SaveTextAsync(filePath, targetObjectJson, overwrite);

Expand Down Expand Up @@ -242,7 +252,7 @@ public static bool TryPopulateObject<T>(ICacheKey key, T target)
{
return false;
}

if (IsExpired(key, filePath))
{
return false;
Expand Down
104 changes: 104 additions & 0 deletions src/SAM/Common/Behaviors/DataGridColumnHeaderRowBehavior.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using DevExpress.Mvvm.UI.Interactivity;

namespace SAM.Behaviors;

public class DataGridColumnHeaderRowBehavior : Behavior<ContentControl>
{
public static readonly DependencyProperty DataGridProperty =
DependencyProperty.Register(nameof(DataGrid), typeof(DataGrid), typeof(DataGridColumnHeaderRowBehavior));

public static readonly DependencyProperty ColumnHeaderRowProperty =
DependencyProperty.Register(nameof(ColumnHeaderRow), typeof(DataGridColumnHeadersPresenter), typeof(DataGridColumnHeaderRowBehavior),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));

public DataGrid DataGrid
{
get => (DataGrid) GetValue(DataGridProperty);
set => SetValue(DataGridProperty, value);
}

public DataGridColumnHeadersPresenter ColumnHeaderRow
{
get => (DataGridColumnHeadersPresenter) GetValue(ColumnHeaderRowProperty);
set => SetValue(ColumnHeaderRowProperty, value);
}

protected override void OnAttached()
{
base.OnAttached();

Update();
}

protected override void OnDetaching()
{
base.OnDetaching();
}

private void Update()
{
var columnHeaderRow = FindChild<DataGridColumnHeadersPresenter>(DataGrid); // @"PART_ColumnHeadersPresenter"

if (columnHeaderRow == null) return;

// TODO: remove from parent
var p = columnHeaderRow.Parent as Grid;
p?.Children.Remove(columnHeaderRow);

AssociatedObject.Content = columnHeaderRow;
}

private T FindChild<T>(DependencyObject parent, string childName = null)
where T : DependencyObject
{
// confirm parent and childName are valid.
if (parent == null)
{
return null;
}

T foundChild = null;

var childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (var i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
// If the child is not of the request child type child
if (child is not T childType)
{
// recursively drill down the tree
foundChild = FindChild<T>(child, childName);

// If the child is found, break so we do not overwrite the found child.
if (foundChild != null)
{
break;
}
}
else if (!string.IsNullOrEmpty(childName))
{
// If the child's name is set for search
if (childType is not FrameworkElement frameworkElement || frameworkElement.Name != childName)
{
continue;
}

// if the child's name is of the request name
foundChild = childType;
break;
}
else
{
// child element found.
foundChild = childType;
break;
}
}

return foundChild;
}
}
71 changes: 71 additions & 0 deletions src/SAM/Common/Converters/EnumToVisibilityConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
using SAM.Core.Extensions;

namespace SAM.Converters;

[ValueConversion(typeof (Enum), typeof (Visibility))]
public class EnumToVisibilityConverter : IValueConverter
{
public bool Inverse { get; set; }
public bool HiddenInsteadOfCollapsed { get; set; }

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var trueResult = Visibility.Visible;
var falseResult = HiddenInsteadOfCollapsed ? Visibility.Hidden : Visibility.Collapsed;

if (value == null) return null;
if (value is not Enum valueEnum)
{
return Inverse ? falseResult : trueResult;
}

if (parameter == null) return null;
if (parameter is not Enum paramEnum)
{
return Inverse ? falseResult : trueResult;
}

var equal = Equals(valueEnum, paramEnum);

if (Inverse)
{
return equal ? falseResult : trueResult;
}

return equal
? trueResult
: falseResult;
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}

public class EnumToVisibilityConverterExtension : MarkupExtension
{
public bool Inverse { get; set; }
public bool HiddenInsteadOfCollapsed { get; set; }
public IValueConverter ItemConverter { get; set; }

public EnumToVisibilityConverterExtension() { }
public EnumToVisibilityConverterExtension(IValueConverter itemConverter)
{
ItemConverter = itemConverter;
}

public override object ProvideValue(IServiceProvider serviceProvider)
{
return new EnumToVisibilityConverter
{
Inverse = Inverse,
HiddenInsteadOfCollapsed = HiddenInsteadOfCollapsed
};
}
}
12 changes: 12 additions & 0 deletions src/SAM/Interfaces/ILibrarySettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.ComponentModel;

namespace SAM;

public interface ILibrarySettings : INotifyPropertyChanged
{
bool EnableGrouping { get; set; }
bool ShowFavoritesOnly { get; set; }
bool ShowHidden { get; set; }

void Save();
}
6 changes: 1 addition & 5 deletions src/SAM/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,6 @@
</ResourceDictionary>
</ui:FluentWindow.Resources>

<!--<ui:FluentWindow.DataContext>
<vm:MainWindowViewModel />
</ui:FluentWindow.DataContext>-->

<dxmvvm:Interaction.Behaviors>
<behaviors:WindowAutoSaveBehavior />
<dxmvvm:EventToCommand EventName="Loaded" Command="{Binding OnLoadedCommand}" />
Expand All @@ -40,7 +36,7 @@
CloseWindowByDoubleClickOnIcon="True"
FontSize="13">
<ui:TitleBar.Header>
<views:MenuView />
<views:MenuView DataContext="{Binding MenuVm}" />
</ui:TitleBar.Header>
<ui:TitleBar.Icon>
<ui:ImageIcon Source="/SAM.png" />
Expand Down
53 changes: 48 additions & 5 deletions src/SAM/Models/Settings/HomeSettings.cs
Original file line number Diff line number Diff line change
@@ -1,45 +1,63 @@
using System;
using System.ComponentModel.DataAnnotations;
using DevExpress.Mvvm;
using DevExpress.Mvvm.CodeGenerators;
using log4net;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using SAM.Core;
using SAM.Core.Storage;

namespace SAM;

[GenerateViewModel]
[MetadataType(typeof(HomeSettingsMetaData))]
public partial class HomeSettings : BindableBase
{
private const string NAME = "homeSettings.json";

private static readonly ILog log = LogManager.GetLogger(typeof(HomeSettings));
private static readonly CacheKey cacheKey = new (NAME, CacheKeyType.Settings);

[GenerateProperty(OnChangedMethod=nameof(Save))] private LibraryView _view;
[GenerateProperty] private LibraryGridSettings _gridSettings = new ();
[GenerateProperty] private LibraryTileSettings _tileSettings = new ();
[GenerateProperty(OnChangedMethod=nameof(Save))]
private LibraryView _view;

[GenerateProperty]
private LibraryGridSettings _gridSettings;

[GenerateProperty]
private LibraryTileSettings _tileSettings;

[JsonIgnore]
public bool IsGridView
{
get => GetProperty(() => IsGridView);
set => SetProperty(() => IsGridView, value);
}

[JsonIgnore]
public bool IsTileView
{
get => GetProperty(() => IsTileView);
set => SetProperty(() => IsTileView, value);
}

[JsonIgnore]
public bool Loaded { get; private set; }

private HomeSettings()
{

}

public static HomeSettings Load()
{
try
{
// TODO: this is ugly but unfortunately the partial classes don't play well with serialization
var settings = new HomeSettings();
CacheManager.TryPopulateObject(cacheKey, settings);
settings.GridSettings = LibraryGridSettings.Load();
settings.TileSettings = LibraryTileSettings.Load();
settings.Loaded = true;
return settings;
}
catch (Exception e)
Expand All @@ -51,14 +69,39 @@ public static HomeSettings Load()

public void Save()
{
if (!Loaded) return;

try
{
CacheManager.CacheObject(cacheKey, this);

TileSettings!.Save();
GridSettings!.Save();

log.Info($"Saved {nameof(HomeSettings)}.");
}
catch (Exception e)
{
log.Error($"An error occurred attempting to save the {nameof(HomeSettings)}. {e.Message}", e);
}
}
}

public class HomeSettingsMetaData
{
[JsonProperty(ItemConverterType = typeof(StringEnumConverter))]
public LibraryView View { get; set; }

[JsonProperty]
public LibraryGridSettings GridSettings { get; set; }

[JsonProperty]
public LibraryTileSettings TileSettings { get; set; }

[JsonIgnore]
public bool IsGridView { get; set; }

[JsonIgnore]
public bool IsTileView { get; set; }
}

Loading

0 comments on commit cad015e

Please sign in to comment.