From d61ccd276735a85ccdd5bf64c78cc25129959b99 Mon Sep 17 00:00:00 2001 From: Gabriel Lima <44784408+gablm@users.noreply.github.com> Date: Thu, 20 Jun 2024 02:34:59 +0100 Subject: [PATCH 001/110] Move Playtime logic into GameManagement --- .../BaseClass/GamePlaytimeBase.cs | 32 +++ .../GamePlaytime/Universal/Context.cs | 8 + .../GamePlaytime/Universal/GamePlaytime.cs | 109 +++++++++ .../Universal/RegistryClass/Playtime.cs | 215 ++++++++++++++++++ CollapseLauncher/Classes/GamePropertyVault.cs | 31 +-- .../Classes/Interfaces/IGamePlaytime.cs | 16 ++ .../XAMLs/MainApp/Pages/HomePage.xaml.cs | 179 ++++----------- Hi3Helper.Core/Lang/en_US.json | 2 +- 8 files changed, 444 insertions(+), 148 deletions(-) create mode 100644 CollapseLauncher/Classes/GameManagement/GamePlaytime/BaseClass/GamePlaytimeBase.cs create mode 100644 CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/Context.cs create mode 100644 CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/GamePlaytime.cs create mode 100644 CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/RegistryClass/Playtime.cs create mode 100644 CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs diff --git a/CollapseLauncher/Classes/GameManagement/GamePlaytime/BaseClass/GamePlaytimeBase.cs b/CollapseLauncher/Classes/GameManagement/GamePlaytime/BaseClass/GamePlaytimeBase.cs new file mode 100644 index 000000000..74dbcb120 --- /dev/null +++ b/CollapseLauncher/Classes/GameManagement/GamePlaytime/BaseClass/GamePlaytimeBase.cs @@ -0,0 +1,32 @@ +using CollapseLauncher.GamePlaytime.Universal; +using CollapseLauncher.Interfaces; +using Microsoft.Win32; +using System; +using System.IO; + +namespace CollapseLauncher.GamePlaytime.Base +{ + internal class GamePlaytimeBase + { +#nullable enable + #region Properties + internal static DateTime BaseDate => new(2012, 2, 13, 0, 0, 0, DateTimeKind.Utc); + + protected RegistryKey? RegistryRoot; + protected Playtime? _playtime; + #endregion + + public GamePlaytimeBase(IGameVersionCheck GameVersionManager) + { + _gameVersionManager = GameVersionManager; + } +#nullable disable + + protected IGameVersionCheck _gameVersionManager { get; set; } + + protected static string TimeSpanToString(TimeSpan timeSpan) + { + return $"{timeSpan.Days * 24 + timeSpan.Hours}h {timeSpan.Minutes}m"; + } + } +} diff --git a/CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/Context.cs b/CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/Context.cs new file mode 100644 index 000000000..0a97f3097 --- /dev/null +++ b/CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/Context.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace CollapseLauncher.GamePlaytime.Universal +{ + [JsonSourceGenerationOptions(IncludeFields = false, GenerationMode = JsonSourceGenerationMode.Metadata, IgnoreReadOnlyFields = true)] + [JsonSerializable(typeof(Playtime))] + internal sealed partial class UniversalPlaytimeJSONContext : JsonSerializerContext { } +} diff --git a/CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/GamePlaytime.cs b/CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/GamePlaytime.cs new file mode 100644 index 000000000..ede6755a1 --- /dev/null +++ b/CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/GamePlaytime.cs @@ -0,0 +1,109 @@ +using CollapseLauncher.GamePlaytime.Base; +using CollapseLauncher.Interfaces; +using Hi3Helper; +using Microsoft.Win32; +using System; +using System.Diagnostics; +using System.IO; +using System.Timers; +using static Hi3Helper.Logger; +using static CollapseLauncher.Dialogs.SimpleDialogs; +using static CollapseLauncher.InnerLauncherConfig; + +namespace CollapseLauncher.GamePlaytime.Universal +{ + internal class GamePlaytime : GamePlaytimeBase, IGamePlaytime + { + #region Properties + public event EventHandler PlaytimeUpdated; + #endregion + + public GamePlaytime(IGameVersionCheck GameVersionManager) : base(GameVersionManager) + { + string registryPath = Path.Combine($"Software\\{GameVersionManager.VendorTypeProp.VendorType}", GameVersionManager.GamePreset.InternalGameNameInConfig); + RegistryRoot = Registry.CurrentUser.OpenSubKey(registryPath, true); + + RegistryRoot ??= Registry.CurrentUser.CreateSubKey(registryPath, true, RegistryOptions.None); + + _gameVersionManager = GameVersionManager; + + _playtime = Playtime.Load(RegistryRoot); + } + + public void ForceUpdate() + { + PlaytimeUpdated?.Invoke(this, _playtime); + } + + public void Update(TimeSpan timeSpan) + { + TimeSpan oldTimeSpan = _playtime.CurrentPlaytime; + + _playtime.Update(timeSpan); + PlaytimeUpdated?.Invoke(this, _playtime); + + LogWriteLine($"Playtime counter changed to {TimeSpanToString(timeSpan)}m. (Previous value: {TimeSpanToString(oldTimeSpan)})", writeToLog: true); + } + + public void Reset() + { + TimeSpan oldTimeSpan = _playtime.CurrentPlaytime; + + _playtime.Reset(); + PlaytimeUpdated?.Invoke(this, _playtime); + + LogWriteLine($"Playtime counter was reset! (Previous value: {TimeSpanToString(oldTimeSpan)})", writeToLog: true); + } + + public async void StartSession(Process proc) + { + DateTime begin = DateTime.Now; + TimeSpan initialTimeSpan = _playtime.CurrentPlaytime; + + _playtime.LastPlayed = begin; + _playtime.Save(); + +#if DEBUG + LogWriteLine($"{_gameVersionManager.GamePreset.ProfileName} - Started session at {begin.ToLongTimeString()}."); +#endif + int elapsedSeconds = 0; + + using (var inGameTimer = new Timer()) + { + inGameTimer.Interval = 60000; + inGameTimer.Elapsed += (_, _) => + { + elapsedSeconds += 60; + DateTime now = DateTime.Now; + + _playtime.Add(TimeSpan.FromMinutes(1)); + PlaytimeUpdated?.Invoke(this, _playtime); + +#if DEBUG + LogWriteLine($"{_gameVersionManager.GamePreset.ProfileName} - {elapsedSeconds}s elapsed. ({now.ToLongTimeString()})"); +#endif + }; + + inGameTimer.Start(); + await proc.WaitForExitAsync(); + inGameTimer.Stop(); + } + + DateTime end = DateTime.Now; + double totalElapsedSeconds = (end - begin).TotalSeconds; + if (totalElapsedSeconds < 0) + { + LogWriteLine($"[HomePage::StartPlaytimeCounter] Date difference cannot be lower than 0. ({elapsedSeconds}s)", LogType.Error); + Dialog_InvalidPlaytime(m_mainPage?.Content, elapsedSeconds); + totalElapsedSeconds = elapsedSeconds; + } + + TimeSpan totalTimeSpan = TimeSpan.FromSeconds(totalElapsedSeconds); + LogWriteLine($"Added {totalElapsedSeconds}s [{totalTimeSpan.Hours}h {totalTimeSpan.Minutes}m {totalTimeSpan.Seconds}s] " + + $"to {_gameVersionManager.GamePreset.ProfileName} playtime.", LogType.Default, true); + + _playtime.Update(initialTimeSpan.Add(totalTimeSpan), false); + PlaytimeUpdated?.Invoke(this, _playtime); + } + } +} diff --git a/CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/RegistryClass/Playtime.cs b/CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/RegistryClass/Playtime.cs new file mode 100644 index 000000000..efc91da79 --- /dev/null +++ b/CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/RegistryClass/Playtime.cs @@ -0,0 +1,215 @@ +using CollapseLauncher.GamePlaytime.Base; +using Hi3Helper; +using Microsoft.Win32; +using System; +using System.Globalization; +using System.Text; +using static Hi3Helper.Logger; + +namespace CollapseLauncher.GamePlaytime.Universal +{ + internal class Playtime + { + #region Fields + + private const string _ValueName = "CollapseLauncher_Playtime"; + private const string _OldLastPlayedValueName = "CollapseLauncher_LastPlayed"; + private RegistryKey _RegistryRoot; + + private static bool _IsDeserializing; + + #endregion + + #region Properties + + /// + /// Represents the total time a game was played.

+ /// Default: TimeSpan.MinValue + ///
+ public TimeSpan CurrentPlaytime { get; set; } = TimeSpan.Zero; + + /// + /// Represents the daily playtime.
+ /// The ControlDate field is used to check if this value should be reset.

+ /// Default: TimeSpan.Zero + ///
+ public TimeSpan DailyPlaytime { get; set; } = TimeSpan.Zero; + + /// + /// Represents the weekly playtime.
+ /// The ControlDate field is used to check if this value should be reset.

+ /// Default: TimeSpan.Zero + ///
+ public TimeSpan WeeklyPlaytime { get; set; } = TimeSpan.Zero; + + /// + /// Represents the monthly playtime.
+ /// The ControlDate field is used to check if this value should be reset.

+ /// Default: TimeSpan.Zero + ///
+ public TimeSpan MonthlyPlaytime { get; set; } = TimeSpan.Zero; + + /// + /// Represents the last time the game was launched.

+ /// Default: null + ///
+ public DateTime? LastPlayed { get; set; } = null; + + /// + /// Represents a control date.
+ /// This date is used to check if a specific playtime statistic should be reset.

+ /// Default: DateTime.Today + ///
+ public DateTime ControlDate { get; set; } = DateTime.Today; + #endregion + + #region Methods +#nullable enable + /// + /// Reads from the Registry and deserializes the contents.
+ /// Converts RegistryKey values if they are of type DWORD (that is, if they were saved by the old implementation). + ///
+ public static Playtime Load(RegistryKey root) + { + try + { + _IsDeserializing = true; + if (root == null) throw new NullReferenceException($"Cannot load {_ValueName} RegistryKey is unexpectedly not initialized!"); + + object? value = root.GetValue(_ValueName, null); + + // The value being an int means this value was saved by the old implementaion and represents the total number of seconds played. + if (value is int oldPlaytime) + { + object? lastPlayed = root.GetValue(_OldLastPlayedValueName, null); + root.DeleteValue(_OldLastPlayedValueName, false); + + LogWriteLine($"Found old Playtime RegistryKey! Converting to the new format... (Playtime: {oldPlaytime} | Last Played: {lastPlayed})", writeToLog: true); + _IsDeserializing = false; + + Playtime playtime = new Playtime() + { + CurrentPlaytime = TimeSpan.FromSeconds(oldPlaytime), + LastPlayed = lastPlayed != null ? GamePlaytimeBase.BaseDate.AddSeconds((int)lastPlayed) : null, + _RegistryRoot = root + }; + playtime.Save(); + + return playtime; + } + + if (value != null) + { + ReadOnlySpan byteStr = (byte[])value; +#if DEBUG + LogWriteLine($"Loaded Playtime:\r\n{Encoding.UTF8.GetString(byteStr.TrimEnd((byte)0))}", LogType.Debug, true); +#endif + Playtime playtime = byteStr.Deserialize(UniversalPlaytimeJSONContext.Default) ?? new Playtime(); + playtime._RegistryRoot = root; + + return playtime; + } + } + catch (Exception ex) + { + LogWriteLine($"Failed while reading {_ValueName}\r\n{ex}", LogType.Error, true); + } + finally + { + _IsDeserializing = false; + } + + return new Playtime(); + } + + /// + /// Serializes all fields and saves them to the Registry. + /// + public void Save() + { + try + { + if (_RegistryRoot == null) throw new NullReferenceException($"Cannot save {_ValueName} since RegistryKey is unexpectedly not initialized!"); + + string data = this.Serialize(UniversalPlaytimeJSONContext.Default, true); + byte[] dataByte = Encoding.UTF8.GetBytes(data); +#if DEBUG + LogWriteLine($"Saved Playtime:\r\n{data}", LogType.Debug, true); +#endif + _RegistryRoot.SetValue(_ValueName, dataByte, RegistryValueKind.Binary); + } + catch (Exception ex) + { + LogWriteLine($"Failed to save {_ValueName}!\r\n{ex}", LogType.Error, true); + } + } + + /// + /// Resets all fields and saves to the Registry. + /// + public void Reset() + { + CurrentPlaytime = TimeSpan.Zero; + DailyPlaytime = TimeSpan.Zero; + WeeklyPlaytime = TimeSpan.Zero; + MonthlyPlaytime = TimeSpan.Zero; + ControlDate = DateTime.Today; + + if (!_IsDeserializing) Save(); + } + + /// + /// Updates the current Playtime TimeSpan to the provided value and saves to the Registry.

+ ///
+ /// New playtime value + /// Reset all other fields + public void Update(TimeSpan timeSpan, bool reset = true) + { + if (reset) + { + DailyPlaytime = TimeSpan.Zero; + WeeklyPlaytime = TimeSpan.Zero; + MonthlyPlaytime = TimeSpan.Zero; + ControlDate = DateTime.Today; + } + + CurrentPlaytime = timeSpan; + + if (!_IsDeserializing) Save(); + } + + /// + /// Updates all fields, and checks if any should be reset.
+ /// After it saves to the Registry.

+ ///
+ /// New playtime value + public void Add(TimeSpan timeSpan) + { + CurrentPlaytime = CurrentPlaytime.Add(timeSpan); + + DateTime today = DateTime.Today; + if (today == DateTime.Today) + { + DailyPlaytime = DailyPlaytime.Add(timeSpan); + WeeklyPlaytime = WeeklyPlaytime.Add(timeSpan); + MonthlyPlaytime = MonthlyPlaytime.Add(timeSpan); + } + else + { + DailyPlaytime = timeSpan; + WeeklyPlaytime = IsDifferentWeek(ControlDate, today) ? timeSpan : WeeklyPlaytime.Add(timeSpan); + MonthlyPlaytime = IsDifferentMonth(ControlDate, today) ? timeSpan : MonthlyPlaytime.Add(timeSpan); + + ControlDate = today; + } + + if (!_IsDeserializing) Save(); + } + + private bool IsDifferentMonth(DateTime date1, DateTime date2) => date1.Year != date2.Year || date1.Month != date2.Month; + + private bool IsDifferentWeek(DateTime date1, DateTime date2) => date1.Year != date2.Year || ISOWeek.GetWeekOfYear(date1) != ISOWeek.GetWeekOfYear(date2); + + #endregion + } +} diff --git a/CollapseLauncher/Classes/GamePropertyVault.cs b/CollapseLauncher/Classes/GamePropertyVault.cs index 50a23db3d..8f49b61c9 100644 --- a/CollapseLauncher/Classes/GamePropertyVault.cs +++ b/CollapseLauncher/Classes/GamePropertyVault.cs @@ -30,18 +30,20 @@ internal GamePresetProperty(UIElement UIElementParent, RegionResourceProp APIRes switch (GamePreset!.GameType) { case GameNameType.Honkai: - _GameVersion = new GameTypeHonkaiVersion(UIElementParent, _APIResouceProp, GameName, GameRegion); + _GameVersion = new GameTypeHonkaiVersion(UIElementParent, _APIResouceProp, GameName, GameRegion); _GameSettings = new HonkaiSettings(_GameVersion); - _GameCache = new HonkaiCache(UIElementParent, _GameVersion); - _GameRepair = new HonkaiRepair(UIElementParent, _GameVersion, _GameCache, _GameSettings); - _GameInstall = new HonkaiInstall(UIElementParent, _GameVersion, _GameCache, _GameSettings); + _GameCache = new HonkaiCache(UIElementParent, _GameVersion); + _GameRepair = new HonkaiRepair(UIElementParent, _GameVersion, _GameCache, _GameSettings); + _GameInstall = new HonkaiInstall(UIElementParent, _GameVersion, _GameCache, _GameSettings); + _GamePlaytime = new GamePlaytime.Universal.GamePlaytime(_GameVersion); break; case GameNameType.StarRail: - _GameVersion = new GameTypeStarRailVersion(UIElementParent, _APIResouceProp, GameName, GameRegion); + _GameVersion = new GameTypeStarRailVersion(UIElementParent, _APIResouceProp, GameName, GameRegion); _GameSettings = new StarRailSettings(_GameVersion); - _GameCache = new StarRailCache(UIElementParent, _GameVersion); - _GameRepair = new StarRailRepair(UIElementParent, _GameVersion); - _GameInstall = new StarRailInstall(UIElementParent, _GameVersion); + _GameCache = new StarRailCache(UIElementParent, _GameVersion); + _GameRepair = new StarRailRepair(UIElementParent, _GameVersion); + _GameInstall = new StarRailInstall(UIElementParent, _GameVersion); + _GamePlaytime = new GamePlaytime.Universal.GamePlaytime(_GameVersion); break; case GameNameType.Genshin: _GameVersion = new GameTypeGenshinVersion(UIElementParent, _APIResouceProp, GameName, GameRegion); @@ -49,6 +51,7 @@ internal GamePresetProperty(UIElement UIElementParent, RegionResourceProp APIRes _GameCache = null; _GameRepair = new GenshinRepair(UIElementParent, _GameVersion, _GameVersion.GameAPIProp!.data!.game!.latest!.decompressed_path); _GameInstall = new GenshinInstall(UIElementParent, _GameVersion); + _GamePlaytime = new GamePlaytime.Universal.GamePlaytime(_GameVersion); break; default: throw new NotSupportedException($"[GamePresetProperty.Ctor] Game type: {GamePreset.GameType} ({GamePreset.ProfileName} - {GamePreset.ZoneName}) is not supported!"); @@ -58,6 +61,7 @@ internal GamePresetProperty(UIElement UIElementParent, RegionResourceProp APIRes internal RegionResourceProp _APIResouceProp { get; set; } internal PresetConfig _GamePreset { get => _GameVersion.GamePreset; } internal IGameSettings _GameSettings { get; set; } + internal IGamePlaytime _GamePlaytime { get; set; } internal IRepair _GameRepair { get; set; } internal ICache _GameCache { get; set; } internal IGameVersionCheck _GameVersion { get; set; } @@ -120,11 +124,12 @@ public void Dispose() _GameInstall?.Dispose(); _APIResouceProp = null; - _GameSettings = null; - _GameRepair = null; - _GameCache = null; - _GameVersion = null; - _GameInstall = null; + _GameSettings = null; + _GameRepair = null; + _GameCache = null; + _GameVersion = null; + _GameInstall = null; + _GamePlaytime = null; } } diff --git a/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs b/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs new file mode 100644 index 000000000..f2dbf36c9 --- /dev/null +++ b/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs @@ -0,0 +1,16 @@ +using CollapseLauncher.GamePlaytime.Universal; +using System; +using System.Diagnostics; + +namespace CollapseLauncher.Interfaces +{ + internal interface IGamePlaytime + { + event EventHandler PlaytimeUpdated; + + void Reset(); + void ForceUpdate(); + void Update(TimeSpan timeSpan); + void StartSession(Process proc); + } +} diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs index 6fc99d132..00dd227a7 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs @@ -5,6 +5,7 @@ using CollapseLauncher.Dialogs; using CollapseLauncher.Extension; using CollapseLauncher.FileDialogCOM; +using CollapseLauncher.GamePlaytime.Universal; using CollapseLauncher.GameSettings.Genshin; using CollapseLauncher.Helper; using CollapseLauncher.Helper.Animation; @@ -53,7 +54,6 @@ using Brush = Microsoft.UI.Xaml.Media.Brush; using Image = Microsoft.UI.Xaml.Controls.Image; using Size = System.Drawing.Size; -using Timer = System.Timers.Timer; using UIElementExtensions = CollapseLauncher.Extension.UIElementExtensions; namespace CollapseLauncher.Pages @@ -183,8 +183,9 @@ private async void StartLoadedRoutine(object sender, RoutedEventArgs e) if (await CurrentGameProperty._GameInstall.TryShowFailedDeltaPatchState()) return; if (await CurrentGameProperty._GameInstall.TryShowFailedGameConversionState()) return; - UpdatePlaytime(); - UpdateLastPlayed(); + CurrentGameProperty._GamePlaytime.PlaytimeUpdated += UpdatePlaytime; + CurrentGameProperty._GamePlaytime.ForceUpdate(); + CheckRunningGameInstance(PageToken.Token); StartCarouselAutoScroll(CarouselToken.Token); @@ -234,6 +235,7 @@ private async void StartLoadedRoutine(object sender, RoutedEventArgs e) private void Page_Unloaded(object sender, RoutedEventArgs e) { IsPageUnload = true; + CurrentGameProperty._GamePlaytime.PlaytimeUpdated -= UpdatePlaytime; PageToken.Cancel(); CarouselToken.Cancel(); } @@ -1331,7 +1333,8 @@ private async void StartGame(object sender, RoutedEventArgs e) break; } - StartPlaytimeCounter(proc, _gamePreset); + CurrentGameProperty._GamePlaytime.StartSession(proc); + if (GetAppConfigValue("LowerCollapsePrioOnGameLaunch").ToBool()) CollapsePrioControl(proc); // Set game process priority to Above Normal when GameBoost is on @@ -1818,26 +1821,20 @@ private void ForceUpdatePlaytimeButton_Click(object sender, RoutedEventArgs e) if (_cachedIsGameRunning) return; - UpdatePlaytime(); + CurrentGameProperty._GamePlaytime.ForceUpdate(); } private async void ChangePlaytimeButton_Click(object sender, RoutedEventArgs e) { if (await Dialog_ChangePlaytime(this) != ContentDialogResult.Primary) return; - int playtimeMins = int.Parse("0" + MinutePlaytimeTextBox.Text); - int playtimeHours = int.Parse("0" + HourPlaytimeTextBox.Text); - int finalPlaytimeMinutes = playtimeMins % 60; - int finalPlaytimeHours = playtimeHours + playtimeMins / 60; - if (finalPlaytimeHours > 99999) { finalPlaytimeHours = 99999; finalPlaytimeMinutes = 59; } - MinutePlaytimeTextBox.Text = finalPlaytimeMinutes.ToString(); - HourPlaytimeTextBox.Text = finalPlaytimeHours.ToString(); - - int finalPlaytime = finalPlaytimeHours * 3600 + finalPlaytimeMinutes * 60; - - SavePlaytimeToRegistry(true, CurrentGameProperty._GameVersion.GamePreset.ConfigRegistryLocation, finalPlaytime); - LogWriteLine($"Playtime counter changed to {HourPlaytimeTextBox.Text + "h " + MinutePlaytimeTextBox.Text + "m"}. (Previous value: {PlaytimeMainBtn.Text})"); - UpdatePlaytime(false, finalPlaytime); + int Mins = int.Parse("0" + MinutePlaytimeTextBox.Text); + int Hours = int.Parse("0" + HourPlaytimeTextBox.Text); + + TimeSpan time = TimeSpan.FromMinutes(Hours * 60 + Mins); + if (time.Hours > 99999) time = new TimeSpan(99999, 59, 0); + + CurrentGameProperty._GamePlaytime.Update(time); PlaytimeFlyout.Hide(); } @@ -1845,9 +1842,7 @@ private async void ResetPlaytimeButton_Click(object sender, RoutedEventArgs e) { if (await Dialog_ResetPlaytime(this) != ContentDialogResult.Primary) return; - SavePlaytimeToRegistry(true, CurrentGameProperty._GameVersion.GamePreset.ConfigRegistryLocation, 0); - LogWriteLine($"Playtime counter changed to 0h 0m. (Previous value: {PlaytimeMainBtn.Text})"); - UpdatePlaytime(false, 0); + CurrentGameProperty._GamePlaytime.Reset(); PlaytimeFlyout.Hide(); } @@ -1856,129 +1851,45 @@ private void NumberValidationTextBox(TextBox sender, TextBoxBeforeTextChangingEv sender.MaxLength = sender == HourPlaytimeTextBox ? 5 : 3; args.Cancel = args.NewText.Any(c => !char.IsDigit(c)); } - #endregion - - #region Playtime Tracker Method - private void UpdatePlaytime(bool readRegistry = true, int value = 0) - { - if (readRegistry) - value = ReadPlaytimeFromRegistry(true, CurrentGameProperty._GameVersion.GamePreset.ConfigRegistryLocation); - - HourPlaytimeTextBox.Text = (value / 3600).ToString(); - MinutePlaytimeTextBox.Text = (value % 3600 / 60).ToString(); - PlaytimeMainBtn.Text = string.Format(Lang._HomePage.GamePlaytime_Display, (value / 3600), (value % 3600 / 60)); - } - private DateTime Hoyoception => new(2012, 2, 13, 0, 0, 0, DateTimeKind.Utc); - private void UpdateLastPlayed(bool readRegistry = true, int value = 0) + private void UpdatePlaytime(object sender, Playtime playtime) { - if (readRegistry) - value = ReadPlaytimeFromRegistry(false, CurrentGameProperty._GameVersion.GamePreset.ConfigRegistryLocation); - - DateTime last = Hoyoception.AddSeconds(value).ToLocalTime(); - - if (value == 0) + DispatcherQueue.TryEnqueue(() => { - PlaytimeLastOpen.Visibility = Visibility.Collapsed; - return; - } - - PlaytimeLastOpen.Visibility = Visibility.Visible; - string formattedText = string.Format(Lang._HomePage.GamePlaytime_ToolTipDisplay, last.Day, - last.Month, last.Year, last.Hour, last.Minute); - ToolTipService.SetToolTip(PlaytimeBtn, formattedText); - } - - private async void StartPlaytimeCounter(Process proc, PresetConfig gamePreset) - { - int currentPlaytime = ReadPlaytimeFromRegistry(true, gamePreset.ConfigRegistryLocation); - - DateTime begin = DateTime.Now; - int lastPlayed = (int)(begin.ToUniversalTime() - Hoyoception).TotalSeconds; - SavePlaytimeToRegistry(false, gamePreset.ConfigRegistryLocation, lastPlayed); - UpdateLastPlayed(false, lastPlayed); - int numOfLoops = 0; - -#if DEBUG - LogWriteLine($"{gamePreset.ProfileName} - Started session at {begin.ToLongTimeString()}."); -#endif + PlaytimeMainBtn.Text = FormatTimeStamp(playtime.CurrentPlaytime); + HourPlaytimeTextBox.Text = (playtime.CurrentPlaytime.Days * 24 + playtime.CurrentPlaytime.Hours).ToString(); + MinutePlaytimeTextBox.Text = playtime.CurrentPlaytime.Minutes.ToString(); - using (var inGameTimer = new Timer()) - { - inGameTimer.Interval = 60000; - inGameTimer.Elapsed += (_, _) => + if (playtime.LastPlayed == null) { - numOfLoops++; - - DateTime now = DateTime.Now; - int elapsedSeconds = (int)(now - begin).TotalSeconds; - if (elapsedSeconds < 0) - elapsedSeconds = numOfLoops * 60; - - if (GamePropertyVault.GetCurrentGameProperty()._GamePreset.ProfileName == gamePreset.ProfileName) - m_homePage?.DispatcherQueue?.TryEnqueue(() => - { - m_homePage.UpdatePlaytime(false, currentPlaytime + elapsedSeconds); - }); -#if DEBUG - LogWriteLine($"{gamePreset.ProfileName} - {elapsedSeconds}s elapsed. ({now.ToLongTimeString()})"); -#endif - SavePlaytimeToRegistry(true, gamePreset.ConfigRegistryLocation, currentPlaytime + elapsedSeconds); - }; - - inGameTimer.Start(); - await proc.WaitForExitAsync(); - inGameTimer.Stop(); - } + ToolTipService.SetToolTip(PlaytimeBtn, null); + return; + } - DateTime end = DateTime.Now; - int elapsedSeconds = (int)(end - begin).TotalSeconds; - if (elapsedSeconds < 0) - { - LogWriteLine($"[HomePage::StartPlaytimeCounter] Date difference cannot be lower than 0. ({elapsedSeconds}s)", LogType.Error); - elapsedSeconds = numOfLoops * 60; - Dialog_InvalidPlaytime(m_mainPage?.Content, elapsedSeconds); - } + DateTime? last = playtime.LastPlayed?.ToLocalTime(); + string lastPlayed = string.Format(Lang._HomePage.GamePlaytime_ToolTipDisplay, last?.Day, + last?.Month, last?.Year, last?.Hour, last?.Minute); - SavePlaytimeToRegistry(true, gamePreset.ConfigRegistryLocation, currentPlaytime + elapsedSeconds); - LogWriteLine($"Added {elapsedSeconds}s [{elapsedSeconds / 3600}h {elapsedSeconds % 3600 / 60}m {elapsedSeconds % 3600 % 60}s] " + - $"to {gamePreset.ProfileName} playtime.", LogType.Default, true); - if (GamePropertyVault.GetCurrentGameProperty()._GamePreset.ProfileName == gamePreset.ProfileName) - m_homePage?.DispatcherQueue?.TryEnqueue(() => + StackPanel panel = new StackPanel() { - m_homePage.UpdatePlaytime(false, currentPlaytime + elapsedSeconds); - }); - } + Children = + { + new TextBlock() { Text = "Last opened" }, + new TextBlock() { Text = lastPlayed, FontWeight = FontWeights.Bold, Margin = new Thickness(0, 0, 0, 5) }, + new TextBlock() { Text = "Daily Playtime" }, + new TextBlock() { Text = FormatTimeStamp(playtime.DailyPlaytime), FontWeight = FontWeights.Bold, Margin = new Thickness(0, 0, 0, 5) }, + new TextBlock() { Text = "Weekly Playtime" }, + new TextBlock() { Text = FormatTimeStamp(playtime.WeeklyPlaytime), FontWeight = FontWeights.Bold, Margin = new Thickness(0, 0, 0, 5) }, + new TextBlock() { Text = "Monthly Playtime" }, + new TextBlock() { Text = FormatTimeStamp(playtime.MonthlyPlaytime), FontWeight = FontWeights.Bold, Margin = new Thickness(0, 0, 0, 5) } + } + }; - private const string _playtimeRegName = "CollapseLauncher_Playtime"; - private const string _playtimeLastPlayedRegName = "CollapseLauncher_LastPlayed"; - private static int ReadPlaytimeFromRegistry(bool isPlaytime, string regionRegistryKey) - { - try - { - return (int) - Registry.CurrentUser.OpenSubKey(regionRegistryKey, true)! - .GetValue(isPlaytime ? _playtimeRegName : _playtimeLastPlayedRegName, 0); - } - catch (Exception ex) - { - LogWriteLine($"Playtime - There was an error reading from the registry. \n {ex}"); - return 0; - } - } + ToolTipService.SetToolTip(PlaytimeBtn, panel); + }); + return; - private static void SavePlaytimeToRegistry(bool isPlaytime, string regionRegistryKey, int value) - { - try - { - Registry.CurrentUser.OpenSubKey(regionRegistryKey, true)! - .SetValue(isPlaytime ? _playtimeRegName : _playtimeLastPlayedRegName, value, - RegistryValueKind.DWord); - } - catch (Exception ex) - { - LogWriteLine($"Playtime - There was an error writing to registry. \n {ex}"); - } + static string FormatTimeStamp(TimeSpan time) => string.Format(Lang._HomePage.GamePlaytime_Display, time.Days * 24 + time.Hours, time.Minutes); } #endregion diff --git a/Hi3Helper.Core/Lang/en_US.json b/Hi3Helper.Core/Lang/en_US.json index 57a84ff20..db4f6fc49 100644 --- a/Hi3Helper.Core/Lang/en_US.json +++ b/Hi3Helper.Core/Lang/en_US.json @@ -159,7 +159,7 @@ "GamePlaytime_Running_Info1": "An instance of this game is currently running, therefore playtime can't be edited.", "GamePlaytime_Running_Info2": "Please be aware that fully closing Collapse will stop playtime tracking (saving what was played until that point) & only sessions started using Collapse will be tracked.", "GamePlaytime_Display": "{0}h {1}m", - "GamePlaytime_ToolTipDisplay": "Last opened in {0:00}/{1:00}/{2:0000} at {3:00}:{4:00}", + "GamePlaytime_ToolTipDisplay": "{0:00}/{1:00}/{2:0000} {3:00}:{4:00}", "PostPanel_Events": "Events", "PostPanel_Notices": "Notices", From eba6e58e8bbdd43b13e4c6773d887ada28344564 Mon Sep 17 00:00:00 2001 From: Gabriel Lima <44784408+gablm@users.noreply.github.com> Date: Thu, 20 Jun 2024 22:24:51 +0100 Subject: [PATCH 002/110] Playtime - Add Dispose and rename classes --- .../BaseClass/GamePlaytimeBase.cs | 32 --------------- .../GamePlaytime/{Universal => }/Context.cs | 4 +- .../GamePlaytime.cs => Playtime.cs} | 41 ++++++++++++++----- .../CollapsePlaytime.cs} | 41 ++++++++++--------- CollapseLauncher/Classes/GamePropertyVault.cs | 9 ++-- .../Classes/Interfaces/IGamePlaytime.cs | 6 +-- .../XAMLs/MainApp/Pages/HomePage.xaml.cs | 4 +- 7 files changed, 64 insertions(+), 73 deletions(-) delete mode 100644 CollapseLauncher/Classes/GameManagement/GamePlaytime/BaseClass/GamePlaytimeBase.cs rename CollapseLauncher/Classes/GameManagement/GamePlaytime/{Universal => }/Context.cs (75%) rename CollapseLauncher/Classes/GameManagement/GamePlaytime/{Universal/GamePlaytime.cs => Playtime.cs} (72%) rename CollapseLauncher/Classes/GameManagement/GamePlaytime/{Universal/RegistryClass/Playtime.cs => RegistryClass/CollapsePlaytime.cs} (82%) diff --git a/CollapseLauncher/Classes/GameManagement/GamePlaytime/BaseClass/GamePlaytimeBase.cs b/CollapseLauncher/Classes/GameManagement/GamePlaytime/BaseClass/GamePlaytimeBase.cs deleted file mode 100644 index 74dbcb120..000000000 --- a/CollapseLauncher/Classes/GameManagement/GamePlaytime/BaseClass/GamePlaytimeBase.cs +++ /dev/null @@ -1,32 +0,0 @@ -using CollapseLauncher.GamePlaytime.Universal; -using CollapseLauncher.Interfaces; -using Microsoft.Win32; -using System; -using System.IO; - -namespace CollapseLauncher.GamePlaytime.Base -{ - internal class GamePlaytimeBase - { -#nullable enable - #region Properties - internal static DateTime BaseDate => new(2012, 2, 13, 0, 0, 0, DateTimeKind.Utc); - - protected RegistryKey? RegistryRoot; - protected Playtime? _playtime; - #endregion - - public GamePlaytimeBase(IGameVersionCheck GameVersionManager) - { - _gameVersionManager = GameVersionManager; - } -#nullable disable - - protected IGameVersionCheck _gameVersionManager { get; set; } - - protected static string TimeSpanToString(TimeSpan timeSpan) - { - return $"{timeSpan.Days * 24 + timeSpan.Hours}h {timeSpan.Minutes}m"; - } - } -} diff --git a/CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/Context.cs b/CollapseLauncher/Classes/GameManagement/GamePlaytime/Context.cs similarity index 75% rename from CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/Context.cs rename to CollapseLauncher/Classes/GameManagement/GamePlaytime/Context.cs index 0a97f3097..843d46ae3 100644 --- a/CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/Context.cs +++ b/CollapseLauncher/Classes/GameManagement/GamePlaytime/Context.cs @@ -1,8 +1,8 @@ using System.Text.Json.Serialization; -namespace CollapseLauncher.GamePlaytime.Universal +namespace CollapseLauncher.GamePlaytime { [JsonSourceGenerationOptions(IncludeFields = false, GenerationMode = JsonSourceGenerationMode.Metadata, IgnoreReadOnlyFields = true)] - [JsonSerializable(typeof(Playtime))] + [JsonSerializable(typeof(CollapsePlaytime))] internal sealed partial class UniversalPlaytimeJSONContext : JsonSerializerContext { } } diff --git a/CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/GamePlaytime.cs b/CollapseLauncher/Classes/GameManagement/GamePlaytime/Playtime.cs similarity index 72% rename from CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/GamePlaytime.cs rename to CollapseLauncher/Classes/GameManagement/GamePlaytime/Playtime.cs index ede6755a1..b2cb5c9a0 100644 --- a/CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/GamePlaytime.cs +++ b/CollapseLauncher/Classes/GameManagement/GamePlaytime/Playtime.cs @@ -1,4 +1,4 @@ -using CollapseLauncher.GamePlaytime.Base; +using CollapseLauncher.Extension; using CollapseLauncher.Interfaces; using Hi3Helper; using Microsoft.Win32; @@ -10,25 +10,32 @@ using static CollapseLauncher.Dialogs.SimpleDialogs; using static CollapseLauncher.InnerLauncherConfig; -namespace CollapseLauncher.GamePlaytime.Universal +namespace CollapseLauncher.GamePlaytime { - internal class GamePlaytime : GamePlaytimeBase, IGamePlaytime + internal class Playtime : IGamePlaytime { #region Properties - public event EventHandler PlaytimeUpdated; + public event EventHandler PlaytimeUpdated; +#nullable enable + private RegistryKey? _registryRoot; + private CollapsePlaytime? _playtime; + private IGameVersionCheck _gameVersionManager; + + private CancellationTokenSourceWrapper _token = new(); #endregion - public GamePlaytime(IGameVersionCheck GameVersionManager) : base(GameVersionManager) + public Playtime(IGameVersionCheck GameVersionManager) { string registryPath = Path.Combine($"Software\\{GameVersionManager.VendorTypeProp.VendorType}", GameVersionManager.GamePreset.InternalGameNameInConfig); - RegistryRoot = Registry.CurrentUser.OpenSubKey(registryPath, true); + _registryRoot = Registry.CurrentUser.OpenSubKey(registryPath, true); - RegistryRoot ??= Registry.CurrentUser.CreateSubKey(registryPath, true, RegistryOptions.None); + _registryRoot ??= Registry.CurrentUser.CreateSubKey(registryPath, true, RegistryOptions.None); _gameVersionManager = GameVersionManager; - _playtime = Playtime.Load(RegistryRoot); + _playtime = CollapsePlaytime.Load(_registryRoot); } +#nullable disable public void ForceUpdate() { @@ -85,7 +92,7 @@ public async void StartSession(Process proc) }; inGameTimer.Start(); - await proc.WaitForExitAsync(); + await proc.WaitForExitAsync(_token.Token); inGameTimer.Stop(); } @@ -93,7 +100,7 @@ public async void StartSession(Process proc) double totalElapsedSeconds = (end - begin).TotalSeconds; if (totalElapsedSeconds < 0) { - LogWriteLine($"[HomePage::StartPlaytimeCounter] Date difference cannot be lower than 0. ({elapsedSeconds}s)", LogType.Error); + LogWriteLine($"[HomePage::StartPlaytimeCounter] Date difference cannot be lower than 0. ({totalElapsedSeconds}s)", LogType.Error); Dialog_InvalidPlaytime(m_mainPage?.Content, elapsedSeconds); totalElapsedSeconds = elapsedSeconds; } @@ -105,5 +112,19 @@ public async void StartSession(Process proc) _playtime.Update(initialTimeSpan.Add(totalTimeSpan), false); PlaytimeUpdated?.Invoke(this, _playtime); } + + private static string TimeSpanToString(TimeSpan timeSpan) + { + return $"{timeSpan.Days * 24 + timeSpan.Hours}h {timeSpan.Minutes}m"; + } + + public void Dispose() + { + _token.Cancel(); + _playtime?.Save(); + + _playtime = null; + _registryRoot = null; + } } } diff --git a/CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/RegistryClass/Playtime.cs b/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs similarity index 82% rename from CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/RegistryClass/Playtime.cs rename to CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs index efc91da79..7be4c4e63 100644 --- a/CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/RegistryClass/Playtime.cs +++ b/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs @@ -1,27 +1,26 @@ -using CollapseLauncher.GamePlaytime.Base; -using Hi3Helper; +using Hi3Helper; using Microsoft.Win32; using System; using System.Globalization; using System.Text; using static Hi3Helper.Logger; -namespace CollapseLauncher.GamePlaytime.Universal +namespace CollapseLauncher.GamePlaytime { - internal class Playtime + internal class CollapsePlaytime { #region Fields + private static DateTime BaseDate => new(2012, 2, 13, 0, 0, 0, DateTimeKind.Utc); - private const string _ValueName = "CollapseLauncher_Playtime"; - private const string _OldLastPlayedValueName = "CollapseLauncher_LastPlayed"; - private RegistryKey _RegistryRoot; + private const string _ValueName = "CollapseLauncher_Playtime"; + private const string _OldLastPlayedValueName = "CollapseLauncher_LastPlayed"; - private static bool _IsDeserializing; + private static bool _IsDeserializing; + private RegistryKey _registryRoot; #endregion #region Properties - /// /// Represents the total time a game was played.

/// Default: TimeSpan.MinValue @@ -69,7 +68,7 @@ internal class Playtime /// Reads from the Registry and deserializes the contents.
/// Converts RegistryKey values if they are of type DWORD (that is, if they were saved by the old implementation). ///
- public static Playtime Load(RegistryKey root) + public static CollapsePlaytime Load(RegistryKey root) { try { @@ -87,11 +86,11 @@ public static Playtime Load(RegistryKey root) LogWriteLine($"Found old Playtime RegistryKey! Converting to the new format... (Playtime: {oldPlaytime} | Last Played: {lastPlayed})", writeToLog: true); _IsDeserializing = false; - Playtime playtime = new Playtime() + CollapsePlaytime playtime = new CollapsePlaytime() { CurrentPlaytime = TimeSpan.FromSeconds(oldPlaytime), - LastPlayed = lastPlayed != null ? GamePlaytimeBase.BaseDate.AddSeconds((int)lastPlayed) : null, - _RegistryRoot = root + LastPlayed = lastPlayed != null ? BaseDate.AddSeconds((int)lastPlayed) : null, + _registryRoot = root }; playtime.Save(); @@ -104,8 +103,8 @@ public static Playtime Load(RegistryKey root) #if DEBUG LogWriteLine($"Loaded Playtime:\r\n{Encoding.UTF8.GetString(byteStr.TrimEnd((byte)0))}", LogType.Debug, true); #endif - Playtime playtime = byteStr.Deserialize(UniversalPlaytimeJSONContext.Default) ?? new Playtime(); - playtime._RegistryRoot = root; + CollapsePlaytime playtime = byteStr.Deserialize(UniversalPlaytimeJSONContext.Default) ?? new CollapsePlaytime(); + playtime._registryRoot = root; return playtime; } @@ -119,7 +118,7 @@ public static Playtime Load(RegistryKey root) _IsDeserializing = false; } - return new Playtime(); + return new CollapsePlaytime(); } /// @@ -129,14 +128,14 @@ public void Save() { try { - if (_RegistryRoot == null) throw new NullReferenceException($"Cannot save {_ValueName} since RegistryKey is unexpectedly not initialized!"); + if (_registryRoot == null) throw new NullReferenceException($"Cannot save {_ValueName} since RegistryKey is unexpectedly not initialized!"); string data = this.Serialize(UniversalPlaytimeJSONContext.Default, true); byte[] dataByte = Encoding.UTF8.GetBytes(data); #if DEBUG LogWriteLine($"Saved Playtime:\r\n{data}", LogType.Debug, true); #endif - _RegistryRoot.SetValue(_ValueName, dataByte, RegistryValueKind.Binary); + _registryRoot.SetValue(_ValueName, dataByte, RegistryValueKind.Binary); } catch (Exception ex) { @@ -206,10 +205,12 @@ public void Add(TimeSpan timeSpan) if (!_IsDeserializing) Save(); } - private bool IsDifferentMonth(DateTime date1, DateTime date2) => date1.Year != date2.Year || date1.Month != date2.Month; + #endregion - private bool IsDifferentWeek(DateTime date1, DateTime date2) => date1.Year != date2.Year || ISOWeek.GetWeekOfYear(date1) != ISOWeek.GetWeekOfYear(date2); + #region Utility Methods + private static bool IsDifferentMonth(DateTime date1, DateTime date2) => date1.Year != date2.Year || date1.Month != date2.Month; + private static bool IsDifferentWeek(DateTime date1, DateTime date2) => date1.Year != date2.Year || ISOWeek.GetWeekOfYear(date1) != ISOWeek.GetWeekOfYear(date2); #endregion } } diff --git a/CollapseLauncher/Classes/GamePropertyVault.cs b/CollapseLauncher/Classes/GamePropertyVault.cs index 8f49b61c9..f93f1f145 100644 --- a/CollapseLauncher/Classes/GamePropertyVault.cs +++ b/CollapseLauncher/Classes/GamePropertyVault.cs @@ -1,4 +1,5 @@ -using CollapseLauncher.GameSettings.Genshin; +using CollapseLauncher.GamePlaytime; +using CollapseLauncher.GameSettings.Genshin; using CollapseLauncher.GameSettings.Honkai; using CollapseLauncher.GameSettings.StarRail; using CollapseLauncher.GameVersioning; @@ -35,7 +36,6 @@ internal GamePresetProperty(UIElement UIElementParent, RegionResourceProp APIRes _GameCache = new HonkaiCache(UIElementParent, _GameVersion); _GameRepair = new HonkaiRepair(UIElementParent, _GameVersion, _GameCache, _GameSettings); _GameInstall = new HonkaiInstall(UIElementParent, _GameVersion, _GameCache, _GameSettings); - _GamePlaytime = new GamePlaytime.Universal.GamePlaytime(_GameVersion); break; case GameNameType.StarRail: _GameVersion = new GameTypeStarRailVersion(UIElementParent, _APIResouceProp, GameName, GameRegion); @@ -43,7 +43,6 @@ internal GamePresetProperty(UIElement UIElementParent, RegionResourceProp APIRes _GameCache = new StarRailCache(UIElementParent, _GameVersion); _GameRepair = new StarRailRepair(UIElementParent, _GameVersion); _GameInstall = new StarRailInstall(UIElementParent, _GameVersion); - _GamePlaytime = new GamePlaytime.Universal.GamePlaytime(_GameVersion); break; case GameNameType.Genshin: _GameVersion = new GameTypeGenshinVersion(UIElementParent, _APIResouceProp, GameName, GameRegion); @@ -51,11 +50,12 @@ internal GamePresetProperty(UIElement UIElementParent, RegionResourceProp APIRes _GameCache = null; _GameRepair = new GenshinRepair(UIElementParent, _GameVersion, _GameVersion.GameAPIProp!.data!.game!.latest!.decompressed_path); _GameInstall = new GenshinInstall(UIElementParent, _GameVersion); - _GamePlaytime = new GamePlaytime.Universal.GamePlaytime(_GameVersion); break; default: throw new NotSupportedException($"[GamePresetProperty.Ctor] Game type: {GamePreset.GameType} ({GamePreset.ProfileName} - {GamePreset.ZoneName}) is not supported!"); } + + _GamePlaytime = new Playtime(_GameVersion); } internal RegionResourceProp _APIResouceProp { get; set; } @@ -122,6 +122,7 @@ public void Dispose() _GameRepair?.Dispose(); _GameCache?.Dispose(); _GameInstall?.Dispose(); + _GamePlaytime?.Dispose(); _APIResouceProp = null; _GameSettings = null; diff --git a/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs b/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs index f2dbf36c9..291b31003 100644 --- a/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs +++ b/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs @@ -1,12 +1,12 @@ -using CollapseLauncher.GamePlaytime.Universal; +using CollapseLauncher.GamePlaytime; using System; using System.Diagnostics; namespace CollapseLauncher.Interfaces { - internal interface IGamePlaytime + internal interface IGamePlaytime : IDisposable { - event EventHandler PlaytimeUpdated; + event EventHandler PlaytimeUpdated; void Reset(); void ForceUpdate(); diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs index 00dd227a7..c3e30519a 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs @@ -5,7 +5,7 @@ using CollapseLauncher.Dialogs; using CollapseLauncher.Extension; using CollapseLauncher.FileDialogCOM; -using CollapseLauncher.GamePlaytime.Universal; +using CollapseLauncher.GamePlaytime; using CollapseLauncher.GameSettings.Genshin; using CollapseLauncher.Helper; using CollapseLauncher.Helper.Animation; @@ -1852,7 +1852,7 @@ private void NumberValidationTextBox(TextBox sender, TextBoxBeforeTextChangingEv args.Cancel = args.NewText.Any(c => !char.IsDigit(c)); } - private void UpdatePlaytime(object sender, Playtime playtime) + private void UpdatePlaytime(object sender, CollapsePlaytime playtime) { DispatcherQueue.TryEnqueue(() => { From 9fcecc095ee7e930b1cb0f87afcd93d2461182f3 Mon Sep 17 00:00:00 2001 From: Gabriel Lima <44784408+gablm@users.noreply.github.com> Date: Sat, 22 Jun 2024 12:12:23 +0100 Subject: [PATCH 003/110] Playtime - Add localization support --- .../GamePlaytime/RegistryClass/CollapsePlaytime.cs | 2 +- .../XAMLs/MainApp/Pages/HomePage.xaml.cs | 12 ++++++------ Hi3Helper.Core/Lang/Locale/LangHomePage.cs | 6 +++++- Hi3Helper.Core/Lang/en_US.json | 6 +++++- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs b/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs index 7be4c4e63..0096aea62 100644 --- a/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs +++ b/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs @@ -207,7 +207,7 @@ public void Add(TimeSpan timeSpan) #endregion - #region Utility Methods + #region Utility private static bool IsDifferentMonth(DateTime date1, DateTime date2) => date1.Year != date2.Year || date1.Month != date2.Month; private static bool IsDifferentWeek(DateTime date1, DateTime date2) => date1.Year != date2.Year || ISOWeek.GetWeekOfYear(date1) != ISOWeek.GetWeekOfYear(date2); diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs index c3e30519a..d30974832 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs @@ -1815,7 +1815,7 @@ private async void MoveGameLocationButton_Click(object sender, RoutedEventArgs e } #endregion - #region Playtime Buttons + #region Playtime private void ForceUpdatePlaytimeButton_Click(object sender, RoutedEventArgs e) { if (_cachedIsGameRunning) @@ -1867,20 +1867,20 @@ private void UpdatePlaytime(object sender, CollapsePlaytime playtime) } DateTime? last = playtime.LastPlayed?.ToLocalTime(); - string lastPlayed = string.Format(Lang._HomePage.GamePlaytime_ToolTipDisplay, last?.Day, + string lastPlayed = string.Format(Lang._HomePage.GamePlaytime_DateDisplay, last?.Day, last?.Month, last?.Year, last?.Hour, last?.Minute); StackPanel panel = new StackPanel() { Children = { - new TextBlock() { Text = "Last opened" }, + new TextBlock() { Text = Lang._HomePage.GamePlaytime_Stats_LastPlayed }, new TextBlock() { Text = lastPlayed, FontWeight = FontWeights.Bold, Margin = new Thickness(0, 0, 0, 5) }, - new TextBlock() { Text = "Daily Playtime" }, + new TextBlock() { Text = Lang._HomePage.GamePlaytime_Stats_Daily }, new TextBlock() { Text = FormatTimeStamp(playtime.DailyPlaytime), FontWeight = FontWeights.Bold, Margin = new Thickness(0, 0, 0, 5) }, - new TextBlock() { Text = "Weekly Playtime" }, + new TextBlock() { Text = Lang._HomePage.GamePlaytime_Stats_Weekly }, new TextBlock() { Text = FormatTimeStamp(playtime.WeeklyPlaytime), FontWeight = FontWeights.Bold, Margin = new Thickness(0, 0, 0, 5) }, - new TextBlock() { Text = "Monthly Playtime" }, + new TextBlock() { Text = Lang._HomePage.GamePlaytime_Stats_Monthly }, new TextBlock() { Text = FormatTimeStamp(playtime.MonthlyPlaytime), FontWeight = FontWeights.Bold, Margin = new Thickness(0, 0, 0, 5) } } }; diff --git a/Hi3Helper.Core/Lang/Locale/LangHomePage.cs b/Hi3Helper.Core/Lang/Locale/LangHomePage.cs index c53daa33a..22de83977 100644 --- a/Hi3Helper.Core/Lang/Locale/LangHomePage.cs +++ b/Hi3Helper.Core/Lang/Locale/LangHomePage.cs @@ -58,7 +58,11 @@ public sealed class LangHomePage public string GamePlaytime_Running_Info1 { get; set; } = LangFallback?._HomePage.GamePlaytime_Running_Info1; public string GamePlaytime_Running_Info2 { get; set; } = LangFallback?._HomePage.GamePlaytime_Running_Info2; public string GamePlaytime_Display { get; set; } = LangFallback?._HomePage.GamePlaytime_Display; - public string GamePlaytime_ToolTipDisplay { get; set; } = LangFallback?._HomePage.GamePlaytime_ToolTipDisplay; + public string GamePlaytime_DateDisplay { get; set; } = LangFallback?._HomePage.GamePlaytime_DateDisplay; + public string GamePlaytime_Stats_LastPlayed { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_LastPlayed; + public string GamePlaytime_Stats_Daily { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_Daily; + public string GamePlaytime_Stats_Weekly { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_Weekly; + public string GamePlaytime_Stats_Monthly { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_Monthly; public string PostPanel_Events { get; set; } = LangFallback?._HomePage.PostPanel_Events; public string PostPanel_Notices { get; set; } = LangFallback?._HomePage.PostPanel_Notices; public string PostPanel_Info { get; set; } = LangFallback?._HomePage.PostPanel_Info; diff --git a/Hi3Helper.Core/Lang/en_US.json b/Hi3Helper.Core/Lang/en_US.json index db4f6fc49..ae8722930 100644 --- a/Hi3Helper.Core/Lang/en_US.json +++ b/Hi3Helper.Core/Lang/en_US.json @@ -159,7 +159,11 @@ "GamePlaytime_Running_Info1": "An instance of this game is currently running, therefore playtime can't be edited.", "GamePlaytime_Running_Info2": "Please be aware that fully closing Collapse will stop playtime tracking (saving what was played until that point) & only sessions started using Collapse will be tracked.", "GamePlaytime_Display": "{0}h {1}m", - "GamePlaytime_ToolTipDisplay": "{0:00}/{1:00}/{2:0000} {3:00}:{4:00}", + "GamePlaytime_DateDisplay": "{0:00}/{1:00}/{2:0000} {3:00}:{4:00}", + "GamePlaytime_Stats_LastPlayed": "Last played at", + "GamePlaytime_Stats_Daily": "Daily Playtime", + "GamePlaytime_Stats_Weekly": "Weekly Playtime", + "GamePlaytime_Stats_Monthly": "Monthly Playtime", "PostPanel_Events": "Events", "PostPanel_Notices": "Notices", From a9235116c73476c6df6159cce02d4e83908805d3 Mon Sep 17 00:00:00 2001 From: Gabriel Lima <44784408+gablm@users.noreply.github.com> Date: Wed, 26 Jun 2024 16:56:02 +0100 Subject: [PATCH 004/110] Playtime - Misc fixes + Last Session Played --- .../GameManagement/GamePlaytime/Playtime.cs | 28 ++++--- .../RegistryClass/CollapsePlaytime.cs | 74 +++++++++++-------- .../Classes/Interfaces/IGamePlaytime.cs | 2 +- .../XAMLs/MainApp/Pages/HomePage.xaml.cs | 14 ++-- Hi3Helper.Core/Lang/Locale/LangHomePage.cs | 1 + Hi3Helper.Core/Lang/en_US.json | 3 +- 6 files changed, 69 insertions(+), 53 deletions(-) diff --git a/CollapseLauncher/Classes/GameManagement/GamePlaytime/Playtime.cs b/CollapseLauncher/Classes/GameManagement/GamePlaytime/Playtime.cs index b2cb5c9a0..6992b66ae 100644 --- a/CollapseLauncher/Classes/GameManagement/GamePlaytime/Playtime.cs +++ b/CollapseLauncher/Classes/GameManagement/GamePlaytime/Playtime.cs @@ -17,8 +17,10 @@ internal class Playtime : IGamePlaytime #region Properties public event EventHandler PlaytimeUpdated; #nullable enable + public CollapsePlaytime CollapsePlaytime => _playtime; + private RegistryKey? _registryRoot; - private CollapsePlaytime? _playtime; + private CollapsePlaytime _playtime; private IGameVersionCheck _gameVersionManager; private CancellationTokenSourceWrapper _token = new(); @@ -26,25 +28,20 @@ internal class Playtime : IGamePlaytime public Playtime(IGameVersionCheck GameVersionManager) { - string registryPath = Path.Combine($"Software\\{GameVersionManager.VendorTypeProp.VendorType}", GameVersionManager.GamePreset.InternalGameNameInConfig); + string registryPath = Path.Combine($"Software\\{GameVersionManager.VendorTypeProp.VendorType}", GameVersionManager.GamePreset.InternalGameNameInConfig!); _registryRoot = Registry.CurrentUser.OpenSubKey(registryPath, true); _registryRoot ??= Registry.CurrentUser.CreateSubKey(registryPath, true, RegistryOptions.None); _gameVersionManager = GameVersionManager; - _playtime = CollapsePlaytime.Load(_registryRoot); + _playtime = CollapsePlaytime.Load(_registryRoot, _gameVersionManager.GamePreset.HashID); } #nullable disable - public void ForceUpdate() - { - PlaytimeUpdated?.Invoke(this, _playtime); - } - public void Update(TimeSpan timeSpan) { - TimeSpan oldTimeSpan = _playtime.CurrentPlaytime; + TimeSpan oldTimeSpan = _playtime.TotalPlaytime; _playtime.Update(timeSpan); PlaytimeUpdated?.Invoke(this, _playtime); @@ -54,7 +51,7 @@ public void Update(TimeSpan timeSpan) public void Reset() { - TimeSpan oldTimeSpan = _playtime.CurrentPlaytime; + TimeSpan oldTimeSpan = _playtime.TotalPlaytime; _playtime.Reset(); PlaytimeUpdated?.Invoke(this, _playtime); @@ -65,10 +62,12 @@ public void Reset() public async void StartSession(Process proc) { DateTime begin = DateTime.Now; - TimeSpan initialTimeSpan = _playtime.CurrentPlaytime; + TimeSpan initialTimeSpan = _playtime.TotalPlaytime; - _playtime.LastPlayed = begin; + _playtime.LastPlayed = begin; + _playtime.LastSession = TimeSpan.Zero; _playtime.Save(); + PlaytimeUpdated?.Invoke(this, _playtime); #if DEBUG LogWriteLine($"{_gameVersionManager.GamePreset.ProfileName} - Started session at {begin.ToLongTimeString()}."); @@ -83,9 +82,8 @@ public async void StartSession(Process proc) elapsedSeconds += 60; DateTime now = DateTime.Now; - _playtime.Add(TimeSpan.FromMinutes(1)); + _playtime.AddMinute(); PlaytimeUpdated?.Invoke(this, _playtime); - #if DEBUG LogWriteLine($"{_gameVersionManager.GamePreset.ProfileName} - {elapsedSeconds}s elapsed. ({now.ToLongTimeString()})"); #endif @@ -121,7 +119,7 @@ private static string TimeSpanToString(TimeSpan timeSpan) public void Dispose() { _token.Cancel(); - _playtime?.Save(); + _playtime.Save(); _playtime = null; _registryRoot = null; diff --git a/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs b/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs index 0096aea62..3cd28dd70 100644 --- a/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs +++ b/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs @@ -1,6 +1,7 @@ using Hi3Helper; using Microsoft.Win32; using System; +using System.Collections.Generic; using System.Globalization; using System.Text; using static Hi3Helper.Logger; @@ -15,17 +16,24 @@ internal class CollapsePlaytime private const string _ValueName = "CollapseLauncher_Playtime"; private const string _OldLastPlayedValueName = "CollapseLauncher_LastPlayed"; - private static bool _IsDeserializing; - private RegistryKey _registryRoot; + private static Dictionary _IsDeserializing = []; + private RegistryKey _registryRoot; + private int _hashID; #endregion #region Properties /// /// Represents the total time a game was played.

- /// Default: TimeSpan.MinValue + /// Default: TimeSpan.Zero ///
- public TimeSpan CurrentPlaytime { get; set; } = TimeSpan.Zero; + public TimeSpan TotalPlaytime { get; set; } = TimeSpan.Zero; + + /// + /// Represents the total time the last/current session lasted.

+ /// Default: TimeSpan.Zero + ///
+ public TimeSpan LastSession { get; set; } = TimeSpan.Zero; /// /// Represents the daily playtime.
@@ -68,11 +76,11 @@ internal class CollapsePlaytime /// Reads from the Registry and deserializes the contents.
/// Converts RegistryKey values if they are of type DWORD (that is, if they were saved by the old implementation). ///
- public static CollapsePlaytime Load(RegistryKey root) + public static CollapsePlaytime Load(RegistryKey root, int hashID) { try { - _IsDeserializing = true; + _IsDeserializing[hashID] = true; if (root == null) throw new NullReferenceException($"Cannot load {_ValueName} RegistryKey is unexpectedly not initialized!"); object? value = root.GetValue(_ValueName, null); @@ -84,13 +92,14 @@ public static CollapsePlaytime Load(RegistryKey root) root.DeleteValue(_OldLastPlayedValueName, false); LogWriteLine($"Found old Playtime RegistryKey! Converting to the new format... (Playtime: {oldPlaytime} | Last Played: {lastPlayed})", writeToLog: true); - _IsDeserializing = false; + _IsDeserializing[hashID] = false; CollapsePlaytime playtime = new CollapsePlaytime() { - CurrentPlaytime = TimeSpan.FromSeconds(oldPlaytime), + TotalPlaytime = TimeSpan.FromSeconds(oldPlaytime), LastPlayed = lastPlayed != null ? BaseDate.AddSeconds((int)lastPlayed) : null, - _registryRoot = root + _registryRoot = root, + _hashID = hashID }; playtime.Save(); @@ -105,6 +114,7 @@ public static CollapsePlaytime Load(RegistryKey root) #endif CollapsePlaytime playtime = byteStr.Deserialize(UniversalPlaytimeJSONContext.Default) ?? new CollapsePlaytime(); playtime._registryRoot = root; + playtime._hashID = hashID; return playtime; } @@ -115,10 +125,10 @@ public static CollapsePlaytime Load(RegistryKey root) } finally { - _IsDeserializing = false; + _IsDeserializing[hashID] = false; } - return new CollapsePlaytime(); + return new CollapsePlaytime() { _hashID = hashID, _registryRoot = root }; } /// @@ -148,13 +158,14 @@ public void Save() /// public void Reset() { - CurrentPlaytime = TimeSpan.Zero; - DailyPlaytime = TimeSpan.Zero; - WeeklyPlaytime = TimeSpan.Zero; + TotalPlaytime = TimeSpan.Zero; + LastSession = TimeSpan.Zero; + DailyPlaytime = TimeSpan.Zero; + WeeklyPlaytime = TimeSpan.Zero; MonthlyPlaytime = TimeSpan.Zero; - ControlDate = DateTime.Today; + ControlDate = DateTime.Today; - if (!_IsDeserializing) Save(); + if (!_IsDeserializing[_hashID]) Save(); } /// @@ -166,43 +177,46 @@ public void Update(TimeSpan timeSpan, bool reset = true) { if (reset) { + LastSession = TimeSpan.Zero; DailyPlaytime = TimeSpan.Zero; WeeklyPlaytime = TimeSpan.Zero; MonthlyPlaytime = TimeSpan.Zero; ControlDate = DateTime.Today; } - CurrentPlaytime = timeSpan; + TotalPlaytime = timeSpan; - if (!_IsDeserializing) Save(); + if (!_IsDeserializing[_hashID]) Save(); } /// - /// Updates all fields, and checks if any should be reset.
+ /// Adds a minute to all fields, and checks if any should be reset.
/// After it saves to the Registry.

///
- /// New playtime value - public void Add(TimeSpan timeSpan) + public void AddMinute() { - CurrentPlaytime = CurrentPlaytime.Add(timeSpan); + TimeSpan minute = TimeSpan.FromMinutes(1); + DateTime today = DateTime.Today; - DateTime today = DateTime.Today; + TotalPlaytime = TotalPlaytime.Add(minute); + LastSession = LastSession.Add(minute); + if (today == DateTime.Today) { - DailyPlaytime = DailyPlaytime.Add(timeSpan); - WeeklyPlaytime = WeeklyPlaytime.Add(timeSpan); - MonthlyPlaytime = MonthlyPlaytime.Add(timeSpan); + DailyPlaytime = DailyPlaytime.Add(minute); + WeeklyPlaytime = WeeklyPlaytime.Add(minute); + MonthlyPlaytime = MonthlyPlaytime.Add(minute); } else { - DailyPlaytime = timeSpan; - WeeklyPlaytime = IsDifferentWeek(ControlDate, today) ? timeSpan : WeeklyPlaytime.Add(timeSpan); - MonthlyPlaytime = IsDifferentMonth(ControlDate, today) ? timeSpan : MonthlyPlaytime.Add(timeSpan); + DailyPlaytime = minute; + WeeklyPlaytime = IsDifferentWeek(ControlDate, today) ? minute : WeeklyPlaytime.Add(minute); + MonthlyPlaytime = IsDifferentMonth(ControlDate, today) ? minute : MonthlyPlaytime.Add(minute); ControlDate = today; } - if (!_IsDeserializing) Save(); + if (!_IsDeserializing[_hashID]) Save(); } #endregion diff --git a/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs b/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs index 291b31003..9b6a21529 100644 --- a/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs +++ b/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs @@ -7,9 +7,9 @@ namespace CollapseLauncher.Interfaces internal interface IGamePlaytime : IDisposable { event EventHandler PlaytimeUpdated; + CollapsePlaytime? CollapsePlaytime { get; } void Reset(); - void ForceUpdate(); void Update(TimeSpan timeSpan); void StartSession(Process proc); } diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs index d30974832..dc5ebb64a 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs @@ -184,7 +184,7 @@ private async void StartLoadedRoutine(object sender, RoutedEventArgs e) if (await CurrentGameProperty._GameInstall.TryShowFailedGameConversionState()) return; CurrentGameProperty._GamePlaytime.PlaytimeUpdated += UpdatePlaytime; - CurrentGameProperty._GamePlaytime.ForceUpdate(); + UpdatePlaytime(null, CurrentGameProperty._GamePlaytime.CollapsePlaytime); CheckRunningGameInstance(PageToken.Token); StartCarouselAutoScroll(CarouselToken.Token); @@ -1821,7 +1821,7 @@ private void ForceUpdatePlaytimeButton_Click(object sender, RoutedEventArgs e) if (_cachedIsGameRunning) return; - CurrentGameProperty._GamePlaytime.ForceUpdate(); + UpdatePlaytime(null, CurrentGameProperty._GamePlaytime.CollapsePlaytime); } private async void ChangePlaytimeButton_Click(object sender, RoutedEventArgs e) @@ -1856,9 +1856,9 @@ private void UpdatePlaytime(object sender, CollapsePlaytime playtime) { DispatcherQueue.TryEnqueue(() => { - PlaytimeMainBtn.Text = FormatTimeStamp(playtime.CurrentPlaytime); - HourPlaytimeTextBox.Text = (playtime.CurrentPlaytime.Days * 24 + playtime.CurrentPlaytime.Hours).ToString(); - MinutePlaytimeTextBox.Text = playtime.CurrentPlaytime.Minutes.ToString(); + PlaytimeMainBtn.Text = FormatTimeStamp(playtime.TotalPlaytime); + HourPlaytimeTextBox.Text = (playtime.TotalPlaytime.Days * 24 + playtime.TotalPlaytime.Hours).ToString(); + MinutePlaytimeTextBox.Text = playtime.TotalPlaytime.Minutes.ToString(); if (playtime.LastPlayed == null) { @@ -1876,12 +1876,14 @@ private void UpdatePlaytime(object sender, CollapsePlaytime playtime) { new TextBlock() { Text = Lang._HomePage.GamePlaytime_Stats_LastPlayed }, new TextBlock() { Text = lastPlayed, FontWeight = FontWeights.Bold, Margin = new Thickness(0, 0, 0, 5) }, + new TextBlock() { Text = Lang._HomePage.GamePlaytime_Stats_LastSession }, + new TextBlock() { Text = FormatTimeStamp(playtime.LastSession), FontWeight = FontWeights.Bold, Margin = new Thickness(0, 0, 0, 5) }, new TextBlock() { Text = Lang._HomePage.GamePlaytime_Stats_Daily }, new TextBlock() { Text = FormatTimeStamp(playtime.DailyPlaytime), FontWeight = FontWeights.Bold, Margin = new Thickness(0, 0, 0, 5) }, new TextBlock() { Text = Lang._HomePage.GamePlaytime_Stats_Weekly }, new TextBlock() { Text = FormatTimeStamp(playtime.WeeklyPlaytime), FontWeight = FontWeights.Bold, Margin = new Thickness(0, 0, 0, 5) }, new TextBlock() { Text = Lang._HomePage.GamePlaytime_Stats_Monthly }, - new TextBlock() { Text = FormatTimeStamp(playtime.MonthlyPlaytime), FontWeight = FontWeights.Bold, Margin = new Thickness(0, 0, 0, 5) } + new TextBlock() { Text = FormatTimeStamp(playtime.MonthlyPlaytime), FontWeight = FontWeights.Bold } } }; diff --git a/Hi3Helper.Core/Lang/Locale/LangHomePage.cs b/Hi3Helper.Core/Lang/Locale/LangHomePage.cs index 22de83977..056bf47e5 100644 --- a/Hi3Helper.Core/Lang/Locale/LangHomePage.cs +++ b/Hi3Helper.Core/Lang/Locale/LangHomePage.cs @@ -60,6 +60,7 @@ public sealed class LangHomePage public string GamePlaytime_Display { get; set; } = LangFallback?._HomePage.GamePlaytime_Display; public string GamePlaytime_DateDisplay { get; set; } = LangFallback?._HomePage.GamePlaytime_DateDisplay; public string GamePlaytime_Stats_LastPlayed { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_LastPlayed; + public string GamePlaytime_Stats_LastSession { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_LastSession; public string GamePlaytime_Stats_Daily { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_Daily; public string GamePlaytime_Stats_Weekly { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_Weekly; public string GamePlaytime_Stats_Monthly { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_Monthly; diff --git a/Hi3Helper.Core/Lang/en_US.json b/Hi3Helper.Core/Lang/en_US.json index ae8722930..60531ac5b 100644 --- a/Hi3Helper.Core/Lang/en_US.json +++ b/Hi3Helper.Core/Lang/en_US.json @@ -160,7 +160,8 @@ "GamePlaytime_Running_Info2": "Please be aware that fully closing Collapse will stop playtime tracking (saving what was played until that point) & only sessions started using Collapse will be tracked.", "GamePlaytime_Display": "{0}h {1}m", "GamePlaytime_DateDisplay": "{0:00}/{1:00}/{2:0000} {3:00}:{4:00}", - "GamePlaytime_Stats_LastPlayed": "Last played at", + "GamePlaytime_Stats_LastPlayed": "Last Played At", + "GamePlaytime_Stats_LastSession": "Last Session", "GamePlaytime_Stats_Daily": "Daily Playtime", "GamePlaytime_Stats_Weekly": "Weekly Playtime", "GamePlaytime_Stats_Monthly": "Monthly Playtime", From aa5e5c773e4ea705d56afa63e3f073cab67526c3 Mon Sep 17 00:00:00 2001 From: Gabriel Lima <44784408+gablm@users.noreply.github.com> Date: Wed, 26 Jun 2024 18:53:48 +0100 Subject: [PATCH 005/110] Playtime - Grid Stats Display --- .../Classes/Interfaces/IGamePlaytime.cs | 2 +- .../XAMLs/MainApp/Pages/HomePage.xaml | 7 +- .../XAMLs/MainApp/Pages/HomePage.xaml.cs | 243 +++++++++++------- Hi3Helper.Core/Lang/Locale/LangHomePage.cs | 1 + Hi3Helper.Core/Lang/en_US.json | 7 +- 5 files changed, 155 insertions(+), 105 deletions(-) diff --git a/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs b/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs index 9b6a21529..037e99e55 100644 --- a/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs +++ b/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs @@ -7,7 +7,7 @@ namespace CollapseLauncher.Interfaces internal interface IGamePlaytime : IDisposable { event EventHandler PlaytimeUpdated; - CollapsePlaytime? CollapsePlaytime { get; } + CollapsePlaytime CollapsePlaytime { get; } void Reset(); void Update(TimeSpan timeSpan); diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml index 31ab10c52..5dee37ed8 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml @@ -920,8 +920,7 @@ + MaxWidth="224"> @@ -938,8 +937,7 @@ BeforeTextChanging="NumberValidationTextBox" Text="" /> - + ImageFileInfo.Load(copyIconFileStream)); - var width = (int)(iconImageInfo.Frames[0].Width * scaleFactor); - var height = (int)(iconImageInfo.Frames[0].Height * scaleFactor); + var width = (int)(iconImageInfo.Frames[0].Width * scaleFactor); + var height = (int)(iconImageInfo.Frames[0].Height * scaleFactor); copyIconFileStream.Position = 0; // Reset the original icon stream position await ImageLoaderHelper.ResizeImageStream(copyIconFileStream, cachedIconFileStream, @@ -344,7 +345,7 @@ public async void StartCarouselAutoScroll(int delaySeconds = 5) } } - private void CarouselPointerExited(object sender = null, PointerRoutedEventArgs e = null) => CarouselRestartScroll(5); + private void CarouselPointerExited(object sender = null, PointerRoutedEventArgs e = null) => CarouselRestartScroll(5); private async void CarouselPointerEntered(object sender = null, PointerRoutedEventArgs e = null) => await CarouselStopScroll(); public async void CarouselRestartScroll(int delaySeconds = 5) @@ -373,10 +374,10 @@ private async void HideImageCarousel(bool hide) HideImageEventImg(hide); - Storyboard storyboard = new Storyboard(); + Storyboard storyboard = new Storyboard(); DoubleAnimation OpacityAnimation = new DoubleAnimation(); - OpacityAnimation.From = hide ? 1 : 0; - OpacityAnimation.To = hide ? 0 : 1; + OpacityAnimation.From = hide ? 1 : 0; + OpacityAnimation.To = hide ? 0 : 1; OpacityAnimation.Duration = new Duration(TimeSpan.FromSeconds(0.10)); Storyboard.SetTarget(OpacityAnimation, ImageCarouselAndPostPanel); @@ -397,8 +398,8 @@ private void FadeInSocMedButton(object sender, PointerRoutedEventArgs e) Button btn = (Button)sender; btn.Translation = Shadow16; - Grid iconGrid = btn.FindDescendant(); - Image iconFirst = iconGrid!.FindDescendant("Icon") as Image; + Grid iconGrid = btn.FindDescendant(); + Image iconFirst = iconGrid!.FindDescendant("Icon") as Image; Image iconSecond = iconGrid!.FindDescendant("IconHover") as Image; TimeSpan dur = TimeSpan.FromSeconds(0.25f); @@ -418,8 +419,8 @@ private void FadeOutSocMedButton(object sender, PointerRoutedEventArgs e) flyout!.Hide(); } - Grid iconGrid = btn.FindDescendant(); - Image iconFirst = iconGrid!.FindDescendant("Icon") as Image; + Grid iconGrid = btn.FindDescendant(); + Image iconFirst = iconGrid!.FindDescendant("Icon") as Image; Image iconSecond = iconGrid!.FindDescendant("IconHover") as Image; TimeSpan dur = TimeSpan.FromSeconds(0.25f); @@ -858,12 +859,12 @@ private void RaiseBackgroundInstallationStatus(GameInstallStateEnum GameInstalla private async void CheckRunningGameInstance(CancellationToken Token) { - TextBlock StartGameBtnText = (StartGameBtn.Content as Grid)!.Children.OfType().FirstOrDefault(); - FontIcon StartGameBtnIcon = (StartGameBtn.Content as Grid)!.Children.OfType().FirstOrDefault(); - Grid StartGameBtnAnimatedIconGrid = (StartGameBtn.Content as Grid)!.Children.OfType().FirstOrDefault(); + TextBlock StartGameBtnText = (StartGameBtn.Content as Grid)!.Children.OfType().FirstOrDefault(); + FontIcon StartGameBtnIcon = (StartGameBtn.Content as Grid)!.Children.OfType().FirstOrDefault(); + Grid StartGameBtnAnimatedIconGrid = (StartGameBtn.Content as Grid)!.Children.OfType().FirstOrDefault(); // AnimatedVisualPlayer StartGameBtnAnimatedIcon = StartGameBtnAnimatedIconGrid!.Children.OfType().FirstOrDefault(); - string StartGameBtnIconGlyph = StartGameBtnIcon!.Glyph; - string StartGameBtnRunningIconGlyph = ""; + string StartGameBtnIconGlyph = StartGameBtnIcon!.Glyph; + string StartGameBtnRunningIconGlyph = ""; StartGameBtnIcon.EnableSingleImplicitAnimation(VisualPropertyType.Opacity); StartGameBtnAnimatedIconGrid.EnableSingleImplicitAnimation(VisualPropertyType.Opacity); @@ -876,52 +877,52 @@ private async void CheckRunningGameInstance(CancellationToken Token) { _cachedIsGameRunning = true; - StartGameBtn.IsEnabled = false; - StartGameBtnText!.Text = Lang._HomePage.StartBtnRunning; - StartGameBtnIcon.Glyph = StartGameBtnRunningIconGlyph; - StartGameBtnAnimatedIconGrid.Opacity = 0; - StartGameBtnIcon.Opacity = 1; + StartGameBtn.IsEnabled = false; + StartGameBtnText!.Text = Lang._HomePage.StartBtnRunning; + StartGameBtnIcon.Glyph = StartGameBtnRunningIconGlyph; + StartGameBtnAnimatedIconGrid.Opacity = 0; + StartGameBtnIcon.Opacity = 1; //GameStartupSetting.IsEnabled = false; - RepairGameButton.IsEnabled = false; - UninstallGameButton.IsEnabled = false; - ConvertVersionButton.IsEnabled = false; - CustomArgsTextBox.IsEnabled = false; + RepairGameButton.IsEnabled = false; + UninstallGameButton.IsEnabled = false; + ConvertVersionButton.IsEnabled = false; + CustomArgsTextBox.IsEnabled = false; MoveGameLocationButton.IsEnabled = false; - StopGameButton.IsEnabled = true; + StopGameButton.IsEnabled = true; - PlaytimeIdleStack.Visibility = Visibility.Collapsed; + PlaytimeIdleStack.Visibility = Visibility.Collapsed; PlaytimeRunningStack.Visibility = Visibility.Visible; - #if !DISABLEDISCORD +#if !DISABLEDISCORD AppDiscordPresence?.SetActivity(ActivityType.Play); - #endif +#endif await Task.Delay(RefreshRate, Token); } _cachedIsGameRunning = false; - StartGameBtn.IsEnabled = true; - StartGameBtnText!.Text = Lang._HomePage.StartBtn; - StartGameBtnIcon.Glyph = StartGameBtnIconGlyph; - StartGameBtnAnimatedIconGrid.Opacity = 1; - StartGameBtnIcon.Opacity = 0; + StartGameBtn.IsEnabled = true; + StartGameBtnText!.Text = Lang._HomePage.StartBtn; + StartGameBtnIcon.Glyph = StartGameBtnIconGlyph; + StartGameBtnAnimatedIconGrid.Opacity = 1; + StartGameBtnIcon.Opacity = 0; - GameStartupSetting.IsEnabled = true; - RepairGameButton.IsEnabled = true; + GameStartupSetting.IsEnabled = true; + RepairGameButton.IsEnabled = true; MoveGameLocationButton.IsEnabled = true; - UninstallGameButton.IsEnabled = true; - ConvertVersionButton.IsEnabled = true; - CustomArgsTextBox.IsEnabled = true; - StopGameButton.IsEnabled = false; + UninstallGameButton.IsEnabled = true; + ConvertVersionButton.IsEnabled = true; + CustomArgsTextBox.IsEnabled = true; + StopGameButton.IsEnabled = false; - PlaytimeIdleStack.Visibility = Visibility.Visible; + PlaytimeIdleStack.Visibility = Visibility.Visible; PlaytimeRunningStack.Visibility = Visibility.Collapsed; - #if !DISABLEDISCORD +#if !DISABLEDISCORD AppDiscordPresence?.SetActivity(ActivityType.Idle); - #endif +#endif await Task.Delay(RefreshRate, Token); } @@ -931,7 +932,7 @@ private async void CheckRunningGameInstance(CancellationToken Token) // Ignore } catch (Exception e) - { + { LogWriteLine($"Error when checking if game is running!\r\n{e}", LogType.Error, true); } } @@ -1497,7 +1498,7 @@ internal async void StartResizableWindowPayload(string executableName, IGameSett #region Game Launch Argument Builder bool RequireWindowExclusivePayload; - + internal string GetLaunchArguments(IGameSettingsUniversal _Settings) { StringBuilder parameter = new StringBuilder(); @@ -1552,12 +1553,12 @@ internal string GetLaunchArguments(IGameSettingsUniversal _Settings) parameter.Append("-window-mode exclusive -screen-fullscreen 1 "); RequireWindowExclusivePayload = true; } - + // Enable mobile mode if (_Settings.SettingsCollapseMisc.LaunchMobileMode) { - const string regLoc = GameSettings.StarRail.Model._ValueName; - var regRoot = GameSettings.Base.SettingsBase.RegistryRoot; + const string regLoc = GameSettings.StarRail.Model._ValueName; + var regRoot = GameSettings.Base.SettingsBase.RegistryRoot; if (regRoot != null || !string.IsNullOrEmpty(regLoc)) { @@ -1603,7 +1604,7 @@ internal string GetLaunchArguments(IGameSettingsUniversal _Settings) RequireWindowExclusivePayload = true; LogWriteLine($"Exclusive mode is enabled in Genshin Impact, stability may suffer!\r\nTry not to Alt+Tab when game is on its loading screen :)", LogType.Warning, true); } - + // Enable mobile mode if (_Settings.SettingsCollapseMisc.LaunchMobileMode) parameter.Append("use_mobile_platform -is_cloud 1 -platform_type CLOUD_THIRD_PARTY_MOBILE "); @@ -1689,9 +1690,9 @@ public async void TryInstallMediaPack() { StartInfo = new ProcessStartInfo { - FileName = Path.Combine(AppFolder, "Misc", "InstallMediaPack.cmd"), + FileName = Path.Combine(AppFolder, "Misc", "InstallMediaPack.cmd"), UseShellExecute = true, - Verb = "runas" + Verb = "runas" } }; @@ -1738,23 +1739,23 @@ public async void ReadOutputLog() Directory.CreateDirectory(Path.GetDirectoryName(logPath)!); await using (FileStream fs = new FileStream(logPath, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite)) - using (StreamReader reader = new StreamReader(fs)) + using (StreamReader reader = new StreamReader(fs)) + { + while (true) { - while (true) + while (!reader.EndOfStream) { - while (!reader.EndOfStream) + var line = await reader.ReadLineAsync(WatchOutputLog.Token); + if (RequireWindowExclusivePayload && line == "MoleMole.MonoGameEntry:Awake()") { - var line = await reader.ReadLineAsync(WatchOutputLog.Token); - if (RequireWindowExclusivePayload && line == "MoleMole.MonoGameEntry:Awake()") - { - StartExclusiveWindowPayload(); - RequireWindowExclusivePayload = false; - } - LogWriteLine(line!, LogType.Game, GetAppConfigValue("IncludeGameLogs").ToBool()); + StartExclusiveWindowPayload(); + RequireWindowExclusivePayload = false; } - await Task.Delay(100, WatchOutputLog.Token); + LogWriteLine(line!, LogType.Game, GetAppConfigValue("IncludeGameLogs").ToBool()); } + await Task.Delay(100, WatchOutputLog.Token); } + } } catch (OperationCanceledException) { } catch (Exception ex) @@ -1876,12 +1877,12 @@ private async void ChangePlaytimeButton_Click(object sender, RoutedEventArgs e) { if (await Dialog_ChangePlaytime(this) != ContentDialogResult.Primary) return; - int Mins = int.Parse("0" + MinutePlaytimeTextBox.Text); - int Hours = int.Parse("0" + HourPlaytimeTextBox.Text); - - TimeSpan time = TimeSpan.FromMinutes(Hours * 60 + Mins); + int Mins = int.Parse("0" + MinutePlaytimeTextBox.Text); + int Hours = int.Parse("0" + HourPlaytimeTextBox.Text); + + TimeSpan time = TimeSpan.FromMinutes(Hours * 60 + Mins); if (time.Hours > 99999) time = new TimeSpan(99999, 59, 0); - + CurrentGameProperty._GamePlaytime.Update(time); PlaytimeFlyout.Hide(); } @@ -1922,16 +1923,44 @@ private void UpdatePlaytime(object sender, CollapsePlaytime playtime) { Children = { - new TextBlock() { Text = Lang._HomePage.GamePlaytime_Stats_LastPlayed }, - new TextBlock() { Text = lastPlayed, FontWeight = FontWeights.Bold, Margin = new Thickness(0, 0, 0, 5) }, - new TextBlock() { Text = Lang._HomePage.GamePlaytime_Stats_LastSession }, - new TextBlock() { Text = FormatTimeStamp(playtime.LastSession), FontWeight = FontWeights.Bold, Margin = new Thickness(0, 0, 0, 5) }, - new TextBlock() { Text = Lang._HomePage.GamePlaytime_Stats_Daily }, - new TextBlock() { Text = FormatTimeStamp(playtime.DailyPlaytime), FontWeight = FontWeights.Bold, Margin = new Thickness(0, 0, 0, 5) }, - new TextBlock() { Text = Lang._HomePage.GamePlaytime_Stats_Weekly }, - new TextBlock() { Text = FormatTimeStamp(playtime.WeeklyPlaytime), FontWeight = FontWeights.Bold, Margin = new Thickness(0, 0, 0, 5) }, - new TextBlock() { Text = Lang._HomePage.GamePlaytime_Stats_Monthly }, - new TextBlock() { Text = FormatTimeStamp(playtime.MonthlyPlaytime), FontWeight = FontWeights.Bold } + new Grid() + { + ColumnDefinitions = + { + new ColumnDefinition(), + new ColumnDefinition() { Width = GridLength.Auto }, + new ColumnDefinition() + }, + ColumnSpacing = 20, + Children = + { + TimeSpanPanel(Lang._HomePage.GamePlaytime_Stats_Daily, playtime.DailyPlaytime, HorizontalAlignment.Left, gridCol:0), + TimeSpanPanel(Lang._HomePage.GamePlaytime_Stats_Weekly, playtime.WeeklyPlaytime, gridCol:1), + TimeSpanPanel(Lang._HomePage.GamePlaytime_Stats_Monthly, playtime.MonthlyPlaytime, HorizontalAlignment.Right, gridCol:2) + } + }, + new Grid() + { + ColumnDefinitions = + { + new ColumnDefinition() { Width = GridLength.Auto }, + new ColumnDefinition() + }, + ColumnSpacing = 20, + Children = + { + new StackPanel() + { + HorizontalAlignment = HorizontalAlignment.Left, + Children = + { + new TextBlock() { Text = Lang._HomePage.GamePlaytime_Stats_LastPlayed }, + new TextBlock() { Text = lastPlayed, FontWeight = FontWeights.Bold } + } + }, + TimeSpanPanel(Lang._HomePage.GamePlaytime_Stats_LastSession, playtime.LastSession, HorizontalAlignment.Right, isLast:true, gridCol:1) + } + } } }; @@ -1939,6 +1968,28 @@ private void UpdatePlaytime(object sender, CollapsePlaytime playtime) }); return; + static StackPanel TimeSpanPanel(string label, TimeSpan time, HorizontalAlignment alignment = HorizontalAlignment.Center, bool isLast = false, int gridCol = 0) + { + StackPanel panel = new StackPanel() + { + HorizontalAlignment = alignment, + Orientation = Orientation.Vertical, + Children = + { + new TextBlock() { Text = label }, + new TextBlock() + { + Text = FormatTimeStamp(time), FontWeight = FontWeights.Bold, + Margin = new Thickness(0, 0, 0, isLast ? 0 : 5), + HorizontalAlignment = alignment + } + } + }; + Grid.SetColumn(panel, gridCol); + + return panel; + } + static string FormatTimeStamp(TimeSpan time) => string.Format(Lang._HomePage.GamePlaytime_Display, time.Days * 24 + time.Hours, time.Minutes); } #endregion @@ -2091,7 +2142,7 @@ private async void CollapsePrioControl(Process proc) using (Process collapseProcess = Process.GetCurrentProcess()) { collapseProcess.PriorityBoostEnabled = false; - collapseProcess.PriorityClass = ProcessPriorityClass.BelowNormal; + collapseProcess.PriorityClass = ProcessPriorityClass.BelowNormal; LogWriteLine($"Collapse process [PID {collapseProcess.Id}] priority is set to Below Normal, " + $"PriorityBoost is off, carousel is temporarily stopped", LogType.Default, true); } @@ -2102,7 +2153,7 @@ private async void CollapsePrioControl(Process proc) using (Process collapseProcess = Process.GetCurrentProcess()) { collapseProcess.PriorityBoostEnabled = true; - collapseProcess.PriorityClass = ProcessPriorityClass.Normal; + collapseProcess.PriorityClass = ProcessPriorityClass.Normal; LogWriteLine($"Collapse process [PID {collapseProcess.Id}] priority is set to Normal, " + $"PriorityBoost is on, carousel is started", LogType.Default, true); } @@ -2133,7 +2184,7 @@ private void GenshinHDREnforcer() private async void GameBoost_Invoke(GamePresetProperty gameProp) { - #nullable enable +#nullable enable // Init new target process Process? toTargetProc = null; try @@ -2169,7 +2220,7 @@ private async void GameBoost_Invoke(GamePresetProperty gameProp) LogWriteLine($"[HomePage::GameBoost_Invoke] There has been error while boosting game priority to Above Normal!\r\n" + $"\tTarget Process : {toTargetProc?.ProcessName} [{toTargetProc?.Id}]\r\n{ex}", LogType.Error, true); } - #nullable restore +#nullable restore } #endregion @@ -2361,7 +2412,7 @@ private void ApplyShadowToImageElement(object sender, RoutedEventArgs e) private bool IsPointerInsideSidePanel; private bool IsSidePanelCurrentlyScaledOut; - + private async void SidePanelScaleOutHoveredPointerEntered(object sender, PointerRoutedEventArgs e) { IsPointerInsideSidePanel = true; @@ -2371,9 +2422,9 @@ private async void SidePanelScaleOutHoveredPointerEntered(object sender, Pointer if (IsSidePanelCurrentlyScaledOut) return; if (!IsPointerInsideSidePanel) return; - var toScale = WindowSize.WindowSize.CurrentWindowSize.PostEventPanelScaleFactor; + var toScale = WindowSize.WindowSize.CurrentWindowSize.PostEventPanelScaleFactor; var storyboard = new Storyboard(); - var transform = (CompositeTransform)elementPanel.RenderTransform; + var transform = (CompositeTransform)elementPanel.RenderTransform; transform.CenterY = elementPanel.ActualHeight + 8; var cubicEaseOut = new CubicEase() { @@ -2382,9 +2433,9 @@ private async void SidePanelScaleOutHoveredPointerEntered(object sender, Pointer var scaleXAnim = new DoubleAnimation { - From = transform.ScaleX, - To = toScale, - Duration = new Duration(TimeSpan.FromSeconds(0.2)), + From = transform.ScaleX, + To = toScale, + Duration = new Duration(TimeSpan.FromSeconds(0.2)), EasingFunction = cubicEaseOut }; Storyboard.SetTarget(scaleXAnim, transform); @@ -2393,9 +2444,9 @@ private async void SidePanelScaleOutHoveredPointerEntered(object sender, Pointer var scaleYAnim = new DoubleAnimation { - From = transform.ScaleY, - To = toScale, - Duration = new Duration(TimeSpan.FromSeconds(0.2)), + From = transform.ScaleY, + To = toScale, + Duration = new Duration(TimeSpan.FromSeconds(0.2)), EasingFunction = cubicEaseOut }; Storyboard.SetTarget(scaleYAnim, transform); @@ -2421,7 +2472,7 @@ private async void SidePanelScaleInHoveredPointerExited(object sender, PointerRo HideImageEventImg(false); var storyboard = new Storyboard(); - var transform = (CompositeTransform)elementPanel.RenderTransform; + var transform = (CompositeTransform)elementPanel.RenderTransform; transform.CenterY = elementPanel.ActualHeight + 8; var cubicEaseOut = new CubicEase() { @@ -2430,9 +2481,9 @@ private async void SidePanelScaleInHoveredPointerExited(object sender, PointerRo var scaleXAnim = new DoubleAnimation { - From = transform.ScaleX, - To = 1, - Duration = new Duration(TimeSpan.FromSeconds(0.25)), + From = transform.ScaleX, + To = 1, + Duration = new Duration(TimeSpan.FromSeconds(0.25)), EasingFunction = cubicEaseOut }; Storyboard.SetTarget(scaleXAnim, transform); @@ -2441,9 +2492,9 @@ private async void SidePanelScaleInHoveredPointerExited(object sender, PointerRo var scaleYAnim = new DoubleAnimation { - From = transform.ScaleY, - To = 1, - Duration = new Duration(TimeSpan.FromSeconds(0.25)), + From = transform.ScaleY, + To = 1, + Duration = new Duration(TimeSpan.FromSeconds(0.25)), EasingFunction = cubicEaseOut }; Storyboard.SetTarget(scaleYAnim, transform); diff --git a/Hi3Helper.Core/Lang/Locale/LangHomePage.cs b/Hi3Helper.Core/Lang/Locale/LangHomePage.cs index 056bf47e5..b965a62dd 100644 --- a/Hi3Helper.Core/Lang/Locale/LangHomePage.cs +++ b/Hi3Helper.Core/Lang/Locale/LangHomePage.cs @@ -59,6 +59,7 @@ public sealed class LangHomePage public string GamePlaytime_Running_Info2 { get; set; } = LangFallback?._HomePage.GamePlaytime_Running_Info2; public string GamePlaytime_Display { get; set; } = LangFallback?._HomePage.GamePlaytime_Display; public string GamePlaytime_DateDisplay { get; set; } = LangFallback?._HomePage.GamePlaytime_DateDisplay; + public string GamePlaytime_Stats_Title { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_Title; public string GamePlaytime_Stats_LastPlayed { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_LastPlayed; public string GamePlaytime_Stats_LastSession { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_LastSession; public string GamePlaytime_Stats_Daily { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_Daily; diff --git a/Hi3Helper.Core/Lang/en_US.json b/Hi3Helper.Core/Lang/en_US.json index b6c1e9134..4404a1236 100644 --- a/Hi3Helper.Core/Lang/en_US.json +++ b/Hi3Helper.Core/Lang/en_US.json @@ -160,11 +160,12 @@ "GamePlaytime_Running_Info2": "Please be aware that fully closing Collapse will stop playtime tracking (saving what was played until that point) & only sessions started using Collapse will be tracked.", "GamePlaytime_Display": "{0}h {1}m", "GamePlaytime_DateDisplay": "{0:00}/{1:00}/{2:0000} {3:00}:{4:00}", + "GamePlaytime_Stats_Title": "Playtime Statistics", "GamePlaytime_Stats_LastPlayed": "Last Played At", "GamePlaytime_Stats_LastSession": "Last Session", - "GamePlaytime_Stats_Daily": "Daily Playtime", - "GamePlaytime_Stats_Weekly": "Weekly Playtime", - "GamePlaytime_Stats_Monthly": "Monthly Playtime", + "GamePlaytime_Stats_Daily": "Daily", + "GamePlaytime_Stats_Weekly": "Weekly", + "GamePlaytime_Stats_Monthly": "Monthly", "PostPanel_Events": "Events", "PostPanel_Notices": "Notices", From 7741bf599cdfd8468340658c14b4ed8c94af5a5d Mon Sep 17 00:00:00 2001 From: Gabriel Lima <44784408+gablm@users.noreply.github.com> Date: Wed, 26 Jun 2024 19:35:15 +0100 Subject: [PATCH 006/110] Playtime - Change stats ui --- .../XAMLs/MainApp/Pages/HomePage.xaml.cs | 106 +++++++++--------- 1 file changed, 50 insertions(+), 56 deletions(-) diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs index 395ffbdf6..4bfb8db33 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs @@ -56,6 +56,7 @@ using Size = System.Drawing.Size; using UIElementExtensions = CollapseLauncher.Extension.UIElementExtensions; using CollapseLauncher.InstallManager.Base; +using Microsoft.UI.Xaml.Documents; using Orientation = Microsoft.UI.Xaml.Controls.Orientation; namespace CollapseLauncher.Pages @@ -1919,48 +1920,44 @@ private void UpdatePlaytime(object sender, CollapsePlaytime playtime) string lastPlayed = string.Format(Lang._HomePage.GamePlaytime_DateDisplay, last?.Day, last?.Month, last?.Year, last?.Hour, last?.Minute); + Grid grid = new Grid() + { + ColumnDefinitions = + { + new ColumnDefinition(), + new ColumnDefinition() + }, + RowDefinitions = + { + new RowDefinition(), + new RowDefinition(), + new RowDefinition(), + new RowDefinition() + } + }; + + TimeSpanPanel(grid, Lang._HomePage.GamePlaytime_Stats_Daily, playtime.DailyPlaytime); + TimeSpanPanel(grid, Lang._HomePage.GamePlaytime_Stats_Weekly, playtime.WeeklyPlaytime, 1); + TimeSpanPanel(grid, Lang._HomePage.GamePlaytime_Stats_Monthly, playtime.MonthlyPlaytime, 2); + TimeSpanPanel(grid, Lang._HomePage.GamePlaytime_Stats_LastSession, playtime.LastSession, 3); + + TextBlock lastPlayedBlock = new TextBlock() + { + Margin = new Thickness(0, 0, 0, 5), + Inlines = + { + new Run() { Text = "(Started at " }, + new Run() { Text = lastPlayed, FontWeight = FontWeights.Bold }, + new Run() { Text = ")" } + } + }; + StackPanel panel = new StackPanel() { Children = { - new Grid() - { - ColumnDefinitions = - { - new ColumnDefinition(), - new ColumnDefinition() { Width = GridLength.Auto }, - new ColumnDefinition() - }, - ColumnSpacing = 20, - Children = - { - TimeSpanPanel(Lang._HomePage.GamePlaytime_Stats_Daily, playtime.DailyPlaytime, HorizontalAlignment.Left, gridCol:0), - TimeSpanPanel(Lang._HomePage.GamePlaytime_Stats_Weekly, playtime.WeeklyPlaytime, gridCol:1), - TimeSpanPanel(Lang._HomePage.GamePlaytime_Stats_Monthly, playtime.MonthlyPlaytime, HorizontalAlignment.Right, gridCol:2) - } - }, - new Grid() - { - ColumnDefinitions = - { - new ColumnDefinition() { Width = GridLength.Auto }, - new ColumnDefinition() - }, - ColumnSpacing = 20, - Children = - { - new StackPanel() - { - HorizontalAlignment = HorizontalAlignment.Left, - Children = - { - new TextBlock() { Text = Lang._HomePage.GamePlaytime_Stats_LastPlayed }, - new TextBlock() { Text = lastPlayed, FontWeight = FontWeights.Bold } - } - }, - TimeSpanPanel(Lang._HomePage.GamePlaytime_Stats_LastSession, playtime.LastSession, HorizontalAlignment.Right, isLast:true, gridCol:1) - } - } + grid, + lastPlayedBlock } }; @@ -1968,26 +1965,23 @@ private void UpdatePlaytime(object sender, CollapsePlaytime playtime) }); return; - static StackPanel TimeSpanPanel(string label, TimeSpan time, HorizontalAlignment alignment = HorizontalAlignment.Center, bool isLast = false, int gridCol = 0) + static void TimeSpanPanel(Grid grid, string label, TimeSpan time, int row = 0, bool isLast = false) { - StackPanel panel = new StackPanel() - { - HorizontalAlignment = alignment, - Orientation = Orientation.Vertical, - Children = - { - new TextBlock() { Text = label }, - new TextBlock() - { - Text = FormatTimeStamp(time), FontWeight = FontWeights.Bold, - Margin = new Thickness(0, 0, 0, isLast ? 0 : 5), - HorizontalAlignment = alignment - } - } - }; - Grid.SetColumn(panel, gridCol); + TextBlock labelBlock = new TextBlock() { Text = label }; + Grid.SetColumn(labelBlock, 0); + Grid.SetRow(labelBlock, row); + + TextBlock timeBlock = new TextBlock() + { + Text = FormatTimeStamp(time), FontWeight = FontWeights.Bold, + Margin = new Thickness(0, 0, 0, isLast ? 0 : 5), + HorizontalAlignment = HorizontalAlignment.Right + }; + Grid.SetColumn(timeBlock, 2); + Grid.SetRow(timeBlock, row); - return panel; + grid.Children.Add(labelBlock); + grid.Children.Add(timeBlock); } static string FormatTimeStamp(TimeSpan time) => string.Format(Lang._HomePage.GamePlaytime_Display, time.Days * 24 + time.Hours, time.Minutes); From e4d0a6a1e2a6a49ba39eddaa59c145d65e9acbbf Mon Sep 17 00:00:00 2001 From: Gabriel Lima <44784408+gablm@users.noreply.github.com> Date: Sun, 30 Jun 2024 16:40:53 +0100 Subject: [PATCH 007/110] Playtime - Align last played date to the right --- .../XAMLs/MainApp/Pages/HomePage.xaml | 2 +- .../XAMLs/MainApp/Pages/HomePage.xaml.cs | 19 +++++++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml index 6ead46a1d..66d7edebb 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml @@ -922,7 +922,7 @@ VerticalAlignment="Center" FontWeight="Medium" Text="-" /> - Date: Mon, 8 Jul 2024 19:30:47 +0100 Subject: [PATCH 008/110] Playtime - Move Stats UI into Xaml --- .../RegistryClass/CollapsePlaytime.cs | 1 + .../XAMLs/MainApp/Pages/HomePage.xaml | 94 ++++++++++++++++++- .../XAMLs/MainApp/Pages/HomePage.xaml.cs | 70 ++------------ Hi3Helper.Core/Lang/en_US.json | 6 +- 4 files changed, 103 insertions(+), 68 deletions(-) diff --git a/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs b/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs index 3cd28dd70..7ca87e463 100644 --- a/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs +++ b/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs @@ -164,6 +164,7 @@ public void Reset() WeeklyPlaytime = TimeSpan.Zero; MonthlyPlaytime = TimeSpan.Zero; ControlDate = DateTime.Today; + LastPlayed = null; if (!_IsDeserializing[_hashID]) Save(); } diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml index 66d7edebb..a08a48c7f 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml @@ -928,7 +928,99 @@ FontFamily="{ThemeResource FontAwesomeSolid}" FontSize="18" Opacity="0.25" - Text="" /> + Text=""> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs index 82363df7f..282b7f286 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs @@ -1942,7 +1942,7 @@ private void UpdatePlaytime(object sender, CollapsePlaytime playtime) HourPlaytimeTextBox.Text = (playtime.TotalPlaytime.Days * 24 + playtime.TotalPlaytime.Hours).ToString(); MinutePlaytimeTextBox.Text = playtime.TotalPlaytime.Minutes.ToString(); - string lastPlayed = "-"; + string lastPlayed = "Never Played"; if (playtime.LastPlayed != null) { DateTime? last = playtime.LastPlayed?.ToLocalTime(); @@ -1950,72 +1950,14 @@ private void UpdatePlaytime(object sender, CollapsePlaytime playtime) last?.Month, last?.Year, last?.Hour, last?.Minute); } - Grid grid = new Grid() - { - ColumnDefinitions = - { - new ColumnDefinition(), - new ColumnDefinition() - }, - ColumnSpacing = 8, - RowDefinitions = - { - new RowDefinition(), - new RowDefinition(), - new RowDefinition(), - new RowDefinition() - } - }; - - TimeSpanPanel(grid, Lang._HomePage.GamePlaytime_Stats_Daily, playtime.DailyPlaytime); - TimeSpanPanel(grid, Lang._HomePage.GamePlaytime_Stats_Weekly, playtime.WeeklyPlaytime, 1); - TimeSpanPanel(grid, Lang._HomePage.GamePlaytime_Stats_Monthly, playtime.MonthlyPlaytime, 2); - TimeSpanPanel(grid, Lang._HomePage.GamePlaytime_Stats_LastSession, playtime.LastSession, 3); - - TextBlock lastPlayedBlock = new TextBlock() - { - Margin = new Thickness(0, 0, 0, 5), - Inlines = - { - new Run() { Text = "(Started at " }, - new Run() { Text = lastPlayed, FontWeight = FontWeights.Bold }, - new Run() { Text = ")" } - }, - HorizontalAlignment = HorizontalAlignment.Right - }; - - StackPanel panel = new StackPanel() - { - Children = - { - grid, - lastPlayedBlock - } - }; - - ToolTipService.SetToolTip(PlaytimeBtn, panel); + PlaytimeStatsDaily.Text = FormatTimeStamp(playtime.DailyPlaytime); + PlaytimeStatsWeekly.Text = FormatTimeStamp(playtime.WeeklyPlaytime); + PlaytimeStatsMonthly.Text = FormatTimeStamp(playtime.MonthlyPlaytime); + PlaytimeStatsLastSession.Text = FormatTimeStamp(playtime.LastSession); + PlaytimeStatsLastPlayed.Text = lastPlayed; }); return; - static void TimeSpanPanel(Grid grid, string label, TimeSpan time, int row = 0, bool isLast = false) - { - TextBlock labelBlock = new TextBlock() { Text = label }; - Grid.SetColumn(labelBlock, 0); - Grid.SetRow(labelBlock, row); - - TextBlock timeBlock = new TextBlock() - { - Text = FormatTimeStamp(time), FontWeight = FontWeights.Bold, - Margin = new Thickness(0, 0, 0, isLast ? 0 : 5), - HorizontalAlignment = HorizontalAlignment.Right - }; - Grid.SetColumn(timeBlock, 2); - Grid.SetRow(timeBlock, row); - - grid.Children.Add(labelBlock); - grid.Children.Add(timeBlock); - } - static string FormatTimeStamp(TimeSpan time) => string.Format(Lang._HomePage.GamePlaytime_Display, time.Days * 24 + time.Hours, time.Minutes); } #nullable restore diff --git a/Hi3Helper.Core/Lang/en_US.json b/Hi3Helper.Core/Lang/en_US.json index 6a1ed7da8..e8200dd15 100644 --- a/Hi3Helper.Core/Lang/en_US.json +++ b/Hi3Helper.Core/Lang/en_US.json @@ -166,9 +166,9 @@ "GamePlaytime_Stats_Title": "Playtime Statistics", "GamePlaytime_Stats_LastPlayed": "Last Played At", "GamePlaytime_Stats_LastSession": "Last Session", - "GamePlaytime_Stats_Daily": "Daily", - "GamePlaytime_Stats_Weekly": "Weekly", - "GamePlaytime_Stats_Monthly": "Monthly", + "GamePlaytime_Stats_Daily": "Today", + "GamePlaytime_Stats_Weekly": "Week", + "GamePlaytime_Stats_Monthly": "Month", "PostPanel_Events": "Events", "PostPanel_Notices": "Notices", From c91a65e79241ebb148880760c4d057afed59ffa5 Mon Sep 17 00:00:00 2001 From: Gabriel Lima <44784408+gablm@users.noreply.github.com> Date: Thu, 22 Aug 2024 18:42:31 +0100 Subject: [PATCH 009/110] Playtime - Ensure compatibility with other versions --- .../RegistryClass/CollapsePlaytime.cs | 69 +++++++++---------- 1 file changed, 34 insertions(+), 35 deletions(-) diff --git a/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs b/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs index 7ca87e463..ab3ebc975 100644 --- a/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs +++ b/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.Runtime.Serialization; using System.Text; using static Hi3Helper.Logger; @@ -13,8 +14,9 @@ internal class CollapsePlaytime #region Fields private static DateTime BaseDate => new(2012, 2, 13, 0, 0, 0, DateTimeKind.Utc); - private const string _ValueName = "CollapseLauncher_Playtime"; - private const string _OldLastPlayedValueName = "CollapseLauncher_LastPlayed"; + private const string _TotalTimeValueName = "CollapseLauncher_Playtime"; + private const string _LastPlayedValueName = "CollapseLauncher_LastPlayed"; + private const string _StatsValueName = "CollapseLauncher_PlaytimeStats"; private static Dictionary _IsDeserializing = []; private RegistryKey _registryRoot; @@ -27,12 +29,14 @@ internal class CollapsePlaytime /// Represents the total time a game was played.

/// Default: TimeSpan.Zero ///
+ [IgnoreDataMember] public TimeSpan TotalPlaytime { get; set; } = TimeSpan.Zero; /// /// Represents the total time the last/current session lasted.

/// Default: TimeSpan.Zero ///
+ [IgnoreDataMember] public TimeSpan LastSession { get; set; } = TimeSpan.Zero; /// @@ -81,47 +85,37 @@ public static CollapsePlaytime Load(RegistryKey root, int hashID) try { _IsDeserializing[hashID] = true; - if (root == null) throw new NullReferenceException($"Cannot load {_ValueName} RegistryKey is unexpectedly not initialized!"); + if (root == null) throw new NullReferenceException($"Cannot load playtime. RegistryKey is unexpectedly not initialized!"); - object? value = root.GetValue(_ValueName, null); + int? totalTime = (int?)root.GetValue(_TotalTimeValueName,null); + int? lastPlayed = (int?)root.GetValue(_LastPlayedValueName,null); + object? stats = root.GetValue(_StatsValueName, null); - // The value being an int means this value was saved by the old implementaion and represents the total number of seconds played. - if (value is int oldPlaytime) - { - object? lastPlayed = root.GetValue(_OldLastPlayedValueName, null); - root.DeleteValue(_OldLastPlayedValueName, false); - - LogWriteLine($"Found old Playtime RegistryKey! Converting to the new format... (Playtime: {oldPlaytime} | Last Played: {lastPlayed})", writeToLog: true); - _IsDeserializing[hashID] = false; - - CollapsePlaytime playtime = new CollapsePlaytime() - { - TotalPlaytime = TimeSpan.FromSeconds(oldPlaytime), - LastPlayed = lastPlayed != null ? BaseDate.AddSeconds((int)lastPlayed) : null, - _registryRoot = root, - _hashID = hashID - }; - playtime.Save(); - - return playtime; - } + CollapsePlaytime playtime; - if (value != null) + if (stats != null) { - ReadOnlySpan byteStr = (byte[])value; + ReadOnlySpan byteStr = (byte[])stats; #if DEBUG LogWriteLine($"Loaded Playtime:\r\n{Encoding.UTF8.GetString(byteStr.TrimEnd((byte)0))}", LogType.Debug, true); #endif - CollapsePlaytime playtime = byteStr.Deserialize(UniversalPlaytimeJSONContext.Default) ?? new CollapsePlaytime(); - playtime._registryRoot = root; - playtime._hashID = hashID; - - return playtime; + playtime = byteStr.Deserialize(UniversalPlaytimeJSONContext.Default) ?? new CollapsePlaytime(); + } + else + { + playtime = new CollapsePlaytime(); } + + playtime._registryRoot = root; + playtime._hashID = hashID; + playtime.TotalPlaytime = TimeSpan.FromSeconds(totalTime ?? 0); + playtime.LastPlayed = lastPlayed != null ? BaseDate.AddSeconds((int)lastPlayed) : null; + + return playtime; } catch (Exception ex) { - LogWriteLine($"Failed while reading {_ValueName}\r\n{ex}", LogType.Error, true); + LogWriteLine($"Failed while reading playtime.\r\n{ex}", LogType.Error, true); } finally { @@ -138,18 +132,23 @@ public void Save() { try { - if (_registryRoot == null) throw new NullReferenceException($"Cannot save {_ValueName} since RegistryKey is unexpectedly not initialized!"); + if (_registryRoot == null) throw new NullReferenceException($"Cannot save playtime since RegistryKey is unexpectedly not initialized!"); string data = this.Serialize(UniversalPlaytimeJSONContext.Default, true); byte[] dataByte = Encoding.UTF8.GetBytes(data); #if DEBUG LogWriteLine($"Saved Playtime:\r\n{data}", LogType.Debug, true); #endif - _registryRoot.SetValue(_ValueName, dataByte, RegistryValueKind.Binary); + _registryRoot.SetValue(_StatsValueName, dataByte, RegistryValueKind.Binary); + _registryRoot.SetValue(_TotalTimeValueName, TotalPlaytime.TotalSeconds, RegistryValueKind.DWord); + + double? lastPlayed = (LastPlayed?.ToUniversalTime() - BaseDate)?.TotalSeconds; + if (lastPlayed != null) + _registryRoot.SetValue(_LastPlayedValueName, lastPlayed); } catch (Exception ex) { - LogWriteLine($"Failed to save {_ValueName}!\r\n{ex}", LogType.Error, true); + LogWriteLine($"Failed to save playtime!\r\n{ex}", LogType.Error, true); } } From 1d61cf9088dab44abcad0066fcc373526e3d02db Mon Sep 17 00:00:00 2001 From: Gabriel Lima <44784408+gablm@users.noreply.github.com> Date: Fri, 23 Aug 2024 00:34:38 +0100 Subject: [PATCH 010/110] Playtime - Reload playtime before session and when trying to edit --- .../GameManagement/GamePlaytime/Playtime.cs | 17 ++++++++++++++++- .../RegistryClass/CollapsePlaytime.cs | 6 +++--- .../Classes/Interfaces/IGamePlaytime.cs | 1 + .../XAMLs/MainApp/Pages/HomePage.xaml.cs | 1 + 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/CollapseLauncher/Classes/GameManagement/GamePlaytime/Playtime.cs b/CollapseLauncher/Classes/GameManagement/GamePlaytime/Playtime.cs index 6992b66ae..a965c3675 100644 --- a/CollapseLauncher/Classes/GameManagement/GamePlaytime/Playtime.cs +++ b/CollapseLauncher/Classes/GameManagement/GamePlaytime/Playtime.cs @@ -19,6 +19,7 @@ internal class Playtime : IGamePlaytime #nullable enable public CollapsePlaytime CollapsePlaytime => _playtime; + public bool IsSessionRunning { get; protected set; } private RegistryKey? _registryRoot; private CollapsePlaytime _playtime; private IGameVersionCheck _gameVersionManager; @@ -39,6 +40,13 @@ public Playtime(IGameVersionCheck GameVersionManager) } #nullable disable + public void Reload() + { + if (IsSessionRunning) return; + + _playtime = CollapsePlaytime.Load(_registryRoot!, _gameVersionManager.GamePreset.HashID); + } + public void Update(TimeSpan timeSpan) { TimeSpan oldTimeSpan = _playtime.TotalPlaytime; @@ -46,7 +54,7 @@ public void Update(TimeSpan timeSpan) _playtime.Update(timeSpan); PlaytimeUpdated?.Invoke(this, _playtime); - LogWriteLine($"Playtime counter changed to {TimeSpanToString(timeSpan)}m. (Previous value: {TimeSpanToString(oldTimeSpan)})", writeToLog: true); + LogWriteLine($"Playtime counter changed to {TimeSpanToString(timeSpan)}. (Previous value: {TimeSpanToString(oldTimeSpan)})", writeToLog: true); } public void Reset() @@ -61,6 +69,11 @@ public void Reset() public async void StartSession(Process proc) { + if (IsSessionRunning) return; + + Reload(); + IsSessionRunning = true; + DateTime begin = DateTime.Now; TimeSpan initialTimeSpan = _playtime.TotalPlaytime; @@ -109,6 +122,8 @@ public async void StartSession(Process proc) _playtime.Update(initialTimeSpan.Add(totalTimeSpan), false); PlaytimeUpdated?.Invoke(this, _playtime); + + IsSessionRunning = false; } private static string TimeSpanToString(TimeSpan timeSpan) diff --git a/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs b/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs index ab3ebc975..eade37247 100644 --- a/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs +++ b/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs @@ -97,7 +97,7 @@ public static CollapsePlaytime Load(RegistryKey root, int hashID) { ReadOnlySpan byteStr = (byte[])stats; #if DEBUG - LogWriteLine($"Loaded Playtime:\r\n{Encoding.UTF8.GetString(byteStr.TrimEnd((byte)0))}", LogType.Debug, true); + LogWriteLine($"Loaded Playtime:\r\nTotal: {totalTime}s\r\nLastPlayed: {lastPlayed}\r\nStats: {Encoding.UTF8.GetString(byteStr.TrimEnd((byte)0))}", LogType.Debug, true); #endif playtime = byteStr.Deserialize(UniversalPlaytimeJSONContext.Default) ?? new CollapsePlaytime(); } @@ -144,7 +144,7 @@ public void Save() double? lastPlayed = (LastPlayed?.ToUniversalTime() - BaseDate)?.TotalSeconds; if (lastPlayed != null) - _registryRoot.SetValue(_LastPlayedValueName, lastPlayed); + _registryRoot.SetValue(_LastPlayedValueName, lastPlayed, RegistryValueKind.DWord); } catch (Exception ex) { @@ -201,7 +201,7 @@ public void AddMinute() TotalPlaytime = TotalPlaytime.Add(minute); LastSession = LastSession.Add(minute); - if (today == DateTime.Today) + if (ControlDate == today) { DailyPlaytime = DailyPlaytime.Add(minute); WeeklyPlaytime = WeeklyPlaytime.Add(minute); diff --git a/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs b/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs index 037e99e55..bddc993f3 100644 --- a/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs +++ b/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs @@ -10,6 +10,7 @@ internal interface IGamePlaytime : IDisposable CollapsePlaytime CollapsePlaytime { get; } void Reset(); + void Reload(); void Update(TimeSpan timeSpan); void StartSession(Process proc); } diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs index 4a13a117e..d32c87e21 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs @@ -2132,6 +2132,7 @@ private void ForceUpdatePlaytimeButton_Click(object sender, RoutedEventArgs e) { if (_cachedIsGameRunning) return; + CurrentGameProperty._GamePlaytime.Reload(); UpdatePlaytime(null, CurrentGameProperty._GamePlaytime.CollapsePlaytime); } From 4a601cfcbdc763428c6d13a621917ce583f2ae46 Mon Sep 17 00:00:00 2001 From: Gabriel Lima <44784408+gablm@users.noreply.github.com> Date: Tue, 27 Aug 2024 00:14:00 +0100 Subject: [PATCH 011/110] Playtime - Ignore total playtime and last session serializer wise --- .../GameManagement/GamePlaytime/Playtime.cs | 9 ------ .../RegistryClass/CollapsePlaytime.cs | 30 +++++++++---------- .../Classes/Interfaces/IGamePlaytime.cs | 1 - .../XAMLs/MainApp/Pages/HomePage.xaml.cs | 1 - 4 files changed, 15 insertions(+), 26 deletions(-) diff --git a/CollapseLauncher/Classes/GameManagement/GamePlaytime/Playtime.cs b/CollapseLauncher/Classes/GameManagement/GamePlaytime/Playtime.cs index a965c3675..c0b939fc0 100644 --- a/CollapseLauncher/Classes/GameManagement/GamePlaytime/Playtime.cs +++ b/CollapseLauncher/Classes/GameManagement/GamePlaytime/Playtime.cs @@ -40,13 +40,6 @@ public Playtime(IGameVersionCheck GameVersionManager) } #nullable disable - public void Reload() - { - if (IsSessionRunning) return; - - _playtime = CollapsePlaytime.Load(_registryRoot!, _gameVersionManager.GamePreset.HashID); - } - public void Update(TimeSpan timeSpan) { TimeSpan oldTimeSpan = _playtime.TotalPlaytime; @@ -71,7 +64,6 @@ public async void StartSession(Process proc) { if (IsSessionRunning) return; - Reload(); IsSessionRunning = true; DateTime begin = DateTime.Now; @@ -136,7 +128,6 @@ public void Dispose() _token.Cancel(); _playtime.Save(); - _playtime = null; _registryRoot = null; } } diff --git a/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs b/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs index eade37247..45ecac1dd 100644 --- a/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs +++ b/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs @@ -3,8 +3,8 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Runtime.Serialization; using System.Text; +using System.Text.Json.Serialization; using static Hi3Helper.Logger; namespace CollapseLauncher.GamePlaytime @@ -29,49 +29,49 @@ internal class CollapsePlaytime /// Represents the total time a game was played.

/// Default: TimeSpan.Zero ///
- [IgnoreDataMember] - public TimeSpan TotalPlaytime { get; set; } = TimeSpan.Zero; - - /// - /// Represents the total time the last/current session lasted.

- /// Default: TimeSpan.Zero - ///
- [IgnoreDataMember] - public TimeSpan LastSession { get; set; } = TimeSpan.Zero; + [JsonIgnore] + public TimeSpan TotalPlaytime { get; private set; } = TimeSpan.Zero; /// /// Represents the daily playtime.
/// The ControlDate field is used to check if this value should be reset.

/// Default: TimeSpan.Zero ///
- public TimeSpan DailyPlaytime { get; set; } = TimeSpan.Zero; + public TimeSpan DailyPlaytime { get; private set; } = TimeSpan.Zero; /// /// Represents the weekly playtime.
/// The ControlDate field is used to check if this value should be reset.

/// Default: TimeSpan.Zero ///
- public TimeSpan WeeklyPlaytime { get; set; } = TimeSpan.Zero; + public TimeSpan WeeklyPlaytime { get; private set; } = TimeSpan.Zero; /// /// Represents the monthly playtime.
/// The ControlDate field is used to check if this value should be reset.

/// Default: TimeSpan.Zero ///
- public TimeSpan MonthlyPlaytime { get; set; } = TimeSpan.Zero; + public TimeSpan MonthlyPlaytime { get; private set; } = TimeSpan.Zero; + + /// + /// Represents the total time the last/current session lasted.

+ /// Default: TimeSpan.Zero + ///
+ public TimeSpan LastSession { get; set; } = TimeSpan.Zero; /// /// Represents the last time the game was launched.

/// Default: null ///
- public DateTime? LastPlayed { get; set; } = null; + [JsonIgnore] + public DateTime? LastPlayed { get; set; } /// /// Represents a control date.
/// This date is used to check if a specific playtime statistic should be reset.

/// Default: DateTime.Today ///
- public DateTime ControlDate { get; set; } = DateTime.Today; + public DateTime ControlDate { get; private set; } = DateTime.Today; #endregion #region Methods diff --git a/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs b/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs index bddc993f3..037e99e55 100644 --- a/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs +++ b/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs @@ -10,7 +10,6 @@ internal interface IGamePlaytime : IDisposable CollapsePlaytime CollapsePlaytime { get; } void Reset(); - void Reload(); void Update(TimeSpan timeSpan); void StartSession(Process proc); } diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs index d32c87e21..4a13a117e 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs @@ -2132,7 +2132,6 @@ private void ForceUpdatePlaytimeButton_Click(object sender, RoutedEventArgs e) { if (_cachedIsGameRunning) return; - CurrentGameProperty._GamePlaytime.Reload(); UpdatePlaytime(null, CurrentGameProperty._GamePlaytime.CollapsePlaytime); } From ee702159e9fb7255eeab660d068a6ef7585354b4 Mon Sep 17 00:00:00 2001 From: Gabriel Lima <44784408+gablm@users.noreply.github.com> Date: Tue, 27 Aug 2024 00:15:32 +0100 Subject: [PATCH 012/110] Fix stop game keyboard shortcut --- CollapseLauncher/XAMLs/MainApp/MainPage.xaml.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CollapseLauncher/XAMLs/MainApp/MainPage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/MainPage.xaml.cs index 3fc9b1280..9e67d4b84 100644 --- a/CollapseLauncher/XAMLs/MainApp/MainPage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/MainPage.xaml.cs @@ -1967,9 +1967,9 @@ private void OpenGameCacheFolder_Invoked(KeyboardAccelerator sender, KeyboardAcc private void ForceCloseGame_Invoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args) { - if (!CurrentGameProperty.IsGameRunning) return; + if (!GetCurrentGameProperty().IsGameRunning) return; - PresetConfig gamePreset = CurrentGameProperty._GameVersion.GamePreset; + PresetConfig gamePreset = GetCurrentGameProperty()._GameVersion.GamePreset; try { var gameProcess = Process.GetProcessesByName(gamePreset.GameExecutableName!.Split('.')[0]); From 89dcad6c7a1bff8e1bfb49a62d802d712a956b18 Mon Sep 17 00:00:00 2001 From: Bagus Nur Listiyono Date: Sat, 14 Sep 2024 22:59:43 +0700 Subject: [PATCH 013/110] Initial upgrade to .NET 9 --- .github/workflows/build.yml | 6 +- CollapseLauncher/CollapseLauncher.csproj | 13 +- CollapseLauncher/packages.lock.json | 871 +----------------- ColorThief | 2 +- Hi3Helper.Core/Hi3Helper.Core.csproj | 2 +- Hi3Helper.Core/packages.lock.json | 8 +- Hi3Helper.EncTool | 2 +- .../Hi3Helper.EncTool.Test.csproj | 2 +- Hi3Helper.EncTool.Test/packages.lock.json | 8 +- Hi3Helper.Http | 2 +- Hi3Helper.SharpDiscordRPC | 2 +- Hi3Helper.Sophon | 2 +- ImageEx | 2 +- InnoSetupHelper/InnoSetupHelper.csproj | 4 +- InnoSetupHelper/packages.lock.json | 10 +- SevenZipExtractor | 2 +- 16 files changed, 67 insertions(+), 871 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3a7ca0d08..bdc41dff9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,13 +23,14 @@ jobs: matrix: configuration: [Release] # No need to distribute Debug builds platform: [x64] - framework: [net8.0-windows10.0.22621.0] + framework: [net9.0-windows10.0.22621.0] env: Configuration: ${{ matrix.configuration }} Platform: ${{ matrix.platform }} DOTNET_INSTALL_DIR: '.\.dotnet' - DOTNET_VERSION: '8.x' + DOTNET_VERSION: '9.x' + DOTNET_QUALITY: 'preview' NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages steps: @@ -42,6 +43,7 @@ jobs: uses: actions/setup-dotnet@v4 with: dotnet-version: ${{ env.DOTNET_VERSION }} + dotnet-quality: ${{ env.DOTNET_QUALITY }} cache: true cache-dependency-path: CollapseLauncher/packages.lock.json diff --git a/CollapseLauncher/CollapseLauncher.csproj b/CollapseLauncher/CollapseLauncher.csproj index 358dccace..9bd083095 100644 --- a/CollapseLauncher/CollapseLauncher.csproj +++ b/CollapseLauncher/CollapseLauncher.csproj @@ -20,7 +20,7 @@ preview x64 - net8.0-windows10.0.22621.0 + net9.0-windows10.0.22621.0 10.0.22621.41 10.0.17763.0 win-x64 @@ -96,7 +96,6 @@ - @@ -109,13 +108,11 @@ - - + - - - - + + + diff --git a/CollapseLauncher/packages.lock.json b/CollapseLauncher/packages.lock.json index 77ef854fe..644a06bc1 100644 --- a/CollapseLauncher/packages.lock.json +++ b/CollapseLauncher/packages.lock.json @@ -1,7 +1,7 @@ { "version": 1, "dependencies": { - "net8.0-windows10.0.22621": { + "net9.0-windows10.0.22621": { "Clowd.Squirrel": { "type": "Direct", "requested": "[2.11.1, )", @@ -153,12 +153,6 @@ "Microsoft.WindowsAppSDK": "1.6.240531000-experimental1" } }, - "Microsoft.NETCore.Platforms": { - "type": "Direct", - "requested": "[8.0.0-preview.7.23375.6, )", - "resolved": "8.0.0-preview.7.23375.6", - "contentHash": "ym5B/aglUc4K5kkiBOOgsm7EETjenJBbMchAHvcXpfFPmrdTrhnLKC1Tvx5Qnz5kHvhloyCNTpGQvWIpxvINHg==" - }, "Microsoft.NETCore.Targets": { "type": "Direct", "requested": "[6.0.0-preview.4.21253.7, )", @@ -253,43 +247,9 @@ }, "System.Diagnostics.EventLog": { "type": "Direct", - "requested": "[8.0.0, )", - "resolved": "8.0.0", - "contentHash": "fdYxcRjQqTTacKId/2IECojlDSFvp7LP5N78+0z/xH7v/Tuw5ZAxu23Y6PTCRinqyu2ePx+Gn1098NC6jM6d+A==" - }, - "System.Net.Http": { - "type": "Direct", - "requested": "[4.3.4, )", - "resolved": "4.3.4", - "contentHash": "aOa2d51SEbmM+H+Csw7yJOuNZoHkrP2XnAurye5HWYgGVVU54YZDvsLUYRv6h18X3sPnjNCANmN7ZhIPiqMcjA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.1", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.DiagnosticSource": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Extensions": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2" - } + "requested": "[9.0.0-rc.1.24431.7, )", + "resolved": "9.0.0-rc.1.24431.7", + "contentHash": "ARI0u0Z1/eVTCeb6G4oS/xdTX0xvHPuIoT1IAHsRlOYzgzgmoXZxzH28AMD8etSCDXCBT+0icbcHMsNjOQlxDg==" }, "System.Security.AccessControl": { "type": "Direct", @@ -299,33 +259,21 @@ }, "System.Security.Cryptography.ProtectedData": { "type": "Direct", - "requested": "[8.0.0, )", - "resolved": "8.0.0", - "contentHash": "+TUFINV2q2ifyXauQXRwy4CiBhqvDEDZeVJU7qfxya4aRYOKzVBpN+4acx25VcPB9ywUN6C0n8drWl110PhZEg==" + "requested": "[9.0.0-rc.1.24431.7, )", + "resolved": "9.0.0-rc.1.24431.7", + "contentHash": "oPjLCPnuxKQ8lXyY4PPgbwuTu1qpeZGLbh4/c+o1ZcLXuLJlpKYOu9HmqvWD9VpReAFOp72a1GYRSsMmJKZ9aQ==" }, "System.Text.Encoding.CodePages": { "type": "Direct", - "requested": "[8.0.0, )", - "resolved": "8.0.0", - "contentHash": "OZIsVplFGaVY90G2SbpgU7EnCoOO5pw1t4ic21dBF3/1omrJFpAGoNAVpPyMVOC90/hvgkGG3VFqR13YgZMQfg==" + "requested": "[9.0.0-rc.1.24431.7, )", + "resolved": "9.0.0-rc.1.24431.7", + "contentHash": "RRwyNcn8Ph+P3gPGouH+6x1N8l9tc5h3+54zUt6Bhet1AcjypZ51mNMYmPTOz056rxCNoPUNAyDCkk9AsAnUTw==" }, "System.Text.Json": { "type": "Direct", - "requested": "[8.0.4, )", - "resolved": "8.0.4", - "contentHash": "bAkhgDJ88XTsqczoxEMliSrpijKZHhbJQldhAmObj/RbrN3sU5dcokuXmWJWsdQAhiMJ9bTayWsL1C9fbbCRhw==", - "dependencies": { - "System.Text.Encodings.Web": "8.0.0" - } - }, - "System.Text.RegularExpressions": { - "type": "Direct", - "requested": "[4.3.1, )", - "resolved": "4.3.1", - "contentHash": "N0kNRrWe4+nXOWlpLT4LAY5brb8caNFlUuIRpraCVMDLYutKkol1aV079rQjLuSxKMJT2SpBQsYX9xbcTMmzwg==", - "dependencies": { - "System.Runtime": "4.3.1" - } + "requested": "[9.0.0-rc.1.24431.7, )", + "resolved": "9.0.0-rc.1.24431.7", + "contentHash": "f6EHMnCjPQbwIftWreac1RsDolPpRT47lBfdHM/7Y/phtdewEtn3+XKWUVZ5gpB04tpab5y/XaZRkq28p3xzFQ==" }, "TaskScheduler": { "type": "Direct", @@ -415,541 +363,27 @@ }, "Microsoft.Win32.SystemEvents": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "9opKRyOKMCi2xJ7Bj7kxtZ1r9vbzosMvRrdEhVhDz8j8MoBGgB+WmC94yH839NPH+BclAjtQ/pyagvi/8gDLkw==" - }, - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "7VSGO0URRKoMEAq0Sc9cRz8mb6zbyx/BZDEWhgPdzzpmFhkam3fJ1DAGWFXBI4nGlma+uPKpfuMQP5LXRnOH5g==" - }, - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "0oAaTAm6e2oVH+/Zttt0cuhGaePQYKII1dY8iaqP7CvOpVKgLybKRFvQjXR2LtxXOXTVPNv14j0ot8uV+HrUmw==" - }, - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "G24ibsCNi5Kbz0oXWynBoRgtGvsw5ZSVEWjv13/KiCAM8C6wz9zzcCniMeQFIkJ2tasjo2kXlvlBZhplL51kGg==" - }, - "runtime.native.System": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "runtime.native.System.Net.Http": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "runtime.native.System.Security.Cryptography.Apple": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==", - "dependencies": { - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": "4.3.0" - } - }, - "runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "QR1OwtwehHxSeQvZKXe+iSd+d3XZNkEcuWMFYa2i0aG1l+lR739HPicKMlTbJst3spmeekDVBUS7SeS26s4U/g==", - "dependencies": { - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2" - } - }, - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "I+GNKGg2xCHueRd1m9PzeEW7WLbNNLznmTuEi8/vZX71HudUbx1UTwlGkiwMri7JLl8hGaIAWnA/GONhu+LOyQ==" - }, - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "1Z3TAq1ytS1IBRtPXJvEUZdVsfWfeNEhBkbiOCGEl9wwAfsjP2lz3ZFDx5tq8p60/EqbS0HItG5piHuB71RjoA==" - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==" - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "6mU/cVmmHtQiDXhnzUImxIcDL48GbTk+TsptXyJA+MIOG9LRjPoAQC/qBFB7X+UNyK86bmvGwC8t+M66wsYC8w==" - }, - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "vjwG0GGcTW/PPg6KVud8F9GLWYuAV1rrw1BKAqY0oh4jcUqg15oYF1+qkGR2x2ZHM4DQnWKQ7cJgYbfncz/lYg==" - }, - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "7KMFpTkHC/zoExs+PwP8jDCWcrK9H6L7soowT80CUx3e+nxP/AFnq0AQAW5W76z2WYbLAYCRyPfwYFG6zkvQRw==" - }, - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "xrlmRCnKZJLHxyyLIqkZjNXqgxnKdZxfItrPkjI+6pkRo5lHX8YvSZlWrSI5AVwLMi4HbNWP7064hcAWeZKp5w==" - }, - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "leXiwfiIkW7Gmn7cgnNcdtNAU70SjmKW3jxGj1iKHOvdn0zRWsgv/l2OJUO5zdGdiv2VRFnAsxxhDgMzofPdWg==" - }, - "System.Collections": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Collections.Concurrent": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.Diagnostics.Debug": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "tD6kosZnTAGdrEa0tZSuFyunMbt/5KYDnHdndJYGqZoNy00XVXyACd5d6KnE1YgYv3ne2CjtAfNXo/fwEhnKUA==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Diagnostics.Tracing": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } + "resolved": "9.0.0-rc.1.24431.7", + "contentHash": "IK3fwmfSCVArYWKjPUTEeD9nRXF1VENiFb4+hlqWuUxfP1hPVw41y/sOEdLZtRB+BVdgh6NaZjetrVhEuG4Iyg==" }, "System.Drawing.Common": { "type": "Transitive", - "resolved": "8.0.8", - "contentHash": "4ZM1wvLjz9nVVExsfPAaSl/qOvU+QNedJL5rQ+2Wbow+iGeyO0e7XN07707rMBgaffEeeLrCZBwC0oHUuvRdPw==", - "dependencies": { - "Microsoft.Win32.SystemEvents": "8.0.0" - } - }, - "System.Globalization": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Globalization.Calendars": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Globalization.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0" - } - }, - "System.IO": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.IO.FileSystem": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.IO.FileSystem.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==", + "resolved": "9.0.0-rc.1.24451.1", + "contentHash": "5TJtJLhJk7viKkkflNYN+V+Tl/qxoKrkmCTog2j0dzqamIY/ximaAWQfPznYOUR5852XxxvhZsQLd2LzjJFWUw==", "dependencies": { - "System.Runtime": "4.3.0" + "Microsoft.Win32.SystemEvents": "9.0.0-rc.1.24431.7" } }, "System.IO.Hashing": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "ne1843evDugl0md7Fjzy6QjJrzsjh46ZKbhf8GwBXb5f/gw97J4bxMs0NQKifDuThh/f0bZ0e62NPl1jzTuRqA==" - }, - "System.Linq": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - } - }, - "System.Net.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - } - }, - "System.Reflection": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Resources.ResourceManager": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime": { - "type": "Transitive", - "resolved": "4.3.1", - "contentHash": "abhfv1dTK6NXOmu4bgHIONxHyEqFjW8HwXPmpY9gmll+ix9UNo4XDcmzJn6oLooftxNssVHdJC1pGT9jkSynQg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.1", - "Microsoft.NETCore.Targets": "1.1.3" - } - }, - "System.Runtime.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime.Handles": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime.InteropServices": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - } - }, - "System.Runtime.Numerics": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==", - "dependencies": { - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - } - }, - "System.Security.Cryptography.Algorithms": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.Apple": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.Cng": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.Security.Cryptography.Csp": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Security.Cryptography.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Linq": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==", - "dependencies": { - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==", - "dependencies": { - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.Security.Cryptography.X509Certificates": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Calendars": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Cng": "4.3.0", - "System.Security.Cryptography.Csp": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } + "resolved": "9.0.0-rc.1.24431.7", + "contentHash": "giMiDNlne8WcLG7rsEfrETaukDUTBPn8a97QRo9LYR71O0Rb4SJ6TbfGdQ9oiBYldVPSAbSQANb5ThLZOo408Q==" }, "System.Security.Principal.Windows": { "type": "Transitive", "resolved": "5.0.0", "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" }, - "System.Text.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Text.Encodings.Web": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" - }, - "System.Threading": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==", - "dependencies": { - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.Threading.Tasks": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, "System.Threading.Tasks.Extensions": { "type": "Transitive", "resolved": "4.5.4", @@ -972,13 +406,13 @@ "colorthief": { "type": "Project", "dependencies": { - "System.Drawing.Common": "[8.0.8, )" + "System.Drawing.Common": "[9.0.0-rc.1.24451.1, )" } }, "discordrpc": { "type": "Project", "dependencies": { - "System.Text.Json": "[8.0.4, )" + "System.Text.Json": "[9.0.0-rc.1.24431.7, )" } }, "hi3helper.core": { @@ -993,7 +427,7 @@ "dependencies": { "Google.Protobuf": "[3.28.1, )", "Hi3Helper.Http": "[2.0.0, )", - "System.IO.Hashing": "[8.0.0, )" + "System.IO.Hashing": "[9.0.0-rc.1.24431.7, )" } }, "hi3helper.http": { @@ -1004,7 +438,7 @@ "dependencies": { "Google.Protobuf": "[*, )", "Hi3Helper.ZstdNet": "[*, )", - "System.IO.Hashing": "[*, )" + "System.IO.Hashing": "[9.0.0-rc.1.24431.7, )" } }, "imageex": { @@ -1017,14 +451,14 @@ "type": "Project", "dependencies": { "Hi3Helper.Core": "[1.0.0, )", - "System.IO.Hashing": "[8.0.0, )" + "System.IO.Hashing": "[9.0.0-rc.1.24431.7, )" } }, "sevenzipextractor": { "type": "Project" } }, - "net8.0-windows10.0.22621/win-x64": { + "net9.0-windows10.0.22621/win-x64": { "Microsoft.Graphics.Win2D": { "type": "Direct", "requested": "[1.2.1-experimental2, )", @@ -1055,43 +489,9 @@ }, "System.Diagnostics.EventLog": { "type": "Direct", - "requested": "[8.0.0, )", - "resolved": "8.0.0", - "contentHash": "fdYxcRjQqTTacKId/2IECojlDSFvp7LP5N78+0z/xH7v/Tuw5ZAxu23Y6PTCRinqyu2ePx+Gn1098NC6jM6d+A==" - }, - "System.Net.Http": { - "type": "Direct", - "requested": "[4.3.4, )", - "resolved": "4.3.4", - "contentHash": "aOa2d51SEbmM+H+Csw7yJOuNZoHkrP2XnAurye5HWYgGVVU54YZDvsLUYRv6h18X3sPnjNCANmN7ZhIPiqMcjA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.1", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.DiagnosticSource": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Extensions": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2" - } + "requested": "[9.0.0-rc.1.24431.7, )", + "resolved": "9.0.0-rc.1.24431.7", + "contentHash": "ARI0u0Z1/eVTCeb6G4oS/xdTX0xvHPuIoT1IAHsRlOYzgzgmoXZxzH28AMD8etSCDXCBT+0icbcHMsNjOQlxDg==" }, "System.Security.AccessControl": { "type": "Direct", @@ -1101,9 +501,9 @@ }, "System.Text.Encoding.CodePages": { "type": "Direct", - "requested": "[8.0.0, )", - "resolved": "8.0.0", - "contentHash": "OZIsVplFGaVY90G2SbpgU7EnCoOO5pw1t4ic21dBF3/1omrJFpAGoNAVpPyMVOC90/hvgkGG3VFqR13YgZMQfg==" + "requested": "[9.0.0-rc.1.24431.7, )", + "resolved": "9.0.0-rc.1.24431.7", + "contentHash": "RRwyNcn8Ph+P3gPGouH+6x1N8l9tc5h3+54zUt6Bhet1AcjypZ51mNMYmPTOz056rxCNoPUNAyDCkk9AsAnUTw==" }, "Microsoft.Web.WebView2": { "type": "Transitive", @@ -1121,216 +521,13 @@ }, "Microsoft.Win32.SystemEvents": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "9opKRyOKMCi2xJ7Bj7kxtZ1r9vbzosMvRrdEhVhDz8j8MoBGgB+WmC94yH839NPH+BclAjtQ/pyagvi/8gDLkw==" - }, - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "7VSGO0URRKoMEAq0Sc9cRz8mb6zbyx/BZDEWhgPdzzpmFhkam3fJ1DAGWFXBI4nGlma+uPKpfuMQP5LXRnOH5g==" - }, - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "0oAaTAm6e2oVH+/Zttt0cuhGaePQYKII1dY8iaqP7CvOpVKgLybKRFvQjXR2LtxXOXTVPNv14j0ot8uV+HrUmw==" - }, - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "G24ibsCNi5Kbz0oXWynBoRgtGvsw5ZSVEWjv13/KiCAM8C6wz9zzcCniMeQFIkJ2tasjo2kXlvlBZhplL51kGg==" - }, - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "I+GNKGg2xCHueRd1m9PzeEW7WLbNNLznmTuEi8/vZX71HudUbx1UTwlGkiwMri7JLl8hGaIAWnA/GONhu+LOyQ==" - }, - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "1Z3TAq1ytS1IBRtPXJvEUZdVsfWfeNEhBkbiOCGEl9wwAfsjP2lz3ZFDx5tq8p60/EqbS0HItG5piHuB71RjoA==" - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==" - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "6mU/cVmmHtQiDXhnzUImxIcDL48GbTk+TsptXyJA+MIOG9LRjPoAQC/qBFB7X+UNyK86bmvGwC8t+M66wsYC8w==" - }, - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "vjwG0GGcTW/PPg6KVud8F9GLWYuAV1rrw1BKAqY0oh4jcUqg15oYF1+qkGR2x2ZHM4DQnWKQ7cJgYbfncz/lYg==" - }, - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "7KMFpTkHC/zoExs+PwP8jDCWcrK9H6L7soowT80CUx3e+nxP/AFnq0AQAW5W76z2WYbLAYCRyPfwYFG6zkvQRw==" - }, - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "xrlmRCnKZJLHxyyLIqkZjNXqgxnKdZxfItrPkjI+6pkRo5lHX8YvSZlWrSI5AVwLMi4HbNWP7064hcAWeZKp5w==" - }, - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "leXiwfiIkW7Gmn7cgnNcdtNAU70SjmKW3jxGj1iKHOvdn0zRWsgv/l2OJUO5zdGdiv2VRFnAsxxhDgMzofPdWg==" - }, - "System.Globalization.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0" - } - }, - "System.Security.Cryptography.Algorithms": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.Apple": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.Cng": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.Security.Cryptography.Csp": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Security.Cryptography.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Linq": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==", - "dependencies": { - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.X509Certificates": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Calendars": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Cng": "4.3.0", - "System.Security.Cryptography.Csp": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } + "resolved": "9.0.0-rc.1.24431.7", + "contentHash": "IK3fwmfSCVArYWKjPUTEeD9nRXF1VENiFb4+hlqWuUxfP1hPVw41y/sOEdLZtRB+BVdgh6NaZjetrVhEuG4Iyg==" }, "System.Security.Principal.Windows": { "type": "Transitive", "resolved": "5.0.0", "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" - }, - "System.Text.Encodings.Web": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" } } } diff --git a/ColorThief b/ColorThief index 0d73e7cce..4ec986783 160000 --- a/ColorThief +++ b/ColorThief @@ -1 +1 @@ -Subproject commit 0d73e7cce1013b9415c44c911993c494b54eac70 +Subproject commit 4ec9867838d73238eb6372c0fc24d610dd229354 diff --git a/Hi3Helper.Core/Hi3Helper.Core.csproj b/Hi3Helper.Core/Hi3Helper.Core.csproj index bba442d8b..9da4f868f 100644 --- a/Hi3Helper.Core/Hi3Helper.Core.csproj +++ b/Hi3Helper.Core/Hi3Helper.Core.csproj @@ -1,7 +1,7 @@  - net8.0 + net9.0 x64 Debug;Release;Publish 6 diff --git a/Hi3Helper.Core/packages.lock.json b/Hi3Helper.Core/packages.lock.json index dbd9fe431..4e320c966 100644 --- a/Hi3Helper.Core/packages.lock.json +++ b/Hi3Helper.Core/packages.lock.json @@ -1,7 +1,7 @@ { "version": 1, "dependencies": { - "net8.0": { + "net9.0": { "Google.Protobuf": { "type": "Transitive", "resolved": "3.28.1", @@ -9,15 +9,15 @@ }, "System.IO.Hashing": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "ne1843evDugl0md7Fjzy6QjJrzsjh46ZKbhf8GwBXb5f/gw97J4bxMs0NQKifDuThh/f0bZ0e62NPl1jzTuRqA==" + "resolved": "9.0.0-rc.1.24431.7", + "contentHash": "giMiDNlne8WcLG7rsEfrETaukDUTBPn8a97QRo9LYR71O0Rb4SJ6TbfGdQ9oiBYldVPSAbSQANb5ThLZOo408Q==" }, "hi3helper.enctool": { "type": "Project", "dependencies": { "Google.Protobuf": "[3.28.1, )", "Hi3Helper.Http": "[2.0.0, )", - "System.IO.Hashing": "[8.0.0, )" + "System.IO.Hashing": "[9.0.0-rc.1.24431.7, )" } }, "hi3helper.http": { diff --git a/Hi3Helper.EncTool b/Hi3Helper.EncTool index f90f1f961..2c0c94e43 160000 --- a/Hi3Helper.EncTool +++ b/Hi3Helper.EncTool @@ -1 +1 @@ -Subproject commit f90f1f961b2fe5e47383d5f797c6ab48382a9c3b +Subproject commit 2c0c94e4303f8ae1b18b716d16ed1ee6fcf2b864 diff --git a/Hi3Helper.EncTool.Test/Hi3Helper.EncTool.Test.csproj b/Hi3Helper.EncTool.Test/Hi3Helper.EncTool.Test.csproj index 7b361f868..6dfe0c7d1 100644 --- a/Hi3Helper.EncTool.Test/Hi3Helper.EncTool.Test.csproj +++ b/Hi3Helper.EncTool.Test/Hi3Helper.EncTool.Test.csproj @@ -2,7 +2,7 @@ Exe - net8.0 + net9.0 enable enable x64 diff --git a/Hi3Helper.EncTool.Test/packages.lock.json b/Hi3Helper.EncTool.Test/packages.lock.json index dbd9fe431..4e320c966 100644 --- a/Hi3Helper.EncTool.Test/packages.lock.json +++ b/Hi3Helper.EncTool.Test/packages.lock.json @@ -1,7 +1,7 @@ { "version": 1, "dependencies": { - "net8.0": { + "net9.0": { "Google.Protobuf": { "type": "Transitive", "resolved": "3.28.1", @@ -9,15 +9,15 @@ }, "System.IO.Hashing": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "ne1843evDugl0md7Fjzy6QjJrzsjh46ZKbhf8GwBXb5f/gw97J4bxMs0NQKifDuThh/f0bZ0e62NPl1jzTuRqA==" + "resolved": "9.0.0-rc.1.24431.7", + "contentHash": "giMiDNlne8WcLG7rsEfrETaukDUTBPn8a97QRo9LYR71O0Rb4SJ6TbfGdQ9oiBYldVPSAbSQANb5ThLZOo408Q==" }, "hi3helper.enctool": { "type": "Project", "dependencies": { "Google.Protobuf": "[3.28.1, )", "Hi3Helper.Http": "[2.0.0, )", - "System.IO.Hashing": "[8.0.0, )" + "System.IO.Hashing": "[9.0.0-rc.1.24431.7, )" } }, "hi3helper.http": { diff --git a/Hi3Helper.Http b/Hi3Helper.Http index 865527f0a..b64dfd7b9 160000 --- a/Hi3Helper.Http +++ b/Hi3Helper.Http @@ -1 +1 @@ -Subproject commit 865527f0affc11507fa429dfaa531df2a8bb478b +Subproject commit b64dfd7b9ef03c643ea13c9bb9eb1ab74321805c diff --git a/Hi3Helper.SharpDiscordRPC b/Hi3Helper.SharpDiscordRPC index c9a5866a3..8020e6be8 160000 --- a/Hi3Helper.SharpDiscordRPC +++ b/Hi3Helper.SharpDiscordRPC @@ -1 +1 @@ -Subproject commit c9a5866a38c59b81d40311683890052df78f3166 +Subproject commit 8020e6be83468bbc8fb3c8567277f24df8a96010 diff --git a/Hi3Helper.Sophon b/Hi3Helper.Sophon index a13c3ed0a..3753f80d0 160000 --- a/Hi3Helper.Sophon +++ b/Hi3Helper.Sophon @@ -1 +1 @@ -Subproject commit a13c3ed0abb418060afe9069b7a163c9d0b3450e +Subproject commit 3753f80d0dd36463b7e740beafb221cfa79b5336 diff --git a/ImageEx b/ImageEx index 7a59e314b..e01632d8e 160000 --- a/ImageEx +++ b/ImageEx @@ -1 +1 @@ -Subproject commit 7a59e314b8d4a5f1aa58e2ef7ae49ffa8f1a03da +Subproject commit e01632d8e12be7731ff2c9c514ff6be62139fc48 diff --git a/InnoSetupHelper/InnoSetupHelper.csproj b/InnoSetupHelper/InnoSetupHelper.csproj index a71a6a376..a78dc8db9 100644 --- a/InnoSetupHelper/InnoSetupHelper.csproj +++ b/InnoSetupHelper/InnoSetupHelper.csproj @@ -1,7 +1,7 @@  - net8.0 + net9.0 disable enable true @@ -18,7 +18,7 @@ - + diff --git a/InnoSetupHelper/packages.lock.json b/InnoSetupHelper/packages.lock.json index 8b552740e..e54b93f8f 100644 --- a/InnoSetupHelper/packages.lock.json +++ b/InnoSetupHelper/packages.lock.json @@ -1,12 +1,12 @@ { "version": 1, "dependencies": { - "net8.0": { + "net9.0": { "System.IO.Hashing": { "type": "Direct", - "requested": "[8.0.0, )", - "resolved": "8.0.0", - "contentHash": "ne1843evDugl0md7Fjzy6QjJrzsjh46ZKbhf8GwBXb5f/gw97J4bxMs0NQKifDuThh/f0bZ0e62NPl1jzTuRqA==" + "requested": "[9.0.0-rc.1.24431.7, )", + "resolved": "9.0.0-rc.1.24431.7", + "contentHash": "giMiDNlne8WcLG7rsEfrETaukDUTBPn8a97QRo9LYR71O0Rb4SJ6TbfGdQ9oiBYldVPSAbSQANb5ThLZOo408Q==" }, "Google.Protobuf": { "type": "Transitive", @@ -25,7 +25,7 @@ "dependencies": { "Google.Protobuf": "[3.28.1, )", "Hi3Helper.Http": "[2.0.0, )", - "System.IO.Hashing": "[8.0.0, )" + "System.IO.Hashing": "[9.0.0-rc.1.24431.7, )" } }, "hi3helper.http": { diff --git a/SevenZipExtractor b/SevenZipExtractor index e649b7466..051610dca 160000 --- a/SevenZipExtractor +++ b/SevenZipExtractor @@ -1 +1 @@ -Subproject commit e649b7466464a7ba9d5b478ea945575c75f7190f +Subproject commit 051610dcae810aba7acba778c582ddafbf6a8845 From b9f17f6c1f921a57722f32fe50709f24270cc85a Mon Sep 17 00:00:00 2001 From: Bagus Nur Listiyono Date: Sat, 14 Sep 2024 23:13:42 +0700 Subject: [PATCH 014/110] Fix build --- .github/workflows/build.yml | 2 +- .../Publish-DebugCIRelease.pubxml | 32 +++++++++++++++++++ .../Publish-PreviewRelease.pubxml | 2 +- .../Publish-StableRelease.pubxml | 2 +- 4 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 CollapseLauncher/Properties/PublishProfiles/Publish-DebugCIRelease.pubxml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bdc41dff9..96095737d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -53,7 +53,7 @@ jobs: - name: Build run: | - dotnet publish CollapseLauncher -p:PublishProfile=Publish-PreviewRelease -p:PublishDir=".\preview-build\" + dotnet publish CollapseLauncher -p:PublishProfile=Publish-DebugCIRelease -p:PublishDir=".\preview-build\" - name: Upload Artifact (Release) uses: actions/upload-artifact@v4.3.1 diff --git a/CollapseLauncher/Properties/PublishProfiles/Publish-DebugCIRelease.pubxml b/CollapseLauncher/Properties/PublishProfiles/Publish-DebugCIRelease.pubxml new file mode 100644 index 000000000..2e534588e --- /dev/null +++ b/CollapseLauncher/Properties/PublishProfiles/Publish-DebugCIRelease.pubxml @@ -0,0 +1,32 @@ + + + + + Release + x64 + ..\..\CollapseLauncher-ReleaseRepo\preview-build + FileSystem + net9.0-windows10.0.22621.0 + win-x64 + true + false + true + partial + true + false + false + false + false + false + false + true + true + false + true + false + true + Speed + + \ No newline at end of file diff --git a/CollapseLauncher/Properties/PublishProfiles/Publish-PreviewRelease.pubxml b/CollapseLauncher/Properties/PublishProfiles/Publish-PreviewRelease.pubxml index 54ccea8cb..a71bbb327 100644 --- a/CollapseLauncher/Properties/PublishProfiles/Publish-PreviewRelease.pubxml +++ b/CollapseLauncher/Properties/PublishProfiles/Publish-PreviewRelease.pubxml @@ -8,7 +8,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121. x64 ..\..\CollapseLauncher-ReleaseRepo\preview-build FileSystem - net8.0-windows10.0.22621.0 + net9.0-windows10.0.22621.0 win-x64 true false diff --git a/CollapseLauncher/Properties/PublishProfiles/Publish-StableRelease.pubxml b/CollapseLauncher/Properties/PublishProfiles/Publish-StableRelease.pubxml index b76b45239..e9f2ba1f3 100644 --- a/CollapseLauncher/Properties/PublishProfiles/Publish-StableRelease.pubxml +++ b/CollapseLauncher/Properties/PublishProfiles/Publish-StableRelease.pubxml @@ -8,7 +8,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121. x64 ..\..\CollapseLauncher-ReleaseRepo\stable-build FileSystem - net8.0-windows10.0.22621.0 + net9.0-windows10.0.22621.0 win-x64 true false From 7e462fb9d57e130b1280466be6f795913e6d0ed7 Mon Sep 17 00:00:00 2001 From: Bagus Nur Listiyono Date: Sun, 15 Sep 2024 00:01:40 +0700 Subject: [PATCH 015/110] Fix warnings about stream reader Throw read data from stream reader as we don't use them. Also use HTTPS on HI3's CGMetadata fetch if the HTTP override is disabled --- .../GameSettings/BaseClass/ImportExportBase.cs | 10 +++++----- .../InstallManagement/BaseClass/InstallManagerBase.cs | 7 +------ .../Classes/RepairManagement/Honkai/Fetch.cs | 11 +++++------ Hi3Helper.EncTool | 2 +- 4 files changed, 12 insertions(+), 18 deletions(-) diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/ImportExportBase.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/ImportExportBase.cs index 68cb4f2bd..4dc356b66 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/ImportExportBase.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/BaseClass/ImportExportBase.cs @@ -33,7 +33,7 @@ internal class ImportExportBase using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read)) { byte[] head = new byte[6]; - fs.Read(head, 0, head.Length); + _ = fs.Read(head, 0, head.Length); Logger.LogWriteLine($"Importing registry {RegistryPath}..."); @@ -425,7 +425,7 @@ private string ReadValueName(Stream stream) byte[] buffer = ArrayPool.Shared.Rent(length); try { - stream.Read(buffer, 0, length); + _ = stream.Read(buffer, 0, length); return Encoding.UTF8.GetString(buffer, 0, length); } finally @@ -439,21 +439,21 @@ private string ReadValueName(Stream stream) private long ReadInt64(Stream stream) { Span buffer = stackalloc byte[4]; - stream.Read(buffer); + _ = stream.Read(buffer); return MemoryMarshal.Read(buffer); } private int ReadInt32(Stream stream) { Span buffer = stackalloc byte[4]; - stream.Read(buffer); + _ = stream.Read(buffer); return MemoryMarshal.Read(buffer); } private short ReadInt16(Stream stream) { Span buffer = stackalloc byte[2]; - stream.Read(buffer); + _ = stream.Read(buffer); return MemoryMarshal.Read(buffer); } diff --git a/CollapseLauncher/Classes/InstallManagement/BaseClass/InstallManagerBase.cs b/CollapseLauncher/Classes/InstallManagement/BaseClass/InstallManagerBase.cs index 73c046dbb..602de9481 100644 --- a/CollapseLauncher/Classes/InstallManagement/BaseClass/InstallManagerBase.cs +++ b/CollapseLauncher/Classes/InstallManagement/BaseClass/InstallManagerBase.cs @@ -2591,12 +2591,7 @@ protected virtual bool TryGetVoiceOverResourceByLocaleCode(List localeCode) { - // If it's empty or null, return false - if (localeCode == null) - { - return false; - } - + // If it's empty, return false if (localeCode.IsEmpty) { return false; diff --git a/CollapseLauncher/Classes/RepairManagement/Honkai/Fetch.cs b/CollapseLauncher/Classes/RepairManagement/Honkai/Fetch.cs index 1b4b9f678..03a0e5b4b 100644 --- a/CollapseLauncher/Classes/RepairManagement/Honkai/Fetch.cs +++ b/CollapseLauncher/Classes/RepairManagement/Honkai/Fetch.cs @@ -220,7 +220,7 @@ private async ValueTask ThrowIfFileIsNotSenadina(Stream stream, CancellationToke ArgumentNullException.ThrowIfNull(stream); Memory header = new byte[_collapseHeader.Length]; - await stream.ReadAsync(header, token)!; + _ = await stream.ReadAsync(header, token)!; if (!header.Span.SequenceEqual(_collapseHeader)) throw new InvalidDataException($"Daftar pustaka file is corrupted! Expecting header: 0x{BinaryPrimitives.ReadInt64LittleEndian(_collapseHeader):x8} but got: 0x{BinaryPrimitives.ReadInt64LittleEndian(header.Span):x8} instead!"); } @@ -310,14 +310,13 @@ private async Task BuildVideoIndex(DownloadClient downloadClient, CacheAsset cac private async Task BuildAndEnumerateVideoVersioningFile(CancellationToken token, IEnumerable enumEntry, List assetIndex, HonkaiRepairAssetIgnore ignoredAssetIDs, string assetBundleURL) { - ArgumentNullException.ThrowIfNull(token); ArgumentNullException.ThrowIfNull(assetIndex); // Get the base URL - string baseURL = CombineURLFromString("http://" + assetBundleURL, "/Video/"); + string baseURL = CombineURLFromString(GetAppConfigValue("EnableHTTPRepairOverride").ToBool() ? "http://" : "https://" + assetBundleURL, "/Video/"); // Build video versioning file - using (StreamWriter sw = new StreamWriter(Path.Combine(_gamePath!, NormalizePath(_videoBaseLocalPath)!, "Version.txt"), false)) + await using (StreamWriter sw = new StreamWriter(Path.Combine(_gamePath!, NormalizePath(_videoBaseLocalPath)!, "Version.txt"), false)) { // Iterate the metadata to be converted into asset index in parallel await Parallel.ForEachAsync(enumEntry!, new ParallelOptions @@ -340,10 +339,10 @@ private async Task BuildAndEnumerateVideoVersioningFile(CancellationToken token, } } - #if DEBUG + #if DEBUG if (isCGIgnored) LogWriteLine($"Ignoring CG Category: {metadata.CgSubCategory} {(_audioLanguage == AudioLanguageType.Japanese ? metadata.CgPathHighBitrateJP : metadata.CgPathHighBitrateCN)}", LogType.Debug, true); - #endif + #endif if (!metadata.InStreamingAssets && isCGAvailable && !isCGIgnored) { diff --git a/Hi3Helper.EncTool b/Hi3Helper.EncTool index 2c0c94e43..e2afce0bf 160000 --- a/Hi3Helper.EncTool +++ b/Hi3Helper.EncTool @@ -1 +1 @@ -Subproject commit 2c0c94e4303f8ae1b18b716d16ed1ee6fcf2b864 +Subproject commit e2afce0bf363e7fa0679da50891b2782c9a87077 From 3240a898120deef6728b76278583c70b398eb5d5 Mon Sep 17 00:00:00 2001 From: neon-nyan Date: Mon, 16 Sep 2024 01:29:05 +0700 Subject: [PATCH 016/110] Moving TaskScheduler into its own executable This is funn't --- ClearCache.bat | 2 + CollapseLauncher.sln | 9 +- .../Classes/Helper/TaskSchedulerHelper.cs | 208 +++++++++++++++++ CollapseLauncher/CollapseLauncher.csproj | 24 +- CollapseLauncher/Program.cs | 6 +- .../XAMLs/MainApp/Pages/SettingsPage.xaml.cs | 65 +----- CollapseLauncher/packages.lock.json | 39 ---- Hi3Helper.TaskScheduler/FodyWeavers.xml | 3 + Hi3Helper.TaskScheduler/FodyWeavers.xsd | 141 +++++++++++ .../Hi3Helper.TaskScheduler.csproj | 24 ++ Hi3Helper.TaskScheduler/Program.cs | 221 ++++++++++++++++++ .../PublishProfiles/AsRelease.pubxml | 15 ++ .../Properties/launchSettings.json | 8 + 13 files changed, 652 insertions(+), 113 deletions(-) create mode 100644 CollapseLauncher/Classes/Helper/TaskSchedulerHelper.cs create mode 100644 Hi3Helper.TaskScheduler/FodyWeavers.xml create mode 100644 Hi3Helper.TaskScheduler/FodyWeavers.xsd create mode 100644 Hi3Helper.TaskScheduler/Hi3Helper.TaskScheduler.csproj create mode 100644 Hi3Helper.TaskScheduler/Program.cs create mode 100644 Hi3Helper.TaskScheduler/Properties/PublishProfiles/AsRelease.pubxml create mode 100644 Hi3Helper.TaskScheduler/Properties/launchSettings.json diff --git a/ClearCache.bat b/ClearCache.bat index 50a691b8b..da41cb4b0 100644 --- a/ClearCache.bat +++ b/ClearCache.bat @@ -13,6 +13,8 @@ echo Clearing Http cache rmdir /S /Q Hi3Helper.Http\bin && rmdir /S /Q Hi3Helper.Http\obj echo Clearing Http tester cache rmdir /S /Q Hi3Helper.Http\Test\bin && rmdir /S /Q Hi3Helper.Http\Test\obj +echo Clearing TaskScheduler cache +rmdir /S /Q Hi3Helper.TaskScheduler\bin && rmdir /S /Q Hi3Helper.TaskScheduler\obj echo Clearing HDiff cache rmdir /S /Q Hi3Helper.SharpHDiffPatch\Hi3Helper.SharpHDiffPatch\bin && rmdir /S /Q Hi3Helper.SharpHDiffPatch\Hi3Helper.SharpHDiffPatch\obj echo Clearing 2nd HDiff cache diff --git a/CollapseLauncher.sln b/CollapseLauncher.sln index aa68aa993..2d74138ef 100644 --- a/CollapseLauncher.sln +++ b/CollapseLauncher.sln @@ -24,6 +24,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hi3Helper.Sophon", "Hi3Help EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageEx", "ImageEx\ImageEx\ImageEx.csproj", "{A6AF9DE9-1A18-4C2D-B106-B68A0A7CD07D}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hi3Helper.TaskScheduler", "Hi3Helper.TaskScheduler\Hi3Helper.TaskScheduler.csproj", "{C9CBAF52-49C7-4B72-A03B-130F596E24CB}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -92,10 +94,13 @@ Global {3F87DCD0-39B7-4F8C-8F34-A0FC7C6E65A0}.Release|x64.Build.0 = Release|x64 {A6AF9DE9-1A18-4C2D-B106-B68A0A7CD07D}.Debug|x64.ActiveCfg = Debug|x64 {A6AF9DE9-1A18-4C2D-B106-B68A0A7CD07D}.Debug|x64.Build.0 = Debug|x64 - {A6AF9DE9-1A18-4C2D-B106-B68A0A7CD07D}.Publish|x64.ActiveCfg = Debug|x64 - {A6AF9DE9-1A18-4C2D-B106-B68A0A7CD07D}.Publish|x64.Build.0 = Debug|x64 + {A6AF9DE9-1A18-4C2D-B106-B68A0A7CD07D}.Publish|x64.ActiveCfg = Release|x64 + {A6AF9DE9-1A18-4C2D-B106-B68A0A7CD07D}.Publish|x64.Build.0 = Release|x64 {A6AF9DE9-1A18-4C2D-B106-B68A0A7CD07D}.Release|x64.ActiveCfg = Release|x64 {A6AF9DE9-1A18-4C2D-B106-B68A0A7CD07D}.Release|x64.Build.0 = Release|x64 + {C9CBAF52-49C7-4B72-A03B-130F596E24CB}.Debug|x64.ActiveCfg = Release|x64 + {C9CBAF52-49C7-4B72-A03B-130F596E24CB}.Publish|x64.ActiveCfg = Release|x64 + {C9CBAF52-49C7-4B72-A03B-130F596E24CB}.Release|x64.ActiveCfg = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/CollapseLauncher/Classes/Helper/TaskSchedulerHelper.cs b/CollapseLauncher/Classes/Helper/TaskSchedulerHelper.cs new file mode 100644 index 000000000..7f74abb90 --- /dev/null +++ b/CollapseLauncher/Classes/Helper/TaskSchedulerHelper.cs @@ -0,0 +1,208 @@ +using Hi3Helper; +using Hi3Helper.Shared.Region; +using System; +using System.Diagnostics; +using System.IO; +using System.Text; + +namespace CollapseLauncher.Helper +{ + internal static class TaskSchedulerHelper + { + private const string _collapseStartupTaskName = "CollapseLauncherStartupTask"; + + internal static bool IsInitialized = false; + internal static bool Cached_IsOnTrayEnabled = false; + internal static bool Cached_IsEnabled = false; + + internal static bool IsOnTrayEnabled() + { + if (!IsInitialized) + InvokeGetStatusCommand(); + + return Cached_IsOnTrayEnabled; + } + + internal static bool IsEnabled() + { + if (!IsInitialized) + InvokeGetStatusCommand(); + + return Cached_IsEnabled; + } + + internal static void InvokeGetStatusCommand() + { + // Get current stub or main executable path + string currentExecPath = MainEntryPoint.FindCollapseStubPath(); + + // Build the argument and mode to set + StringBuilder argumentBuilder = new StringBuilder(); + argumentBuilder.Append("IsEnabled"); + + // Append task name and stub path + AppendTaskNameAndPathArgument(argumentBuilder); + + // Store argument builder as string + string argumentString = argumentBuilder.ToString(); + + // Invoke command and get return code + int returnCode = GetInvokeCommandReturnCode(argumentString); + + (Cached_IsEnabled, Cached_IsOnTrayEnabled) = returnCode switch + { + // -1 means task is disabled with tray enabled + -1 => (false, true), + // 0 means task is disabled with tray disabled + 0 => (false, false), + // 1 means task is enabled with tray disabled + 1 => (true, false), + // 2 means task is enabled with tray enabled + 2 => (true, true), + // Otherwise, return both disabled (due to failure) + _ => (false, false) + }; + + // Print init determination + CheckInitDetermination(returnCode); + } + + private static void CheckInitDetermination(int returnCode) + { + // If the return code is within range, then set as initialized + if (returnCode > -2 && returnCode < 3) + { + // Set as initialized + IsInitialized = true; + } + // Otherwise, log the return code + else + { + string reason = returnCode switch + { + int.MaxValue => "ARGUMENT_INVALID", + int.MinValue => "UNHANDLED_ERROR", + short.MaxValue => "INTERNALINVOKE_ERROR", + short.MinValue => "APPLET_NOTFOUND", + _ => $"UNKNOWN_{returnCode}" + }; + Logger.LogWriteLine($"Error while getting task status from applet with reason: {reason}", LogType.Error, true); + } + } + + internal static void ToggleTrayEnabled(bool isEnabled) + { + Cached_IsOnTrayEnabled = isEnabled; + InvokeToggleCommand(); + } + + internal static void ToggleEnabled(bool isEnabled) + { + Cached_IsEnabled = isEnabled; + InvokeToggleCommand(); + } + + private static void InvokeToggleCommand() + { + // Build the argument and mode to set + StringBuilder argumentBuilder = new StringBuilder(); + argumentBuilder.Append(Cached_IsEnabled ? "Enable" : "Disable"); + + // Append argument whether to toggle the tray or not + if (Cached_IsOnTrayEnabled) + argumentBuilder.Append("ToTray"); + + // Append task name and stub path + AppendTaskNameAndPathArgument(argumentBuilder); + + // Store argument builder as string + string argumentString = argumentBuilder.ToString(); + + // Invoke applet + int returnCode = GetInvokeCommandReturnCode(argumentString); + + // Print init determination + CheckInitDetermination(returnCode); + } + + private static void AppendTaskNameAndPathArgument(StringBuilder argumentBuilder) + { + // Get current stub or main executable path + string currentExecPath = MainEntryPoint.FindCollapseStubPath(); + + // Build argument to the task name + argumentBuilder.Append(" \""); + argumentBuilder.Append(_collapseStartupTaskName); + argumentBuilder.Append('"'); + + // Build argument to the executable path + argumentBuilder.Append(" \""); + argumentBuilder.Append(currentExecPath); + argumentBuilder.Append('"'); + } + + private static int GetInvokeCommandReturnCode(string argument) + { + const string RETURNVALMARK = "RETURNVAL_"; + + // Get the applet path and check if the file exist + string appletPath = Path.Combine(LauncherConfig.AppFolder, "Lib", "win-x64", "Hi3Helper.TaskScheduler.exe"); + if (!File.Exists(appletPath)) + { + Logger.LogWriteLine($"Task Scheduler Applet does not exist in this path: {appletPath}", LogType.Error, true); + return short.MinValue; + } + + // Try make process instance for the applet + using (Process process = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = appletPath, + Arguments = argument, + UseShellExecute = false, + RedirectStandardOutput = true, + CreateNoWindow = true + } + }) + { + int lastErrCode = short.MaxValue; + try + { + // Start the applet and wait until it exit. + process.Start(); + while (!process.StandardOutput.EndOfStream) + { + string consoleStdOut = process.StandardOutput.ReadLine(); + Logger.LogWriteLine("[TaskScheduler] " + consoleStdOut, LogType.Debug, true); + + // Parse if it has RETURNVAL_ + if (consoleStdOut.StartsWith(RETURNVALMARK)) + { + ReadOnlySpan span = consoleStdOut.AsSpan(RETURNVALMARK.Length); + if (int.TryParse(span, null, out int resultReturnCode)) + { + lastErrCode = resultReturnCode; + } + } + } + + if (lastErrCode != process.ExitCode) + { + lastErrCode = process.ExitCode; + } + process.WaitForExit(); + } + catch (Exception ex) + { + // If error happened, then return. + Logger.LogWriteLine($"An error has occurred while invoking Task Scheduler applet!\r\n{ex}", LogType.Error, true); + return short.MaxValue; + } + + // Get return code + return lastErrCode; + } + } + } +} diff --git a/CollapseLauncher/CollapseLauncher.csproj b/CollapseLauncher/CollapseLauncher.csproj index 9bd083095..442218986 100644 --- a/CollapseLauncher/CollapseLauncher.csproj +++ b/CollapseLauncher/CollapseLauncher.csproj @@ -1,4 +1,4 @@ - + WinExe @@ -113,7 +113,6 @@ - @@ -255,12 +254,17 @@ --> - - - - - - - - + + + + + + + + + + + + + diff --git a/CollapseLauncher/Program.cs b/CollapseLauncher/Program.cs index 9f4224b56..fa1c7af72 100644 --- a/CollapseLauncher/Program.cs +++ b/CollapseLauncher/Program.cs @@ -1,4 +1,5 @@ -using CollapseLauncher.Helper.Update; +using CollapseLauncher.Helper; +using CollapseLauncher.Helper.Update; using Hi3Helper; using Hi3Helper.Http.Legacy; using Hi3Helper.Shared.ClassStruct; @@ -88,6 +89,9 @@ public static void Main(params string[] args) ParseArguments(args); InitializeAppSettings(); + // Initialize TaskSchedulerHelper + TaskSchedulerHelper.InvokeGetStatusCommand(); + HttpLogInvoker.DownloadLog += HttpClientLogWatcher!; switch (m_appMode) diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/SettingsPage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/SettingsPage.xaml.cs index 9b5a81366..2857c553d 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/SettingsPage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/SettingsPage.xaml.cs @@ -22,7 +22,6 @@ using Microsoft.UI.Xaml.Input; using Microsoft.UI.Xaml.Media; using Microsoft.UI.Xaml.Media.Animation; - using Microsoft.Win32.TaskScheduler; using System; using System.Collections.Generic; using System.Diagnostics; @@ -39,7 +38,6 @@ using static Hi3Helper.Shared.Region.LauncherConfig; using CollapseUIExt = CollapseLauncher.Extension.UIElementExtensions; using MediaType = CollapseLauncher.Helper.Background.BackgroundMediaUtility.MediaType; - using TaskSched = Microsoft.Win32.TaskScheduler.Task; using Task = System.Threading.Tasks.Task; // ReSharper disable CheckNamespace @@ -53,7 +51,6 @@ public sealed partial class SettingsPage : Page { #region Properties - private const string _collapseStartupTaskName = "CollapseLauncherStartupTask"; private const string RepoUrl = "https://github.com/CollapseLauncher/Collapse/commit/"; private readonly bool _initIsInstantRegionChange; @@ -443,26 +440,6 @@ private void Egg(object sender, PointerRoutedEventArgs e) HerLegacy.Visibility = Visibility.Visible; } - private TaskSched CreateScheduledTask(string taskName) - { - string collapseStartupTarget = MainEntryPoint.FindCollapseStubPath(); - - using TaskService ts = new TaskService(); - - TaskDefinition taskDefinition = TaskService.Instance.NewTask(); - taskDefinition.RegistrationInfo.Author = "CollapseLauncher"; - taskDefinition.RegistrationInfo.Description = "Run Collapse Launcher automatically when computer starts"; - taskDefinition.Principal.LogonType = TaskLogonType.InteractiveToken; - taskDefinition.Principal.RunLevel = TaskRunLevel.Highest; - taskDefinition.Settings.Enabled = false; - taskDefinition.Triggers.Add(new LogonTrigger()); - taskDefinition.Actions.Add(new ExecAction(collapseStartupTarget)); - - TaskSched task = TaskService.Instance.RootFolder.RegisterTaskDefinition(taskName, taskDefinition); - taskDefinition.Dispose(); - return task; - } - private void EnableHeaderMouseEvent(object sender, RoutedEventArgs e) { ((UIElement)VisualTreeHelper.GetParent((DependencyObject)sender)).IsHitTestVisible = true; @@ -1018,13 +995,7 @@ private bool IsLaunchOnStartup { get { - using TaskService ts = new TaskService(); - - TaskSched task = ts.GetTask(_collapseStartupTaskName); - if (task == null) task = CreateScheduledTask(_collapseStartupTaskName); - - bool value = task.Definition.Settings.Enabled; - task.Dispose(); + bool value = TaskSchedulerHelper.IsEnabled(); if (value) StartupToTrayToggle.Visibility = Visibility.Visible; else StartupToTrayToggle.Visibility = Visibility.Collapsed; @@ -1033,12 +1004,7 @@ private bool IsLaunchOnStartup } set { - using TaskService ts = new TaskService(); - - TaskSched task = ts.GetTask(_collapseStartupTaskName); - task.Definition.Settings.Enabled = value; - task.RegisterChanges(); - task.Dispose(); + TaskSchedulerHelper.ToggleEnabled(value); if (value) StartupToTrayToggle.Visibility = Visibility.Visible; else StartupToTrayToggle.Visibility = Visibility.Collapsed; @@ -1047,31 +1013,8 @@ private bool IsLaunchOnStartup private bool IsStartupToTray { - get - { - using TaskService ts = new TaskService(); - - TaskSched task = ts.GetTask(_collapseStartupTaskName); - if (task == null) task = CreateScheduledTask(_collapseStartupTaskName); - - bool? value = false; - if (task.Definition.Actions[0] is ExecAction execAction) - value = execAction.Arguments?.Trim().Contains("tray", StringComparison.OrdinalIgnoreCase); - - task.Dispose(); - return value ?? false; - } - set - { - string collapseStartupTarget = MainEntryPoint.FindCollapseStubPath(); - using TaskService ts = new TaskService(); - - TaskSched task = ts.GetTask(_collapseStartupTaskName); - task.Definition.Actions.Clear(); - task.Definition.Actions.Add(new ExecAction(collapseStartupTarget, value ? "tray" : null)); - task.RegisterChanges(); - task.Dispose(); - } + get => TaskSchedulerHelper.IsOnTrayEnabled(); + set => TaskSchedulerHelper.ToggleTrayEnabled(value); } private bool IsEnableSophon diff --git a/CollapseLauncher/packages.lock.json b/CollapseLauncher/packages.lock.json index 644a06bc1..4e6ba6b25 100644 --- a/CollapseLauncher/packages.lock.json +++ b/CollapseLauncher/packages.lock.json @@ -275,17 +275,6 @@ "resolved": "9.0.0-rc.1.24431.7", "contentHash": "f6EHMnCjPQbwIftWreac1RsDolPpRT47lBfdHM/7Y/phtdewEtn3+XKWUVZ5gpB04tpab5y/XaZRkq28p3xzFQ==" }, - "TaskScheduler": { - "type": "Direct", - "requested": "[2.11.0, )", - "resolved": "2.11.0", - "contentHash": "p9wH58XSNIyUtO7PIFAEldaKUzpYmlj+YWAfnUqBKnGxIZRY51I9BrsBGJijUVwlxrgmLLPUigRIv2ZTD4uPJA==", - "dependencies": { - "Microsoft.Win32.Registry": "5.0.0", - "System.Diagnostics.EventLog": "8.0.0", - "System.Security.AccessControl": "6.0.1" - } - }, "CommunityToolkit.WinUI.Animations": { "type": "Transitive", "resolved": "8.1.240821", @@ -352,15 +341,6 @@ "resolved": "1.0.2651.64", "contentHash": "f5sc/vcAoTCTEW7Nqzp4galAuTRguZViw8ksn+Nx2uskEBPm0/ubzy6gVjvXS/P96jLS89C8T9I0hPc417xpNg==" }, - "Microsoft.Win32.Registry": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", - "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" - } - }, "Microsoft.Win32.SystemEvents": { "type": "Transitive", "resolved": "9.0.0-rc.1.24431.7", @@ -379,11 +359,6 @@ "resolved": "9.0.0-rc.1.24431.7", "contentHash": "giMiDNlne8WcLG7rsEfrETaukDUTBPn8a97QRo9LYR71O0Rb4SJ6TbfGdQ9oiBYldVPSAbSQANb5ThLZOo408Q==" }, - "System.Security.Principal.Windows": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" - }, "System.Threading.Tasks.Extensions": { "type": "Transitive", "resolved": "4.5.4", @@ -510,24 +485,10 @@ "resolved": "1.0.2651.64", "contentHash": "f5sc/vcAoTCTEW7Nqzp4galAuTRguZViw8ksn+Nx2uskEBPm0/ubzy6gVjvXS/P96jLS89C8T9I0hPc417xpNg==" }, - "Microsoft.Win32.Registry": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", - "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" - } - }, "Microsoft.Win32.SystemEvents": { "type": "Transitive", "resolved": "9.0.0-rc.1.24431.7", "contentHash": "IK3fwmfSCVArYWKjPUTEeD9nRXF1VENiFb4+hlqWuUxfP1hPVw41y/sOEdLZtRB+BVdgh6NaZjetrVhEuG4Iyg==" - }, - "System.Security.Principal.Windows": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" } } } diff --git a/Hi3Helper.TaskScheduler/FodyWeavers.xml b/Hi3Helper.TaskScheduler/FodyWeavers.xml new file mode 100644 index 000000000..5029e7060 --- /dev/null +++ b/Hi3Helper.TaskScheduler/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Hi3Helper.TaskScheduler/FodyWeavers.xsd b/Hi3Helper.TaskScheduler/FodyWeavers.xsd new file mode 100644 index 000000000..05e92c114 --- /dev/null +++ b/Hi3Helper.TaskScheduler/FodyWeavers.xsd @@ -0,0 +1,141 @@ + + + + + + + + + + + + A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks + + + + + A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks. + + + + + A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks + + + + + A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks. + + + + + A list of unmanaged 32 bit assembly names to include, delimited with line breaks. + + + + + A list of unmanaged 64 bit assembly names to include, delimited with line breaks. + + + + + The order of preloaded assemblies, delimited with line breaks. + + + + + + This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file. + + + + + Controls if .pdbs for reference assemblies are also embedded. + + + + + Controls if runtime assemblies are also embedded. + + + + + Controls whether the runtime assemblies are embedded with their full path or only with their assembly name. + + + + + Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option. + + + + + As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off. + + + + + Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code. + + + + + Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior. + + + + + A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with | + + + + + A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |. + + + + + A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with | + + + + + A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with |. + + + + + A list of unmanaged 32 bit assembly names to include, delimited with |. + + + + + A list of unmanaged 64 bit assembly names to include, delimited with |. + + + + + The order of preloaded assemblies, delimited with |. + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/Hi3Helper.TaskScheduler/Hi3Helper.TaskScheduler.csproj b/Hi3Helper.TaskScheduler/Hi3Helper.TaskScheduler.csproj new file mode 100644 index 000000000..6dcbe1d58 --- /dev/null +++ b/Hi3Helper.TaskScheduler/Hi3Helper.TaskScheduler.csproj @@ -0,0 +1,24 @@ + + + + Exe + netframework462 + disable + x64 + en + portable + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + diff --git a/Hi3Helper.TaskScheduler/Program.cs b/Hi3Helper.TaskScheduler/Program.cs new file mode 100644 index 000000000..c289c49a2 --- /dev/null +++ b/Hi3Helper.TaskScheduler/Program.cs @@ -0,0 +1,221 @@ +using Microsoft.Win32.TaskScheduler; +using System; +using System.Diagnostics; +using System.IO; +using System.Linq; +using TaskSched = Microsoft.Win32.TaskScheduler.Task; + +namespace Hi3Helper.TaskScheduler +{ + public static class ReturnExtension + { + public static int ReturnValAsConsole(this int returnVal) + { + Console.WriteLine($"RETURNVAL_{returnVal}"); + return returnVal; + } + } + + public class Program + { + static int PrintUsage() + { + string executableName = Path.GetFileNameWithoutExtension(Process.GetCurrentProcess().MainModule.FileName); + Console.WriteLine($"Usage:\r\n{executableName} [IsEnabled] \"Scheduler name\" \"Executable path\""); + Console.WriteLine($"{executableName} [Enable] \"Scheduler name\" \"Executable path\""); + Console.WriteLine($"{executableName} [EnableToTray] \"Scheduler name\" \"Executable path\""); + Console.WriteLine($"{executableName} [Disable] \"Scheduler name\" \"Executable path\""); + Console.WriteLine($"{executableName} [DisableToTray] \"Scheduler name\" \"Executable path\""); + return int.MaxValue; + } + + static int Main(string[] args) + { + try + { + if (args.Length < 3) + return PrintUsage().ReturnValAsConsole(); + + string action = args[0].ToLower(); + string schedName = args[1]; + string execPath = args[2]; + + switch (action) + { + case "isenabled": + return IsEnabled(schedName, execPath).ReturnValAsConsole(); + case "enable": + ToggleTask(true, false, schedName, execPath); + break; + case "enabletotray": + ToggleTask(true, true, schedName, execPath); + break; + case "disable": + ToggleTask(false, false, schedName, execPath); + break; + case "disabletotray": + ToggleTask(false, true, schedName, execPath); + break; + default: + return PrintUsage().ReturnValAsConsole(); + } + + WriteConsole($"Operation: {action} \"{schedName}\" \"{execPath}\" has been executed!"); + } + catch (Exception ex) + { + WriteConsole($"An unexpected error has occurred!\r\n{ex}"); + return int.MinValue.ReturnValAsConsole(); + } + + return 0.ReturnValAsConsole(); + } + + static TaskSched Create(TaskService taskService, string schedName, string execPath) + { + using (TaskDefinition taskDefinition = TaskService.Instance.NewTask()) + { + taskDefinition.RegistrationInfo.Author = "CollapseLauncher"; + taskDefinition.RegistrationInfo.Description = "Run Collapse Launcher automatically when computer starts"; + taskDefinition.Principal.LogonType = TaskLogonType.InteractiveToken; + taskDefinition.Principal.RunLevel = TaskRunLevel.Highest; + taskDefinition.Settings.Enabled = false; + taskDefinition.Triggers.Add(new LogonTrigger()); + taskDefinition.Actions.Add(new ExecAction(execPath)); + + TaskSched task = taskService.RootFolder.RegisterTaskDefinition(schedName, taskDefinition); + WriteConsole($"New task schedule has been created!"); + return task; + } + } + + static void TryDelete(TaskService taskService, string schedName) + { + // Try get the tasks + TaskSched[] tasks = taskService.FindAllTasks(new System.Text.RegularExpressions.Regex(schedName), false); + + // If null, then ignore + if (tasks == null || tasks.Length == 0) + { + WriteConsole($"None of the existing task: {schedName} exist but trying to delete, ignoring!"); + return; + } + + // Remove possible tasks + foreach (TaskSched task in tasks) + { + using (task) + { + WriteConsole($"Deleting redundant task: {task.Name}"); + taskService.RootFolder.DeleteTask(task.Name); + } + } + } + + static TaskSched GetExistingTask(TaskService taskService, string schedName, string execPath) + { + // Try get the tasks + TaskSched[] tasks = taskService.FindAllTasks(new System.Text.RegularExpressions.Regex(schedName), false); + + // Try get the first task + TaskSched task = tasks? + .FirstOrDefault(x => + x.Name.Equals(schedName, StringComparison.OrdinalIgnoreCase)); + + // Return null as empty + if (task == null) + { + return null; + } + + // Get actionPath + string actionPath = task.Definition.Actions?.FirstOrDefault()?.ToString(); + + // If actionPath is null, then return null as empty + if (string.IsNullOrEmpty(actionPath)) + { + return null; + } + + // if actionPath isn't matched, then replace with current executable path + if (!actionPath.StartsWith(execPath, StringComparison.OrdinalIgnoreCase)) + { + // Check if the last action path runs on tray + bool isLastHasTray = actionPath.EndsWith("tray", StringComparison.OrdinalIgnoreCase); + + // Register changes + task.Definition.Actions.Clear(); + task.Definition.Actions.Add(new ExecAction(execPath, isLastHasTray ? "tray" : null)); + task.RegisterChanges(); + } + + // If the task matches, then return the task + return task; + } + + static int IsEnabled(string schedName, string execPath) + { + using (TaskService taskService = new TaskService()) + { + // Get the task + TaskSched task = GetExistingTask(taskService, schedName, execPath); + + // If the task is not null, then do further check + if (task != null) + { + // Check if it's enabled with tray + bool isOnTray = task.Definition?.Actions?.FirstOrDefault()?.ToString()?.EndsWith("tray", StringComparison.OrdinalIgnoreCase) ?? false; + + // If the task definition is enabled, then return 1 (true) or 2 (true with tray) + if (task.Definition.Settings.Enabled) + return isOnTray ? 2 : 1; + + // Otherwise, if the task exist but not enabled, then return 0 (false) or -1 (false with tray) + return isOnTray ? -1 : 0; + } + } + + // Otherwise, return 0 + return 0; + } + + static void ToggleTask(bool isEnabled, bool isStartupToTray, string schedName, string execPath) + { + using (TaskService taskService = new TaskService()) + { + // Try get existing task + TaskSched task = GetExistingTask(taskService, schedName, execPath); + + try + { + // If the task is null due to its' non existence or + // there are some unmatched tasks, then try recreate the task + // by try deleting and create a new one. + if (task == null) + { + TryDelete(taskService, schedName); + task = Create(taskService, schedName, execPath); + } + + // Try clear the existing actions and set the new one + task.Definition.Actions.Clear(); + task.Definition.Actions.Add(new ExecAction(execPath, isStartupToTray ? "tray" : null)); + task.Definition.Settings.Enabled = isEnabled; + } + finally + { + // Register the changes if it's not null. + if (task != null) + { + task.RegisterChanges(); + task.Dispose(); + WriteConsole($"ToggledStatus: isEnabled -> {isEnabled} & isStartupToTray -> {isStartupToTray}"); + } + } + } + } + + static void WriteConsole(string message) => + Console.WriteLine(message); + } +} diff --git a/Hi3Helper.TaskScheduler/Properties/PublishProfiles/AsRelease.pubxml b/Hi3Helper.TaskScheduler/Properties/PublishProfiles/AsRelease.pubxml new file mode 100644 index 000000000..7d3019a67 --- /dev/null +++ b/Hi3Helper.TaskScheduler/Properties/PublishProfiles/AsRelease.pubxml @@ -0,0 +1,15 @@ + + + + + Release + x64 + bin\publish + FileSystem + <_TargetId>Folder + netframework462 + win-x64 + + \ No newline at end of file diff --git a/Hi3Helper.TaskScheduler/Properties/launchSettings.json b/Hi3Helper.TaskScheduler/Properties/launchSettings.json new file mode 100644 index 000000000..633b3ccd4 --- /dev/null +++ b/Hi3Helper.TaskScheduler/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "Hi3Helper.TaskScheduler": { + "commandName": "Project", + "commandLineArgs": "IsEnabled \"CollapseLauncherStartupTask\" \"E:\\myGit\\Collapse\\CollapseLauncher\\bin\\x64\\Debug\\net9.0-windows10.0.22621.0\\win-x64\\CollapseLauncher.exe\"" + } + } +} \ No newline at end of file From 97f0ca611b24c9c40dd28a268937739ceeac7a6b Mon Sep 17 00:00:00 2001 From: neon-nyan Date: Mon, 16 Sep 2024 17:53:07 +0700 Subject: [PATCH 017/110] Enable NativeAOT compilation on Preview and Debug --- CollapseLauncher/CollapseLauncher.csproj | 3 + .../Publish-PreviewRelease.pubxml | 1 + .../XAMLs/MainApp/Pages/HomePage.xaml.cs | 64 +++++++++++++++---- .../XAMLs/MainApp/Pages/RepairPage.xaml | 32 +++++----- .../XAMLs/MainApp/WebView2FramePage.xaml.cs | 15 ++++- Hi3Helper.Core/packages.lock.json | 28 ++++++++ InnoSetupHelper/packages.lock.json | 28 ++++++++ 7 files changed, 142 insertions(+), 29 deletions(-) diff --git a/CollapseLauncher/CollapseLauncher.csproj b/CollapseLauncher/CollapseLauncher.csproj index 442218986..479775cbc 100644 --- a/CollapseLauncher/CollapseLauncher.csproj +++ b/CollapseLauncher/CollapseLauncher.csproj @@ -59,6 +59,8 @@ --> DISABLE_XAML_GENERATED_MAIN;USENEWZIPDECOMPRESS;ENABLEHTTPREPAIR;DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION;PREVIEW;DUMPGIJSON;SIMULATEGIHDR;GSPBYPASSGAMERUNNING;MHYPLUGINSUPPORT + true + partial DISABLE_XAML_GENERATED_MAIN;USENEWZIPDECOMPRESS;ENABLEHTTPREPAIR;DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION;PREVIEW;MHYPLUGINSUPPORT @@ -104,6 +106,7 @@ + diff --git a/CollapseLauncher/Properties/PublishProfiles/Publish-PreviewRelease.pubxml b/CollapseLauncher/Properties/PublishProfiles/Publish-PreviewRelease.pubxml index a71bbb327..cd033b6df 100644 --- a/CollapseLauncher/Properties/PublishProfiles/Publish-PreviewRelease.pubxml +++ b/CollapseLauncher/Properties/PublishProfiles/Publish-PreviewRelease.pubxml @@ -13,6 +13,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121. true false true + true partial true false diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs index 6f726f610..bebff5478 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs @@ -2,6 +2,7 @@ using CollapseLauncher.DiscordPresence; #endif using CollapseLauncher.CustomControls; +using CollapseLauncher.Helper.LauncherApiLoader.Sophon; using CollapseLauncher.Dialogs; using CollapseLauncher.Extension; using CollapseLauncher.FileDialogCOM; @@ -39,7 +40,6 @@ using System.IO; using System.Linq; using System.Numerics; -using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using System.Threading; @@ -429,9 +429,9 @@ private void FadeInSocMedButton(object sender, PointerRoutedEventArgs e) Button btn = (Button)sender; btn.Translation = Shadow16; - Grid iconGrid = btn.FindDescendant(); - Image iconFirst = iconGrid!.FindDescendant("Icon") as Image; - Image iconSecond = iconGrid!.FindDescendant("IconHover") as Image; + Grid iconGrid = btn.FindDescendant(); + FrameworkElement iconFirst = iconGrid!.FindDescendant("Icon"); + FrameworkElement iconSecond = iconGrid!.FindDescendant("IconHover"); ElementScaleOutHoveredPointerEnteredInner(iconGrid, 0, -2); @@ -445,16 +445,16 @@ private void FadeOutSocMedButton(object sender, PointerRoutedEventArgs e) Button btn = (Button)sender; btn.Translation = new Vector3(0); - Flyout flyout = btn.Resources["SocMedFlyout"] as Flyout; + FlyoutBase flyout = btn.Flyout; Point pos = e.GetCurrentPoint(btn).Position; if (pos.Y <= 0 || pos.Y >= btn.Height || pos.X <= -8 || pos.X >= btn.Width) { - flyout!.Hide(); + flyout?.Hide(); } - Grid iconGrid = btn.FindDescendant(); - Image iconFirst = iconGrid!.FindDescendant("Icon") as Image; - Image iconSecond = iconGrid!.FindDescendant("IconHover") as Image; + Grid iconGrid = btn.FindDescendant(); + FrameworkElement iconFirst = iconGrid!.FindDescendant("Icon"); + FrameworkElement iconSecond = iconGrid!.FindDescendant("IconHover"); ElementScaleInHoveredPointerExitedInner(iconGrid, 0, -2); @@ -525,19 +525,43 @@ private void OpenSocMedLink(object sender, RoutedEventArgs e) private void ShowSocMedFlyout(object sender, RoutedEventArgs e) { ToolTip tooltip = sender as ToolTip; - FlyoutBase.ShowAttachedFlyout(tooltip!.Tag as FrameworkElement); + if (tooltip?.Tag is Button button) + { + Flyout flyout = button.Flyout as Flyout; + if (flyout != null) + { + Panel contextPanel = flyout.Content as Panel; + if (contextPanel != null && contextPanel.Tag is LauncherGameNewsSocialMedia socMedData) + { + if (!socMedData.IsHasDescription && !socMedData.IsHasLinks && !socMedData.IsHasQr) + { + return; + } + } + } + } + + + FlyoutBase.ShowAttachedFlyout(tooltip?.Tag as FrameworkElement); } private void HideSocMedFlyout(object sender, RoutedEventArgs e) { - Flyout flyout = ((StackPanel)sender).Tag as Flyout; - flyout!.Hide(); + Grid dummyGrid = (sender as Panel).FindChild(); + if (dummyGrid != null) + { + Flyout flyout = dummyGrid.Tag as Flyout; + flyout?.Hide(); + } } private void OnLoadedSocMedFlyout(object sender, RoutedEventArgs e) { // Prevent the flyout showing when there is no content visible StackPanel stackPanel = sender as StackPanel; + + ApplySocialMediaBinding(stackPanel); + bool visible = false; foreach (var child in stackPanel!.Children) { @@ -2880,6 +2904,22 @@ private void ApplyShadowToImageElement(object sender, RoutedEventArgs e) isStart = false; } } + + foreach (ImageEx.ImageEx imageElement in panel.Children.OfType()) + { + imageElement.ApplyDropShadow(opacity: 0.5f); + if (isStart) + { + imageElement.Opacity = 0.0f; + imageElement.Loaded += (_, _) => + { + Compositor compositor = imageElement.GetElementCompositor(); + imageElement.StartAnimationDetached(TimeSpan.FromSeconds(0.25f), + compositor.CreateScalarKeyFrameAnimation("Opacity", 1.0f)); + }; + isStart = false; + } + } } } diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/RepairPage.xaml b/CollapseLauncher/XAMLs/MainApp/Pages/RepairPage.xaml index 59fa23a19..422710818 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/RepairPage.xaml +++ b/CollapseLauncher/XAMLs/MainApp/Pages/RepairPage.xaml @@ -228,44 +228,44 @@ Spacing="8"> diff --git a/CollapseLauncher/XAMLs/MainApp/WebView2FramePage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/WebView2FramePage.xaml.cs index 3ff66179a..68f7663cf 100644 --- a/CollapseLauncher/XAMLs/MainApp/WebView2FramePage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/WebView2FramePage.xaml.cs @@ -94,7 +94,20 @@ private void SetWebView2Bindings() } private void CoreWebView2_DocumentTitleChanged(CoreWebView2 sender, object args) => WebViewWindowTitle.Text = sender.DocumentTitle; - private void WebView2Window_CoreWebView2Initialized(WebView2 sender, CoreWebView2InitializedEventArgs args) => sender.CoreWebView2.DocumentTitleChanged += CoreWebView2_DocumentTitleChanged; + private void WebView2Window_CoreWebView2Initialized(WebView2 sender, CoreWebView2InitializedEventArgs args) + { + try + { + sender.CoreWebView2.DocumentTitleChanged += CoreWebView2_DocumentTitleChanged; + } + catch (Exception ex) + { + if (ex is NotSupportedException nsEx) + { + LogWriteLine($"Half-baked NativeAOT Bug (nice MSFT!) :) https://github.com/MicrosoftEdge/WebView2Feedback/issues/4783\r\n{nsEx}", LogType.Error, true); + } + } + } private void WebView2Window_PageLoaded(WebView2 sender, CoreWebView2NavigationCompletedEventArgs args) => WebView2LoadingStatus.IsIndeterminate = false; private void WebView2Window_PageLoading(WebView2 sender, CoreWebView2NavigationStartingEventArgs args) => WebView2LoadingStatus.IsIndeterminate = true; private void WebView2BackBtn_Click(object sender, RoutedEventArgs e) => WebView2Runtime.GoBack(); diff --git a/Hi3Helper.Core/packages.lock.json b/Hi3Helper.Core/packages.lock.json index 4e320c966..26197f47f 100644 --- a/Hi3Helper.Core/packages.lock.json +++ b/Hi3Helper.Core/packages.lock.json @@ -2,6 +2,18 @@ "version": 1, "dependencies": { "net9.0": { + "Microsoft.DotNet.ILCompiler": { + "type": "Direct", + "requested": "[9.0.0-rc.1.24431.7, )", + "resolved": "9.0.0-rc.1.24431.7", + "contentHash": "FN38PfBwjKY4D753sz6GDSnvtHU9UUPb9fNAcPwImOn2j8SyUFZMnVmMVwnYX4dlDB9XL98m4g6szsjIx5UWgg==" + }, + "Microsoft.NET.ILLink.Tasks": { + "type": "Direct", + "requested": "[9.0.0-rc.1.24431.7, )", + "resolved": "9.0.0-rc.1.24431.7", + "contentHash": "B8jHIIszeUdDY+O4G1/xPOuhrriwsv9Z8/u3XSOAqc8Z0mQCeLBKg2oLGElDZeRF8wWXDGklAKY3f0IHopyXkQ==" + }, "Google.Protobuf": { "type": "Transitive", "resolved": "3.28.1", @@ -23,6 +35,22 @@ "hi3helper.http": { "type": "Project" } + }, + "net9.0/win-x64": { + "Microsoft.DotNet.ILCompiler": { + "type": "Direct", + "requested": "[9.0.0-rc.1.24431.7, )", + "resolved": "9.0.0-rc.1.24431.7", + "contentHash": "FN38PfBwjKY4D753sz6GDSnvtHU9UUPb9fNAcPwImOn2j8SyUFZMnVmMVwnYX4dlDB9XL98m4g6szsjIx5UWgg==", + "dependencies": { + "runtime.win-x64.Microsoft.DotNet.ILCompiler": "9.0.0-rc.1.24431.7" + } + }, + "runtime.win-x64.Microsoft.DotNet.ILCompiler": { + "type": "Transitive", + "resolved": "9.0.0-rc.1.24431.7", + "contentHash": "hOXKEHhsK/sWwrMEL1Yrfj0Mw1WnsBsi8OoEEEmS4hgOxo39Kic0aap2nKm9eWRl8Fe5S+K10a2nVkt/T2AF7Q==" + } } } } \ No newline at end of file diff --git a/InnoSetupHelper/packages.lock.json b/InnoSetupHelper/packages.lock.json index e54b93f8f..bb1fc58ff 100644 --- a/InnoSetupHelper/packages.lock.json +++ b/InnoSetupHelper/packages.lock.json @@ -2,6 +2,18 @@ "version": 1, "dependencies": { "net9.0": { + "Microsoft.DotNet.ILCompiler": { + "type": "Direct", + "requested": "[9.0.0-rc.1.24431.7, )", + "resolved": "9.0.0-rc.1.24431.7", + "contentHash": "FN38PfBwjKY4D753sz6GDSnvtHU9UUPb9fNAcPwImOn2j8SyUFZMnVmMVwnYX4dlDB9XL98m4g6szsjIx5UWgg==" + }, + "Microsoft.NET.ILLink.Tasks": { + "type": "Direct", + "requested": "[9.0.0-rc.1.24431.7, )", + "resolved": "9.0.0-rc.1.24431.7", + "contentHash": "B8jHIIszeUdDY+O4G1/xPOuhrriwsv9Z8/u3XSOAqc8Z0mQCeLBKg2oLGElDZeRF8wWXDGklAKY3f0IHopyXkQ==" + }, "System.IO.Hashing": { "type": "Direct", "requested": "[9.0.0-rc.1.24431.7, )", @@ -31,6 +43,22 @@ "hi3helper.http": { "type": "Project" } + }, + "net9.0/win-x64": { + "Microsoft.DotNet.ILCompiler": { + "type": "Direct", + "requested": "[9.0.0-rc.1.24431.7, )", + "resolved": "9.0.0-rc.1.24431.7", + "contentHash": "FN38PfBwjKY4D753sz6GDSnvtHU9UUPb9fNAcPwImOn2j8SyUFZMnVmMVwnYX4dlDB9XL98m4g6szsjIx5UWgg==", + "dependencies": { + "runtime.win-x64.Microsoft.DotNet.ILCompiler": "9.0.0-rc.1.24431.7" + } + }, + "runtime.win-x64.Microsoft.DotNet.ILCompiler": { + "type": "Transitive", + "resolved": "9.0.0-rc.1.24431.7", + "contentHash": "hOXKEHhsK/sWwrMEL1Yrfj0Mw1WnsBsi8OoEEEmS4hgOxo39Kic0aap2nKm9eWRl8Fe5S+K10a2nVkt/T2AF7Q==" + } } } } \ No newline at end of file From e068b4f2c4a78a5a716d241e097a3aebcf2ac354 Mon Sep 17 00:00:00 2001 From: neon-nyan Date: Mon, 16 Sep 2024 18:05:36 +0700 Subject: [PATCH 018/110] Ignore cancellation exception on image helper --- CollapseLauncher/Classes/Helper/Image/ImageLoaderHelper.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CollapseLauncher/Classes/Helper/Image/ImageLoaderHelper.cs b/CollapseLauncher/Classes/Helper/Image/ImageLoaderHelper.cs index 1f6e871c8..aaaf12080 100644 --- a/CollapseLauncher/Classes/Helper/Image/ImageLoaderHelper.cs +++ b/CollapseLauncher/Classes/Helper/Image/ImageLoaderHelper.cs @@ -446,6 +446,9 @@ public static async ValueTask TryDownloadToCompleteness(string url, FileInfo fil Logger.LogWriteLine($"Resource download from: {url} has been completed and stored locally into:" + $"\"{fileInfo.FullName}\" with size: {ConverterTool.SummarizeSizeSimple(fileLength)} ({fileLength} bytes)", LogType.Default, true); } + // Ignore cancellation exceptions + catch (TaskCanceledException) { } + catch (OperationCanceledException) { } #if !DEBUG catch (Exception ex) { From 4219665087d161db952dad3cac7e01a293761716 Mon Sep 17 00:00:00 2001 From: neon-nyan Date: Mon, 16 Sep 2024 18:06:29 +0700 Subject: [PATCH 019/110] Cast shadow for ImageEx instance --- CollapseLauncher/Classes/Extension/UIElementExtensions.cs | 3 +++ ImageEx | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CollapseLauncher/Classes/Extension/UIElementExtensions.cs b/CollapseLauncher/Classes/Extension/UIElementExtensions.cs index cb79a82f3..0c44bee5b 100644 --- a/CollapseLauncher/Classes/Extension/UIElementExtensions.cs +++ b/CollapseLauncher/Classes/Extension/UIElementExtensions.cs @@ -704,6 +704,9 @@ void AssignShadowAttachment(FrameworkElement thisElement, bool innerMasked) case Image imageElement: AttachShadow(imageElement, true, offset); break; + case ImageEx.ImageEx imageExElement: + AttachShadow(imageExElement, true, offset); + break; default: AttachShadow(element, innerMasked, offset); break; diff --git a/ImageEx b/ImageEx index e01632d8e..99e65e0d6 160000 --- a/ImageEx +++ b/ImageEx @@ -1 +1 @@ -Subproject commit e01632d8e12be7731ff2c9c514ff6be62139fc48 +Subproject commit 99e65e0d63d93fc8532c1e792344f7fb63662443 From aa9cd94559cc618b5f728f6e85f30978a7f79701 Mon Sep 17 00:00:00 2001 From: neon-nyan Date: Mon, 16 Sep 2024 18:08:30 +0700 Subject: [PATCH 020/110] Make NativeAOT works + Changing data binding from {Binding x} to {x:Bind x} + Create a new BindingHelper for binding unreachable binder on HomePage elements + Auto-generate Bindable properties using WinRT's GeneratedBindableCustomProperty --- CollapseLauncher/App.xaml | 3825 +++++++++++------ .../Sophon/LauncherGameNews.cs | 7 +- CollapseLauncher/XAMLs/MainApp/MainPage.xaml | 28 +- .../XAMLs/MainApp/Pages/CachesPage.xaml | 32 +- .../GenshinGameSettingsPage.xaml | 8 +- .../MainApp/Pages/HomePage.BindingHelper.cs | 126 + .../XAMLs/MainApp/Pages/HomePage.xaml | 318 +- CollapseLauncher/packages.lock.json | 7 + XamlStyler.bat | 2 +- 9 files changed, 2741 insertions(+), 1612 deletions(-) create mode 100644 CollapseLauncher/XAMLs/MainApp/Pages/HomePage.BindingHelper.cs diff --git a/CollapseLauncher/App.xaml b/CollapseLauncher/App.xaml index 03ea596c7..8b38800df 100644 --- a/CollapseLauncher/App.xaml +++ b/CollapseLauncher/App.xaml @@ -1,4 +1,4 @@ - - + @@ -28,345 +28,334 @@ #ffd52a #ffd52a #ffd52a - - - - - + + + + + + TintColor="{ThemeResource SystemAccentColor}" /> + Color="{ThemeResource SystemAccentColorLight2}" /> + Color="#FFFFFF" /> + Color="#000000" /> - - - - + Color="#242424" /> + + + + #ffd52a + Color="{ThemeResource DialogTitleColor}" /> - - - + TintOpacity="0.0" /> + + + + TintOpacity="0.0" /> + TintOpacity="0" /> + TintOpacity="0" /> + TintOpacity="0.0" /> + TintOpacity="0.0" /> + TintOpacity="1" /> + TintOpacity="0.6" /> + TintOpacity="0.0" /> + TintOpacity="0.75" /> + TintOpacity="0.0" /> + TintLuminosityOpacity="0.2" + TintOpacity="0.2" /> + TintOpacity="0.0" /> + TintOpacity="0.0" /> + TintOpacity="0.0" /> + TintOpacity="0" /> + TintOpacity="0.0" /> + TintOpacity="0.0" /> + TintOpacity="1" /> - + + TintOpacity="0.0" /> + TintOpacity="0.0" /> - - - + TintOpacity="0.0" /> + + + + Color="#00000000" /> + Color="#22000000" /> + Color="#11000000" /> + Color="#0A000000" /> + Color="#22000000" /> + Color="#11000000" /> + Color="#0A000000" /> + Color="#44000000" /> + Color="#55000000" /> + Color="#33000000" /> + Color="#22000000" /> - - - + + + + TintOpacity="0.0" /> + TintOpacity="0.0" /> + TintOpacity="0.0" /> + TintOpacity="0.0" /> + TintOpacity="0.0" /> + TintOpacity="0.0" /> + TintOpacity="0.0" /> + TintOpacity="0.0" /> + TintOpacity="0.0" /> + TintOpacity="0.0" /> + TintOpacity="0.0" /> - + TintOpacity="0.0" /> + + TintOpacity="0.0" /> + TintOpacity="0.0" /> + TintOpacity="0.0" /> + TintOpacity="0.0" /> - + TintOpacity="0.0" /> + + TintOpacity="0" /> + Opacity="0.9" + TintColor="{ThemeResource SystemAccentColor}" /> + Opacity="0.75" + TintColor="{ThemeResource SystemAccentColor}" /> + FallbackColor="{ThemeResource SystemAccentColor}" + TintColor="{ThemeResource SystemAccentColor}" /> + Opacity="0.75" + Color="#DDFFFFFF" /> + Color="#EE000000" /> + Color="#CC000000" /> + Color="#FF111111" /> + TintOpacity="0" /> + TintOpacity="0" /> + TintOpacity="0.0" /> + TintOpacity="0.0" /> + TintOpacity="0.0" /> - + TintOpacity="0.0" /> + + Color="#00FFFFFF" /> + Color="#00000000" /> + Color="#00FF0000" /> @@ -378,338 +367,327 @@ #693758 #693758 #693758 - - - - - + + + + + + TintColor="{ThemeResource SystemAccentColor}" /> + Color="{ThemeResource SystemAccentColor}" /> + Color="#000000" /> + Color="#FFFFFF" /> - - - - + Color="#ECECEC" /> + + + + #693758 + Color="{ThemeResource DialogTitleColor}" /> - - - + TintOpacity="0.0" /> + + + + TintOpacity="0.0" /> + TintOpacity="1" /> + TintOpacity="0" /> + TintOpacity="0" /> + TintOpacity="0.0" /> + TintOpacity="0.25" /> + TintOpacity="0.25" /> + TintOpacity="0" /> + TintOpacity="0" /> + TintOpacity="0.0" /> + TintOpacity="0.75" /> + TintOpacity="0.7" /> + TintOpacity="0" /> + TintOpacity="0" /> + TintOpacity="0" /> + TintOpacity="0.0" /> + TintOpacity="0.0" /> - + TintOpacity="1" /> + + TintOpacity="0.0" /> + TintOpacity="0.0" /> - - - + TintOpacity="0.0" /> + + + + Color="#00FFFFFF" /> + Color="#66FFFFFF" /> + Color="#44FFFFFF" /> + Color="#22FFFFFF" /> + Color="#66FFFFFF" /> + Color="#44FFFFFF" /> + Color="#22FFFFFF" /> + Color="#88FFFFFF" /> + Color="#55FFFFFF" /> + Color="#44FFFFFF" /> - - - - + Color="#22FFFFFF" /> + + + + - + + TintOpacity="0.0" /> + TintOpacity="0.0" /> + TintOpacity="0.0" /> + TintOpacity="0.0" /> + TintOpacity="0.0" /> + TintOpacity="0.0" /> + TintOpacity="0.0" /> + TintOpacity="0.0" /> + TintOpacity="0.0" /> + TintOpacity="0.0" /> + TintOpacity="0.0" /> - + TintOpacity="0.0" /> + + TintOpacity="0.0" /> + TintOpacity="0.0" /> + TintOpacity="0.0" /> + TintOpacity="0.0" /> - + TintOpacity="0.0" /> + + FallbackColor="#88FFFFFF" + TintColor="#88FFFFFF" /> + Opacity="0.9" + TintColor="{ThemeResource SystemAccentColor}" /> + Opacity="0.8" + TintColor="{ThemeResource SystemAccentColor}" /> + FallbackColor="{ThemeResource SystemAccentColor}" + TintColor="{ThemeResource SystemAccentColor}" /> + Color="#88000000" /> + Color="#DDFFFFFF" /> + Color="#CCFFFFFF" /> + Color="#FFFFFF" /> + TintOpacity="0.0" /> + TintOpacity="0.0" /> + TintOpacity="0.0" /> + TintOpacity="0.0" /> + TintOpacity="0.0" /> - + TintOpacity="0.0" /> + + Color="#00000000" /> + Color="#00000000" /> + Color="#00FF0000" /> @@ -718,38 +696,28 @@ + - + - - - - - - - - - + + + + + + + + ms-appx:///Assets/Fonts/FontAwesomeBrand6.otf#Font Awesome 6 Brands diff --git a/CollapseLauncher/Classes/Helper/LauncherApiLoader/Sophon/LauncherGameNews.cs b/CollapseLauncher/Classes/Helper/LauncherApiLoader/Sophon/LauncherGameNews.cs index 00496cc1c..7b09ca66b 100644 --- a/CollapseLauncher/Classes/Helper/LauncherApiLoader/Sophon/LauncherGameNews.cs +++ b/CollapseLauncher/Classes/Helper/LauncherApiLoader/Sophon/LauncherGameNews.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Text.Json.Serialization; using System.Threading; +using WinRT; namespace CollapseLauncher.Helper.LauncherApiLoader.Sophon { @@ -220,7 +221,8 @@ public string? CarouselImg public int? CarouselOrder { get; init; } } - public class LauncherGameNewsSocialMedia : ILauncherGameNewsDataTokenized + [GeneratedBindableCustomProperty] + public partial class LauncherGameNewsSocialMedia : ILauncherGameNewsDataTokenized { private readonly string? _qrImg; private readonly List? _qrLinks; @@ -303,7 +305,8 @@ public List? QrLinks [JsonIgnore] public bool IsHasQrDescription => !string.IsNullOrEmpty(QrTitle); } - public class LauncherGameNewsSocialMediaQrLinks + [GeneratedBindableCustomProperty] + public partial class LauncherGameNewsSocialMediaQrLinks { [JsonPropertyName("title")] [JsonConverter(typeof(EmptyStringAsNullConverter))] diff --git a/CollapseLauncher/XAMLs/MainApp/MainPage.xaml b/CollapseLauncher/XAMLs/MainApp/MainPage.xaml index 3a305e1e5..2bf6cd884 100644 --- a/CollapseLauncher/XAMLs/MainApp/MainPage.xaml +++ b/CollapseLauncher/XAMLs/MainApp/MainPage.xaml @@ -234,8 +234,8 @@ + Text="{x:Bind helper:Locale.Lang._MainPage.RegionChangeConfirm}" + TextWrapping="Wrap" /> - - + + - + HorizontalAlignment="Center" + VerticalAlignment="Center" + RenderTransformOrigin="0.5, 0.5"> + Width="18" + Height="18"> - + - + - diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/CachesPage.xaml b/CollapseLauncher/XAMLs/MainApp/Pages/CachesPage.xaml index 92fb5d17e..8e2850259 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/CachesPage.xaml +++ b/CollapseLauncher/XAMLs/MainApp/Pages/CachesPage.xaml @@ -201,44 +201,44 @@ diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/GenshinGameSettingsPage.xaml b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/GenshinGameSettingsPage.xaml index 1018f0351..9e9570f7c 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/GenshinGameSettingsPage.xaml +++ b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/GenshinGameSettingsPage.xaml @@ -268,15 +268,15 @@ - + - + diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.BindingHelper.cs b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.BindingHelper.cs new file mode 100644 index 000000000..66d2e2dc9 --- /dev/null +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.BindingHelper.cs @@ -0,0 +1,126 @@ +using CollapseLauncher.Helper.LauncherApiLoader.Sophon; +using CommunityToolkit.WinUI; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Data; + +#nullable enable +namespace CollapseLauncher.Pages +{ + file static class HomePageExtension + { + internal static BooleanVisibilityConverter BooleanVisibilityConverter = new BooleanVisibilityConverter(); + + internal static void BindProperty(this FrameworkElement element, DependencyProperty dependencyProperty, object objectToBind, string propertyName, IValueConverter? converter = null, BindingMode bindingMode = BindingMode.OneWay) + { + // Create a new binding instance + Binding binding = new Binding + { + Source = objectToBind, + Mode = bindingMode, + Path = new PropertyPath(propertyName), + UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged + }; + + // If the converter is assigned, then add the converter + if (converter != null) + { + binding.Converter = converter; + } + + // Set binding to the element + element.SetBinding(dependencyProperty, binding); + } + } + + public partial class HomePage + { + private void ApplySocialMediaBinding(Panel panel) + { + // Bind QR Code Overlay + LauncherGameNewsSocialMedia? dataBind = panel.Tag as LauncherGameNewsSocialMedia; + Panel? qrParentPanel = panel.FindChild("SocialMediaParentPanel_QR") as Panel; + Panel? linksParentPanel = panel.FindChild("SocialMediaParentPanel_Links") as Panel; + TextBlock? descriptionParentPanel = panel.FindChild("SocialMediaParentPanel_Description") as TextBlock; + + // If already assigned, then return + if ((panel.Tag is bool isAssigned && isAssigned) || dataBind == null) + { + panel.Tag = true; + return; + } + + if (dataBind == null) + { + return; + } + + // If qrParentPanel is not null, then proceed + if (qrParentPanel != null) + { + BindSocialMediaQR(qrParentPanel, dataBind); + } + + // If qrParentPanel is not null, then proceed + if (linksParentPanel != null) + { + BindSocialMediaLinks(linksParentPanel, dataBind); + } + + // If descriptionParentPanel is not null, then proceed + if (descriptionParentPanel != null) + { + BindSocialMediaDescription(descriptionParentPanel, dataBind); + } + + // Set as assigned + panel.Tag = true; + + // Set visibility to visible + panel.Visibility = Visibility.Visible; + } + + private void BindSocialMediaQR(Panel parentPanel, LauncherGameNewsSocialMedia dataBind) + { + // Bind visibility if dataBind has QR + parentPanel.BindProperty(StackPanel.VisibilityProperty, dataBind, "IsHasQr", HomePageExtension.BooleanVisibilityConverter); + + // Find the child grid + Grid? childGrid = parentPanel.FindChild(); + if (childGrid == null) return; + + // Bind the QR properties + ImageEx.ImageEx? qrImageInstance = childGrid.FindChild(); + qrImageInstance?.BindProperty(ImageEx.ImageEx.SourceProperty, dataBind, "QrImg"); + + TextBlock? textBlockInstance = childGrid.FindChild(); + textBlockInstance?.BindProperty(TextBlock.TextProperty, dataBind, "QrTitle"); + textBlockInstance?.BindProperty(TextBlock.VisibilityProperty, dataBind, "IsHasQrDescription", HomePageExtension.BooleanVisibilityConverter); + } + + private void BindSocialMediaLinks(Panel parentPanel, LauncherGameNewsSocialMedia dataBind) + { + // Bind visibility if dataBind has Links + parentPanel.BindProperty(StackPanel.VisibilityProperty, dataBind, "IsHasLinks", HomePageExtension.BooleanVisibilityConverter); + + // Find the ItemsControl + ItemsControl? itemsControl = parentPanel.FindChild(); + if (itemsControl == null) return; + + // If dataBind has links, then assign the ItemsSource + if (dataBind.IsHasLinks) + { + itemsControl.ItemsSource = dataBind.QrLinks; + } + } + + private void BindSocialMediaDescription(TextBlock parentPanel, LauncherGameNewsSocialMedia dataBind) + { + // Bind visibility if dataBind has Description + parentPanel.BindProperty(TextBlock.VisibilityProperty, dataBind, "IsHasDescription", HomePageExtension.BooleanVisibilityConverter); + + // Bind description text + parentPanel.BindProperty(TextBlock.TextProperty, dataBind, "Title"); + } + } +} diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml index 09f68b11e..f2bafea62 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml @@ -10,6 +10,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:extension="using:CollapseLauncher.Extension" xmlns:helper="using:Hi3Helper" + xmlns:hypTypes="using:CollapseLauncher.Helper.LauncherApiLoader.HoYoPlay" xmlns:imageex="using:ImageEx" xmlns:local="using:CollapseLauncher" xmlns:localPage="using:CollapseLauncher.Pages" @@ -18,6 +19,7 @@ xmlns:lottie="using:CollapseLauncher.AnimatedVisuals.Lottie" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:s="using:CommunityToolkit.WinUI" + xmlns:sophonTypes="using:CollapseLauncher.Helper.LauncherApiLoader.Sophon" x:Name="HomePage_Page" x:FieldModifier="public" Unloaded="Page_Unloaded" @@ -442,7 +444,7 @@ - + @@ -616,16 +624,16 @@ Style="{ThemeResource CollapseFlipViewStyle}" Translation="0,0,12"> - + + Tag="{x:Bind CarouselUrl}" + ToolTipService.ToolTip="{x:Bind CarouselTitle}" /> @@ -654,7 +662,8 @@ HorizontalAlignment="Left" SelectedIndex="{x:Bind DefaultPostPanelIndex}"> - + diff --git a/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropperThumb.cs b/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropperThumb.cs new file mode 100644 index 000000000..03b534e5f --- /dev/null +++ b/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropperThumb.cs @@ -0,0 +1,137 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Hi3Helper.CommunityToolkit.WinUI.Controls; + +/// +/// The control is used for . +/// +public partial class ImageCropperThumb : Control +{ + private readonly TranslateTransform _layoutTransform = new(); + internal const string NormalState = "Normal"; + internal const string PointerOverState = "PointerOver"; + internal const string PressedState = "Pressed"; + internal const string DisabledState = "Disabled"; + internal ThumbPosition Position { get; set; } + + /// + /// Gets or sets the X coordinate of the ImageCropperThumb. + /// + public double X + { + get { return (double)GetValue(XProperty); } + set { SetValue(XProperty, value); } + } + + /// + /// Gets or sets the Y coordinate of the ImageCropperThumb. + /// + public double Y + { + get { return (double)GetValue(YProperty); } + set { SetValue(YProperty, value); } + } + + /// + /// Initializes a new instance of the class. + /// + public ImageCropperThumb() + { + DefaultStyleKey = typeof(ImageCropperThumb); + RenderTransform = _layoutTransform; + ManipulationMode = ManipulationModes.TranslateX | ManipulationModes.TranslateY; + SizeChanged += ImageCropperThumb_SizeChanged; + + } + + protected override void OnApplyTemplate() + { + PointerEntered -= Control_PointerEntered; + PointerExited -= Control_PointerExited; + PointerCaptureLost -= Control_PointerCaptureLost; + PointerCanceled -= Control_PointerCanceled; + + PointerEntered += Control_PointerEntered; + PointerExited += Control_PointerExited; + PointerCaptureLost += Control_PointerCaptureLost; + PointerCanceled += Control_PointerCanceled; + } + + private void ImageCropperThumb_SizeChanged(object sender, SizeChangedEventArgs e) + { + UpdatePosition(); + } + + private void UpdatePosition() + { + if (_layoutTransform != null) + { + _layoutTransform.X = X - (this.ActualWidth / 2); + _layoutTransform.Y = Y - (this.ActualHeight / 2); + } + } + + private static void OnXChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var target = (ImageCropperThumb)d; + target.UpdatePosition(); + } + + + + private static void OnYChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var target = (ImageCropperThumb)d; + target.UpdatePosition(); + } + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty XProperty = + DependencyProperty.Register(nameof(X), typeof(double), typeof(ImageCropperThumb), new PropertyMetadata(0d, OnXChanged)); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty YProperty = + DependencyProperty.Register(nameof(Y), typeof(double), typeof(ImageCropperThumb), new PropertyMetadata(0d, OnYChanged)); + + public void Control_PointerEntered(object sender, PointerRoutedEventArgs e) + { + base.OnPointerEntered(e); + VisualStateManager.GoToState(this, PointerOverState, true); + } + + public void Control_PointerExited(object sender, PointerRoutedEventArgs e) + { + base.OnPointerExited(e); + VisualStateManager.GoToState(this, NormalState, true); + } + + private void Control_PointerCaptureLost(object sender, PointerRoutedEventArgs e) + { + base.OnPointerCaptureLost(e); + VisualStateManager.GoToState(this, NormalState, true); + } + + private void Control_PointerCanceled(object sender, PointerRoutedEventArgs e) + { + base.OnPointerCanceled(e); + VisualStateManager.GoToState(this, NormalState, true); + } + + protected override void OnPointerPressed(PointerRoutedEventArgs e) + { + base.OnPointerPressed(e); + VisualStateManager.GoToState(this, PressedState, true); + } + + protected override void OnPointerReleased(PointerRoutedEventArgs e) + { + base.OnPointerReleased(e); + VisualStateManager.GoToState(this, NormalState, true); + } +} diff --git a/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropperThumb.xaml b/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropperThumb.xaml new file mode 100644 index 000000000..cd287c40e --- /dev/null +++ b/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropperThumb.xaml @@ -0,0 +1,60 @@ + + + diff --git a/Hi3Helper.CommunityToolkit/ImageCropper/MultiTarget.props b/Hi3Helper.CommunityToolkit/ImageCropper/MultiTarget.props new file mode 100644 index 000000000..76b965dd5 --- /dev/null +++ b/Hi3Helper.CommunityToolkit/ImageCropper/MultiTarget.props @@ -0,0 +1,9 @@ + + + + wasdk; + + diff --git a/Hi3Helper.CommunityToolkit/ImageCropper/ReadMe.md b/Hi3Helper.CommunityToolkit/ImageCropper/ReadMe.md new file mode 100644 index 000000000..07942198d --- /dev/null +++ b/Hi3Helper.CommunityToolkit/ImageCropper/ReadMe.md @@ -0,0 +1,38 @@ + +# Windows Community Toolkit - ImageCropper + +This package is part of the [Windows Community Toolkit](https://aka.ms/toolkit/windows) from the [.NET Foundation](https://dotnetfoundation.org). + +## Package Contents + +This package contains the following controls in the `CommunityToolkit.WinUI.Controls` namespace: + +- ImageCropper + +## Which Package is for me? + +If you're developing with _UWP/WinUI 2 or Uno.UI_ you should be using the `CommunityToolkit.Uwp.Controls.ImageCropper` package. + +If you're developing with _WindowsAppSDK/WinUI 3 or Uno.WinUI_ you should be using the `CommunityToolkit.WinUI.Controls.ImageCropper` package. + +## WinUI Resources (UWP) + +For UWP projects, the WinUI 2 reference requires you include the WinUI XAML Resources in your App.xaml file: + +```xml + + + +``` + +See [Getting Started in WinUI 2](https://learn.microsoft.com/windows/apps/winui/winui2/getting-started) for more information. + +## Documentation + +Further documentation about these components can be found at: https://aka.ms/windowstoolkitdocs + +## License + +MIT + +See License.md in package for more details. diff --git a/Hi3Helper.CommunityToolkit/ImageCropper/Themes/Generic.xaml b/Hi3Helper.CommunityToolkit/ImageCropper/Themes/Generic.xaml new file mode 100644 index 000000000..af0bb3738 --- /dev/null +++ b/Hi3Helper.CommunityToolkit/ImageCropper/Themes/Generic.xaml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/Hi3Helper.CommunityToolkit/ImageCropper/ThumbPlacement.cs b/Hi3Helper.CommunityToolkit/ImageCropper/ThumbPlacement.cs new file mode 100644 index 000000000..021365593 --- /dev/null +++ b/Hi3Helper.CommunityToolkit/ImageCropper/ThumbPlacement.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Hi3Helper.CommunityToolkit.WinUI.Controls; + +/// +/// Thumb placement enumeration. +/// Default is +/// +public enum ThumbPlacement +{ + /// + /// Shows the thumbs in all four corners, top, bottom, left and right. + /// + All, + + /// + /// Shows the thumbs in all four corners. + /// + Corners +} diff --git a/Hi3Helper.CommunityToolkit/ImageCropper/ThumbPosition.cs b/Hi3Helper.CommunityToolkit/ImageCropper/ThumbPosition.cs new file mode 100644 index 000000000..0f73feada --- /dev/null +++ b/Hi3Helper.CommunityToolkit/ImageCropper/ThumbPosition.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Hi3Helper.CommunityToolkit.WinUI.Controls; + +/// +/// Thumb position enumeration. +/// +internal enum ThumbPosition +{ + Top, + Bottom, + Left, + Right, + UpperLeft, + UpperRight, + LowerLeft, + LowerRight +} diff --git a/Hi3Helper.CommunityToolkit/SettingsControls/CommunityToolkit.WinUI.Controls.SettingsControls.csproj b/Hi3Helper.CommunityToolkit/SettingsControls/CommunityToolkit.WinUI.Controls.SettingsControls.csproj new file mode 100644 index 000000000..885a8e0a6 --- /dev/null +++ b/Hi3Helper.CommunityToolkit/SettingsControls/CommunityToolkit.WinUI.Controls.SettingsControls.csproj @@ -0,0 +1,26 @@ + + + + + SettingsControls + Create a Windows 11 style settings experiences with these controls. + + + CommunityToolkit.WinUI.Controls.SettingsControlsRns + ReadMe.md + + + + + + + + True + \ + + + + + $(PackageIdPrefix).$(PackageIdVariant).Controls.$(ToolkitComponentName) + + diff --git a/Hi3Helper.CommunityToolkit/SettingsControls/Helpers/ControlHelper.cs b/Hi3Helper.CommunityToolkit/SettingsControls/Helpers/ControlHelper.cs new file mode 100644 index 000000000..9e0ea88ea --- /dev/null +++ b/Hi3Helper.CommunityToolkit/SettingsControls/Helpers/ControlHelper.cs @@ -0,0 +1,9 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Hi3Helper.CommunityToolkit.WinUI.Controls; +internal static partial class ControlHelpers +{ + internal static bool IsXamlRootAvailable { get; } = Windows.Foundation.Metadata.ApiInformation.IsPropertyPresent("Windows.UI.Xaml.UIElement", "XamlRoot"); +} diff --git a/Hi3Helper.CommunityToolkit/SettingsControls/Helpers/CornerRadiusConverter.cs b/Hi3Helper.CommunityToolkit/SettingsControls/Helpers/CornerRadiusConverter.cs new file mode 100644 index 000000000..08279e579 --- /dev/null +++ b/Hi3Helper.CommunityToolkit/SettingsControls/Helpers/CornerRadiusConverter.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Hi3Helper.CommunityToolkit.WinUI.Controls; +internal partial class CornerRadiusConverter : IValueConverter +{ + public object Convert(object value, Type targetType, object parameter, string language) + { + if (value is CornerRadius cornerRadius) + { + return new CornerRadius(0, 0, cornerRadius.BottomRight, cornerRadius.BottomLeft); + } + else + { + return value; + } + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + return value; + } +} diff --git a/Hi3Helper.CommunityToolkit/SettingsControls/Helpers/ResourceDictionaryExtensions.cs b/Hi3Helper.CommunityToolkit/SettingsControls/Helpers/ResourceDictionaryExtensions.cs new file mode 100644 index 000000000..8cee3d9ed --- /dev/null +++ b/Hi3Helper.CommunityToolkit/SettingsControls/Helpers/ResourceDictionaryExtensions.cs @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Hi3Helper.CommunityToolkit.WinUI.Controls; + +// Adapted from https://github.com/rudyhuyn/XamlPlus +internal static class ResourceDictionaryExtensions +{ + /// + /// Copies the provided as a parameter into the calling dictionary, includes overwriting the source location, theme dictionaries, and merged dictionaries. + /// + /// ResourceDictionary to copy values to. + /// ResourceDictionary to copy values from. + internal static void CopyFrom(this ResourceDictionary destination, ResourceDictionary source) + { + if (source.Source != null) + { + destination.Source = source.Source; + } + else + { + // Clone theme dictionaries + if (source.ThemeDictionaries != null) + { + foreach (var theme in source.ThemeDictionaries) + { + if (theme.Value is ResourceDictionary themedResource) + { + var themeDictionary = new ResourceDictionary(); + themeDictionary.CopyFrom(themedResource); + destination.ThemeDictionaries[theme.Key] = themeDictionary; + } + else + { + destination.ThemeDictionaries[theme.Key] = theme.Value; + } + } + } + + // Clone merged dictionaries + if (source.MergedDictionaries != null) + { + foreach (var mergedResource in source.MergedDictionaries) + { + var themeDictionary = new ResourceDictionary(); + themeDictionary.CopyFrom(mergedResource); + destination.MergedDictionaries.Add(themeDictionary); + } + } + + // Clone all contents + foreach (var item in source) + { + destination[item.Key] = item.Value; + } + } + } +} diff --git a/Hi3Helper.CommunityToolkit/SettingsControls/Helpers/StyleExtensions.cs b/Hi3Helper.CommunityToolkit/SettingsControls/Helpers/StyleExtensions.cs new file mode 100644 index 000000000..92eff3996 --- /dev/null +++ b/Hi3Helper.CommunityToolkit/SettingsControls/Helpers/StyleExtensions.cs @@ -0,0 +1,73 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Hi3Helper.CommunityToolkit.WinUI.Controls; + +// Adapted from https://github.com/rudyhuyn/XamlPlus +public static partial class StyleExtensions +{ + // Used to distinct normal ResourceDictionary and the one we add. + private sealed class StyleExtensionResourceDictionary : ResourceDictionary + { + } + + public static ResourceDictionary GetResources(Style obj) + { + return (ResourceDictionary)obj.GetValue(ResourcesProperty); + } + + public static void SetResources(Style obj, ResourceDictionary value) + { + obj.SetValue(ResourcesProperty, value); + } + + public static readonly DependencyProperty ResourcesProperty = + DependencyProperty.RegisterAttached("Resources", typeof(ResourceDictionary), typeof(StyleExtensions), new PropertyMetadata(null, ResourcesChanged)); + + private static void ResourcesChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) + { + if (!(sender is FrameworkElement frameworkElement)) + { + return; + } + + var mergedDictionaries = frameworkElement.Resources?.MergedDictionaries; + if (mergedDictionaries == null) + { + return; + } + + var existingResourceDictionary = + mergedDictionaries.FirstOrDefault(c => c is StyleExtensionResourceDictionary); + if (existingResourceDictionary != null) + { + // Remove the existing resource dictionary + mergedDictionaries.Remove(existingResourceDictionary); + } + + if (e.NewValue is ResourceDictionary resource) + { + var clonedResources = new StyleExtensionResourceDictionary(); + clonedResources.CopyFrom(resource); + mergedDictionaries.Add(clonedResources); + } + + if (frameworkElement.IsLoaded) + { + // Only force if the style was applied after the control was loaded + ForceControlToReloadThemeResources(frameworkElement); + } + } + + private static void ForceControlToReloadThemeResources(FrameworkElement frameworkElement) + { + // To force the refresh of all resource references. + // Note: Doesn't work when in high-contrast. + var currentRequestedTheme = frameworkElement.RequestedTheme; + frameworkElement.RequestedTheme = currentRequestedTheme == ElementTheme.Dark + ? ElementTheme.Light + : ElementTheme.Dark; + frameworkElement.RequestedTheme = currentRequestedTheme; + } +} diff --git a/Hi3Helper.CommunityToolkit/SettingsControls/MultiTarget.props b/Hi3Helper.CommunityToolkit/SettingsControls/MultiTarget.props new file mode 100644 index 000000000..fb4d71c7c --- /dev/null +++ b/Hi3Helper.CommunityToolkit/SettingsControls/MultiTarget.props @@ -0,0 +1,9 @@ + + + + wasdk; + + \ No newline at end of file diff --git a/Hi3Helper.CommunityToolkit/SettingsControls/ReadMe.md b/Hi3Helper.CommunityToolkit/SettingsControls/ReadMe.md new file mode 100644 index 000000000..a12ae1fa5 --- /dev/null +++ b/Hi3Helper.CommunityToolkit/SettingsControls/ReadMe.md @@ -0,0 +1,39 @@ + +# Windows Community Toolkit - SettingsControls + +This package is part of the [Windows Community Toolkit](https://aka.ms/toolkit/windows) from the [.NET Foundation](https://dotnetfoundation.org). + +## Package Contents + +This package contains the following controls in the `CommunityToolkit.WinUI.Controls` namespace: + +- SettingsCard +- SettingsExpander + +## Which Package is for me? + +If you're developing with _UWP/WinUI 2 or Uno.UI_ you should be using the `CommunityToolkit.Uwp.Controls.SettingsControls` package. + +If you're developing with _WindowsAppSDK/WinUI 3 or Uno.WinUI_ you should be using the `CommunityToolkit.WinUI.Controls.SettingsControls` package. + +## WinUI Resources (UWP) + +For UWP projects, the WinUI 2 reference requires you include the WinUI XAML Resources in your App.xaml file: + +```xml + + + +``` + +See [Getting Started in WinUI 2](https://learn.microsoft.com/windows/apps/winui/winui2/getting-started) for more information. + +## Documentation + +Further documentation about these components can be found at: https://aka.ms/windowstoolkitdocs + +## License + +MIT + +See License.md in package for more details. diff --git a/Hi3Helper.CommunityToolkit/SettingsControls/SettingsCard/SettingsCard.Properties.cs b/Hi3Helper.CommunityToolkit/SettingsControls/SettingsCard/SettingsCard.Properties.cs new file mode 100644 index 000000000..8871d9838 --- /dev/null +++ b/Hi3Helper.CommunityToolkit/SettingsControls/SettingsCard/SettingsCard.Properties.cs @@ -0,0 +1,194 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Hi3Helper.CommunityToolkit.WinUI.Controls; + +public partial class SettingsCard : ButtonBase +{ + /// + /// The backing for the property. + /// + public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register( + nameof(Header), + typeof(object), + typeof(SettingsCard), + new PropertyMetadata(defaultValue: null, (d, e) => ((SettingsCard)d).OnHeaderPropertyChanged((object)e.OldValue, (object)e.NewValue))); + + /// + /// The backing for the property. + /// + public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register( + nameof(Description), + typeof(object), + typeof(SettingsCard), + new PropertyMetadata(defaultValue: null, (d, e) => ((SettingsCard)d).OnDescriptionPropertyChanged((object)e.OldValue, (object)e.NewValue))); + + /// + /// The backing for the property. + /// + public static readonly DependencyProperty HeaderIconProperty = DependencyProperty.Register( + nameof(HeaderIcon), + typeof(IconElement), + typeof(SettingsCard), + new PropertyMetadata(defaultValue: null, (d, e) => ((SettingsCard)d).OnHeaderIconPropertyChanged((IconElement)e.OldValue, (IconElement)e.NewValue))); + + /// + /// The backing for the property. + /// + public static readonly DependencyProperty ActionIconProperty = DependencyProperty.Register( + nameof(ActionIcon), + typeof(IconElement), + typeof(SettingsCard), + new PropertyMetadata(defaultValue: "\ue974")); + + /// + /// The backing for the property. + /// + public static readonly DependencyProperty ActionIconToolTipProperty = DependencyProperty.Register( + nameof(ActionIconToolTip), + typeof(string), + typeof(SettingsCard), + new PropertyMetadata(defaultValue: null)); + + /// + /// The backing for the property. + /// + public static readonly DependencyProperty IsClickEnabledProperty = DependencyProperty.Register( + nameof(IsClickEnabled), + typeof(bool), + typeof(SettingsCard), + new PropertyMetadata(defaultValue: false, (d, e) => ((SettingsCard)d).OnIsClickEnabledPropertyChanged((bool)e.OldValue, (bool)e.NewValue))); + + /// + /// The backing for the property. + /// + public static readonly DependencyProperty ContentAlignmentProperty = DependencyProperty.Register( + nameof(ContentAlignment), + typeof(ContentAlignment), + typeof(SettingsCard), + new PropertyMetadata(defaultValue: ContentAlignment.Right)); + + /// + /// The backing for the property. + /// + public static readonly DependencyProperty IsActionIconVisibleProperty = DependencyProperty.Register( + nameof(IsActionIconVisible), + typeof(bool), + typeof(SettingsCard), + new PropertyMetadata(defaultValue: true, (d, e) => ((SettingsCard)d).OnIsActionIconVisiblePropertyChanged((bool)e.OldValue, (bool)e.NewValue))); + + /// + /// Gets or sets the Header. + /// + public object Header + { + get => (object)GetValue(HeaderProperty); + set => SetValue(HeaderProperty, value); + } + + /// + /// Gets or sets the description. + /// +#pragma warning disable CS0109 // Member does not hide an inherited member; new keyword is not required + public new object Description +#pragma warning restore CS0109 // Member does not hide an inherited member; new keyword is not required + { + get => (object)GetValue(DescriptionProperty); + set => SetValue(DescriptionProperty, value); + } + + /// + /// Gets or sets the icon on the left. + /// + public IconElement HeaderIcon + { + get => (IconElement)GetValue(HeaderIconProperty); + set => SetValue(HeaderIconProperty, value); + } + + /// + /// Gets or sets the icon that is shown when IsClickEnabled is set to true. + /// + public IconElement ActionIcon + { + get => (IconElement)GetValue(ActionIconProperty); + set => SetValue(ActionIconProperty, value); + } + + /// + /// Gets or sets the tooltip of the ActionIcon. + /// + public string ActionIconToolTip + { + get => (string)GetValue(ActionIconToolTipProperty); + set => SetValue(ActionIconToolTipProperty, value); + } + + /// + /// Gets or sets if the card can be clicked. + /// + public bool IsClickEnabled + { + get => (bool)GetValue(IsClickEnabledProperty); + set => SetValue(IsClickEnabledProperty, value); + } + + /// + /// Gets or sets the alignment of the Content + /// + public ContentAlignment ContentAlignment + { + get => (ContentAlignment)GetValue(ContentAlignmentProperty); + set => SetValue(ContentAlignmentProperty, value); + } + + /// + /// Gets or sets if the ActionIcon is shown. + /// + public bool IsActionIconVisible + { + get => (bool)GetValue(IsActionIconVisibleProperty); + set => SetValue(IsActionIconVisibleProperty, value); + } + + protected virtual void OnIsClickEnabledPropertyChanged(bool oldValue, bool newValue) + { + OnIsClickEnabledChanged(); + } + protected virtual void OnHeaderIconPropertyChanged(IconElement oldValue, IconElement newValue) + { + OnHeaderIconChanged(); + } + + protected virtual void OnHeaderPropertyChanged(object oldValue, object newValue) + { + OnHeaderChanged(); + } + + protected virtual void OnDescriptionPropertyChanged(object oldValue, object newValue) + { + OnDescriptionChanged(); + } + + protected virtual void OnIsActionIconVisiblePropertyChanged(bool oldValue, bool newValue) + { + OnActionIconChanged(); + } +} + +public enum ContentAlignment +{ + /// + /// The Content is aligned to the right. Default state. + /// + Right, + /// + /// The Content is left-aligned while the Header, HeaderIcon and Description are collapsed. This is commonly used for Content types such as CheckBoxes, RadioButtons and custom layouts. + /// + Left, + /// + /// The Content is vertically aligned. + /// + Vertical +} diff --git a/Hi3Helper.CommunityToolkit/SettingsControls/SettingsCard/SettingsCard.cs b/Hi3Helper.CommunityToolkit/SettingsControls/SettingsCard/SettingsCard.cs new file mode 100644 index 000000000..4a02e4da9 --- /dev/null +++ b/Hi3Helper.CommunityToolkit/SettingsControls/SettingsCard/SettingsCard.cs @@ -0,0 +1,313 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.ComponentModel.Design; + +namespace Hi3Helper.CommunityToolkit.WinUI.Controls; + +/// +/// This is the base control to create consistent settings experiences, inline with the Windows 11 design language. +/// A SettingsCard can also be hosted within a SettingsExpander. +/// + +[TemplatePart(Name = ActionIconPresenterHolder, Type = typeof(Viewbox))] +[TemplatePart(Name = HeaderPresenter, Type = typeof(ContentPresenter))] +[TemplatePart(Name = DescriptionPresenter, Type = typeof(ContentPresenter))] +[TemplatePart(Name = HeaderIconPresenterHolder, Type = typeof(Viewbox))] + +[TemplateVisualState(Name = NormalState, GroupName = CommonStates)] +[TemplateVisualState(Name = PointerOverState, GroupName = CommonStates)] +[TemplateVisualState(Name = PressedState, GroupName = CommonStates)] +[TemplateVisualState(Name = DisabledState, GroupName = CommonStates)] + +[TemplateVisualState(Name = RightState, GroupName = ContentAlignmentStates)] +[TemplateVisualState(Name = RightWrappedState, GroupName = ContentAlignmentStates)] +[TemplateVisualState(Name = RightWrappedNoIconState, GroupName = ContentAlignmentStates)] +[TemplateVisualState(Name = LeftState, GroupName = ContentAlignmentStates)] +[TemplateVisualState(Name = VerticalState, GroupName = ContentAlignmentStates)] + +[TemplateVisualState(Name = NoContentSpacingState, GroupName = ContentSpacingStates)] +[TemplateVisualState(Name = ContentSpacingState, GroupName = ContentSpacingStates)] + +public partial class SettingsCard : ButtonBase +{ + internal const string CommonStates = "CommonStates"; + internal const string NormalState = "Normal"; + internal const string PointerOverState = "PointerOver"; + internal const string PressedState = "Pressed"; + internal const string DisabledState = "Disabled"; + + internal const string ContentAlignmentStates = "ContentAlignmentStates"; + internal const string RightState = "Right"; + internal const string RightWrappedState = "RightWrapped"; + internal const string RightWrappedNoIconState = "RightWrappedNoIcon"; + internal const string LeftState = "Left"; + internal const string VerticalState = "Vertical"; + + internal const string ContentSpacingStates = "ContentSpacingStates"; + internal const string NoContentSpacingState = "NoContentSpacing"; + internal const string ContentSpacingState = "ContentSpacing"; + + internal const string ActionIconPresenterHolder = "PART_ActionIconPresenterHolder"; + internal const string HeaderPresenter = "PART_HeaderPresenter"; + internal const string DescriptionPresenter = "PART_DescriptionPresenter"; + internal const string HeaderIconPresenterHolder = "PART_HeaderIconPresenterHolder"; + + + /// + /// Creates a new instance of the class. + /// + public SettingsCard() + { + this.DefaultStyleKey = typeof(SettingsCard); + } + + /// + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + IsEnabledChanged -= OnIsEnabledChanged; + OnActionIconChanged(); + OnHeaderChanged(); + OnHeaderIconChanged(); + OnDescriptionChanged(); + OnIsClickEnabledChanged(); + CheckInitialVisualState(); + SetAccessibleContentName(); + RegisterPropertyChangedCallback(ContentProperty, OnContentChanged); + IsEnabledChanged += OnIsEnabledChanged; + } + + private void CheckInitialVisualState() + { + VisualStateManager.GoToState(this, IsEnabled ? NormalState : DisabledState, true); + + if (GetTemplateChild("ContentAlignmentStates") is VisualStateGroup contentAlignmentStatesGroup) + { + contentAlignmentStatesGroup.CurrentStateChanged -= this.ContentAlignmentStates_Changed; + CheckVerticalSpacingState(contentAlignmentStatesGroup.CurrentState); + contentAlignmentStatesGroup.CurrentStateChanged += this.ContentAlignmentStates_Changed; + } + } + + // We automatically set the AutomationProperties.Name of the Content if not configured. + private void SetAccessibleContentName() + { + if (Header is string headerString && headerString != string.Empty) + { + // We don't want to override an AutomationProperties.Name that is manually set, or if the Content basetype is of type ButtonBase (the ButtonBase.Content will be used then) + if (Content is UIElement element && string.IsNullOrEmpty(AutomationProperties.GetName(element)) && element.GetType().BaseType != typeof(ButtonBase) && element.GetType() != typeof(TextBlock)) + { + AutomationProperties.SetName(element, headerString); + } + } + } + + private void EnableButtonInteraction() + { + DisableButtonInteraction(); + + IsTabStop = true; + PointerEntered += Control_PointerEntered; + PointerExited += Control_PointerExited; + PointerCaptureLost += Control_PointerCaptureLost; + PointerCanceled += Control_PointerCanceled; + PreviewKeyDown += Control_PreviewKeyDown; + PreviewKeyUp += Control_PreviewKeyUp; + } + + private void DisableButtonInteraction() + { + IsTabStop = false; + PointerEntered -= Control_PointerEntered; + PointerExited -= Control_PointerExited; + PointerCaptureLost -= Control_PointerCaptureLost; + PointerCanceled -= Control_PointerCanceled; + PreviewKeyDown -= Control_PreviewKeyDown; + PreviewKeyUp -= Control_PreviewKeyUp; + } + + private void Control_PreviewKeyUp(object sender, KeyRoutedEventArgs e) + { + if (e.Key == Windows.System.VirtualKey.Enter || e.Key == Windows.System.VirtualKey.Space || e.Key == Windows.System.VirtualKey.GamepadA) + { + VisualStateManager.GoToState(this, NormalState, true); + } + } + + private void Control_PreviewKeyDown(object sender, KeyRoutedEventArgs e) + { + if (e.Key == Windows.System.VirtualKey.Enter || e.Key == Windows.System.VirtualKey.Space || e.Key == Windows.System.VirtualKey.GamepadA) + { + // Check if the active focus is on the card itself - only then we show the pressed state. + if (GetFocusedElement() is SettingsCard) + { + VisualStateManager.GoToState(this, PressedState, true); + } + } + } + + public void Control_PointerEntered(object sender, PointerRoutedEventArgs e) + { + base.OnPointerEntered(e); + VisualStateManager.GoToState(this, PointerOverState, true); + } + + public void Control_PointerExited(object sender, PointerRoutedEventArgs e) + { + base.OnPointerExited(e); + VisualStateManager.GoToState(this, NormalState, true); + } + + private void Control_PointerCaptureLost(object sender, PointerRoutedEventArgs e) + { + base.OnPointerCaptureLost(e); + VisualStateManager.GoToState(this, NormalState, true); + } + + private void Control_PointerCanceled(object sender, PointerRoutedEventArgs e) + { + base.OnPointerCanceled(e); + VisualStateManager.GoToState(this, NormalState, true); + } + + protected override void OnPointerPressed(PointerRoutedEventArgs e) + { + // e.Handled = true; + if (IsClickEnabled) + { + base.OnPointerPressed(e); + VisualStateManager.GoToState(this, PressedState, true); + } + } + + protected override void OnPointerReleased(PointerRoutedEventArgs e) + { + if (IsClickEnabled) + { + base.OnPointerReleased(e); + VisualStateManager.GoToState(this, NormalState, true); + } + } + + /// + /// Creates AutomationPeer + /// + /// An automation peer for . + protected override AutomationPeer OnCreateAutomationPeer() + { + return new SettingsCardAutomationPeer(this); + } + + private void OnIsClickEnabledChanged() + { + OnActionIconChanged(); + if (IsClickEnabled) + { + EnableButtonInteraction(); + } + else + { + DisableButtonInteraction(); + } + } + + private void OnIsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e) + { + VisualStateManager.GoToState(this, IsEnabled ? NormalState : DisabledState, true); + } + + private void OnActionIconChanged() + { + if (GetTemplateChild(ActionIconPresenterHolder) is FrameworkElement actionIconPresenter) + { + if (IsClickEnabled && IsActionIconVisible) + { + actionIconPresenter.Visibility = Visibility.Visible; + } + else + { + actionIconPresenter.Visibility =Visibility.Collapsed; + } + } + } + + private void OnHeaderIconChanged() + { + if (GetTemplateChild(HeaderIconPresenterHolder) is FrameworkElement headerIconPresenter) + { + headerIconPresenter.Visibility = HeaderIcon != null + ? Visibility.Visible + : Visibility.Collapsed; + } + } + + private void OnDescriptionChanged() + { + if (GetTemplateChild(DescriptionPresenter) is FrameworkElement descriptionPresenter) + { + descriptionPresenter.Visibility = IsNullOrEmptyString(Description) + ? Visibility.Collapsed + : Visibility.Visible; + } + + } + + private void OnHeaderChanged() + { + if (GetTemplateChild(HeaderPresenter) is FrameworkElement headerPresenter) + { + headerPresenter.Visibility = IsNullOrEmptyString(Header) + ? Visibility.Collapsed + : Visibility.Visible; + } + + } + + private void ContentAlignmentStates_Changed(object sender, VisualStateChangedEventArgs e) + { + CheckVerticalSpacingState(e.NewState); + } + + private void CheckVerticalSpacingState(VisualState s) + { + // On state change, checking if the Content should be wrapped (e.g. when the card is made smaller or the ContentAlignment is set to Vertical). If the Content and the Header or Description are not null, we add spacing between the Content and the Header/Description. + + if (s != null && (s.Name == RightWrappedState || s.Name == RightWrappedNoIconState || s.Name == VerticalState) && (Content != null) && (!IsNullOrEmptyString(Header) || !IsNullOrEmptyString(Description))) + { + VisualStateManager.GoToState(this, ContentSpacingState, true); + } + else + { + VisualStateManager.GoToState(this, NoContentSpacingState, true); + } + } + + private FrameworkElement? GetFocusedElement() + { + if (ControlHelpers.IsXamlRootAvailable && XamlRoot != null) + { + return FocusManager.GetFocusedElement(XamlRoot) as FrameworkElement; + } + else + { + return FocusManager.GetFocusedElement() as FrameworkElement; + } + } + + private static bool IsNullOrEmptyString(object obj) + { + if (obj == null) + { + return true; + } + + if (obj is string objString && objString == string.Empty) + { + return true; + } + + return false; + } +} diff --git a/Hi3Helper.CommunityToolkit/SettingsControls/SettingsCard/SettingsCard.xaml b/Hi3Helper.CommunityToolkit/SettingsControls/SettingsCard/SettingsCard.xaml new file mode 100644 index 000000000..3d2e4dd3a --- /dev/null +++ b/Hi3Helper.CommunityToolkit/SettingsControls/SettingsCard/SettingsCard.xaml @@ -0,0 +1,1085 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 16,16,16,16 + 148 + 68 + 12 + 20 + 0 + 120 + 2,0,20,0 + 14,0,0,0 + 13 + 8 + 476 + 286 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Hi3Helper.CommunityToolkit/SettingsControls/SettingsCard/SettingsCardAutomationPeer.cs b/Hi3Helper.CommunityToolkit/SettingsControls/SettingsCard/SettingsCardAutomationPeer.cs new file mode 100644 index 000000000..0975b01db --- /dev/null +++ b/Hi3Helper.CommunityToolkit/SettingsControls/SettingsCard/SettingsCardAutomationPeer.cs @@ -0,0 +1,68 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Hi3Helper.CommunityToolkit.WinUI.Controls; + +/// +/// AutomationPeer for SettingsCard +/// +public partial class SettingsCardAutomationPeer : FrameworkElementAutomationPeer +{ + /// + /// Initializes a new instance of the class. + /// + /// SettingsCard + public SettingsCardAutomationPeer(SettingsCard owner) + : base(owner) + { + } + + /// + /// Gets the control type for the element that is associated with the UI Automation peer. + /// + /// The control type. + protected override AutomationControlType GetAutomationControlTypeCore() + { + if (Owner is SettingsCard settingsCard && settingsCard.IsClickEnabled) + { + return AutomationControlType.Button; + } + else + { + return AutomationControlType.Group; + } + } + + /// + /// Called by GetClassName that gets a human readable name that, in addition to AutomationControlType, + /// differentiates the control represented by this AutomationPeer. + /// + /// The string that contains the name. + protected override string GetClassNameCore() + { + return Owner.GetType().Name; + } + + protected override string GetNameCore() + { + // We only want to announce the button card name if it is clickable, else it's just a regular card that does not receive focus + if (Owner is SettingsCard owner && owner.IsClickEnabled) + { + string name = AutomationProperties.GetName(owner); + if (!string.IsNullOrEmpty(name)) + { + return name; + } + else + { + if (owner.Header is string headerString && !string.IsNullOrEmpty(headerString)) + { + return headerString; + } + } + } + + return base.GetNameCore(); + } +} diff --git a/Hi3Helper.CommunityToolkit/SettingsControls/SettingsExpander/SettingsExpander.Events.cs b/Hi3Helper.CommunityToolkit/SettingsControls/SettingsExpander/SettingsExpander.Events.cs new file mode 100644 index 000000000..8476075b0 --- /dev/null +++ b/Hi3Helper.CommunityToolkit/SettingsControls/SettingsExpander/SettingsExpander.Events.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Hi3Helper.CommunityToolkit.WinUI.Controls; + +public partial class SettingsExpander +{ + /// + /// Fires when the SettingsExpander is opened + /// + public event EventHandler? Expanded; + + /// + /// Fires when the expander is closed + /// + public event EventHandler? Collapsed; +} diff --git a/Hi3Helper.CommunityToolkit/SettingsControls/SettingsExpander/SettingsExpander.ItemsControl.cs b/Hi3Helper.CommunityToolkit/SettingsControls/SettingsExpander/SettingsExpander.ItemsControl.cs new file mode 100644 index 000000000..b6530059d --- /dev/null +++ b/Hi3Helper.CommunityToolkit/SettingsControls/SettingsExpander/SettingsExpander.ItemsControl.cs @@ -0,0 +1,71 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Hi3Helper.CommunityToolkit.WinUI.Controls; + +//// Implement properties for ItemsControl like behavior. +public partial class SettingsExpander +{ + public IList Items + { + get { return (IList)GetValue(ItemsProperty); } + set { SetValue(ItemsProperty, value); } + } + + public static readonly DependencyProperty ItemsProperty = + DependencyProperty.Register(nameof(Items), typeof(IList), typeof(SettingsExpander), new PropertyMetadata(null, OnItemsConnectedPropertyChanged)); + + public object ItemsSource + { + get { return (object)GetValue(ItemsSourceProperty); } + set { SetValue(ItemsSourceProperty, value); } + } + + public static readonly DependencyProperty ItemsSourceProperty = + DependencyProperty.Register(nameof(ItemsSource), typeof(object), typeof(SettingsExpander), new PropertyMetadata(null, OnItemsConnectedPropertyChanged)); + + public object ItemTemplate + { + get { return (object)GetValue(ItemTemplateProperty); } + set { SetValue(ItemTemplateProperty, value); } + } + + public static readonly DependencyProperty ItemTemplateProperty = + DependencyProperty.Register(nameof(ItemTemplate), typeof(object), typeof(SettingsExpander), new PropertyMetadata(null)); + + public StyleSelector ItemContainerStyleSelector + { + get { return (StyleSelector)GetValue(ItemContainerStyleSelectorProperty); } + set { SetValue(ItemContainerStyleSelectorProperty, value); } + } + + public static readonly DependencyProperty ItemContainerStyleSelectorProperty = + DependencyProperty.Register(nameof(ItemContainerStyleSelector), typeof(StyleSelector), typeof(SettingsExpander), new PropertyMetadata(null)); + + private static void OnItemsConnectedPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args) + { + if (dependencyObject is SettingsExpander expander && expander._itemsRepeater is not null) + { + var datasource = expander.ItemsSource; + + if (datasource is null) + { + datasource = expander.Items; + } + + expander._itemsRepeater.ItemsSource = datasource; + } + } + + private void ItemsRepeater_ElementPrepared(MUXC.ItemsRepeater sender, MUXC.ItemsRepeaterElementPreparedEventArgs args) + { + if (ItemContainerStyleSelector != null && + args.Element is FrameworkElement element && + element.ReadLocalValue(FrameworkElement.StyleProperty) == DependencyProperty.UnsetValue) + { + // TODO: Get item from args.Index? + element.Style = ItemContainerStyleSelector.SelectStyle(null, element); + } + } +} diff --git a/Hi3Helper.CommunityToolkit/SettingsControls/SettingsExpander/SettingsExpander.Properties.cs b/Hi3Helper.CommunityToolkit/SettingsControls/SettingsExpander/SettingsExpander.Properties.cs new file mode 100644 index 000000000..472d72a14 --- /dev/null +++ b/Hi3Helper.CommunityToolkit/SettingsControls/SettingsExpander/SettingsExpander.Properties.cs @@ -0,0 +1,153 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Hi3Helper.CommunityToolkit.WinUI.Controls; + +[ContentProperty(Name = nameof(Content))] +public partial class SettingsExpander +{ + /// + /// The backing for the property. + /// + public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register( + nameof(Header), + typeof(object), + typeof(SettingsExpander), + new PropertyMetadata(defaultValue: null)); + + /// + /// The backing for the property. + /// + public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register( + nameof(Description), + typeof(object), + typeof(SettingsExpander), + new PropertyMetadata(defaultValue: null)); + + /// + /// The backing for the property. + /// + public static readonly DependencyProperty HeaderIconProperty = DependencyProperty.Register( + nameof(HeaderIcon), + typeof(IconElement), + typeof(SettingsExpander), + new PropertyMetadata(defaultValue: null)); + + + /// + /// The backing for the property. + /// + public static readonly DependencyProperty ContentProperty = DependencyProperty.Register( + nameof(Content), + typeof(object), + typeof(SettingsExpander), + new PropertyMetadata(defaultValue: null)); + + /// + /// The backing for the property. + /// + public static readonly DependencyProperty ItemsHeaderProperty = DependencyProperty.Register( + nameof(ItemsHeader), + typeof(UIElement), + typeof(SettingsExpander), + new PropertyMetadata(defaultValue: null)); + + /// + /// The backing for the property. + /// + public static readonly DependencyProperty ItemsFooterProperty = DependencyProperty.Register( + nameof(ItemsFooter), + typeof(UIElement), + typeof(SettingsExpander), + new PropertyMetadata(defaultValue: null)); + + /// + /// The backing for the property. + /// + public static readonly DependencyProperty IsExpandedProperty = DependencyProperty.Register( + nameof(IsExpanded), + typeof(bool), + typeof(SettingsExpander), + new PropertyMetadata(defaultValue: false, (d, e) => ((SettingsExpander)d).OnIsExpandedPropertyChanged((bool)e.OldValue, (bool)e.NewValue))); + + /// + /// + /// + /// Gets or sets the Header. + /// + public object Header + { + get => (object)GetValue(HeaderProperty); + set => SetValue(HeaderProperty, value); + } + + /// + /// Gets or sets the Description. + /// +#pragma warning disable CS0109 // Member does not hide an inherited member; new keyword is not required + public new object Description +#pragma warning restore CS0109 // Member does not hide an inherited member; new keyword is not required + { + get => (object)GetValue(DescriptionProperty); + set => SetValue(DescriptionProperty, value); + } + + /// + /// Gets or sets the HeaderIcon. + /// + public IconElement HeaderIcon + { + get => (IconElement)GetValue(HeaderIconProperty); + set => SetValue(HeaderIconProperty, value); + } + + /// + /// Gets or sets the Content. + /// + public object Content + { + get => (object)GetValue(ContentProperty); + set => SetValue(ContentProperty, value); + } + + /// + /// Gets or sets the ItemsFooter. + /// + public UIElement ItemsHeader + { + get => (UIElement)GetValue(ItemsHeaderProperty); + set => SetValue(ItemsHeaderProperty, value); + } + + /// + /// Gets or sets the ItemsFooter. + /// + public UIElement ItemsFooter + { + get => (UIElement)GetValue(ItemsFooterProperty); + set => SetValue(ItemsFooterProperty, value); + } + + /// + /// Gets or sets the IsExpanded state. + /// + public bool IsExpanded + { + get => (bool)GetValue(IsExpandedProperty); + set => SetValue(IsExpandedProperty, value); + } + protected virtual void OnIsExpandedPropertyChanged(bool oldValue, bool newValue) + { + OnIsExpandedChanged(oldValue, newValue); + + if (newValue) + { + Expanded?.Invoke(this, EventArgs.Empty); + } + else + { + Collapsed?.Invoke(this, EventArgs.Empty); + } + } +} diff --git a/Hi3Helper.CommunityToolkit/SettingsControls/SettingsExpander/SettingsExpander.cs b/Hi3Helper.CommunityToolkit/SettingsControls/SettingsExpander/SettingsExpander.cs new file mode 100644 index 000000000..6fbcd0356 --- /dev/null +++ b/Hi3Helper.CommunityToolkit/SettingsControls/SettingsExpander/SettingsExpander.cs @@ -0,0 +1,71 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Hi3Helper.CommunityToolkit.WinUI.Controls; + +//// Note: ItemsRepeater will request all the available horizontal space: https://github.com/microsoft/microsoft-ui-xaml/issues/3842 +[TemplatePart(Name = PART_ItemsRepeater, Type = typeof(MUXC.ItemsRepeater))] +public partial class SettingsExpander : Control +{ + private const string PART_ItemsRepeater = "PART_ItemsRepeater"; + + private MUXC.ItemsRepeater? _itemsRepeater; + + /// + /// The SettingsExpander is a collapsable control to host multiple SettingsCards. + /// + public SettingsExpander() + { + this.DefaultStyleKey = typeof(SettingsExpander); + Items = new List(); + } + + /// + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + SetAccessibleName(); + + if (_itemsRepeater != null) + { + _itemsRepeater.ElementPrepared -= this.ItemsRepeater_ElementPrepared; + } + + _itemsRepeater = GetTemplateChild(PART_ItemsRepeater) as MUXC.ItemsRepeater; + + if (_itemsRepeater != null) + { + _itemsRepeater.ElementPrepared += this.ItemsRepeater_ElementPrepared; + + // Update it's source based on our current items properties. + OnItemsConnectedPropertyChanged(this, null!); // Can't get it to accept type here? (DependencyPropertyChangedEventArgs)EventArgs.Empty + } + } + + private void SetAccessibleName() + { + if (string.IsNullOrEmpty(AutomationProperties.GetName(this))) + { + if (Header is string headerString && !string.IsNullOrEmpty(headerString)) + { + AutomationProperties.SetName(this, headerString); + } + } + } + + /// + /// Creates AutomationPeer + /// + /// An automation peer for . + protected override AutomationPeer OnCreateAutomationPeer() + { + return new SettingsExpanderAutomationPeer(this); + } + + private void OnIsExpandedChanged(bool oldValue, bool newValue) + { + var peer = FrameworkElementAutomationPeer.FromElement(this) as SettingsExpanderAutomationPeer; + peer?.RaiseExpandedChangedEvent(newValue); + } +} diff --git a/Hi3Helper.CommunityToolkit/SettingsControls/SettingsExpander/SettingsExpander.xaml b/Hi3Helper.CommunityToolkit/SettingsControls/SettingsExpander/SettingsExpander.xaml new file mode 100644 index 000000000..bfc0cde77 --- /dev/null +++ b/Hi3Helper.CommunityToolkit/SettingsControls/SettingsExpander/SettingsExpander.xaml @@ -0,0 +1,582 @@ + + + + + + + + + Show all settings + 16,16,4,16 + 58,8,44,8 + 0,1,0,0 + 58,8,16,8 + 16 + 32 + 32 + + + + + + + + + + + + + + + + + diff --git a/Hi3Helper.CommunityToolkit/SettingsControls/SettingsExpander/SettingsExpanderAutomationPeer.cs b/Hi3Helper.CommunityToolkit/SettingsControls/SettingsExpander/SettingsExpanderAutomationPeer.cs new file mode 100644 index 000000000..ee9e2c0bd --- /dev/null +++ b/Hi3Helper.CommunityToolkit/SettingsControls/SettingsExpander/SettingsExpanderAutomationPeer.cs @@ -0,0 +1,85 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Text; + +namespace Hi3Helper.CommunityToolkit.WinUI.Controls; + +/// +/// AutomationPeer for SettingsExpander +/// +public partial class SettingsExpanderAutomationPeer : FrameworkElementAutomationPeer +{ + /// + /// Initializes a new instance of the class. + /// + /// SettingsExpander + public SettingsExpanderAutomationPeer(SettingsExpander owner) + : base(owner) + { + } + + /// + /// Gets the control type for the element that is associated with the UI Automation peer. + /// + /// The control type. + protected override AutomationControlType GetAutomationControlTypeCore() + { + return AutomationControlType.Group; + } + + /// + /// Called by GetClassName that gets a human readable name that, in addition to AutomationControlType, + /// differentiates the control represented by this AutomationPeer. + /// + /// The string that contains the name. + protected override string GetClassNameCore() + { + return Owner.GetType().Name; + } + + protected override string GetNameCore() + { + string name = base.GetNameCore(); + + if (Owner is SettingsExpander owner) + { + if (!string.IsNullOrEmpty(AutomationProperties.GetName(owner))) + { + name = AutomationProperties.GetName(owner); + } + else + { + if (owner.Header is string headerString && !string.IsNullOrEmpty(headerString)) + { + name = headerString; + } + } + } + return name; + } + + /// + /// Raises the property changed event for this AutomationPeer for the provided identifier. + /// Narrator does not announce this due to: https://github.com/microsoft/microsoft-ui-xaml/issues/3469 + /// + /// New Expanded state + public void RaiseExpandedChangedEvent(bool newValue) + { + ExpandCollapseState newState = (newValue == true) ? + ExpandCollapseState.Expanded : + ExpandCollapseState.Collapsed; + + ExpandCollapseState oldState = (newState == ExpandCollapseState.Expanded) ? + ExpandCollapseState.Collapsed : + ExpandCollapseState.Expanded; + + #if !HAS_UNO + RaisePropertyChangedEvent(ExpandCollapsePatternIdentifiers.ExpandCollapseStateProperty, oldState, newState); + #endif + } +} diff --git a/Hi3Helper.CommunityToolkit/SettingsControls/SettingsExpander/SettingsExpanderItemStyleSelector.cs b/Hi3Helper.CommunityToolkit/SettingsControls/SettingsExpander/SettingsExpanderItemStyleSelector.cs new file mode 100644 index 000000000..2729d674e --- /dev/null +++ b/Hi3Helper.CommunityToolkit/SettingsControls/SettingsExpander/SettingsExpanderItemStyleSelector.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Hi3Helper.CommunityToolkit.WinUI.Controls; + +/// +/// used by to choose the proper container style (clickable or not). +/// +public partial class SettingsExpanderItemStyleSelector : StyleSelector +{ + /// + /// Gets or sets the default . + /// + public Style DefaultStyle { get; set; } + + /// + /// Gets or sets the when clickable. + /// + public Style ClickableStyle { get; set; } + + /// + /// Initializes a new instance of the class. + /// +#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. + public SettingsExpanderItemStyleSelector() +#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. + { + } + + /// + protected override Style SelectStyleCore(object item, DependencyObject container) + { + if (container is SettingsCard card && card.IsClickEnabled) + { + return ClickableStyle; + } + else + { + return DefaultStyle; + } + } +} diff --git a/Hi3Helper.CommunityToolkit/SettingsControls/Themes/Generic.xaml b/Hi3Helper.CommunityToolkit/SettingsControls/Themes/Generic.xaml new file mode 100644 index 000000000..5a35ea171 --- /dev/null +++ b/Hi3Helper.CommunityToolkit/SettingsControls/Themes/Generic.xaml @@ -0,0 +1,8 @@ + + + + + + + diff --git a/Hi3Helper.CommunityToolkit/commitinfo.txt b/Hi3Helper.CommunityToolkit/commitinfo.txt new file mode 100644 index 000000000..c4d6cb691 --- /dev/null +++ b/Hi3Helper.CommunityToolkit/commitinfo.txt @@ -0,0 +1 @@ +https://github.com/CommunityToolkit/Windows/commit/44d3d9d3601bb543625078fbbab55a2a867b0662 \ No newline at end of file diff --git a/Hi3Helper.Core/packages.lock.json b/Hi3Helper.Core/packages.lock.json index 26197f47f..4e320c966 100644 --- a/Hi3Helper.Core/packages.lock.json +++ b/Hi3Helper.Core/packages.lock.json @@ -2,18 +2,6 @@ "version": 1, "dependencies": { "net9.0": { - "Microsoft.DotNet.ILCompiler": { - "type": "Direct", - "requested": "[9.0.0-rc.1.24431.7, )", - "resolved": "9.0.0-rc.1.24431.7", - "contentHash": "FN38PfBwjKY4D753sz6GDSnvtHU9UUPb9fNAcPwImOn2j8SyUFZMnVmMVwnYX4dlDB9XL98m4g6szsjIx5UWgg==" - }, - "Microsoft.NET.ILLink.Tasks": { - "type": "Direct", - "requested": "[9.0.0-rc.1.24431.7, )", - "resolved": "9.0.0-rc.1.24431.7", - "contentHash": "B8jHIIszeUdDY+O4G1/xPOuhrriwsv9Z8/u3XSOAqc8Z0mQCeLBKg2oLGElDZeRF8wWXDGklAKY3f0IHopyXkQ==" - }, "Google.Protobuf": { "type": "Transitive", "resolved": "3.28.1", @@ -35,22 +23,6 @@ "hi3helper.http": { "type": "Project" } - }, - "net9.0/win-x64": { - "Microsoft.DotNet.ILCompiler": { - "type": "Direct", - "requested": "[9.0.0-rc.1.24431.7, )", - "resolved": "9.0.0-rc.1.24431.7", - "contentHash": "FN38PfBwjKY4D753sz6GDSnvtHU9UUPb9fNAcPwImOn2j8SyUFZMnVmMVwnYX4dlDB9XL98m4g6szsjIx5UWgg==", - "dependencies": { - "runtime.win-x64.Microsoft.DotNet.ILCompiler": "9.0.0-rc.1.24431.7" - } - }, - "runtime.win-x64.Microsoft.DotNet.ILCompiler": { - "type": "Transitive", - "resolved": "9.0.0-rc.1.24431.7", - "contentHash": "hOXKEHhsK/sWwrMEL1Yrfj0Mw1WnsBsi8OoEEEmS4hgOxo39Kic0aap2nKm9eWRl8Fe5S+K10a2nVkt/T2AF7Q==" - } } } } \ No newline at end of file diff --git a/InnoSetupHelper/packages.lock.json b/InnoSetupHelper/packages.lock.json index bb1fc58ff..e54b93f8f 100644 --- a/InnoSetupHelper/packages.lock.json +++ b/InnoSetupHelper/packages.lock.json @@ -2,18 +2,6 @@ "version": 1, "dependencies": { "net9.0": { - "Microsoft.DotNet.ILCompiler": { - "type": "Direct", - "requested": "[9.0.0-rc.1.24431.7, )", - "resolved": "9.0.0-rc.1.24431.7", - "contentHash": "FN38PfBwjKY4D753sz6GDSnvtHU9UUPb9fNAcPwImOn2j8SyUFZMnVmMVwnYX4dlDB9XL98m4g6szsjIx5UWgg==" - }, - "Microsoft.NET.ILLink.Tasks": { - "type": "Direct", - "requested": "[9.0.0-rc.1.24431.7, )", - "resolved": "9.0.0-rc.1.24431.7", - "contentHash": "B8jHIIszeUdDY+O4G1/xPOuhrriwsv9Z8/u3XSOAqc8Z0mQCeLBKg2oLGElDZeRF8wWXDGklAKY3f0IHopyXkQ==" - }, "System.IO.Hashing": { "type": "Direct", "requested": "[9.0.0-rc.1.24431.7, )", @@ -43,22 +31,6 @@ "hi3helper.http": { "type": "Project" } - }, - "net9.0/win-x64": { - "Microsoft.DotNet.ILCompiler": { - "type": "Direct", - "requested": "[9.0.0-rc.1.24431.7, )", - "resolved": "9.0.0-rc.1.24431.7", - "contentHash": "FN38PfBwjKY4D753sz6GDSnvtHU9UUPb9fNAcPwImOn2j8SyUFZMnVmMVwnYX4dlDB9XL98m4g6szsjIx5UWgg==", - "dependencies": { - "runtime.win-x64.Microsoft.DotNet.ILCompiler": "9.0.0-rc.1.24431.7" - } - }, - "runtime.win-x64.Microsoft.DotNet.ILCompiler": { - "type": "Transitive", - "resolved": "9.0.0-rc.1.24431.7", - "contentHash": "hOXKEHhsK/sWwrMEL1Yrfj0Mw1WnsBsi8OoEEEmS4hgOxo39Kic0aap2nKm9eWRl8Fe5S+K10a2nVkt/T2AF7Q==" - } } } } \ No newline at end of file From 17841250978d59583dd0e80a76978e42b4c9ccab Mon Sep 17 00:00:00 2001 From: neon-nyan Date: Mon, 16 Sep 2024 22:28:21 +0700 Subject: [PATCH 022/110] [skip ci] BROKEN - Update ImageCropper --- .gitignore | 4 +- ...Toolkit.WinUI.Controls.ImageCropper.csproj | 0 Backup/ReadMe.md | 38 ++++++++++++ CollapseLauncher.sln | 8 +++ .../ImageCropper/Extension.cs | 36 +++++++++++ .../CsWinRT/WinRTEventHelpers.cs | 13 ++++ .../Generated Files/CsWinRT/cswinrt.rsp | 7 +++ .../CsWinRT/cswinrt_internal.rsp | 7 +++ ...Toolkit.WinUI.Controls.ImageCropper.csproj | 51 ++++++++++++++++ .../ImageCropper/ImageCropper.Animations.cs | 5 +- .../ImageCropper/ImageCropper.Constants.cs | 31 +++++----- .../ImageCropper/ImageCropper.Events.cs | 3 + .../ImageCropper/ImageCropper.Helpers.cs | 8 +-- .../ImageCropper/ImageCropper.Logic.cs | 4 +- .../ImageCropper/ImageCropper.Properties.cs | 2 + .../ImageCropper/ImageCropper.cs | 60 ++++++++++--------- .../ImageCropper/ImageCropper.xaml | 2 +- .../ImageCropper/ImageCropperThumb.cs | 6 +- .../ImageCropper/ImageCropperThumb.xaml | 2 +- .../ImageCropper/MultiTarget.props | 9 --- .../ImageCropper/Themes/Generic.xaml | 6 +- ...Toolkit.WinUI.Controls.ImageCropper.csproj | 27 +++++++++ 22 files changed, 259 insertions(+), 70 deletions(-) rename {Hi3Helper.CommunityToolkit/ImageCropper => Backup}/CommunityToolkit.WinUI.Controls.ImageCropper.csproj (100%) create mode 100644 Backup/ReadMe.md create mode 100644 Hi3Helper.CommunityToolkit/ImageCropper/Extension.cs create mode 100644 Hi3Helper.CommunityToolkit/ImageCropper/Generated Files/CsWinRT/WinRTEventHelpers.cs create mode 100644 Hi3Helper.CommunityToolkit/ImageCropper/Generated Files/CsWinRT/cswinrt.rsp create mode 100644 Hi3Helper.CommunityToolkit/ImageCropper/Generated Files/CsWinRT/cswinrt_internal.rsp create mode 100644 Hi3Helper.CommunityToolkit/ImageCropper/Hi3Helper.CommunityToolkit.WinUI.Controls.ImageCropper.csproj delete mode 100644 Hi3Helper.CommunityToolkit/ImageCropper/MultiTarget.props create mode 100644 Hi3Helper.CommunityToolkit/ImageCropper/old.CommunityToolkit.WinUI.Controls.ImageCropper.csproj diff --git a/.gitignore b/.gitignore index 7fe060537..0747305b1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ -*/bin/* -*/obj/* +**/bin/* +**/obj/* *build/* .vs/* *.user diff --git a/Hi3Helper.CommunityToolkit/ImageCropper/CommunityToolkit.WinUI.Controls.ImageCropper.csproj b/Backup/CommunityToolkit.WinUI.Controls.ImageCropper.csproj similarity index 100% rename from Hi3Helper.CommunityToolkit/ImageCropper/CommunityToolkit.WinUI.Controls.ImageCropper.csproj rename to Backup/CommunityToolkit.WinUI.Controls.ImageCropper.csproj diff --git a/Backup/ReadMe.md b/Backup/ReadMe.md new file mode 100644 index 000000000..07942198d --- /dev/null +++ b/Backup/ReadMe.md @@ -0,0 +1,38 @@ + +# Windows Community Toolkit - ImageCropper + +This package is part of the [Windows Community Toolkit](https://aka.ms/toolkit/windows) from the [.NET Foundation](https://dotnetfoundation.org). + +## Package Contents + +This package contains the following controls in the `CommunityToolkit.WinUI.Controls` namespace: + +- ImageCropper + +## Which Package is for me? + +If you're developing with _UWP/WinUI 2 or Uno.UI_ you should be using the `CommunityToolkit.Uwp.Controls.ImageCropper` package. + +If you're developing with _WindowsAppSDK/WinUI 3 or Uno.WinUI_ you should be using the `CommunityToolkit.WinUI.Controls.ImageCropper` package. + +## WinUI Resources (UWP) + +For UWP projects, the WinUI 2 reference requires you include the WinUI XAML Resources in your App.xaml file: + +```xml + + + +``` + +See [Getting Started in WinUI 2](https://learn.microsoft.com/windows/apps/winui/winui2/getting-started) for more information. + +## Documentation + +Further documentation about these components can be found at: https://aka.ms/windowstoolkitdocs + +## License + +MIT + +See License.md in package for more details. diff --git a/CollapseLauncher.sln b/CollapseLauncher.sln index 2d74138ef..6eb4cb35b 100644 --- a/CollapseLauncher.sln +++ b/CollapseLauncher.sln @@ -26,6 +26,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageEx", "ImageEx\ImageEx\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hi3Helper.TaskScheduler", "Hi3Helper.TaskScheduler\Hi3Helper.TaskScheduler.csproj", "{C9CBAF52-49C7-4B72-A03B-130F596E24CB}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hi3Helper.CommunityToolkit.WinUI.Controls.ImageCropper", "Hi3Helper.CommunityToolkit\ImageCropper\Hi3Helper.CommunityToolkit.WinUI.Controls.ImageCropper.csproj", "{558A1D17-BEB4-49DF-A200-15ABE283BDED}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -101,6 +103,12 @@ Global {C9CBAF52-49C7-4B72-A03B-130F596E24CB}.Debug|x64.ActiveCfg = Release|x64 {C9CBAF52-49C7-4B72-A03B-130F596E24CB}.Publish|x64.ActiveCfg = Release|x64 {C9CBAF52-49C7-4B72-A03B-130F596E24CB}.Release|x64.ActiveCfg = Release|x64 + {558A1D17-BEB4-49DF-A200-15ABE283BDED}.Debug|x64.ActiveCfg = Debug|x64 + {558A1D17-BEB4-49DF-A200-15ABE283BDED}.Debug|x64.Build.0 = Debug|x64 + {558A1D17-BEB4-49DF-A200-15ABE283BDED}.Publish|x64.ActiveCfg = Debug|x64 + {558A1D17-BEB4-49DF-A200-15ABE283BDED}.Publish|x64.Build.0 = Debug|x64 + {558A1D17-BEB4-49DF-A200-15ABE283BDED}.Release|x64.ActiveCfg = Release|x64 + {558A1D17-BEB4-49DF-A200-15ABE283BDED}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Hi3Helper.CommunityToolkit/ImageCropper/Extension.cs b/Hi3Helper.CommunityToolkit/ImageCropper/Extension.cs new file mode 100644 index 000000000..b89111868 --- /dev/null +++ b/Hi3Helper.CommunityToolkit/ImageCropper/Extension.cs @@ -0,0 +1,36 @@ +using System.Diagnostics.Contracts; +using System.Runtime.CompilerServices; + +namespace Hi3Helper.CommunityToolkit.WinUI.Controls +{ + internal static class Extension + { + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rect ToRect(this Size size) + { + return new Rect(0, 0, size.Width, size.Height); + } + + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rect ToRect(this Point point, double width, double height) + { + return new Rect(point.X, point.Y, width, height); + } + + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rect ToRect(this Point point, Point end) + { + return new Rect(point, end); + } + + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rect ToRect(this Point point, Size size) + { + return new Rect(point, size); + } + } +} diff --git a/Hi3Helper.CommunityToolkit/ImageCropper/Generated Files/CsWinRT/WinRTEventHelpers.cs b/Hi3Helper.CommunityToolkit/ImageCropper/Generated Files/CsWinRT/WinRTEventHelpers.cs new file mode 100644 index 000000000..41efc07a1 --- /dev/null +++ b/Hi3Helper.CommunityToolkit/ImageCropper/Generated Files/CsWinRT/WinRTEventHelpers.cs @@ -0,0 +1,13 @@ +//------------------------------------------------------------------------------ +// +// This file was generated by cswinrt.exe version 2.1.3.240909.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +using System; +namespace WinRT +{ + +} \ No newline at end of file diff --git a/Hi3Helper.CommunityToolkit/ImageCropper/Generated Files/CsWinRT/cswinrt.rsp b/Hi3Helper.CommunityToolkit/ImageCropper/Generated Files/CsWinRT/cswinrt.rsp new file mode 100644 index 000000000..30b02b4a1 --- /dev/null +++ b/Hi3Helper.CommunityToolkit/ImageCropper/Generated Files/CsWinRT/cswinrt.rsp @@ -0,0 +1,7 @@ +-target net8.0 +-input 10.0.22621.0 +-input "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0\Microsoft.UI.Text.winmd" "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0\Microsoft.UI.Xaml.winmd" "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0\Microsoft.Windows.ApplicationModel.DynamicDependency.winmd" "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0\Microsoft.Windows.ApplicationModel.Resources.winmd" "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0\Microsoft.Windows.ApplicationModel.WindowsAppRuntime.winmd" "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0\Microsoft.Windows.AppLifecycle.winmd" "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0\Microsoft.Windows.AppNotifications.Builder.winmd" "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0\Microsoft.Windows.AppNotifications.winmd" "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0\Microsoft.Windows.Globalization.winmd" "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0\Microsoft.Windows.Management.Deployment.winmd" "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0\Microsoft.Windows.PushNotifications.winmd" "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0\Microsoft.Windows.Security.AccessControl.winmd" "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0\Microsoft.Windows.Storage.winmd" "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0\Microsoft.Windows.System.Power.winmd" "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0\Microsoft.Windows.System.winmd" "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0\Microsoft.Windows.Widgets.winmd" "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0.18362\Microsoft.Foundation.winmd" "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0.18362\Microsoft.Graphics.winmd" "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0.18362\Microsoft.UI.winmd" +-output "Generated Files\\CsWinRT" + +-exclude Windows +-exclude Microsoft diff --git a/Hi3Helper.CommunityToolkit/ImageCropper/Generated Files/CsWinRT/cswinrt_internal.rsp b/Hi3Helper.CommunityToolkit/ImageCropper/Generated Files/CsWinRT/cswinrt_internal.rsp new file mode 100644 index 000000000..30b02b4a1 --- /dev/null +++ b/Hi3Helper.CommunityToolkit/ImageCropper/Generated Files/CsWinRT/cswinrt_internal.rsp @@ -0,0 +1,7 @@ +-target net8.0 +-input 10.0.22621.0 +-input "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0\Microsoft.UI.Text.winmd" "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0\Microsoft.UI.Xaml.winmd" "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0\Microsoft.Windows.ApplicationModel.DynamicDependency.winmd" "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0\Microsoft.Windows.ApplicationModel.Resources.winmd" "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0\Microsoft.Windows.ApplicationModel.WindowsAppRuntime.winmd" "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0\Microsoft.Windows.AppLifecycle.winmd" "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0\Microsoft.Windows.AppNotifications.Builder.winmd" "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0\Microsoft.Windows.AppNotifications.winmd" "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0\Microsoft.Windows.Globalization.winmd" "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0\Microsoft.Windows.Management.Deployment.winmd" "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0\Microsoft.Windows.PushNotifications.winmd" "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0\Microsoft.Windows.Security.AccessControl.winmd" "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0\Microsoft.Windows.Storage.winmd" "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0\Microsoft.Windows.System.Power.winmd" "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0\Microsoft.Windows.System.winmd" "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0\Microsoft.Windows.Widgets.winmd" "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0.18362\Microsoft.Foundation.winmd" "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0.18362\Microsoft.Graphics.winmd" "C:\Users\neon-nyan\.nuget\packages\microsoft.windowsappsdk\1.6.240829007\lib\uap10.0.18362\Microsoft.UI.winmd" +-output "Generated Files\\CsWinRT" + +-exclude Windows +-exclude Microsoft diff --git a/Hi3Helper.CommunityToolkit/ImageCropper/Hi3Helper.CommunityToolkit.WinUI.Controls.ImageCropper.csproj b/Hi3Helper.CommunityToolkit/ImageCropper/Hi3Helper.CommunityToolkit.WinUI.Controls.ImageCropper.csproj new file mode 100644 index 000000000..b09734240 --- /dev/null +++ b/Hi3Helper.CommunityToolkit/ImageCropper/Hi3Helper.CommunityToolkit.WinUI.Controls.ImageCropper.csproj @@ -0,0 +1,51 @@ + + + + + x64 + net9.0-windows10.0.22621.0 + 10.0.22621.41 + 10.0.17763.0 + win-x64 + true + true + Library + 12.0 + true + embedded + + ImageCropper + Hi3Helper.CommunityToolkit.WinUI.Controls.ImageCropper + Hi3Helper.CommunityToolkit.WinUI.Controls.ImageCropper + 1.0.0 + https://github.com/CollapseLauncher/Collapse + enable + disable + WINAPPSDK + + + + true + win-x64 + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropper.Animations.cs b/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropper.Animations.cs index 89ccd1c06..6d4b4c55d 100644 --- a/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropper.Animations.cs +++ b/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropper.Animations.cs @@ -4,12 +4,15 @@ #if WINAPPSDK using Microsoft.UI.Composition; +using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Hosting; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Media.Animation; + #else using Windows.UI.Composition; using Windows.UI.Xaml.Hosting; #endif -using System.Numerics; namespace Hi3Helper.CommunityToolkit.WinUI.Controls; diff --git a/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropper.Constants.cs b/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropper.Constants.cs index 1aa555c33..310c96773 100644 --- a/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropper.Constants.cs +++ b/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropper.Constants.cs @@ -7,70 +7,71 @@ namespace Hi3Helper.CommunityToolkit.WinUI.Controls; /// /// The control allows user to crop image freely. /// -public partial class ImageCropper + +public static class ImageCropperNames { /// /// Key of the root layout container. /// - private const string LayoutGridName = "PART_LayoutGrid"; + internal const string LayoutGridName = "PART_LayoutGrid"; /// /// Key of the Canvas that contains the image and ImageCropperThumbs. /// - private const string ImageCanvasPartName = "PART_ImageCanvas"; + internal const string ImageCanvasPartName = "PART_ImageCanvas"; /// /// Key of the Image Control inside the ImageCropper Control. /// - private const string SourceImagePartName = "PART_SourceImage"; + internal const string SourceImagePartName = "PART_SourceImage"; /// /// Key of the mask layer. /// - private const string MaskAreaPathPartName = "PART_MaskAreaPath"; + internal const string MaskAreaPathPartName = "PART_MaskAreaPath"; /// /// Key of the overlay layer. /// - private const string OverlayAreaPathPartName = "PART_OverlayAreaPath"; + internal const string OverlayAreaPathPartName = "PART_OverlayAreaPath"; /// /// Key of the ImageCropperThumb that on the top. /// - private const string TopThumbPartName = "PART_TopThumb"; + internal const string TopThumbPartName = "PART_TopThumb"; /// /// Key of the ImageCropperThumb on the bottom. /// - private const string BottomThumbPartName = "PART_BottomThumb"; + internal const string BottomThumbPartName = "PART_BottomThumb"; /// /// Key of the ImageCropperThumb on the left. /// - private const string LeftThumbPartName = "PART_LeftThumb"; + internal const string LeftThumbPartName = "PART_LeftThumb"; /// /// Key of the ImageCropperThumb on the right. /// - private const string RightThumbPartName = "PART_RightThumb"; + internal const string RightThumbPartName = "PART_RightThumb"; /// /// Key of the ImageCropperThumb that on the upper left. /// - private const string UpperLeftThumbPartName = "PART_UpperLeftThumb"; + internal const string UpperLeftThumbPartName = "PART_UpperLeftThumb"; /// /// Key of the ImageCropperThumb that on the upper right. /// - private const string UpperRightThumbPartName = "PART_UpperRightThumb"; + internal const string UpperRightThumbPartName = "PART_UpperRightThumb"; /// /// Key of the ImageCropperThumb that on the lower left. /// - private const string LowerLeftThumbPartName = "PART_LowerLeftThumb"; + internal const string LowerLeftThumbPartName = "PART_LowerLeftThumb"; /// /// Key of the ImageCropperThumb that on the lower right. /// - private const string LowerRightThumbPartName = "PART_LowerRightThumb"; -} + internal const string LowerRightThumbPartName = "PART_LowerRightThumb"; +} \ No newline at end of file diff --git a/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropper.Events.cs b/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropper.Events.cs index 611d4caaa..1cadcef15 100644 --- a/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropper.Events.cs +++ b/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropper.Events.cs @@ -4,6 +4,9 @@ #if WINAPPSDK using Microsoft.UI.Input; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Input; + #endif using Windows.System; using Windows.UI.Core; diff --git a/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropper.Helpers.cs b/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropper.Helpers.cs index b7560e466..ebd779af4 100644 --- a/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropper.Helpers.cs +++ b/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropper.Helpers.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Numerics; #if WINAPPSDK using Microsoft.UI.Xaml.Media.Imaging; using Colors = Microsoft.UI.Colors; @@ -10,11 +9,6 @@ using Windows.UI.Xaml.Media.Imaging; using Colors = Windows.UI.Colors; #endif -using Microsoft.Graphics.Canvas; -using Microsoft.Graphics.Canvas.Effects; -using Microsoft.Graphics.Canvas.Geometry; -using Windows.Graphics.Imaging; -using Windows.Storage.Streams; namespace Hi3Helper.CommunityToolkit.WinUI.Controls; @@ -36,7 +30,7 @@ private static async Task CropImageAsync(WriteableBitmap writeableBitmap, IRando using (var sourceStream = writeableBitmap.PixelBuffer.AsStream()) { var buffer = new byte[sourceStream.Length]; - await sourceStream.ReadAsync(buffer, 0, buffer.Length); + _ = await sourceStream.ReadAsync(buffer); var bitmapEncoder = await BitmapEncoder.CreateAsync(GetEncoderId(bitmapFileFormat), stream); bitmapEncoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied, (uint)writeableBitmap.PixelWidth, (uint)writeableBitmap.PixelHeight, 96.0, 96.0, buffer); bitmapEncoder.BitmapTransform.Bounds = new BitmapBounds diff --git a/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropper.Logic.cs b/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropper.Logic.cs index e92c4fc74..be6741521 100644 --- a/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropper.Logic.cs +++ b/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropper.Logic.cs @@ -2,9 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Numerics; #if WINAPPSDK +using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Hosting; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Media.Animation; #else using Windows.UI.Xaml.Hosting; #endif diff --git a/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropper.Properties.cs b/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropper.Properties.cs index f169a06b4..13c7bb5b5 100644 --- a/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropper.Properties.cs +++ b/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropper.Properties.cs @@ -3,6 +3,8 @@ // See the LICENSE file in the project root for more information. #if WINAPPSDK +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Media; using Microsoft.UI.Xaml.Media.Imaging; #else using Windows.UI.Xaml.Media.Imaging; diff --git a/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropper.cs b/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropper.cs index 12149755b..e6cb78316 100644 --- a/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropper.cs +++ b/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropper.cs @@ -2,11 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Windows.Storage; -using Windows.Storage.Streams; #if WINAPPSDK -using Path = Microsoft.UI.Xaml.Shapes.Path; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; using Microsoft.UI.Xaml.Media.Imaging; +using Path = Microsoft.UI.Xaml.Shapes.Path; #else using Windows.UI.Xaml.Media.Imaging; using Path = Windows.UI.Xaml.Shapes.Path; @@ -17,19 +19,19 @@ namespace Hi3Helper.CommunityToolkit.WinUI.Controls; /// /// The control allows user to crop image freely. /// -[TemplatePart(Name = LayoutGridName, Type = typeof(Grid))] -[TemplatePart(Name = ImageCanvasPartName, Type = typeof(Canvas))] -[TemplatePart(Name = SourceImagePartName, Type = typeof(Image))] -[TemplatePart(Name = MaskAreaPathPartName, Type = typeof(Path))] -[TemplatePart(Name = OverlayAreaPathPartName, Type = typeof(Path))] -[TemplatePart(Name = TopThumbPartName, Type = typeof(ImageCropperThumb))] -[TemplatePart(Name = BottomThumbPartName, Type = typeof(ImageCropperThumb))] -[TemplatePart(Name = LeftThumbPartName, Type = typeof(ImageCropperThumb))] -[TemplatePart(Name = RightThumbPartName, Type = typeof(ImageCropperThumb))] -[TemplatePart(Name = UpperLeftThumbPartName, Type = typeof(ImageCropperThumb))] -[TemplatePart(Name = UpperRightThumbPartName, Type = typeof(ImageCropperThumb))] -[TemplatePart(Name = LowerLeftThumbPartName, Type = typeof(ImageCropperThumb))] -[TemplatePart(Name = LowerRightThumbPartName, Type = typeof(ImageCropperThumb))] +[TemplatePart(Name = ImageCropperNames.LayoutGridName, Type = typeof(Grid))] +[TemplatePart(Name = ImageCropperNames.ImageCanvasPartName, Type = typeof(Canvas))] +[TemplatePart(Name = ImageCropperNames.SourceImagePartName, Type = typeof(Image))] +[TemplatePart(Name = ImageCropperNames.MaskAreaPathPartName, Type = typeof(Path))] +[TemplatePart(Name = ImageCropperNames.OverlayAreaPathPartName, Type = typeof(Path))] +[TemplatePart(Name = ImageCropperNames.TopThumbPartName, Type = typeof(ImageCropperThumb))] +[TemplatePart(Name = ImageCropperNames.BottomThumbPartName, Type = typeof(ImageCropperThumb))] +[TemplatePart(Name = ImageCropperNames.LeftThumbPartName, Type = typeof(ImageCropperThumb))] +[TemplatePart(Name = ImageCropperNames.RightThumbPartName, Type = typeof(ImageCropperThumb))] +[TemplatePart(Name = ImageCropperNames.UpperLeftThumbPartName, Type = typeof(ImageCropperThumb))] +[TemplatePart(Name = ImageCropperNames.UpperRightThumbPartName, Type = typeof(ImageCropperThumb))] +[TemplatePart(Name = ImageCropperNames.LowerLeftThumbPartName, Type = typeof(ImageCropperThumb))] +[TemplatePart(Name = ImageCropperNames.LowerRightThumbPartName, Type = typeof(ImageCropperThumb))] public partial class ImageCropper : Control { private readonly CompositeTransform _imageTransform = new CompositeTransform(); @@ -164,19 +166,19 @@ private Size MinSelectSize protected override void OnApplyTemplate() { UnhookEvents(); - _layoutGrid = GetTemplateChild(LayoutGridName) as Grid; - _imageCanvas = GetTemplateChild(ImageCanvasPartName) as Canvas; - _sourceImage = GetTemplateChild(SourceImagePartName) as Image; - _maskAreaPath = GetTemplateChild(MaskAreaPathPartName) as Path; - _overlayAreaPath = GetTemplateChild(OverlayAreaPathPartName) as Path; - _topThumb = GetTemplateChild(TopThumbPartName) as ImageCropperThumb; - _bottomThumb = GetTemplateChild(BottomThumbPartName) as ImageCropperThumb; - _leftThumb = GetTemplateChild(LeftThumbPartName) as ImageCropperThumb; - _rightThumb = GetTemplateChild(RightThumbPartName) as ImageCropperThumb; - _upperLeftThumb = GetTemplateChild(UpperLeftThumbPartName) as ImageCropperThumb; - _upperRightThumb = GetTemplateChild(UpperRightThumbPartName) as ImageCropperThumb; - _lowerLeftThumb = GetTemplateChild(LowerLeftThumbPartName) as ImageCropperThumb; - _lowerRigthThumb = GetTemplateChild(LowerRightThumbPartName) as ImageCropperThumb; + _layoutGrid = GetTemplateChild(ImageCropperNames.LayoutGridName) as Grid; + _imageCanvas = GetTemplateChild(ImageCropperNames.ImageCanvasPartName) as Canvas; + _sourceImage = GetTemplateChild(ImageCropperNames.SourceImagePartName) as Image; + _maskAreaPath = GetTemplateChild(ImageCropperNames.MaskAreaPathPartName) as Path; + _overlayAreaPath = GetTemplateChild(ImageCropperNames.OverlayAreaPathPartName) as Path; + _topThumb = GetTemplateChild(ImageCropperNames.TopThumbPartName) as ImageCropperThumb; + _bottomThumb = GetTemplateChild(ImageCropperNames.BottomThumbPartName) as ImageCropperThumb; + _leftThumb = GetTemplateChild(ImageCropperNames.LeftThumbPartName) as ImageCropperThumb; + _rightThumb = GetTemplateChild(ImageCropperNames.RightThumbPartName) as ImageCropperThumb; + _upperLeftThumb = GetTemplateChild(ImageCropperNames.UpperLeftThumbPartName) as ImageCropperThumb; + _upperRightThumb = GetTemplateChild(ImageCropperNames.UpperRightThumbPartName) as ImageCropperThumb; + _lowerLeftThumb = GetTemplateChild(ImageCropperNames.LowerLeftThumbPartName) as ImageCropperThumb; + _lowerRigthThumb = GetTemplateChild(ImageCropperNames.LowerRightThumbPartName) as ImageCropperThumb; HookUpEvents(); UpdateThumbsVisibility(); } diff --git a/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropper.xaml b/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropper.xaml index 877bbc82b..1a920d69d 100644 --- a/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropper.xaml +++ b/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropper.xaml @@ -1,6 +1,6 @@ + + - diff --git a/CollapseLauncher/Classes/Helper/Image/ImageLoaderHelper.cs b/CollapseLauncher/Classes/Helper/Image/ImageLoaderHelper.cs index aaaf12080..7845eb873 100644 --- a/CollapseLauncher/Classes/Helper/Image/ImageLoaderHelper.cs +++ b/CollapseLauncher/Classes/Helper/Image/ImageLoaderHelper.cs @@ -6,6 +6,7 @@ using CommunityToolkit.WinUI.Controls; using CommunityToolkit.WinUI.Media; using Hi3Helper; +using Hi3Helper.CommunityToolkit.WinUI.Controls; using Hi3Helper.Data; using Microsoft.UI.Text; using Microsoft.UI.Xaml; @@ -28,6 +29,11 @@ using static Hi3Helper.Shared.Region.LauncherConfig; using Orientation = Microsoft.UI.Xaml.Controls.Orientation; +using ImageCropper = Hi3Helper.CommunityToolkit.WinUI.Controls.ImageCropper; +using CropShape = Hi3Helper.CommunityToolkit.WinUI.Controls.CropShape; +using ThumbPlacement = Hi3Helper.CommunityToolkit.WinUI.Controls.ThumbPlacement; +using BitmapFileFormat = Hi3Helper.CommunityToolkit.WinUI.Controls.BitmapFileFormat; + namespace CollapseLauncher.Helper.Image { internal static class ImageLoaderHelper diff --git a/Hi3Helper.CommunityToolkit/ImageCropper/Hi3Helper.CommunityToolkit.WinUI.Controls.ImageCropper.csproj b/Hi3Helper.CommunityToolkit/ImageCropper/Hi3Helper.CommunityToolkit.WinUI.Controls.ImageCropper.csproj index 949ba5c9c..76ac9d5e2 100644 --- a/Hi3Helper.CommunityToolkit/ImageCropper/Hi3Helper.CommunityToolkit.WinUI.Controls.ImageCropper.csproj +++ b/Hi3Helper.CommunityToolkit/ImageCropper/Hi3Helper.CommunityToolkit.WinUI.Controls.ImageCropper.csproj @@ -28,6 +28,11 @@ true win-x64 + + + + + @@ -59,4 +64,15 @@ + + + $(DefaultXamlRuntime) + Designer + + + $(DefaultXamlRuntime) + Designer + + + \ No newline at end of file diff --git a/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropper.xaml b/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropper.xaml index 1a920d69d..a239a0ebb 100644 --- a/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropper.xaml +++ b/Hi3Helper.CommunityToolkit/ImageCropper/ImageCropper.xaml @@ -2,17 +2,22 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:controls="using:Hi3Helper.CommunityToolkit.WinUI.Controls" xmlns:ui="using:CommunityToolkit.WinUI"> + + + + + - - - - - - - - - - - - ms-appx:///Assets/Fonts/FontAwesomeBrand6.otf#Font Awesome 6 Brands diff --git a/CollapseLauncher/XAMLs/MainApp/MainPage.xaml b/CollapseLauncher/XAMLs/MainApp/MainPage.xaml index 25d23847f..4b8259d93 100644 --- a/CollapseLauncher/XAMLs/MainApp/MainPage.xaml +++ b/CollapseLauncher/XAMLs/MainApp/MainPage.xaml @@ -44,7 +44,7 @@ CornerRadius="0,8,8,0" Opacity="0" Shadow="{ThemeResource SharedShadow}" - Translation="0,0,16" /> + Translation="-48,0,0" /> + (); @@ -1361,8 +1421,12 @@ private void NavView_Loaded(object sender, RoutedEventArgs e) toggleIcon?.ApplyDropShadow(Colors.Gray, 20); } - private async void NavView_PanePointerEntered(object sender, PointerRoutedEventArgs e) + private void NavView_PanePointerEntered(object sender, PointerRoutedEventArgs e) { + IsCursorInNavBarHoverArea = true; + NavViewPaneBackground.Opacity = 1; + NavViewPaneBackground.Translation = new System.Numerics.Vector3(0, 0, 32); + /* if (!NavigationViewControl.IsPaneOpen) { var duration = TimeSpan.FromSeconds(0.25); @@ -1371,15 +1435,33 @@ private async void NavView_PanePointerEntered(object sender, PointerRoutedEventA .CreateScalarKeyFrameAnimation("Opacity", 1, current); await NavViewPaneBackground.StartAnimation(duration, animation); } + */ } - private async void NavView_PanePointerExited(object sender, PointerRoutedEventArgs e) + private bool IsCursorInNavBarHoverArea; + + private void NavView_PanePointerExited(object sender, PointerRoutedEventArgs e) { + PointerPoint pointerPoint = e.GetCurrentPoint(NavViewPaneBackgroundHoverArea); + IsCursorInNavBarHoverArea = pointerPoint.Position.X <= NavViewPaneBackgroundHoverArea.Width - 8 && pointerPoint.Position.X > 1; + if (!IsCursorInNavBarHoverArea && !NavigationViewControl.IsPaneOpen) + { + NavViewPaneBackground.Opacity = 0; + NavViewPaneBackground.Translation = new System.Numerics.Vector3(-48, 0, 0); + } + + if (IsCursorInNavBarHoverArea && !NavigationViewControl.IsPaneOpen) + { + NavViewPaneBackground.Opacity = 1; + NavViewPaneBackground.Translation = new System.Numerics.Vector3(0, 0, 32); + } + /* var duration = TimeSpan.FromSeconds(0.25); var current = (float)NavViewPaneBackground.Opacity; var animation = NavViewPaneBackground.GetElementCompositor()! .CreateScalarKeyFrameAnimation("Opacity", 0, current); await NavViewPaneBackground.StartAnimation(duration, animation); + */ } private void NavView_ItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args) @@ -1547,15 +1629,26 @@ private void NavigationPanelOpening_Event(NavigationView sender, object args) GridBG_Icon.Margin = curMargin; IsTitleIconForceShow = true; ToggleTitleIcon(false); + + NavViewPaneBackgroundHoverArea.Width = NavigationViewControl.OpenPaneLength; } - private void NavigationPanelClosing_Event(NavigationView sender, NavigationViewPaneClosingEventArgs args) + private async void NavigationPanelClosing_Event(NavigationView sender, NavigationViewPaneClosingEventArgs args) { Thickness curMargin = GridBG_Icon.Margin; curMargin.Left = 58; GridBG_Icon.Margin = curMargin; IsTitleIconForceShow = false; ToggleTitleIcon(true); + + NavViewPaneBackgroundHoverArea.Width = NavViewPaneBackground.Width; + + await Task.Delay(200); + if (!IsCursorInNavBarHoverArea) + { + NavViewPaneBackground.Opacity = 0; + NavViewPaneBackground.Translation = new System.Numerics.Vector3(-48, 0, 0); + } } #endregion diff --git a/CollapseLauncher/XAMLs/MainApp/MainWindow.xaml b/CollapseLauncher/XAMLs/MainApp/MainWindow.xaml index f5e05593d..5684b2678 100644 --- a/CollapseLauncher/XAMLs/MainApp/MainWindow.xaml +++ b/CollapseLauncher/XAMLs/MainApp/MainWindow.xaml @@ -163,54 +163,75 @@ Grid.Row="0"> - + + + + + + + + + + + diff --git a/CollapseLauncher/XAMLs/MainApp/MainWindow.xaml.cs b/CollapseLauncher/XAMLs/MainApp/MainWindow.xaml.cs index 7943a69d1..42a956721 100644 --- a/CollapseLauncher/XAMLs/MainApp/MainWindow.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/MainWindow.xaml.cs @@ -1,4 +1,5 @@ using CollapseLauncher.AnimatedVisuals.Lottie; +using CollapseLauncher.Extension; using CollapseLauncher.Helper; using CollapseLauncher.Helper.Animation; using CollapseLauncher.Helper.Image; @@ -9,6 +10,7 @@ using Hi3Helper; using Hi3Helper.Shared.Region; using Microsoft.UI.Composition; +using Microsoft.UI.Input; using Microsoft.UI.Windowing; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; @@ -275,5 +277,8 @@ private void IntroSequenceToggle_PointerExited(object sender, Microsoft.UI.Xaml. curCompositor.CreateScalarKeyFrameAnimation("Opacity", 0.25f) ); } + + private void SetWindowCaptionLoadedCursor(object sender, RoutedEventArgs e) + => (sender as UIElement)?.SetCursor(InputSystemCursor.Create(InputSystemCursorShape.Hand)); } } diff --git a/CollapseLauncher/XAMLs/Theme/NavigationView_ThemeResource.xaml b/CollapseLauncher/XAMLs/Theme/NavigationView_ThemeResource.xaml new file mode 100644 index 000000000..e89a9d8f7 --- /dev/null +++ b/CollapseLauncher/XAMLs/Theme/NavigationView_ThemeResource.xaml @@ -0,0 +1,1558 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 16,0 + 4,0 + 36 + 40 + 16 + 48 + 40 + 48 + 40 + 48 + 40 + 40 + 40 + 40 + 36 + 40 + 16 + 3 + 16 + 2 + 1 + 1 + 0 + 1 + 0 + 4,2 + 16,0 + 12,0 + -24,44,0,0 + 0,4 + -1,3 + 1,1,0,0 + 0,1,0,0 + 0,1,0,0 + 4,0 + 1 + 56,44,0,0 + 0 + 0 + 0 + 0 + 8,4,0,0 + 0 + 0 + 0,3,0,4 + 0,3,0,4 + 3,0,4,0 + 0 + 4,-1,8,-1 + 0,0,0,0 + 8,-1,12,-1 + 12,0 + 0,0,-14,0 + -16,0,0,0 + 0,0,0,0 + -12,0,0,0 + 12,0,20,0 + 16,0,20,0 + -4,0,-8,0 + 12,0,12,0 + 8,0,0,0 + 0 + 0 + 0,2 + 0,2 + 8 + + + + + + + + + + + + \ No newline at end of file diff --git a/Hi3Helper.TaskScheduler/packages.lock.json b/Hi3Helper.TaskScheduler/packages.lock.json index 37ea088e2..6840be16f 100644 --- a/Hi3Helper.TaskScheduler/packages.lock.json +++ b/Hi3Helper.TaskScheduler/packages.lock.json @@ -18,15 +18,6 @@ "resolved": "6.8.2", "contentHash": "sjGHrtGS1+kcrv99WXCvujOFBTQp4zCH3ZC9wo2LAtVaJkuLpHghQx3y4k1Q8ZKuDAbEw+HE6ZjPUJQK3ejepQ==" }, - "Microsoft.NETFramework.ReferenceAssemblies": { - "type": "Direct", - "requested": "[1.0.3, )", - "resolved": "1.0.3", - "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", - "dependencies": { - "Microsoft.NETFramework.ReferenceAssemblies.net462": "1.0.3" - } - }, "System.Net.Http": { "type": "Direct", "requested": "[4.3.4, )", @@ -69,11 +60,6 @@ "resolved": "1.1.0", "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" }, - "Microsoft.NETFramework.ReferenceAssemblies.net462": { - "type": "Transitive", - "resolved": "1.0.3", - "contentHash": "IzAV30z22ESCeQfxP29oVf4qEo8fBGXLXSU6oacv/9Iqe6PzgHDKCaWfwMBak7bSJQM0F5boXWoZS+kChztRIQ==" - }, "Microsoft.Win32.Primitives": { "type": "Transitive", "resolved": "4.3.0", From e6613f86da88a65c9c80aca109e0cb22fc4ae8ef Mon Sep 17 00:00:00 2001 From: Kemal Setya Adhi Date: Thu, 26 Sep 2024 15:46:49 +0700 Subject: [PATCH 064/110] Use Task for ref-type on FallbackCDNUtil --- .../RegionManagement/FallbackCDNUtil.cs | 27 ++++++++----------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/CollapseLauncher/Classes/RegionManagement/FallbackCDNUtil.cs b/CollapseLauncher/Classes/RegionManagement/FallbackCDNUtil.cs index 584b297c3..4600df5e8 100644 --- a/CollapseLauncher/Classes/RegionManagement/FallbackCDNUtil.cs +++ b/CollapseLauncher/Classes/RegionManagement/FallbackCDNUtil.cs @@ -17,6 +17,7 @@ using static Hi3Helper.Logger; using static Hi3Helper.Shared.Region.LauncherConfig; + #if !USEVELOPACK using System.Text; using Squirrel.Sources; @@ -280,7 +281,7 @@ public static async Task DownloadCDNFallbackContent(DownloadClient downloadClien } } - public static async ValueTask TryGetCDNFallbackStream(string relativeURL, CancellationToken token = default, bool isForceUncompressRequest = false) + public static async Task TryGetCDNFallbackStream(string relativeURL, CancellationToken token = default, bool isForceUncompressRequest = false) { // Get the preferred CDN first and try get the content CDNURLProperty preferredCDN = GetPreferredCDN(); @@ -319,7 +320,7 @@ private static void PerformStreamCheckAndSeek(Stream outputStream) outputStream.Position = 0; } - private static async ValueTask TryGetCDNContent(CDNURLProperty cdnProp, string relativeURL, CancellationToken token, bool isForceUncompressRequest) + private static async Task TryGetCDNContent(CDNURLProperty cdnProp, string relativeURL, CancellationToken token, bool isForceUncompressRequest) { try { @@ -502,7 +503,7 @@ void HttpInstanceDownloadProgressAdapter(int read, DownloadProgress downloadProg } } - private static async Task<(bool, string)> TryGetURLStatus(CDNURLProperty cdnProp, Http httpInstance, string relativeURL, CancellationToken token) + private static async ValueTask<(bool, string)> TryGetURLStatus(CDNURLProperty cdnProp, Http httpInstance, string relativeURL, CancellationToken token) { // Concat the URL Prefix and Relative URL string absoluteURL = ConverterTool.CombineURLFromString(cdnProp.URLPrefix, relativeURL); @@ -523,7 +524,7 @@ void HttpInstanceDownloadProgressAdapter(int read, DownloadProgress downloadProg return (true, absoluteURL); } - private static async Task<(bool, string)> TryGetURLStatus(CDNURLProperty cdnProp, DownloadClient downloadClient, string relativeURL, CancellationToken token) + private static async ValueTask<(bool, string)> TryGetURLStatus(CDNURLProperty cdnProp, DownloadClient downloadClient, string relativeURL, CancellationToken token) { // Concat the URL Prefix and Relative URL string absoluteURL = ConverterTool.CombineURLFromString(cdnProp.URLPrefix, relativeURL); @@ -589,19 +590,13 @@ public static CDNURLProperty GetPreferredCDN() public static async Task DownloadAsJSONType(string URL, JsonSerializerContext context, CancellationToken token) => (T)await _client.GetFromJsonAsync(URL, typeof(T), context, token) ?? default; - public static async ValueTask GetURLHttpResponse(string URL, CancellationToken token, bool isForceUncompressRequest = false) - { - return isForceUncompressRequest ? await _clientNoCompression.GetURLHttpResponse(URL, HttpMethod.Get, token) + public static async Task GetURLHttpResponse(string URL, CancellationToken token, bool isForceUncompressRequest = false) + => isForceUncompressRequest ? await _clientNoCompression.GetURLHttpResponse(URL, HttpMethod.Get, token) : await _client.GetURLHttpResponse(URL, HttpMethod.Get, token); - } #nullable enable - public static async ValueTask GetURLHttpResponse(this HttpClient client, string url, HttpMethod? httpMethod = null, CancellationToken token = default) - { - httpMethod ??= HttpMethod.Get; - using HttpRequestMessage requestMsg = new HttpRequestMessage(httpMethod, url); - return await client.SendAsync(requestMsg, HttpCompletionOption.ResponseHeadersRead, token); - } + public static async Task GetURLHttpResponse(this HttpClient client, string url, HttpMethod? httpMethod = null, CancellationToken token = default) + => await client.GetAsync(url, token); public static async ValueTask GetURLStatusCode(string URL, CancellationToken token) => await _client.GetURLStatusCode(URL, token); @@ -613,13 +608,13 @@ public static async ValueTask GetURLStatusCode(this HttpClient client } #nullable restore - public static async ValueTask GetHttpStreamFromResponse(string URL, CancellationToken token) + public static async Task GetHttpStreamFromResponse(string URL, CancellationToken token) { HttpResponseMessage responseMsg = await GetURLHttpResponse(URL, token); return await GetHttpStreamFromResponse(responseMsg, token); } - public static async ValueTask GetHttpStreamFromResponse(HttpResponseMessage responseMsg, CancellationToken token) + public static async Task GetHttpStreamFromResponse(HttpResponseMessage responseMsg, CancellationToken token) => await BridgedNetworkStream.CreateStream(responseMsg, token); public static async ValueTask GetCDNLatencies(CancellationTokenSource tokenSource, int pingCount = 1) From aba194651cf0017aef512462dc57295667174356 Mon Sep 17 00:00:00 2001 From: Kemal Setya Adhi Date: Thu, 26 Sep 2024 16:01:50 +0700 Subject: [PATCH 065/110] Fix cancellation state on "Move Game Location" --- CollapseLauncher/Classes/FileMigrationProcess/Statics.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CollapseLauncher/Classes/FileMigrationProcess/Statics.cs b/CollapseLauncher/Classes/FileMigrationProcess/Statics.cs index 717758e4d..707429ed5 100644 --- a/CollapseLauncher/Classes/FileMigrationProcess/Statics.cs +++ b/CollapseLauncher/Classes/FileMigrationProcess/Statics.cs @@ -21,6 +21,8 @@ internal static async Task CreateJob(UIElement parentUI, s // Check whether the input is a file or not. bool isFileTransfer = File.Exists(inputPath) && !inputPath.StartsWith('\\'); outputPath = await InitializeAndCheckOutputPath(parentUI, dialogTitle, inputPath, outputPath, isFileTransfer); + if (outputPath == null) return null; + if (Helper.FileUtility.IsRootPath(outputPath)) { await SimpleDialogs.SpawnDialog(Lang._HomePage.InstallFolderRootTitle, @@ -30,7 +32,6 @@ await SimpleDialogs.SpawnDialog(Lang._HomePage.InstallFolderRootTitle, null, null, ContentDialogButton.Close, ContentDialogTheme.Error); return null; } - if (outputPath == null) return null; if (showWarningMessage) if (await ShowNotCancellableProcedureMessage(parentUI) == ContentDialogResult.None) From 635eba49ee5c03cd8a4caf06c5e3120338ab9e65 Mon Sep 17 00:00:00 2001 From: Kemal Setya Adhi Date: Thu, 26 Sep 2024 16:19:21 +0700 Subject: [PATCH 066/110] Remove Hi3Helper.Core dependencies on InnoSetupHelper --- InnoSetupHelper/InnoSetupHelper.csproj | 4 --- InnoSetupHelper/InnoSetupLogUpdate.cs | 45 +++++++++++++++++--------- InnoSetupHelper/packages.lock.json | 20 ------------ 3 files changed, 30 insertions(+), 39 deletions(-) diff --git a/InnoSetupHelper/InnoSetupHelper.csproj b/InnoSetupHelper/InnoSetupHelper.csproj index 17253aa65..64e2258da 100644 --- a/InnoSetupHelper/InnoSetupHelper.csproj +++ b/InnoSetupHelper/InnoSetupHelper.csproj @@ -22,8 +22,4 @@ - - - - diff --git a/InnoSetupHelper/InnoSetupLogUpdate.cs b/InnoSetupHelper/InnoSetupLogUpdate.cs index b42409722..bd6ddd9ff 100644 --- a/InnoSetupHelper/InnoSetupLogUpdate.cs +++ b/InnoSetupHelper/InnoSetupLogUpdate.cs @@ -1,17 +1,29 @@ -using Hi3Helper; -using Hi3Helper.EncTool.Parser.InnoUninstallerLog; +using Hi3Helper.EncTool.Parser.InnoUninstallerLog; using LibISULR; using LibISULR.Flags; using LibISULR.Records; using System; using System.IO; using System.Linq; -using static Hi3Helper.Logger; namespace InnoSetupHelper { + public enum InnoSetupLogType + { + Default, Warning, Error + } + + public struct InnoSetupLogStruct + { + public InnoSetupLogType LogType; + public bool IsWriteToLog; + public string Message; + } + public class InnoSetupLogUpdate { + public static event EventHandler? LoggerEvent; + private static readonly string[] excludeDeleteFile = new string[] { // Generic Files @@ -38,10 +50,10 @@ public class InnoSetupLogUpdate public static void UpdateInnoSetupLog(string path) { - string directoryPath = Path.GetDirectoryName(path)!; - string searchValue = GetPathWithoutDriveLetter(directoryPath); + string directoryPath = Path.GetDirectoryName(path)!; + string searchValue = GetPathWithoutDriveLetter(directoryPath); - LogWriteLine($"[InnoSetupLogUpdate::UpdateInnoSetupLog()] Updating Inno Setup file located at: {path}", LogType.Default, true); + LogWriteLine($"[InnoSetupLogUpdate::UpdateInnoSetupLog()] Updating Inno Setup file located at: {path}", InnoSetupLogType.Default, true); try { using InnoUninstallLog innoLog = InnoUninstallLog.Load(path, true); @@ -56,11 +68,11 @@ public static void UpdateInnoSetupLog(string path) // Save the Inno Setup log innoLog.Save(path); - LogWriteLine($"[InnoSetupLogUpdate::UpdateInnoSetupLog()] Inno Setup file: {path} has been successfully updated!", LogType.Default, true); + LogWriteLine($"[InnoSetupLogUpdate::UpdateInnoSetupLog()] Inno Setup file: {path} has been successfully updated!", InnoSetupLogType.Default, true); } catch (Exception ex) { - LogWriteLine($"[InnoSetupLogUpdate::UpdateInnoSetupLog()] Inno Setup file: {path} was failed due to an error: {ex}", LogType.Warning, true); + LogWriteLine($"[InnoSetupLogUpdate::UpdateInnoSetupLog()] Inno Setup file: {path} was failed due to an error: {ex}", InnoSetupLogType.Warning, true); } } @@ -75,7 +87,7 @@ private static void RegisterDirOrFilesRecord(InnoUninstallLog innoLog, string pa DirectoryInfo currentDirectory = new DirectoryInfo(pathToRegister); if (innoLog.Records == null) { - LogWriteLine("[InnoSetupLogUpdate::RegisterDirOrFilesRecord()] Records is uninitialized!", LogType.Error, true); + LogWriteLine("[InnoSetupLogUpdate::RegisterDirOrFilesRecord()] Records is uninitialized!", InnoSetupLogType.Error, true); } else { @@ -84,14 +96,14 @@ private static void RegisterDirOrFilesRecord(InnoUninstallLog innoLog, string pa if (excludeDeleteFile.Any(x => x.IndexOf(fileInfo.FullName, StringComparison.OrdinalIgnoreCase) > -1)) continue; fileInfo.IsReadOnly = false; LogWriteLine($"[InnoSetupLogUpdate::RegisterDirOrFilesRecord()] " + - $"Registering Inno Setup record: (DeleteFileRecord){fileInfo.FullName}", LogType.Default, true); + $"Registering Inno Setup record: (DeleteFileRecord){fileInfo.FullName}", InnoSetupLogType.Default, true); innoLog.Records.Add(DeleteFileRecord.Create(fileInfo.FullName)); } LogWriteLine($"[InnoSetupLogUpdate::RegisterDirOrFilesRecord()] " + - $"Registering Inno Setup record: (DeleteDirOrFilesRecord){pathToRegister}", LogType.Default, true); + $"Registering Inno Setup record: (DeleteDirOrFilesRecord){pathToRegister}", InnoSetupLogType.Default, true); innoLog.Records.Add(DeleteDirOrFilesRecord.Create(pathToRegister)); } - + foreach (DirectoryInfo subdirectories in currentDirectory.EnumerateDirectories("*", SearchOption.TopDirectoryOnly)) { RegisterDirOrFilesRecord(innoLog, subdirectories.FullName); @@ -106,7 +118,7 @@ private static void CleanUpInnoDirOrFilesRecord(InnoUninstallLog innoLog, string if (innoLog.Records == null) { LogWriteLine("[InnoSetupLogUpdate::RegisterDirOrFilesRecord()] Records is uninitialized!", - LogType.Error, true); + InnoSetupLogType.Error, true); return; } BaseRecord baseRecord = innoLog.Records[index]; @@ -116,12 +128,12 @@ private static void CleanUpInnoDirOrFilesRecord(InnoUninstallLog innoLog, string case RecordType.DeleteDirOrFiles: isRecordValid = IsInnoRecordContainsPath(baseRecord, searchValue) && IsDeleteDirOrFilesFlagsValid((DeleteDirOrFilesRecord)baseRecord); - LogWriteLine($"[InnoSetupLogUpdate::CleanUpInnoDirOrFilesRecord()] Removing outdated Inno Setup record: (DeleteDirOrFilesRecord){((DeleteDirOrFilesRecord)baseRecord).Paths[0]}", LogType.Default, true); + LogWriteLine($"[InnoSetupLogUpdate::CleanUpInnoDirOrFilesRecord()] Removing outdated Inno Setup record: (DeleteDirOrFilesRecord){((DeleteDirOrFilesRecord)baseRecord).Paths[0]}", InnoSetupLogType.Default, true); break; case RecordType.DeleteFile: isRecordValid = IsInnoRecordContainsPath(baseRecord, searchValue) && IsDeleteFileFlagsValid((DeleteFileRecord)baseRecord); - LogWriteLine($"[InnoSetupLogUpdate::CleanUpInnoDirOrFilesRecord()] Removing outdated Inno Setup record: (DeleteFileRecord){((DeleteFileRecord)baseRecord).Paths[0]}", LogType.Default, true); + LogWriteLine($"[InnoSetupLogUpdate::CleanUpInnoDirOrFilesRecord()] Removing outdated Inno Setup record: (DeleteFileRecord){((DeleteFileRecord)baseRecord).Paths[0]}", InnoSetupLogType.Default, true); break; } if (isRecordValid) @@ -139,5 +151,8 @@ private static bool IsInnoRecordContainsPath(BaseRecord record, string? where TFlags : Enum => ((BasePathListRecord)record) .Paths[0]! .IndexOf(searchValue!, StringComparison.OrdinalIgnoreCase) > -1; + + private static void LogWriteLine(string message, InnoSetupLogType logType, bool isWriteLog) + => LoggerEvent?.Invoke(null, new InnoSetupLogStruct { IsWriteToLog = isWriteLog, LogType = logType, Message = message }); } } diff --git a/InnoSetupHelper/packages.lock.json b/InnoSetupHelper/packages.lock.json index 95e1cf8bf..0183e6b19 100644 --- a/InnoSetupHelper/packages.lock.json +++ b/InnoSetupHelper/packages.lock.json @@ -14,27 +14,7 @@ "resolved": "9.0.0-rc.1.24431.7", "contentHash": "giMiDNlne8WcLG7rsEfrETaukDUTBPn8a97QRo9LYR71O0Rb4SJ6TbfGdQ9oiBYldVPSAbSQANb5ThLZOo408Q==" }, - "Google.Protobuf": { - "type": "Transitive", - "resolved": "3.28.2", - "contentHash": "Z86ZKAB+v1B/m0LTM+EVamvZlYw/g3VND3/Gs4M/+aDIxa2JE9YPKjDxTpf0gv2sh26hrve3eI03brxBmzn92g==" - }, "hi3helper.core": { - "type": "Project", - "dependencies": { - "Hi3Helper.EncTool": "[1.0.0, )", - "Hi3Helper.Http": "[2.0.0, )" - } - }, - "hi3helper.enctool": { - "type": "Project", - "dependencies": { - "Google.Protobuf": "[3.28.2, )", - "Hi3Helper.Http": "[2.0.0, )", - "System.IO.Hashing": "[9.0.0-rc.1.24431.7, )" - } - }, - "hi3helper.http": { "type": "Project" } } From 710762c40849851c6371d60c48d68bc552071b80 Mon Sep 17 00:00:00 2001 From: Kemal Setya Adhi Date: Thu, 26 Sep 2024 16:20:16 +0700 Subject: [PATCH 067/110] Attach InnoSetupHelper log event --- CollapseLauncher/Program.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CollapseLauncher/Program.cs b/CollapseLauncher/Program.cs index 59e963846..c1c77d9ff 100644 --- a/CollapseLauncher/Program.cs +++ b/CollapseLauncher/Program.cs @@ -25,6 +25,7 @@ using static Hi3Helper.Locale; using static Hi3Helper.Logger; using static Hi3Helper.Shared.Region.LauncherConfig; +using InnoSetupHelper; namespace CollapseLauncher; @@ -97,6 +98,9 @@ public static void Main(params string[] args) // Initialize TaskSchedulerHelper TaskSchedulerHelper.InvokeGetStatusCommand(); + // Initiate InnoSetupHelper's log event + InnoSetupLogUpdate.LoggerEvent += InnoSetupLogUpdate_LoggerEvent; + HttpLogInvoker.DownloadLog += HttpClientLogWatcher!; switch (m_appMode) @@ -151,6 +155,17 @@ public static void Main(params string[] args) } } + private static void InnoSetupLogUpdate_LoggerEvent(object sender, InnoSetupLogStruct e) => + LogWriteLine( + e.Message, + e.LogType switch + { + InnoSetupLogType.Warning => LogType.Warning, + InnoSetupLogType.Error => LogType.Error, + _ => LogType.Default + }, + e.IsWriteToLog); + public static void SpawnFatalErrorConsole(Exception ex) { CurrentAppInstance?.Exit(); From d4f0ca94f7deecad84b6aeedb4c49444cdc67b9e Mon Sep 17 00:00:00 2001 From: Kemal Setya Adhi Date: Thu, 26 Sep 2024 16:20:55 +0700 Subject: [PATCH 068/110] Fix install progress shadow --- .../XAMLs/MainApp/Pages/HomePage.xaml | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml index 6122bea48..413624061 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml @@ -1080,8 +1080,9 @@ Height="32" HorizontalAlignment="Left" VerticalAlignment="Bottom" - CornerRadius="16" - Shadow="{ThemeResource SharedShadow}"> + CornerRadius="{x:Bind extension:UIElementExtensions.AttachRoundedKindCornerRadius(SophonProgressInformationButton)}" + Shadow="{ThemeResource SharedShadow}" + Translation="0,0,16"> + CornerRadius="{x:Bind extension:UIElementExtensions.AttachRoundedKindCornerRadius(SophonProgressSettingsButton)}" + Shadow="{ThemeResource SharedShadow}" + Translation="0,0,16"> + Value="{Binding Value, ElementName=progressRingPerFile}"/> + + CornerRadius="{x:Bind extension:UIElementExtensions.AttachRoundedKindCornerRadius(ProgressSettingsButton)}" + Shadow="{ThemeResource SharedShadow}" + Translation="0,0,16"> Date: Thu, 26 Sep 2024 22:36:53 +0700 Subject: [PATCH 069/110] UI Improvements + Fix locale binding on changing language + Fix "No news" text layout + Fix arbitrary changes on shadow for certain element while updating bindings (this one caused by out-of-bound calls from the UIElement, making AttachDropShadow to be called anonymously) + Fix crash when current "Event Icon" sprite not successfully being downloaded + Set all control type element's cursor to InputSystemCursorShape.Hand when hovering them (For now, it's only applicable for MainPage, HomePage and SettingsPage) --- .../Classes/Extension/UIElementExtensions.cs | 174 ++++++++++++++++++ .../WindowSizeProp/WindowSizeProp.cs | 8 +- CollapseLauncher/XAMLs/MainApp/MainPage.xaml | 1 - .../XAMLs/MainApp/MainPage.xaml.cs | 101 ++++------ .../MainApp/Pages/HomePage.BindingHelper.cs | 23 +-- .../XAMLs/MainApp/Pages/HomePage.xaml | 68 ++++--- .../XAMLs/MainApp/Pages/HomePage.xaml.cs | 79 ++++---- .../XAMLs/MainApp/Pages/SettingsPage.xaml | 3 +- .../XAMLs/MainApp/Pages/SettingsPage.xaml.cs | 6 +- CollapseLauncher/packages.lock.json | 4 +- Hi3Helper.Core/Hi3Helper.Core.csproj | 6 +- .../Lang/Locale/LangAppNotification.cs | 6 +- .../Lang/Locale/LangBackgroundNotification.cs | 8 +- Hi3Helper.Core/Lang/Locale/LangCachesPage.cs | 8 +- .../Lang/Locale/LangCutscenesPage.cs | 8 +- Hi3Helper.Core/Lang/Locale/LangDialogs.cs | 6 +- .../Lang/Locale/LangDisconnectedPage.cs | 8 +- .../Lang/Locale/LangFileCleanupPage.cs | 6 +- .../Lang/Locale/LangFileMigrationProcess.cs | 6 +- .../Lang/Locale/LangGameRepairPage.cs | 8 +- .../Lang/Locale/LangGameSettingsPage.cs | 8 +- .../Locale/LangGenshinGameSettingsPage.cs | 8 +- Hi3Helper.Core/Lang/Locale/LangHomePage.cs | 8 +- .../Lang/Locale/LangInstallConvert.cs | 8 +- .../Lang/Locale/LangInstallManagement.cs | 8 +- .../Lang/Locale/LangInstallMigrateSteam.cs | 8 +- .../Lang/Locale/LangKeyboardShortcuts.cs | 8 +- Hi3Helper.Core/Lang/Locale/LangMainPage.cs | 8 +- Hi3Helper.Core/Lang/Locale/LangMisc.cs | 6 +- .../Lang/Locale/LangNotificationToast.cs | 6 +- .../Lang/Locale/LangOOBEAgreementMenu.cs | 8 +- .../Lang/Locale/LangOOBEStartUpMenu.cs | 5 +- .../Lang/Locale/LangSettingsPage.cs | 8 +- .../Locale/LangStarRailGameSettingsPage.cs | 8 +- Hi3Helper.Core/Lang/Locale/LangStartupPage.cs | 8 +- .../Lang/Locale/LangUnhandledExceptionPage.cs | 8 +- Hi3Helper.Core/Lang/Locale/LangUpdatePage.cs | 8 +- .../Locale/LangZenlessGameSettingsPage.cs | 5 +- Hi3Helper.Core/Lang/Localization.cs | 4 + Hi3Helper.Core/packages.lock.json | 8 +- 40 files changed, 474 insertions(+), 209 deletions(-) diff --git a/CollapseLauncher/Classes/Extension/UIElementExtensions.cs b/CollapseLauncher/Classes/Extension/UIElementExtensions.cs index 7cbe31e02..ad4cebb6a 100644 --- a/CollapseLauncher/Classes/Extension/UIElementExtensions.cs +++ b/CollapseLauncher/Classes/Extension/UIElementExtensions.cs @@ -7,6 +7,7 @@ using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Data; using Microsoft.UI.Xaml.Documents; using Microsoft.UI.Xaml.Media; using System; @@ -20,6 +21,12 @@ namespace CollapseLauncher.Extension { internal enum CornerRadiusKind { Normal, Rounded } + internal class NavigationViewItemLocaleTextProperty + { + public string LocaleSetName { get; set; } + public string LocalePropertyName { get; set; } + } + internal static class UIElementExtensions { /// @@ -30,6 +37,173 @@ internal static class UIElementExtensions [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "set_ProtectedCursor")] internal static extern void SetCursor(this UIElement element, InputCursor inputCursor); +#nullable enable + /// + /// Set the initial navigation view item's locale binding before getting set with + /// + /// The instance to set the initial text binding to. + /// The instance to set the initial text binding to. + /// The instance name of a members. + /// Name of the locale property + /// A reference of the + internal static ref T BindNavigationViewItemText(this T element, string localeSetName, string localePropertyName) + where T : NavigationViewItemBase + { + NavigationViewItemLocaleTextProperty property = new NavigationViewItemLocaleTextProperty + { + LocaleSetName = localeSetName, + LocalePropertyName = localePropertyName + }; + + if (element is NavigationViewItemHeader elementAsHeader) + { + elementAsHeader.Tag = property; + } + else + { + TextBlock textBlock = new TextBlock().WithTag(property); + element.Content = textBlock; + } + return ref Unsafe.AsRef(ref element); + } + + internal static void SetAllControlsCursorRecursive(this UIElement element, InputSystemCursor toCursor) + { + if (element == null) + { + return; + } + + if (element is Panel panelKind) + { + foreach (UIElement panelElements in panelKind.Children) + { + SetAllControlsCursorRecursive(panelElements, toCursor); + } + } + + if (element is RadioButtons radioButtonsKind) + { + foreach (UIElement radioButtonContent in radioButtonsKind.Items.OfType()) + { + radioButtonContent.SetCursor(toCursor); + } + } + + if (element is Border borderKind) + { + SetAllControlsCursorRecursive(borderKind.Child, toCursor); + } + + if (element is ComboBox comboBoxKind) + { + comboBoxKind.SetCursor(toCursor); + } + + if (element is UserControl userControlKind) + { + SetAllControlsCursorRecursive(userControlKind.Content, toCursor); + } + + if (element is ContentControl contentControlKind + && contentControlKind.Content is UIElement contentControlKindInner) + { + SetAllControlsCursorRecursive(contentControlKindInner, toCursor); + } + + if (element is NavigationView navigationViewKind) + { + foreach (UIElement navigationViewElements in navigationViewKind.FindDescendants()) + { + if (navigationViewElements is NavigationViewItem) + { + navigationViewElements.SetCursor(toCursor); + continue; + } + SetAllControlsCursorRecursive(navigationViewElements, toCursor); + } + } + + if (element is ButtonBase buttonBaseKind) + { + buttonBaseKind.SetCursor(toCursor); + if (buttonBaseKind is Button buttonKind && buttonKind.Flyout != null && buttonKind.Flyout is Flyout buttonKindFlyout) + { + SetAllControlsCursorRecursive(buttonKindFlyout.Content, toCursor); + } + } + + if (element is ToggleSwitch) + { + element.SetCursor(toCursor); + } + + if (element.ContextFlyout != null && element.ContextFlyout is Flyout elementFlyoutKind) + { + SetAllControlsCursorRecursive(elementFlyoutKind.Content, toCursor); + } + } + + internal static void ApplyNavigationViewItemLocaleTextBindings(this NavigationView navViewControl) + { + foreach (NavigationViewItemBase navItem in navViewControl + .FindDescendants() + .OfType()) + { + string? localeValue = null; + if (navItem.Content is TextBlock navItemTextBlock + && navItemTextBlock.Tag is NavigationViewItemLocaleTextProperty localeProperty) + { + navItemTextBlock.BindProperty( + TextBlock.TextProperty, + Locale.Lang, + $"{localeProperty.LocaleSetName}.{localeProperty.LocalePropertyName}"); + localeValue = navItemTextBlock.GetValue(TextBlock.TextProperty) as string; + } + else if (navItem is NavigationViewItemHeader navItemAsHeader + && navItemAsHeader.Tag is NavigationViewItemLocaleTextProperty localePropertyOnHeader) + { + navItemAsHeader.BindProperty( + ContentControl.ContentProperty, + Locale.Lang, + $"{localePropertyOnHeader.LocaleSetName}.{localePropertyOnHeader.LocalePropertyName}"); + localeValue = navItemAsHeader.GetValue(ContentControl.ContentProperty) as string; + } + + if (!string.IsNullOrEmpty(localeValue)) + { + ToolTipService.SetToolTip(navItem, localeValue); + } + } + + navViewControl.UpdateLayout(); + } + + internal static ref T BindProperty(this T element, DependencyProperty dependencyProperty, object objectToBind, string propertyName, IValueConverter? converter = null, BindingMode bindingMode = BindingMode.OneWay) + where T : FrameworkElement + { + // Create a new binding instance + Binding binding = new Binding + { + Source = objectToBind, + Mode = bindingMode, + Path = new PropertyPath(propertyName), + UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged + }; + + // If the converter is assigned, then add the converter + if (converter != null) + { + binding.Converter = converter; + } + + // Set binding to the element + element.SetBinding(dependencyProperty, binding); + + return ref Unsafe.AsRef(ref element); + } +#nullable restore + internal static TButtonBase CreateButtonWithIcon(string text = null, string iconGlyph = null, string iconFontFamily = "FontAwesome", string buttonStyle = "DefaultButtonStyle", double iconSize = 16d, double? textSize = null, CornerRadius? cornerRadius = null, FontWeight? textWeight = null) where TButtonBase : ButtonBase, new() diff --git a/CollapseLauncher/Classes/Properties/WindowSizeProp/WindowSizeProp.cs b/CollapseLauncher/Classes/Properties/WindowSizeProp/WindowSizeProp.cs index 38e542342..2aac761a1 100644 --- a/CollapseLauncher/Classes/Properties/WindowSizeProp/WindowSizeProp.cs +++ b/CollapseLauncher/Classes/Properties/WindowSizeProp/WindowSizeProp.cs @@ -31,11 +31,11 @@ Dictionary WindowSizeProfiles EventPostCarouselBounds = new Size(340, 158), PostPanelBounds = new Size(340, 84), PostPanelBottomMargin = new Thickness(0, 0, 0, 20), - PostPanelPaimonHeight = 138, - PostPanelPaimonMargin = new Thickness(0, -48, -64, 0), + PostPanelPaimonHeight = 110, + PostPanelPaimonMargin = new Thickness(0, -48, -56, 0), PostPanelPaimonInnerMargin = new Thickness(0, 0, 0, 0), - PostPanelPaimonTextMargin = new Thickness(0, 0, 8, 28), - PostPanelPaimonTextSize = 14, + PostPanelPaimonTextMargin = new Thickness(0, 0, 0, 18), + PostPanelPaimonTextSize = 11, BannerIconWidth = 136, BannerIconWidthHYP = 111, BannerIconMargin = new Thickness(0, 0, 94, 84), diff --git a/CollapseLauncher/XAMLs/MainApp/MainPage.xaml b/CollapseLauncher/XAMLs/MainApp/MainPage.xaml index 4b8259d93..a8344d263 100644 --- a/CollapseLauncher/XAMLs/MainApp/MainPage.xaml +++ b/CollapseLauncher/XAMLs/MainApp/MainPage.xaml @@ -126,7 +126,6 @@ CornerRadius="15,0,0,15" DropDownClosed="GameComboBox_OnDropDownClosed" DropDownOpened="GameComboBox_OnDropDownOpened" - ItemsSource="{x:Bind p:LauncherMetadataHelper.LauncherGameNameCollection, Mode=OneWay}" PlaceholderText="{x:Bind helper:Locale.Lang._GameClientTitles['Honkai Impact 3rd']}" SelectionChanged="SetGameCategoryChange" Style="{ThemeResource AcrylicComboBoxStyle}" /> diff --git a/CollapseLauncher/XAMLs/MainApp/MainPage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/MainPage.xaml.cs index 0d8593e58..1c3388fa3 100644 --- a/CollapseLauncher/XAMLs/MainApp/MainPage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/MainPage.xaml.cs @@ -208,30 +208,29 @@ private async Task InitBackgroundHandler() #region Invokers private void UpdateBindingsEvent(object sender, EventArgs e) { - NavigationViewControl.MenuItems.Clear(); - NavigationViewControl.FooterMenuItems.Clear(); - Bindings.Update(); - UpdateLayout(); - // Find the last selected category/title and region string lastName = LauncherMetadataHelper.CurrentMetadataConfigGameName; string lastRegion = LauncherMetadataHelper.CurrentMetadataConfigGameRegion; - #nullable enable +#nullable enable + NavigationViewControl?.ApplyNavigationViewItemLocaleTextBindings(); + List? gameNameCollection = LauncherMetadataHelper.GetGameNameCollection()!; List? gameRegionCollection = LauncherMetadataHelper.GetGameRegionCollection(lastName!)!; int indexOfName = gameNameCollection.IndexOf(lastName!); int indexOfRegion = gameRegionCollection.IndexOf(lastRegion!); - #nullable restore +#nullable restore // Rebuild Game Titles and Regions ComboBox items ComboBoxGameCategory.ItemsSource = BuildGameTitleListUI(); ComboBoxGameCategory.SelectedIndex = indexOfName; ComboBoxGameRegion.SelectedIndex = indexOfRegion; - InitializeNavigationItems(false); ChangeTitleDragArea.Change(DragAreaTemplate.Default); + + UpdateLayout(); + Bindings.Update(); } private void ShowLoadingPageInvoker_PageEvent(object sender, ShowLoadingPageProperty e) @@ -1254,53 +1253,61 @@ private void InitializeNavigationItems(bool ResetSelection = true) if (CurrentGameVersionCheck.GamePreset.IsCacheUpdateEnabled ?? false) { NavigationViewControl.MenuItems.Add(new NavigationViewItem() - { Content = Lang._CachesPage.PageTitle, Icon = IconCaches, Tag = "caches" }); + { Icon = IconCaches, Tag = "caches" } + .BindNavigationViewItemText("_CachesPage", "PageTitle")); } return; } NavigationViewControl.MenuItems.Add(new NavigationViewItem() - { Content = Lang._HomePage.PageTitle, Icon = IconLauncher, Tag = "launcher" }); + { Icon = IconLauncher, Tag = "launcher" } + .BindNavigationViewItemText("_HomePage", "PageTitle")); - NavigationViewControl.MenuItems.Add(new NavigationViewItemHeader() { Content = Lang._MainPage.NavigationUtilities }); + NavigationViewControl.MenuItems.Add(new NavigationViewItemHeader() + .BindNavigationViewItemText("_MainPage", "NavigationUtilities")); if (CurrentGameVersionCheck.GamePreset.IsRepairEnabled ?? false) { NavigationViewControl.MenuItems.Add(new NavigationViewItem() - { Content = Lang._GameRepairPage.PageTitle, Icon = IconRepair, Tag = "repair" }); + { Icon = IconRepair, Tag = "repair" } + .BindNavigationViewItemText("_GameRepairPage", "PageTitle")); } if (CurrentGameVersionCheck.GamePreset.IsCacheUpdateEnabled ?? false) { NavigationViewControl.MenuItems.Add(new NavigationViewItem() - { Content = Lang._CachesPage.PageTitle, Icon = IconCaches, Tag = "caches" }); + { Icon = IconCaches, Tag = "caches" } + .BindNavigationViewItemText("_CachesPage", "PageTitle")); } switch (CurrentGameVersionCheck.GameType) { case GameNameType.Honkai: NavigationViewControl.FooterMenuItems.Add(new NavigationViewItem() - { Content = Lang._GameSettingsPage.PageTitle, Icon = IconGameSettings, Tag = "honkaigamesettings" }); + { Icon = IconGameSettings, Tag = "honkaigamesettings" } + .BindNavigationViewItemText("_GameSettingsPage", "PageTitle")); break; case GameNameType.StarRail: NavigationViewControl.FooterMenuItems.Add(new NavigationViewItem() - { Content = Lang._StarRailGameSettingsPage.PageTitle, Icon = IconGameSettings, Tag = "starrailgamesettings" }); + { Icon = IconGameSettings, Tag = "starrailgamesettings" } + .BindNavigationViewItemText("_StarRailGameSettingsPage", "PageTitle")); break; case GameNameType.Genshin: NavigationViewControl.FooterMenuItems.Add(new NavigationViewItem() - { Content = Lang._GenshinGameSettingsPage.PageTitle, Icon = IconGameSettings, Tag = "genshingamesettings" }); + { Icon = IconGameSettings, Tag = "genshingamesettings" } + .BindNavigationViewItemText("_GenshinGameSettingsPage", "PageTitle")); break; case GameNameType.Zenless: NavigationViewControl.FooterMenuItems.Add(new NavigationViewItem() - {Content = Lang._GameSettingsPage.PageTitle, Icon = IconGameSettings, Tag = "zenlessgamesettings"}); + { Icon = IconGameSettings, Tag = "zenlessgamesettings" } + .BindNavigationViewItemText("_GameSettingsPage", "PageTitle")); break; } if (NavigationViewControl.SettingsItem is not null && NavigationViewControl.SettingsItem is NavigationViewItem SettingsItem) { - SettingsItem.Content = Lang._SettingsPage.PageTitle; SettingsItem.Icon = IconAppSettings; - ToolTipService.SetToolTip(SettingsItem, Lang._SettingsPage.PageTitle); + _ = SettingsItem.BindNavigationViewItemText("_SettingsPage", "PageTitle"); } foreach (var deps in NavigationViewControl.FindDescendants()) @@ -1317,56 +1324,10 @@ private void InitializeNavigationItems(bool ResetSelection = true) NavigationViewControl.SelectedItem = (NavigationViewItem)NavigationViewControl.MenuItems[0]; } - InputSystemCursor handCursor = InputSystemCursor.Create(InputSystemCursorShape.Hand); - SetAllButtonsCursorHandRecursive(MainPageGrid, handCursor); - } - - private void SetAllButtonsCursorHandRecursive(UIElement element, InputSystemCursor toCursor) - { - if (element == null) - { - return; - } + NavigationViewControl.ApplyNavigationViewItemLocaleTextBindings(); - if (element is Panel panelKind) - { - foreach (var panelElements in panelKind.Children) - { - SetAllButtonsCursorHandRecursive(panelElements, toCursor); - } - } - - if (element is Border borderKind) - { - SetAllButtonsCursorHandRecursive(borderKind.Child, toCursor); - } - - if (element is NavigationView navigationViewKind) - { - foreach (UIElement navigationViewElements in navigationViewKind.FindDescendants()) - { - if (navigationViewElements is NavigationViewItem) - { - navigationViewElements.SetCursor(toCursor); - continue; - } - SetAllButtonsCursorHandRecursive(navigationViewElements, toCursor); - } - } - - if (element is ButtonBase buttonBaseKind) - { - buttonBaseKind.SetCursor(toCursor); - if (buttonBaseKind is Button buttonKind && buttonKind.Flyout != null && buttonKind.Flyout is Flyout buttonKindFlyout) - { - SetAllButtonsCursorHandRecursive(buttonKindFlyout.Content, toCursor); - } - } - - if (element.ContextFlyout != null && element.ContextFlyout is Flyout elementFlyoutKind) - { - SetAllButtonsCursorHandRecursive(elementFlyoutKind.Content, toCursor); - } + InputSystemCursor handCursor = InputSystemCursor.Create(InputSystemCursorShape.Hand); + MainPageGrid.SetAllControlsCursorRecursive(handCursor); } public static void AttachShadowNavigationPanelItem(FrameworkElement element) @@ -1470,8 +1431,8 @@ private void NavView_ItemInvoked(NavigationView sender, NavigationViewItemInvoke if (args.IsSettingsInvoked && PreviousTag != "settings") Navigate(typeof(SettingsPage), "settings"); #nullable enable - NavigationViewItem? item = sender.MenuItems.OfType().FirstOrDefault(x => (string)x.Content == (string)args.InvokedItem); - item ??= sender.FooterMenuItems.OfType().FirstOrDefault(x => (string)x.Content == (string)args.InvokedItem); + NavigationViewItem? item = sender.MenuItems.OfType().FirstOrDefault(x => (x.Content as TextBlock)?.Text == (args.InvokedItem as TextBlock)?.Text); + item ??= sender.FooterMenuItems.OfType().FirstOrDefault(x => (x.Content as TextBlock)?.Text == (args.InvokedItem as TextBlock)?.Text); if (item == null) return; #nullable restore diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.BindingHelper.cs b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.BindingHelper.cs index 4bd06b768..f06e99f04 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.BindingHelper.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.BindingHelper.cs @@ -1,37 +1,16 @@ #nullable enable + using CollapseLauncher.Extension; using CollapseLauncher.Helper.LauncherApiLoader.Sophon; using CommunityToolkit.WinUI; using ImageEx; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; - using Microsoft.UI.Xaml.Data; namespace CollapseLauncher.Pages { file static class HomePageExtension { internal static readonly BooleanVisibilityConverter BooleanVisibilityConverter = new(); - - internal static void BindProperty(this FrameworkElement element, DependencyProperty dependencyProperty, object objectToBind, string propertyName, IValueConverter? converter = null, BindingMode bindingMode = BindingMode.OneWay) - { - // Create a new binding instance - Binding binding = new Binding - { - Source = objectToBind, - Mode = bindingMode, - Path = new PropertyPath(propertyName), - UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged - }; - - // If the converter is assigned, then add the converter - if (converter != null) - { - binding.Converter = converter; - } - - // Set binding to the element - element.SetBinding(dependencyProperty, binding); - } } public partial class HomePage diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml index 413624061..ce0143dcb 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml @@ -603,11 +603,14 @@ - + + + + + - - - @@ -739,6 +729,7 @@ @@ -813,6 +804,7 @@ @@ -884,7 +876,23 @@ - + + + + + Value="{Binding Value, ElementName=progressRingPerFile}" /> + CornerRadius="{x:Bind extension:UIElementExtensions.AttachRoundedKindCornerRadius(ProgressPerFileCircleShadowCaster)}" + Shadow="{ThemeResource SharedShadow}" + Translation="0,0,32" /> - @@ -1588,6 +1598,7 @@