浏览代码

Documentation

Daniel Chalmers 1 年之前
父节点
当前提交
870a785622

+ 7 - 0
DesktopClock/DateFormatExample.cs

@@ -12,7 +12,14 @@ public record DateFormatExample
         Example = example;
     }
 
+    /// <summary>
+    /// The actual format (<c>dddd, MMMM dd</c>).
+    /// </summary>
     public string Format { get; }
+
+    /// <summary>
+    /// An example of the format in action (<c>Monday, July 15</c>).
+    /// </summary>
     public string Example { get; }
 
     public static DateFormatExample Tutorial => new(string.Empty, "Create my own format...");

+ 3 - 0
DesktopClock/DateTimeUtil.cs

@@ -5,6 +5,9 @@ namespace DesktopClock;
 
 public static class DateTimeUtil
 {
+    /// <summary>
+    /// A cached collection of all the time zones about which information is available on the local system.
+    /// </summary>
     public static IReadOnlyCollection<TimeZoneInfo> TimeZones { get; } = TimeZoneInfo.GetSystemTimeZones();
 
     public static bool TryFindSystemTimeZoneById(string timeZoneId, out TimeZoneInfo timeZoneInfo)

+ 44 - 15
DesktopClock/MainWindow.xaml.cs

@@ -29,13 +29,13 @@ public partial class MainWindow : Window
     private SoundPlayer _soundPlayer;
 
     /// <summary>
-    /// The date and time to countdown to, or null if regular clock is desired.
+    /// The date and time to countdown to, or <c>null</c> if regular clock is desired.
     /// </summary>
     [ObservableProperty]
     private DateTimeOffset? _countdownTo;
 
     /// <summary>
-    /// The current date and time in the selected time zone or countdown as a formatted string.
+    /// The current date and time in the selected time zone, or countdown as a formatted string.
     /// </summary>
     [ObservableProperty]
     private string _currentTimeOrCountdownString;
@@ -88,6 +88,7 @@ public partial class MainWindow : Window
             Settings.Default.TipsShown |= TeachingTips.HideForNow;
         }
 
+        // Minimize the window and update the ShowInTaskbar property to keep it hidden if needed.
         // https://stackoverflow.com/a/28239057.
         ShowInTaskbar = true;
         WindowState = WindowState.Minimized;
@@ -95,19 +96,19 @@ public partial class MainWindow : Window
     }
 
     /// <summary>
-    /// Sets app's theme to given value.
+    /// Sets the app's theme to the given value.
     /// </summary>
     [RelayCommand]
     public void SetTheme(Theme theme) => Settings.Default.Theme = theme;
 
     /// <summary>
-    /// Sets format string in settings to given string.
+    /// Sets the format string in settings to the given string.
     /// </summary>
     [RelayCommand]
     public void SetFormat(string format) => Settings.Default.Format = format;
 
     /// <summary>
-    /// Explains how to write a format, then asks user if they want to view a website and Advanced settings to do so.
+    /// Explains how to write a format, then asks the user if they want to view a website and advanced settings to do so.
     /// </summary>
     [RelayCommand]
     public void FormatWizard()
@@ -125,7 +126,7 @@ public partial class MainWindow : Window
     }
 
     /// <summary>
-    /// Sets time zone ID in settings to given time zone ID.
+    /// Sets the time zone ID in settings to the given time zone ID.
     /// </summary>
     [RelayCommand]
     public void SetTimeZone(TimeZoneInfo tzi) => App.SetTimeZone(tzi);
@@ -157,7 +158,7 @@ public partial class MainWindow : Window
     }
 
     /// <summary>
-    /// Explains how to enable countdown mode, then asks user if they want to view Advanced settings to do so.
+    /// Explains how to enable countdown mode, then asks the user if they want to view advanced settings to do so.
     /// </summary>
     [RelayCommand]
     public void CountdownWizard()
@@ -179,7 +180,7 @@ public partial class MainWindow : Window
     [RelayCommand]
     public void OpenSettings()
     {
-        // Teach user how it works.
+        // Inform user how it works.
         if (!Settings.Default.TipsShown.HasFlag(TeachingTips.AdvancedSettings))
         {
             MessageBox.Show(this,
@@ -202,17 +203,17 @@ public partial class MainWindow : Window
             return;
         }
 
-        // Open settings file in notepad.
+        // Open settings file in Notepad.
         try
         {
             Process.Start("notepad", Settings.FilePath);
         }
         catch (Exception ex)
         {
-            // Lazy scammers on the Microsoft Store may reupload without realizing it gets sandboxed, making it unable to start the Notepad process (#1, #12).
+            // Lazy scammers on the Microsoft Store reupload without realizing it gets sandboxed, making it unable to start the Notepad process (Issues: #1, #12).
             MessageBox.Show(this,
                 "Couldn't open settings file.\n\n" +
-                "This app may have be reuploaded without permission. If you paid for it, ask for a refund and download it for free from the original source: https://github.com/danielchalmers/DesktopClock.\n\n" +
+                "This app may have been reuploaded without permission. If you paid for it, ask for a refund and download it for free from the original source: https://github.com/danielchalmers/DesktopClock.\n\n" +
                 $"If it still doesn't work, create a new Issue at that link with details on what happened and include this error: \"{ex.Message}\"",
                 Title, MessageBoxButton.OK, MessageBoxImage.Error);
         }
@@ -241,7 +242,7 @@ public partial class MainWindow : Window
     }
 
     /// <summary>
-    /// Exits the program.
+    /// Closes the app.
     /// </summary>
     [RelayCommand]
     public void Exit()
@@ -255,6 +256,7 @@ public partial class MainWindow : Window
         {
             if (_trayIcon == null)
             {
+                // Construct the tray from the resources defined.
                 _trayIcon = Resources["TrayIcon"] as TaskbarIcon;
                 _trayIcon.ContextMenu = Resources["MainContextMenu"] as ContextMenu;
                 _trayIcon.ContextMenu.DataContext = this;
@@ -266,6 +268,7 @@ public partial class MainWindow : Window
                 };
             }
 
+            // Show a notice if the icon was moved during runtime, but not at the start because the user will already expect it.
             if (!firstLaunch)
                 _trayIcon.ShowNotification("Hidden from taskbar", "Icon was moved to the tray");
         }
@@ -276,6 +279,9 @@ public partial class MainWindow : Window
         }
     }
 
+    /// <summary>
+    /// Handles property changes in settings and updates the corresponding properties in the UI.
+    /// </summary>
     private void Settings_PropertyChanged(object sender, PropertyChangedEventArgs e)
     {
         switch (e.PropertyName)
@@ -307,6 +313,9 @@ public partial class MainWindow : Window
         }
     }
 
+    /// <summary>
+    /// Handles the event when the system clock timer signals a second change.
+    /// </summary>
     private void SystemClockTimer_SecondChanged(object sender, EventArgs e)
     {
         UpdateTimeString();
@@ -314,6 +323,9 @@ public partial class MainWindow : Window
         TryPlaySound();
     }
 
+    /// <summary>
+    /// Updates the countdown enabled state based on the settings.
+    /// </summary>
     private void UpdateCountdownEnabled()
     {
         if (Settings.Default.CountdownTo == null || Settings.Default.CountdownTo == default(DateTime))
@@ -325,6 +337,9 @@ public partial class MainWindow : Window
         CountdownTo = Settings.Default.CountdownTo.Value.ToDateTimeOffset(_timeZone.BaseUtcOffset);
     }
 
+    /// <summary>
+    /// Updates the sound player enabled state based on the settings.
+    /// </summary>
     private void UpdateSoundPlayerEnabled()
     {
         var soundPlayerEnabled =
@@ -335,11 +350,15 @@ public partial class MainWindow : Window
         _soundPlayer = soundPlayerEnabled ? new() : null;
     }
 
+    /// <summary>
+    /// Tries to play a sound based on the settings if it hits the specified interval and the file exists.
+    /// </summary>
     private void TryPlaySound()
     {
         if (_soundPlayer == null)
             return;
 
+        // Whether we hit the interval specified in settings, which is calculated differently in countdown mode and not.
         var isOnInterval = CountdownTo == null ?
             (int)DateTimeOffset.Now.TimeOfDay.TotalSeconds % (int)Settings.Default.WavFileInterval.TotalSeconds == 0 :
             (int)(CountdownTo.Value - DateTimeOffset.Now).TotalSeconds % (int)Settings.Default.WavFileInterval.TotalSeconds == 0;
@@ -354,7 +373,7 @@ public partial class MainWindow : Window
         }
         catch
         {
-            // Ignore errors.
+            // Ignore errors because we don't want a sound issue to crash the app.
         }
     }
 
@@ -382,11 +401,15 @@ public partial class MainWindow : Window
 
     private void Window_MouseDown(object sender, MouseButtonEventArgs e)
     {
+        // Drag the window to move it.
         if (e.ChangedButton == MouseButton.Left && Settings.Default.DragToMove)
         {
+            // Pause time updates to maintain placement.
             _systemClockTimer.Stop();
+
             DragMove();
             UpdateTimeString();
+
             _systemClockTimer.Start();
         }
     }
@@ -398,6 +421,7 @@ public partial class MainWindow : Window
 
     private void Window_MouseWheel(object sender, MouseWheelEventArgs e)
     {
+        // Resize the window when scrolling if the Ctrl key is pressed.
         if (Keyboard.Modifiers == ModifierKeys.Control)
         {
             // Amount of scroll that occurred and whether it was positive or negative.
@@ -408,7 +432,7 @@ public partial class MainWindow : Window
             var newHeightLogClamped = Math.Min(Math.Max(newHeightLog, MinSizeLog), MaxSizeLog);
             var exp = Math.Exp(newHeightLogClamped);
 
-            // Apply the new height as an integer to make it easier for the user.
+            // Save the new height as an integer to make it easier for the user.
             Settings.Default.Height = (int)exp;
         }
     }
@@ -426,6 +450,7 @@ public partial class MainWindow : Window
 
     private void Window_ContentRendered(object sender, EventArgs e)
     {
+        // Make sure the user is aware that their changes will not be saved.
         if (!Settings.CanBeSaved)
         {
             MessageBox.Show(this,
@@ -439,6 +464,7 @@ public partial class MainWindow : Window
 
     private void Window_Closing(object sender, CancelEventArgs e)
     {
+        // Save the last text and the placement to preserve dimensions and position of the clock.
         Settings.Default.LastDisplay = CurrentTimeOrCountdownString;
         Settings.Default.Placement = this.GetPlacement();
 
@@ -453,6 +479,7 @@ public partial class MainWindow : Window
 
     private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
     {
+        // Adjust the window position for right-alignment.
         if (e.WidthChanged && Settings.Default.RightAligned)
         {
             var widthChange = e.NewSize.Width - e.PreviousSize.Width;
@@ -464,14 +491,16 @@ public partial class MainWindow : Window
     {
         if (WindowState == WindowState.Minimized)
         {
+            // Save resources while minimized.
             _systemClockTimer.Stop();
             EfficiencyModeUtilities.SetEfficiencyMode(true);
         }
         else
         {
+            // Run like normal without withholding resources.
             UpdateTimeString();
             _systemClockTimer.Start();
             EfficiencyModeUtilities.SetEfficiencyMode(false);
         }
     }
-}
+}

+ 124 - 8
DesktopClock/Properties/Settings.cs

@@ -15,31 +15,38 @@ public sealed class Settings : INotifyPropertyChanged, IDisposable
 
     private static readonly JsonSerializerSettings _jsonSerializerSettings = new()
     {
+        // Make it easier to read by a human.
         Formatting = Formatting.Indented,
+
+        // Prevent a single error from taking down the whole file.
         Error = (_, e) => e.ErrorContext.Handled = true,
     };
 
+    // Private constructor to enforce singleton pattern.
     private Settings()
     {
-        // Settings file path from same directory as the executable.
+        // Settings file path from the same directory as the executable.
         var settingsFileName = Path.GetFileNameWithoutExtension(App.MainFileInfo.FullName) + ".settings";
         FilePath = Path.Combine(App.MainFileInfo.DirectoryName, settingsFileName);
 
-        // Watch for changes.
+        // Watch for changes in the settings file.
         _watcher = new(App.MainFileInfo.DirectoryName, settingsFileName)
         {
             EnableRaisingEvents = true,
         };
         _watcher.Changed += FileChanged;
 
-        // Random default theme before getting overwritten.
+        // Set a random default theme which can be overwritten later when the file loads.
         Theme = Theme.GetRandomDefaultTheme();
     }
 
-#pragma warning disable CS0067 // The event 'Settings.PropertyChanged' is never used
+#pragma warning disable CS0067 // The event 'Settings.PropertyChanged' is never used. Handled by Fody.
     public event PropertyChangedEventHandler PropertyChanged;
-#pragma warning restore CS0067 // The event 'Settings.PropertyChanged' is never used
+#pragma warning restore CS0067
 
+    /// <summary>
+    /// The singleton instance of the local settings file.
+    /// </summary>
     public static Settings Default => _default.Value;
 
     /// <summary>
@@ -48,41 +55,150 @@ public sealed class Settings : INotifyPropertyChanged, IDisposable
     public static string FilePath { get; private set; }
 
     /// <summary>
-    /// Can the settings file be saved to?
+    /// Indicates if the settings file can be saved to.
     /// </summary>
+    /// <remarks>
+    /// <c>false</c> could indicate the file is in a folder that requires administrator permissions among other constraints.
+    /// </remarks>
     public static bool CanBeSaved { get; private set; }
 
     /// <summary>
-    /// Does the settings file exist on the disk?
+    /// Checks if the settings file exists on the disk.
     /// </summary>
     public static bool Exists => File.Exists(FilePath);
 
     #region "Properties"
 
+    /// <summary>
+    /// Format string for the date and time shown on the clock display.
+    /// </summary>
+    /// <remarks>
+    /// See: <see href="https://learn.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings">Custom date and time format strings</see>.
+    /// </remarks>
     public string Format { get; set; } = "{ddd}, {MMM dd}, {h:mm:ss tt}";
+
+    /// <summary>
+    /// Format string shown on the clock display when in countdown mode.
+    /// </summary>
+    /// <remarks>
+    /// See: <see href="https://learn.microsoft.com/en-us/dotnet/standard/base-types/custom-timespan-format-strings">Custom TimeSpan format strings</see>.
+    /// </remarks>
     public string CountdownFormat { get; set; } = "";
+
+    /// <summary>
+    /// Target date and time for countdown mode.
+    /// </summary>
     public DateTime? CountdownTo { get; set; } = default(DateTime);
+
+    /// <summary>
+    /// Time zone for the clock display.
+    /// </summary>
     public string TimeZone { get; set; } = string.Empty;
+
+    /// <summary>
+    /// Font family for the clock display.
+    /// </summary>
     public string FontFamily { get; set; } = "Consolas";
+
+    /// <summary>
+    /// Text color for the clock display.
+    /// </summary>
     public Color TextColor { get; set; }
+
+    /// <summary>
+    /// The outer color, either for the background or the outline..
+    /// </summary>
     public Color OuterColor { get; set; }
+
+    /// <summary>
+    /// Shows a full background instead of a simple outline.
+    /// </summary>
     public bool BackgroundEnabled { get; set; } = true;
+
+    /// <summary>
+    /// Opacity of the background.
+    /// </summary>
     public double BackgroundOpacity { get; set; } = 0.90;
+
+    /// <summary>
+    /// Corner radius of the background.
+    /// </summary>
     public double BackgroundCornerRadius { get; set; } = 1;
+
+    /// <summary>
+    /// Path to the background image.
+    /// </summary>
     public string BackgroundImagePath { get; set; } = string.Empty;
+
+    /// <summary>
+    /// Thickness of the outline around the clock.
+    /// </summary>
     public double OutlineThickness { get; set; } = 0.2;
+
+    /// <summary>
+    /// Keeps the clock on top of other windows.
+    /// </summary>
     public bool Topmost { get; set; } = true;
+
+    /// <summary>
+    /// Shows the app icon in the taskbar.
+    /// </summary>
     public bool ShowInTaskbar { get; set; } = true;
+
+    /// <summary>
+    /// Height of the clock display.
+    /// </summary>
     public int Height { get; set; } = 48;
+
+    /// <summary>
+    /// Runs the app on startup.
+    /// </summary>
     public bool RunOnStartup { get; set; } = false;
+
+    /// <summary>
+    /// Allows moving the clock by dragging.
+    /// </summary>
     public bool DragToMove { get; set; } = true;
+
+    /// <summary>
+    /// Aligns the text to the right.
+    /// </summary>
+    /// <remarks>
+    /// Small glitches can happen because programs are naturally meant to be left-anchored.
+    /// </remarks>
     public bool RightAligned { get; set; } = false;
+
+    /// <summary>
+    /// Path to a WAV file for audio alerts.
+    /// </summary>
     public string WavFilePath { get; set; } = string.Empty;
+
+    /// <summary>
+    /// Interval for playing the WAV file if one is specified and exists.
+    /// </summary>
     public TimeSpan WavFileInterval { get; set; }
+
+    /// <summary>
+    /// Teaching tips that have already been shown to the user.
+    /// </summary>
     public TeachingTips TipsShown { get; set; }
+
+    /// <summary>
+    /// The last text shown on the clock, saved to maintain the dimensions on the next launch.
+    /// </summary>
     public string LastDisplay { get; set; }
+
+    /// <summary>
+    /// Window placement settings to preserve the location of the clock on the screen.
+    /// </summary>
     public WindowPlacement Placement { get; set; }
 
+    /// <summary>
+    /// The current theme as a proxy.
+    /// </summary>
+    /// <remarks>
+    /// Ignored during serialization.
+    /// </remarks>
     [JsonIgnore]
     public Theme Theme
     {
@@ -187,4 +303,4 @@ public sealed class Settings : INotifyPropertyChanged, IDisposable
         // We don't dispose of the watcher anymore because it would actually hang indefinitely if you had multiple instances of the same clock open.
         //_watcher?.Dispose();
     }
-}
+}

+ 9 - 0
DesktopClock/SystemClockTimer.cs

@@ -25,10 +25,19 @@ public sealed class SystemClockTimer : IDisposable
     /// </summary>
     private int MillisecondsUntilNextSecond => 1000 - DateTimeOffset.Now.Millisecond;
 
+    /// <summary>
+    /// <inheritdoc/>
+    /// </summary>
     public void Dispose() => _timer.Dispose();
 
+    /// <summary>
+    /// Schedules the timer to start on the next second that elapses.
+    /// </summary>
     public void Start() => ScheduleTickForNextSecond();
 
+    /// <summary>
+    /// Immediately stops the timer.
+    /// </summary>
     public void Stop() => _timer.Change(Timeout.Infinite, Timeout.Infinite);
 
     private void OnTick()

+ 23 - 1
DesktopClock/Theme.cs

@@ -3,10 +3,24 @@ using System.Collections.Generic;
 
 namespace DesktopClock;
 
+/// <summary>
+/// A defined color set.
+/// </summary>
 public readonly record struct Theme
 {
+    /// <summary>
+    /// Friendly name for the theme.
+    /// </summary>
     public string Name { get; }
+
+    /// <summary>
+    /// The primary color, used in the text.
+    /// </summary>
     public string PrimaryColor { get; }
+
+    /// <summary>
+    /// The secondary color, used in the background or outline.
+    /// </summary>
     public string SecondaryColor { get; }
 
     public Theme(string name, string primaryColor, string secondaryColor)
@@ -16,7 +30,12 @@ public readonly record struct Theme
         SecondaryColor = secondaryColor;
     }
 
-    // https://www.materialui.co/colors - A100, A700.
+    /// <summary>
+    /// Built-in themes that the user can use without specifying their own palettes.
+    /// </summary>
+    /// <remarks>
+    /// https://www.materialui.co/colors - A100, A700.
+    /// </remarks>
     public static IReadOnlyList<Theme> DefaultThemes { get; } = new Theme[]
     {
         new("Light Text", "#F5F5F5", "#212121"),
@@ -30,6 +49,9 @@ public readonly record struct Theme
         new("Orange", "#FF6D00", "#FFD180"),
     };
 
+    /// <summary>
+    /// Returns a random theme from <see cref="DefaultThemes"/>.
+    /// </summary>
     public static Theme GetRandomDefaultTheme()
     {
         var random = new Random();