diff --git a/CompactGUI.Core/Analyser.cs b/CompactGUI.Core/Analyser.cs index 0fb1f3a2..920e308e 100644 --- a/CompactGUI.Core/Analyser.cs +++ b/CompactGUI.Core/Analyser.cs @@ -116,7 +116,7 @@ private static bool GetContainsCompressedFiles(List fileCom ? WOFCompressionAlgorithm.NO_COMPRESSION : WOFHelper.DetectCompression(fileInfo); - return new AnalysedFileDetails { FileName = file, CompressedSize = compressedSize, UncompressedSize = uncompressedSize, CompressionMode = compressionMode, FileInfo = fileInfo }; + return new AnalysedFileDetails { FileName = file, CompressedSize = compressedSize, UncompressedSize = uncompressedSize, CompressionMode = compressionMode}; } catch (IOException ex) { @@ -151,7 +151,7 @@ public List GetPoorlyCompressedExtensions() public void Dispose() { _folderMonitor.Dispose(); - _analysedFileDetails?.Clear(); + _analysedFileDetails = null; } } diff --git a/CompactGUI.Core/Settings/Settings.cs b/CompactGUI.Core/Settings/Settings.cs index 2716e23c..4c598fe0 100644 --- a/CompactGUI.Core/Settings/Settings.cs +++ b/CompactGUI.Core/Settings/Settings.cs @@ -46,7 +46,7 @@ public partial class Settings : ObservableRecipient [ObservableProperty] private WindowState windowState = WindowState.Normal; [ObservableProperty] private bool alwaysShowDetailedCompressionMode = false; - [ObservableProperty] private string language = "en-US"; + [ObservableProperty] private string language = "en-AU"; partial void OnScheduledBackgroundIntervalChanged(int value) => UpdateNextScheduledBackgroundRun(); diff --git a/CompactGUI.Core/SharedObjects.cs b/CompactGUI.Core/SharedObjects.cs index 61f67fe9..447c72d5 100644 --- a/CompactGUI.Core/SharedObjects.cs +++ b/CompactGUI.Core/SharedObjects.cs @@ -7,7 +7,6 @@ public sealed class AnalysedFileDetails public long UncompressedSize { get; set; } public long CompressedSize { get; set; } public WOFCompressionAlgorithm CompressionMode { get; set; } - public FileInfo? FileInfo { get; set; } } diff --git a/CompactGUI.Watcher/BackgroundCompactor.vb b/CompactGUI.Watcher/BackgroundCompactor.vb index 8bd72baf..cdd25343 100644 --- a/CompactGUI.Watcher/BackgroundCompactor.vb +++ b/CompactGUI.Watcher/BackgroundCompactor.vb @@ -26,6 +26,7 @@ Public Class BackgroundCompactor Private isCompactingPaused As Boolean = False ' Track if compacting is paused Private _compactor As Core.Compactor + Private _compactorAnalyser As Core.Analyser Private _excludedFileTypes As String() @@ -48,7 +49,9 @@ Public Class BackgroundCompactor Dim effectiveExclusions = If(excludedFileTypes Is Nothing, _excludedFileTypes, excludedFileTypes) - _compactor = New Core.Compactor(folder, compressionLevel, effectiveExclusions, New Core.Analyser(folder, NullLogger(Of Core.Analyser).Instance)) + _compactorAnalyser = New Core.Analyser(folder, NullLogger(Of Core.Analyser).Instance) + _compactor = New Core.Compactor(folder, compressionLevel, effectiveExclusions, _compactorAnalyser) + If isCompactingPaused Then _compactor.Pause() Return _compactor.RunAsync(Nothing) @@ -56,96 +59,133 @@ Public Class BackgroundCompactor Public Async Function StartCompactingAsync(folders As IEnumerable(Of WatchedFolder)) As Task(Of Boolean) WatcherLog.BackgroundCompactingStarted(_logger) + cancellationTokenSource = New CancellationTokenSource() - IsCompactorActive = True + Dim currentProcess = Process.GetCurrentProcess() + Dim originalPriority = currentProcess.PriorityClass - Dim currentProcess As Process = Process.GetCurrentProcess() - currentProcess.PriorityClass = ProcessPriorityClass.Idle + Try + IsCompactorActive = True + isCompacting = True + currentProcess.PriorityClass = ProcessPriorityClass.Idle - isCompacting = True + For Each folder In folders.ToList() + If cancellationTokenSource.IsCancellationRequested Then Return False + If Not Await CompactFolderAsync(folder, folders) Then Return False - For Each folder In folders.ToList - folder.IsWorking = True + WatcherLog.FinishedCompactingFolder(_logger, folder.DisplayName) + Next - WatcherLog.CompactingFolder(_logger, folder.DisplayName) - Dim folderSkipList = If(folder.SkipList Is Nothing, Array.Empty(Of String), folder.SkipList.ToArray()) - Dim compactingTask = BeginCompacting(folder.Folder, folder.CompressionLevel, folderSkipList) + WatcherLog.BackgroundCompactingFinished(_logger) + Return True + Finally + IsCompactorActive = False + isCompacting = False + isCompactingPaused = False + cancellationTokenSource.Dispose() + cancellationTokenSource = Nothing - If cancellationTokenSource.IsCancellationRequested Then - Trace.WriteLine("Compacting cancelled by user.") - folder.IsWorking = False - IsCompactorActive = False - isCompacting = False ' Ensure compacting status is reset after operation - _compactor.Dispose() - Return False - End If + currentProcess.PriorityClass = originalPriority + currentProcess.Dispose() + End Try + End Function - Dim result = Await compactingTask - If result AndAlso folders.Contains(folder) Then - ' Ensure the folder is still in the original collection before updating + Private Async Function CompactFolderAsync(folder As WatchedFolder, folders As IEnumerable(Of WatchedFolder)) As Task(Of Boolean) + folder.IsWorking = True - Dim analyser As New Core.Analyser(folder.Folder, NullLogger(Of Core.Analyser).Instance) + Try + WatcherLog.CompactingFolder(_logger, folder.DisplayName) - Dim analysed = Await analyser.GetAnalysedFilesAsync(Nothing) + Dim folderSkipList = If(folder.SkipList Is Nothing, Array.Empty(Of String)(), folder.SkipList.ToArray()) + Dim compactingTask = BeginCompacting(folder.Folder, folder.CompressionLevel, folderSkipList) - folder.LastCheckedDate = DateTime.Now - folder.LastCheckedSize = analyser.CompressedBytes - folder.LastCompressedSize = analyser.CompressedBytes - folder.LastSystemModifiedDate = DateTime.Now - Dim mainCompressionLVL = analysed.Select(Function(f) f.CompressionMode).Max - folder.CompressionLevel = mainCompressionLVL + If cancellationTokenSource.IsCancellationRequested Then _compactor?.Cancel() - folder.LastCompressedDate = DateTime.Now + Dim result = Await compactingTask - folder.HasTargetChanged = False + If cancellationTokenSource.IsCancellationRequested Then Return False + If result AndAlso folders.Contains(folder) Then + Await UpdateFolderStatisticsAsync(folder) End If + + Return True + Finally + DisposeCurrentCompactor() folder.IsWorking = False folder.RefreshProperties() - _compactor.Dispose() - WatcherLog.FinishedCompactingFolder(_logger, folder.DisplayName) - Next + End Try + End Function - IsCompactorActive = False - isCompacting = False ' Ensure compacting status is reset after operation - WatcherLog.BackgroundCompactingFinished(_logger) - currentProcess.PriorityClass = ProcessPriorityClass.Normal - Return True + Private Async Function UpdateFolderStatisticsAsync(folder As WatchedFolder) As Task + Using analyser As New Core.Analyser(folder.Folder, NullLogger(Of Core.Analyser).Instance) + Dim analysed = Await analyser.GetAnalysedFilesAsync(CancellationToken.None) + + folder.LastCheckedDate = DateTime.Now + folder.LastCheckedSize = analyser.CompressedBytes + folder.LastCompressedSize = analyser.CompressedBytes + folder.LastSystemModifiedDate = DateTime.Now + + If analysed IsNot Nothing AndAlso analysed.Count > 0 Then + folder.CompressionLevel = analysed.Max(Function(file) file.CompressionMode) + End If + + folder.LastCompressedDate = DateTime.Now + folder.HasTargetChanged = False + End Using End Function + Private Sub FinishRun(runCancellation As CancellationTokenSource, currentProcess As Process, originalPriority As ProcessPriorityClass) + If ReferenceEquals(cancellationTokenSource, runCancellation) Then cancellationTokenSource = Nothing + + runCancellation.Dispose() + + IsCompactorActive = False + isCompacting = False + isCompactingPaused = False + + Try + currentProcess.PriorityClass = originalPriority + Finally + currentProcess.Dispose() + End Try + End Sub + Public Sub PauseCompacting() - If Not isCompacting OrElse isCompactingPaused Then - Return - End If + If Not isCompacting OrElse isCompactingPaused Then Return WatcherLog.PausingBackgroundCompactor(_logger) - isCompactingPaused = True ' Indicate compacting is paused + isCompactingPaused = True _compactor?.Pause() End Sub Public Sub ResumeCompacting() - If Not isCompactingPaused OrElse Not isCompacting Then - Return - End If + If Not isCompactingPaused OrElse Not isCompacting Then Return WatcherLog.ResumingBackgroundCompactor(_logger) - isCompactingPaused = False ' Indicate compacting is no longer paused + isCompactingPaused = False _compactor?.Resume() End Sub Public Sub CancelCompacting() - If Not isCompacting Then - Return - End If + If Not isCompacting Then Return + Debug.WriteLine("Cancelling background compactor...") - cancellationTokenSource.Cancel() - cancellationTokenSource.Dispose() + + cancellationTokenSource?.Cancel() _compactor?.Cancel() - _compactor?.Dispose() - isCompacting = False - isCompactingPaused = False ' Reset pause state on cancellation + End Sub + + Private Sub DisposeCurrentCompactor() + Dim compactor = _compactor + _compactor = Nothing + compactor?.Dispose() + + Dim analyser = _compactorAnalyser + _compactorAnalyser = Nothing + analyser?.Dispose() End Sub End Class diff --git a/CompactGUI.Watcher/WatchedFolder.vb b/CompactGUI.Watcher/WatchedFolder.vb index 3bbdb3dc..efaf659d 100644 --- a/CompactGUI.Watcher/WatchedFolder.vb +++ b/CompactGUI.Watcher/WatchedFolder.vb @@ -28,6 +28,9 @@ Public Class WatchedFolder Private _IsEditing As Boolean = False + + Private _IsDriveUnavailable As Boolean = False + ' --- Monitoring State --- Private _HasTargetChanged As Boolean = False @@ -66,13 +69,13 @@ Public Class WatchedFolder End Sub Public Sub PauseMonitoring() - If FSWatcher IsNot Nothing Then + If FSWatcher IsNot Nothing AndAlso Not IsDriveUnavailable Then FSWatcher.EnableRaisingEvents = False End If End Sub Public Sub ResumeMonitoring() - If FSWatcher IsNot Nothing Then + If FSWatcher IsNot Nothing AndAlso Not IsDriveUnavailable Then FSWatcher.EnableRaisingEvents = True End If End Sub @@ -131,4 +134,4 @@ Public Class WatchedFolder Dispose(disposing:=True) GC.SuppressFinalize(Me) End Sub -End Class \ No newline at end of file +End Class diff --git a/CompactGUI.Watcher/Watcher.vb b/CompactGUI.Watcher/Watcher.vb index d8ebe3f8..2c72e9d7 100644 --- a/CompactGUI.Watcher/Watcher.vb +++ b/CompactGUI.Watcher/Watcher.vb @@ -20,7 +20,7 @@ Imports Microsoft.Win32 Imports Microsoft.Win32.Registry -Partial Public Class Watcher : Inherits ObservableRecipient : Implements IRecipient(Of PropertyChangedMessage(Of Boolean)) +Partial Public Class Watcher : Inherits ObservableRecipient : Implements IRecipient(Of PropertyChangedMessage(Of Boolean)), IRecipient(Of PropertyChangedMessage(Of BackgroundMode)) Private ReadOnly _DataFolder As IO.DirectoryInfo Private ReadOnly _parseWatchersSemaphore As New SemaphoreSlim(1, 1) @@ -83,6 +83,7 @@ Partial Public Class Watcher : Inherits ObservableRecipient : Implements IRecipi If bgMode <> BackgroundMode.IdleOnly Then Return BGCompactor.ResumeCompacting() + If IsRunning Then Return Await RunWatcher(False) @@ -93,15 +94,11 @@ Partial Public Class Watcher : Inherits ObservableRecipient : Implements IRecipi Private _isRunning As Boolean = False Public Async Function RunWatcher(Optional runAll As Boolean = True, Optional cToken As CancellationToken = Nothing) As Task(Of Boolean) - RemoveHandler _idleDetector.IsIdle, _idleHandler - IsRunning = True - - For Each watcher In WatchedFolders - watcher.PauseMonitoring() - Next - Try + For Each watcher In WatchedFolders + watcher.PauseMonitoring() + Next _settingsService.AppSettings.ScheduledBackgroundLastRan = DateTime.Now If Not IsWatchingEnabled Then Return False @@ -127,8 +124,6 @@ Partial Public Class Watcher : Inherits ObservableRecipient : Implements IRecipi Catch ex As TaskCanceledException Return False Finally - - AddHandler _idleDetector.IsIdle, _idleHandler For Each watcher In WatchedFolders watcher.ResumeMonitoring() Next @@ -198,9 +193,18 @@ Partial Public Class Watcher : Inherits ObservableRecipient : Implements IRecipi WatchedFolders.Clear() - For Each folder In initialWatchedFolders.Where(Function(f) IO.Directory.Exists(f.Folder)) - WatchedFolders.Add(folder) + For Each folder In initialWatchedFolders + If IO.Directory.Exists(folder.Folder) Then + folder.IsDriveUnavailable = False + folder.InitializeMonitoring() + ElseIf IsRootUnavailable(folder.Folder) Then + folder.IsDriveUnavailable = True + Else + Continue For + End If + folder.LastChangedDate = folder.LastSystemModifiedDate + WatchedFolders.Add(folder) Next UpdateRegistryBasedOnWatchedFolders() @@ -290,16 +294,34 @@ Partial Public Class Watcher : Inherits ObservableRecipient : Implements IRecipi Public Async Function DeleteWatchersWithNonExistentFolders() As Task For i As Integer = WatchedFolders.Count - 1 To 0 Step -1 - If Not IO.Directory.Exists(WatchedFolders(i).Folder) Then - WatcherLog.RemovingNonexistentFolders(_logger, 1) - Await RemoveWatched(WatchedFolders(i), False) + Dim watchedFolder = WatchedFolders(i) + + If IO.Directory.Exists(watchedFolder.Folder) Then + If watchedFolder.IsDriveUnavailable Then watchedFolder.InitializeMonitoring() + watchedFolder.IsDriveUnavailable = False + Continue For End If + + watchedFolder.IsDriveUnavailable = IsRootUnavailable(watchedFolder.Folder) + If watchedFolder.IsDriveUnavailable Then Continue For + + WatcherLog.RemovingNonexistentFolders(_logger, 1) + Await RemoveWatched(watchedFolder, False) Next Await WriteToFileAsync() End Function + Private Shared Function IsRootUnavailable(folderPath As String) As Boolean + Try + Dim rootPath = IO.Path.GetPathRoot(folderPath) + Return Not String.IsNullOrWhiteSpace(rootPath) AndAlso Not IO.Directory.Exists(rootPath) + Catch ex As Exception When TypeOf ex Is ArgumentException OrElse TypeOf ex Is IO.IOException OrElse TypeOf ex Is UnauthorizedAccessException + Return False + End Try + End Function + Private Async Function GetWatchedFoldersFromJson() As Task(Of ObservableCollection(Of WatchedFolder)) @@ -326,12 +348,6 @@ Partial Public Class Watcher : Inherits ObservableRecipient : Implements IRecipi Try validatedResult = JsonSerializer.Deserialize(Of (DateTime, ObservableCollection(Of WatchedFolder)))(WatcherJSON, DeserializeOptions) - If validatedResult.Item2 IsNot Nothing Then - For Each folder In validatedResult.Item2.Where(Function(f) IO.Directory.Exists(f.Folder)) - folder.InitializeMonitoring() - Next - End If - Catch ex As Exception validatedResult = (DateTime.Now, Nothing) WatcherLog.DeserializeWatcherJsonFailed(_logger, ex.Message) @@ -364,10 +380,7 @@ Partial Public Class Watcher : Inherits ObservableRecipient : Implements IRecipi WatcherLog.ParsingWatchers(_logger, ParseAll) Await DeleteWatchersWithNonExistentFolders() - Dim WatchersQuery = If(ParseAll, - WatchedFolders, - WatchedFolders.Where(Function(w) w.HasTargetChanged) - ).OrderBy(Function(f) f.DisplayName) + Dim WatchersQuery = WatchedFolders.Where(Function(w) Not w.IsDriveUnavailable AndAlso (ParseAll OrElse w.HasTargetChanged)).OrderBy(Function(f) f.DisplayName) If Not WatchersQuery.Any() Then Return @@ -396,10 +409,14 @@ Partial Public Class Watcher : Inherits ObservableRecipient : Implements IRecipi Try If watchedFolder Is Nothing Then Return If Not IO.Directory.Exists(watchedFolder.Folder) Then + watchedFolder.IsDriveUnavailable = IsRootUnavailable(watchedFolder.Folder) + If watchedFolder.IsDriveUnavailable Then Return Await RemoveWatched(watchedFolder) Return End If + If watchedFolder.IsDriveUnavailable Then watchedFolder.InitializeMonitoring() + watchedFolder.IsDriveUnavailable = False Await Analyse(watchedFolder.Folder, False) LastAnalysed = DateTime.Now Await WriteToFileAsync() @@ -423,7 +440,7 @@ Partial Public Class Watcher : Inherits ObservableRecipient : Implements IRecipi Dim foldersToCompress = WatchedFolders. Where(Function(folder) - Dim eligible = folder.DecayPercentage <> 0 AndAlso folder.CompressionLevel <> WOFCompressionAlgorithm.NO_COMPRESSION + Dim eligible = Not folder.IsDriveUnavailable AndAlso folder.DecayPercentage <> 0 AndAlso folder.CompressionLevel <> WOFCompressionAlgorithm.NO_COMPRESSION Dim recentlyModified = folder.LastSystemModifiedDate > recentThresholdDate AndAlso Not runAll If eligible AndAlso recentlyModified Then WatcherLog.SkippingRecentlyModifiedFolder(_logger, folder.DisplayName) @@ -491,9 +508,13 @@ Partial Public Class Watcher : Inherits ObservableRecipient : Implements IRecipi Public Sub Receive(message As PropertyChangedMessage(Of Boolean)) Implements IRecipient(Of PropertyChangedMessage(Of Boolean)).Receive If (message.Sender.GetType() IsNot GetType(Settings)) Then Return - If message.PropertyName = NameOf(Settings.EnableBackgroundWatcher) Then : IsWatchingEnabled = message.NewValue - ElseIf message.PropertyName = NameOf(Settings.BackgroundModeSelection) Then : IsBackgroundCompactingEnabled = (CType(message.NewValue, BackgroundMode) = BackgroundMode.IdleOnly) - End If + If message.PropertyName = NameOf(Settings.EnableBackgroundWatcher) Then IsWatchingEnabled = message.NewValue + End Sub + + Public Sub Receive(message As PropertyChangedMessage(Of BackgroundMode)) Implements IRecipient(Of PropertyChangedMessage(Of BackgroundMode)).Receive + If message.Sender.GetType() IsNot GetType(Settings) Then Return + + If message.PropertyName = NameOf(Settings.BackgroundModeSelection) Then IsBackgroundCompactingEnabled = message.NewValue = BackgroundMode.IdleOnly End Sub diff --git a/CompactGUI/Application.xaml.vb b/CompactGUI/Application.xaml.vb index 2faaac82..e1890906 100644 --- a/CompactGUI/Application.xaml.vb +++ b/CompactGUI/Application.xaml.vb @@ -17,7 +17,7 @@ Imports Coravel.Scheduling.Schedule Partial Public Class Application - Public Shared ReadOnly AppVersion As New SemVersion(4, 0, 0, "beta", 8) + Public Shared ReadOnly AppVersion As New SemVersion(4, 0, 0, "beta", 10) Public Shared ReadOnly Property AppVersionText As String Get @@ -58,6 +58,7 @@ Partial Public Class Application 'Settings handler services.AddSingleton(Of ISettingsService)(SettingsService) + services.AddSingleton(Of LocalisationService)() services.AddLogging(Sub(logging) logging.SetMinimumLevel(SettingsService.AppSettings.LogLevel) @@ -103,6 +104,9 @@ Partial Public Class Application services.AddTransient(Of DatabasePage)() services.AddTransient(Of DatabaseViewModel)() + services.AddSingleton(Of SteamMonitorPage)() + services.AddTransient(Of SteamMonitorViewModel)() + 'Other services services.AddSingleton(Of TrayNotifierService)(Function(sp) Return New TrayNotifierService(sp.GetRequiredService(Of MainWindow)(), Icon.ExtractAssociatedIcon(Environment.ProcessPath), "CompactGUI") @@ -131,7 +135,6 @@ Partial Public Class Application Return TryCast(_host?.Services.GetService(GetType(T)), T) End Function - Public Shared ReadOnly mutex As New Mutex(False, "Global\CompactGUI") Private pipeServerCancellation As New CancellationTokenSource() Private pipeServerTask As Task @@ -155,7 +158,7 @@ Partial Public Class Application End If InitializeHost() - LanguageHelper.Initialize(GetService(Of ISettingsService).AppSettings) + Await GetService(Of LocalisationService).InitializeAsync() GetService(Of Watcher.Watcher)() @@ -345,4 +348,4 @@ Partial Public Class Application ' End If 'End Function -End Class \ No newline at end of file +End Class diff --git a/CompactGUI/CompactGUI.vbproj b/CompactGUI/CompactGUI.vbproj index 53f273ae..2705465a 100644 --- a/CompactGUI/CompactGUI.vbproj +++ b/CompactGUI/CompactGUI.vbproj @@ -27,6 +27,7 @@ 41999,42016,42017,42018,42019,42020,42021,42022,42032,42036 + true @@ -45,6 +46,7 @@ + @@ -54,7 +56,10 @@ + + + @@ -79,29 +84,8 @@ - - i18n.es-ES.resx - True - True - - - True - True - i18n.resx - - - - - - i18n - i18n.es-ES.Designer.vb - PublicResXFileCodeGenerator - - - i18n - PublicResXFileCodeGenerator - i18n.Designer.vb - + + diff --git a/CompactGUI/Components/Converters/IValueConverters.vb b/CompactGUI/Components/Converters/IValueConverters.vb index 01acbafc..1ed516cc 100644 --- a/CompactGUI/Components/Converters/IValueConverters.vb +++ b/CompactGUI/Components/Converters/IValueConverters.vb @@ -18,13 +18,13 @@ Public Class BytesToReadableConverter : Implements IValueConverter Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.Convert Dim suf As String() = { - LanguageHelper.GetString("SizeUnit_B"), - LanguageHelper.GetString("SizeUnit_KB"), - LanguageHelper.GetString("SizeUnit_MB"), - LanguageHelper.GetString("SizeUnit_GB"), - LanguageHelper.GetString("SizeUnit_TB"), - LanguageHelper.GetString("SizeUnit_PB"), - LanguageHelper.GetString("SizeUnit_EB") + "B".LT("File size"), + "KB".LT("File size"), + "MB".LT("File size"), + "GB".LT("File size"), + "TB".LT("File size"), + "PB".LT("File size"), + "EB".LT("File size") } If value = 1010101010101010 Then Return "?" @@ -102,16 +102,16 @@ Public Class RelativeDateConverter : Implements IValueConverter Dim ts As TimeSpan = DateTime.Now - dt If ts > TimeSpan.FromDays(19000) Then - Return LanguageHelper.GetString("Time_Unknown") + Return "Unknown".LT("Relative date") End If If ts > TimeSpan.FromDays(2) Then - Return String.Format(LanguageHelper.GetString("Time_DaysAgo"), ts.TotalDays) + Return "{0:0} days ago".LTF(ts.TotalDays) ElseIf ts > TimeSpan.FromHours(2) Then - Return String.Format(LanguageHelper.GetString("Time_HoursAgo"), ts.TotalHours) + Return "{0:0} hours ago".LTF(ts.TotalHours) ElseIf ts > TimeSpan.FromMinutes(2) Then - Return String.Format(LanguageHelper.GetString("Time_MinutesAgo"), ts.TotalMinutes) + Return "{0:0} minutes ago".LTF(ts.TotalMinutes) Else - Return LanguageHelper.GetString("Time_Now") + Return "just now".LT() End If End Function @@ -300,15 +300,15 @@ Public Class FolderStatusToStringConverter : Implements IValueConverter Dim status = CType(value, ActionState) Select Case status Case ActionState.Idle - Return LanguageHelper.GetString("Status_AwaitingCompression") + Return "Awaiting Compression".LT() Case ActionState.Analysing - Return LanguageHelper.GetString("Status_Analysing") + Return "Analysing".LT() Case ActionState.Working, ActionState.Paused - Return LanguageHelper.GetString("Status_Working") + Return "Working".LT() Case ActionState.Results - Return LanguageHelper.GetString("Status_Compressed") + Return "Compressed".LT() Case Else - Return LanguageHelper.GetString("Status_Unknown") + Return "Unknown".LT("Folder status") End Select End Function Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.ConvertBack @@ -493,4 +493,4 @@ Public Class EnumToIntConverter If targetType Is Nothing OrElse Not targetType.IsEnum OrElse value Is Nothing Then Return Binding.DoNothing Return [Enum].ToObject(targetType, value) End Function -End Class \ No newline at end of file +End Class diff --git a/CompactGUI/Components/Settings/Settings_skiplistflyout.xaml b/CompactGUI/Components/Settings/Settings_skiplistflyout.xaml index 5e3eb144..f5752895 100644 --- a/CompactGUI/Components/Settings/Settings_skiplistflyout.xaml +++ b/CompactGUI/Components/Settings/Settings_skiplistflyout.xaml @@ -3,6 +3,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:CompactGUI" + xmlns:loc="clr-namespace:LazyTranslate;assembly=LazyTranslate" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml" Width="620" @@ -89,9 +90,8 @@ + Margin="10" FontSize="22" FontWeight="SemiBold" + loc:L.Value="edit skipped filetypes" /> @@ -104,9 +104,8 @@ Background="{StaticResource CardBackground}" Visibility="Collapsed"> - + @@ -145,15 +144,13 @@ Margin="10 10 10 0" Visibility="Collapsed"> + VerticalAlignment="Center" Checked="UiChkIncludeWiki_Checked" + loc:L.Value="Include smart skipped file types" + ToolTip="{loc:T 'For Steam Games, this uses the database to determine types to skip. For non-Steam folders this is based on the smart analyser.'}" Unchecked="UiChkIncludeWiki_Unchecked" /> + Margin="0 0 130 20" HorizontalAlignment="Right" VerticalAlignment="Bottom" Click="UISave_Click" + loc:L.Context="Skiplist editor" + loc:L.Value="Save" /> @@ -112,7 +112,7 @@ + diff --git a/CompactGUI/Views/Pages/HomePage.xaml.vb b/CompactGUI/Views/Pages/HomePage.xaml.vb index 32e38e6c..b017dcdb 100644 --- a/CompactGUI/Views/Pages/HomePage.xaml.vb +++ b/CompactGUI/Views/Pages/HomePage.xaml.vb @@ -18,7 +18,7 @@ Private Async Sub AddFolderButton_Click(sender As Object, e As RoutedEventArgs) Handles BtnAddFolder1.Click, BtnAddFolder2.Click Dim folderBrowser As New Microsoft.Win32.OpenFolderDialog With { - .Title = "Select a folder to compress", + .Title = "Select a folder to compress".LT(), .Multiselect = True, .ValidateNames = True } diff --git a/CompactGUI/Views/Pages/PendingCompression.xaml b/CompactGUI/Views/Pages/PendingCompression.xaml index 1a00997c..ca9d03ae 100644 --- a/CompactGUI/Views/Pages/PendingCompression.xaml +++ b/CompactGUI/Views/Pages/PendingCompression.xaml @@ -4,6 +4,7 @@ xmlns:core="clr-namespace:CompactGUI.Core;assembly=CompactGUI.Core" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:CompactGUI" + xmlns:loc="clr-namespace:LazyTranslate;assembly=LazyTranslate" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml" d:DataContext="{d:DesignInstance Type=local:FolderViewModel}" @@ -21,9 +22,8 @@ - + --> - + @@ -138,18 +136,16 @@ + Text="{loc:Binding Folder.SkippedFileCount, Format={} {0} files will be skipped}" /> - @@ -161,11 +157,11 @@ + diff --git a/CompactGUI/Views/Pages/ResultsTemplate.xaml b/CompactGUI/Views/Pages/ResultsTemplate.xaml index 29fc7cad..40511184 100644 --- a/CompactGUI/Views/Pages/ResultsTemplate.xaml +++ b/CompactGUI/Views/Pages/ResultsTemplate.xaml @@ -3,6 +3,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:CompactGUI" + xmlns:loc="clr-namespace:LazyTranslate;assembly=LazyTranslate" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml" d:DesignHeight="450" @@ -23,9 +24,8 @@ - + - + loc:L.Context="Compression size" + loc:L.Value="Before" /> - + loc:L.Context="Compression size" + loc:L.Value="After" /> @@ -69,9 +69,8 @@ Margin="0 0 20 20" Padding="10 20 10 20" Background="#30FFFFFF" BorderThickness="0"> - + @@ -81,9 +80,8 @@ Margin="0 0 20 20" Padding="10 20 10 20" Background="#30FFFFFF" BorderThickness="0"> - + @@ -96,9 +94,8 @@ Margin="0 0 20 20" Padding="10 20 10 20" Background="#30FFFFFF" BorderThickness="0"> - + @@ -115,25 +112,22 @@ @@ -151,3 +145,4 @@ + diff --git a/CompactGUI/Views/Pages/SteamMonitorPage.xaml b/CompactGUI/Views/Pages/SteamMonitorPage.xaml new file mode 100644 index 00000000..2d275501 --- /dev/null +++ b/CompactGUI/Views/Pages/SteamMonitorPage.xaml @@ -0,0 +1,472 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CompactGUI/Views/Pages/SteamMonitorPage.xaml.vb b/CompactGUI/Views/Pages/SteamMonitorPage.xaml.vb new file mode 100644 index 00000000..a76eeded --- /dev/null +++ b/CompactGUI/Views/Pages/SteamMonitorPage.xaml.vb @@ -0,0 +1,27 @@ +Public Class SteamMonitorPage + + Private ReadOnly _viewModel As SteamMonitorViewModel + + Public Sub New(viewmodel As SteamMonitorViewModel) + InitializeComponent() + _viewModel = viewmodel + DataContext = viewmodel + End Sub + + Private Async Sub OnLoaded(sender As Object, e As RoutedEventArgs) + Await _viewModel.LoadGamesAsync() + End Sub + + Private Sub OnCompressSplitButtonLoaded(sender As Object, e As RoutedEventArgs) + Dim splitButton = DirectCast(sender, Wpf.Ui.Controls.SplitButton) + splitButton.ApplyTemplate() + + Dim toggleButton = TryCast(splitButton.Template.FindName("PART_ToggleButton", splitButton), Primitives.ToggleButton) + Dim toggleBorder = If(toggleButton Is Nothing, Nothing, TryCast(Media.VisualTreeHelper.GetParent(toggleButton), Border)) + Dim layoutGrid = If(toggleBorder Is Nothing, Nothing, TryCast(Media.VisualTreeHelper.GetParent(toggleBorder), Grid)) + + If layoutGrid Is Nothing OrElse layoutGrid.ColumnDefinitions.Count <> 2 Then Return + layoutGrid.ColumnDefinitions(0).Width = New GridLength(1, GridUnitType.Star) + End Sub + +End Class diff --git a/CompactGUI/Views/Pages/WatcherPage.xaml b/CompactGUI/Views/Pages/WatcherPage.xaml index 936422bf..663d0ef1 100644 --- a/CompactGUI/Views/Pages/WatcherPage.xaml +++ b/CompactGUI/Views/Pages/WatcherPage.xaml @@ -4,12 +4,13 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:CompactGUI" + xmlns:loc="clr-namespace:LazyTranslate;assembly=LazyTranslate" xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800" - Title="{local:Localize PageNameWatcher}" - d:Title="WatcherPage"> + Title="{loc:T 'WatcherPage'}"> + diff --git a/CompactGUI/Views/SettingsPage.xaml b/CompactGUI/Views/SettingsPage.xaml index 6e2515c0..c4989def 100644 --- a/CompactGUI/Views/SettingsPage.xaml +++ b/CompactGUI/Views/SettingsPage.xaml @@ -4,6 +4,7 @@ xmlns:Flags="clr-namespace:FamFamFam.Flags.Wpf;assembly=FamFamFam.Flags.Wpf" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:i="http://schemas.microsoft.com/xaml/behaviors" + xmlns:loc="clr-namespace:LazyTranslate;assembly=LazyTranslate" xmlns:local="clr-namespace:CompactGUI" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml" @@ -20,11 +21,9 @@ - + - + @@ -36,6 +35,7 @@ + @@ -53,7 +53,7 @@ + Text="{Binding NativeName}" /> @@ -62,8 +62,20 @@ + + + + + + + + - -