diff --git a/Hourglass.Bundle/Bundle.wxs b/Hourglass.Bundle/Bundle.wxs index 1dc2b60..82e1d92 100644 --- a/Hourglass.Bundle/Bundle.wxs +++ b/Hourglass.Bundle/Bundle.wxs @@ -3,7 +3,7 @@ diff --git a/Hourglass.Setup/Product.wxs b/Hourglass.Setup/Product.wxs index c62f69c..e2286da 100644 --- a/Hourglass.Setup/Product.wxs +++ b/Hourglass.Setup/Product.wxs @@ -1,7 +1,7 @@  - + diff --git a/Hourglass.Test/Properties/AssemblyInfo.cs b/Hourglass.Test/Properties/AssemblyInfo.cs index d9d054f..21bdff4 100644 --- a/Hourglass.Test/Properties/AssemblyInfo.cs +++ b/Hourglass.Test/Properties/AssemblyInfo.cs @@ -17,5 +17,5 @@ [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] [assembly: Guid("002a4be7-7323-4bf9-ab08-5fc8978d9eb0")] -[assembly: AssemblyVersion("1.15.14.0")] -[assembly: AssemblyFileVersion("1.15.14.0")] +[assembly: AssemblyVersion("1.15.15.0")] +[assembly: AssemblyFileVersion("1.15.15.0")] diff --git a/Hourglass/App.config b/Hourglass/App.config index 1ff8313..36b3098 100644 --- a/Hourglass/App.config +++ b/Hourglass/App.config @@ -25,6 +25,9 @@ False + + False + diff --git a/Hourglass/CommandLineArguments.cs b/Hourglass/CommandLineArguments.cs index e98e426..5663170 100644 --- a/Hourglass/CommandLineArguments.cs +++ b/Hourglass/CommandLineArguments.cs @@ -36,7 +36,7 @@ public static string Usage { string assemblyLocation = Assembly.GetEntryAssembly()!.CodeBase; string assemblyFileName = Path.GetFileName(assemblyLocation); - return string.Format(Resources.Usage, assemblyFileName.ToLowerInvariant()); + return Resources.Usage.Replace("hourglass.exe", assemblyFileName.ToLowerInvariant()); } } @@ -102,6 +102,11 @@ public static string Usage /// public bool ReverseProgressBar { get; private set; } + /// + /// Gets a value indicating whether to display time in the digital clock format. + /// + public bool DigitalClockTime { get; private set; } + /// /// Gets a value indicating whether to show the time elapsed rather than the time left. /// @@ -228,6 +233,7 @@ public TimerOptions GetTimerOptions() ShowProgressInTaskbar = ShowProgressInTaskbar, DoNotKeepComputerAwake = DoNotKeepComputerAwake, ReverseProgressBar = ReverseProgressBar, + DigitalClockTime = DigitalClockTime, ShowTimeElapsed = ShowTimeElapsed, LoopTimer = LoopTimer, PopUpWhenExpired = PopUpWhenExpired, @@ -277,6 +283,7 @@ private static CommandLineArguments GetArgumentsFromMostRecentOptions() ShowProgressInTaskbar = options.ShowProgressInTaskbar, DoNotKeepComputerAwake = options.DoNotKeepComputerAwake, ReverseProgressBar = options.ReverseProgressBar, + DigitalClockTime = options.DigitalClockTime, ShowTimeElapsed = options.ShowTimeElapsed, ShowInNotificationArea = Settings.Default.ShowInNotificationArea, LoopTimer = options.LoopTimer, @@ -317,6 +324,7 @@ private static CommandLineArguments GetArgumentsFromFactoryDefaults() ShowProgressInTaskbar = defaultOptions.ShowProgressInTaskbar, DoNotKeepComputerAwake = defaultOptions.DoNotKeepComputerAwake, ReverseProgressBar = defaultOptions.ReverseProgressBar, + DigitalClockTime = defaultOptions.DigitalClockTime, ShowTimeElapsed = defaultOptions.ShowTimeElapsed, ShowInNotificationArea = false, LoopTimer = defaultOptions.LoopTimer, @@ -455,6 +463,19 @@ private static CommandLineArguments GetCommandLineArguments(IEnumerable argumentsBasedOnFactoryDefaults.ReverseProgressBar = reverseProgressBar; break; + case "--digital-clock-time": + case "-c": + ThrowIfDuplicateSwitch(specifiedSwitches, "--digital-clock-time"); + + bool digitalClockTime = GetBoolValue( + arg, + remainingArgs, + argumentsBasedOnMostRecentOptions.DigitalClockTime); + + argumentsBasedOnMostRecentOptions.DigitalClockTime = digitalClockTime; + argumentsBasedOnFactoryDefaults.DigitalClockTime = digitalClockTime; + break; + case "--show-time-elapsed": case "-u": ThrowIfDuplicateSwitch(specifiedSwitches, "--show-time-elapsed"); @@ -1011,7 +1032,7 @@ private static Rect GetRectValue(string arg, Queue remainingArgs, Rect l arg, value); - throw new ParseException(message); + throw new ParseException(message, ex); } } @@ -1104,5 +1125,15 @@ public ParseException(string message) : base(message) { } + + /// + /// Initializes a new instance of the class. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, or a null reference ( in Visual Basic) if no inner exception is specified. + public ParseException(string message, Exception innerException) + : base(message, innerException) + { + } } } \ No newline at end of file diff --git a/Hourglass/Extensions/TimeSpanExtensions.cs b/Hourglass/Extensions/TimeSpanExtensions.cs index d7b8fdb..7ac6cae 100644 --- a/Hourglass/Extensions/TimeSpanExtensions.cs +++ b/Hourglass/Extensions/TimeSpanExtensions.cs @@ -47,36 +47,48 @@ public static TimeSpan RoundUp(this TimeSpan timeSpan) /// /// A . /// An . + /// Use compact time format. /// The natural string representation of the . - public static string ToNaturalString(this TimeSpan timeSpan, IFormatProvider provider) + public static string ToNaturalString(this TimeSpan timeSpan, IFormatProvider provider, bool compact) { - List parts = new(); + return compact +#pragma warning disable S3358 + ? timeSpan.ToString( + timeSpan.Days != 0 + ? Resources.CompactTimeSpanWithDaysFormat + : Resources.CompactTimeSpanFormat) +#pragma warning restore S3358 + : string.Join( + Resources.ResourceManager.GetString("TimeSpanExtensionsUnitSeparator", provider), + GetParts()); - // Days - if (timeSpan.Days != 0) + IEnumerable GetParts() { - parts.Add(GetStringWithUnits(timeSpan.Days, "Day", provider)); - } + bool hasValue = false; - // Hours - if (timeSpan.Hours != 0 || parts.Count != 0) - { - parts.Add(GetStringWithUnits(timeSpan.Hours, "Hour", provider)); - } + // Days + if (timeSpan.Days != 0) + { + hasValue = true; + yield return GetStringWithUnits(timeSpan.Days, "Day", provider); + } - // Minutes - if (timeSpan.Minutes != 0 || parts.Count != 0) - { - parts.Add(GetStringWithUnits(timeSpan.Minutes, "Minute", provider)); - } + // Hours + if (timeSpan.Hours != 0 || hasValue) + { + hasValue = true; + yield return GetStringWithUnits(timeSpan.Hours, "Hour", provider); + } - // Seconds - parts.Add(GetStringWithUnits(timeSpan.Seconds, "Second", provider)); + // Minutes + if (timeSpan.Minutes != 0 || hasValue) + { + yield return GetStringWithUnits(timeSpan.Minutes, "Minute", provider); + } - // Join parts - return string.Join( - Resources.ResourceManager.GetString("TimeSpanExtensionsUnitSeparator", provider), - parts); + // Seconds + yield return GetStringWithUnits(timeSpan.Seconds, "Second", provider); + } } /// @@ -84,11 +96,12 @@ public static string ToNaturalString(this TimeSpan timeSpan, IFormatProvider pro /// representation. /// /// A . + /// Use compact time format. /// The natural string representation of the represented by , or if is null. - public static string ToNaturalString(this TimeSpan? timeSpan) + public static string ToNaturalString(this TimeSpan? timeSpan, bool compact) { - return timeSpan.ToNaturalString(CultureInfo.CurrentCulture); + return timeSpan.ToNaturalString(CultureInfo.CurrentCulture, compact); } /// @@ -97,11 +110,12 @@ public static string ToNaturalString(this TimeSpan? timeSpan) /// /// A . /// An . + /// Use compact time format. /// The natural string representation of the represented by , or if is null. - public static string ToNaturalString(this TimeSpan? timeSpan, IFormatProvider provider) + public static string ToNaturalString(this TimeSpan? timeSpan, IFormatProvider provider, bool compact) { - return timeSpan.HasValue ? timeSpan.Value.ToNaturalString(provider) : string.Empty; + return timeSpan.HasValue ? timeSpan.Value.ToNaturalString(provider, compact) : string.Empty; } /// diff --git a/Hourglass/Extensions/TimerWindowExtensions.cs b/Hourglass/Extensions/TimerWindowExtensions.cs index 0c0f009..2dab30f 100644 --- a/Hourglass/Extensions/TimerWindowExtensions.cs +++ b/Hourglass/Extensions/TimerWindowExtensions.cs @@ -37,6 +37,11 @@ public static void BringNextToFrontAndActivate(this TimerWindow thisWindow) TimerWindow GetNextWindow() { + if (Application.Current is null) + { + return null; + } + var allWindows = Application.Current.Windows.OfType().Arrange().ToList(); return GetNextApplicableWindow(allWindows.SkipWhile(NotThisWindow).Skip(1)) ?? diff --git a/Hourglass/Extensions/WindowExtensions.cs b/Hourglass/Extensions/WindowExtensions.cs index 080314d..3c544aa 100644 --- a/Hourglass/Extensions/WindowExtensions.cs +++ b/Hourglass/Extensions/WindowExtensions.cs @@ -551,7 +551,8 @@ private static Rect Offset(this Rect rect) return rect; } - const int OffsetAmount = 25; + const int OffsetAmount = 0; + Rect offsetRect = rect; // Move the rect down and to the right diff --git a/Hourglass/Managers/SoundManager.cs b/Hourglass/Managers/SoundManager.cs index caf293d..c881d92 100644 --- a/Hourglass/Managers/SoundManager.cs +++ b/Hourglass/Managers/SoundManager.cs @@ -62,14 +62,14 @@ private SoundManager() /// Gets a collection of the sounds stored in the assembly. /// #pragma warning disable S2365 - public IList BuiltInSounds => _sounds.Where(s => s.IsBuiltIn).ToList(); + public IList BuiltInSounds => _sounds.Where(static s => s.IsBuiltIn).ToList(); #pragma warning restore S2365 /// /// Gets a collection of the sounds stored in the file system. /// #pragma warning disable S2365 - public IList UserProvidedSounds => _sounds.Where(s => !s.IsBuiltIn).ToList(); + public IList UserProvidedSounds => _sounds.Where(static s => !s.IsBuiltIn).ToList(); #pragma warning restore S2365 /// diff --git a/Hourglass/Managers/ThemeManager.cs b/Hourglass/Managers/ThemeManager.cs index c0abde6..9f3ab3e 100644 --- a/Hourglass/Managers/ThemeManager.cs +++ b/Hourglass/Managers/ThemeManager.cs @@ -18,6 +18,9 @@ namespace Hourglass.Managers; /// public sealed class ThemeManager : Manager { + private const string DefaultThemeIdentifier = "blue"; + private const string DefaultDarkThemeIdentifier = $"{DefaultThemeIdentifier}-dark"; + /// /// Singleton instance of the class. /// @@ -38,12 +41,12 @@ private ThemeManager() /// /// Gets the default theme. /// - public Theme DefaultTheme => GetThemeByIdentifier("blue"); + public Theme DefaultTheme => GetThemeByIdentifier(DefaultThemeIdentifier); /// /// Gets the default dark theme. /// - public Theme DefaultDarkTheme => GetThemeByIdentifier("blue-dark"); + public Theme DefaultDarkTheme => GetThemeByIdentifier(DefaultDarkThemeIdentifier); /// /// Gets a collection of the themes stored in the assembly. diff --git a/Hourglass/Managers/TimerManager.cs b/Hourglass/Managers/TimerManager.cs index 7586ccb..c6f5382 100644 --- a/Hourglass/Managers/TimerManager.cs +++ b/Hourglass/Managers/TimerManager.cs @@ -144,6 +144,6 @@ public void ClearResumableTimers() /// A value indicating whether the timer is bound to any . private static bool IsBoundToWindow(Timer timer) { - return Application.Current?.Windows.OfType().Any(w => w.Timer == timer) == true; + return Application.Current?.Windows.OfType().Any(w => ReferenceEquals(w.Timer, timer)) == true; } } \ No newline at end of file diff --git a/Hourglass/Properties/App.manifest b/Hourglass/Properties/App.manifest index 919caf8..f07f161 100644 --- a/Hourglass/Properties/App.manifest +++ b/Hourglass/Properties/App.manifest @@ -1,6 +1,6 @@  - + The simple countdown timer for Windows. diff --git a/Hourglass/Properties/AssemblyInfo.cs b/Hourglass/Properties/AssemblyInfo.cs index 3d9a318..7cea3bd 100644 --- a/Hourglass/Properties/AssemblyInfo.cs +++ b/Hourglass/Properties/AssemblyInfo.cs @@ -25,8 +25,8 @@ [assembly: AssemblyCopyright("Copyright © 2021 Chris Dziemborowicz, 2024 Ivan Ivon")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("1.15.14.0")] -[assembly: AssemblyFileVersion("1.15.14.0")] +[assembly: AssemblyVersion("1.15.15.0")] +[assembly: AssemblyFileVersion("1.15.15.0")] [assembly: NeutralResourcesLanguage("en-US")] [assembly: Guid("83DBAA61-6193-4288-BBB7-BEAEC33FE321")] [assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)] diff --git a/Hourglass/Properties/Resources.Designer.cs b/Hourglass/Properties/Resources.Designer.cs index f9ef7fd..4fe36bf 100644 --- a/Hourglass/Properties/Resources.Designer.cs +++ b/Hourglass/Properties/Resources.Designer.cs @@ -205,6 +205,24 @@ public static string CommandLineArgumentsParseExceptionUnrecognizedSwitchFormatS } } + /// + /// Looks up a localized string similar to hh':'mm':'ss. + /// + public static string CompactTimeSpanFormat { + get { + return ResourceManager.GetString("CompactTimeSpanFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to d'.'hh':'mm':'ss. + /// + public static string CompactTimeSpanWithDaysFormat { + get { + return ResourceManager.GetString("CompactTimeSpanWithDaysFormat", resourceCulture); + } + } + /// /// Looks up a localized string similar to _About. /// @@ -286,6 +304,15 @@ public static string ContextMenuDarkThemeMenuItem { } } + /// + /// Looks up a localized string similar to Display time in the digital _clock format. + /// + public static string ContextMenuDigitalClockTimeMenuItem { + get { + return ResourceManager.GetString("ContextMenuDigitalClockTimeMenuItem", resourceCulture); + } + } + /// /// Looks up a localized string similar to Do not _keep computer awake. /// @@ -2137,6 +2164,15 @@ public static string TimerWindowCouldNotLaunchWebBrowserErrorTitle { } } + /// + /// Looks up a localized string similar to New timer: {0}. + /// + public static string TimerWindowNewTimerFormatString { + get { + return ResourceManager.GetString("TimerWindowNewTimerFormatString", resourceCulture); + } + } + /// /// Looks up a localized string similar to New timer: {0} "{1}". /// @@ -2236,15 +2272,6 @@ public static string TimerWindowUpdateButtonContent { } } - /// - /// Looks up a localized string similar to New timer: {0}. - /// - public static string TimerWindwoNewTimerFormatString { - get { - return ResourceManager.GetString("TimerWindwoNewTimerFormatString", resourceCulture); - } - } - /// /// Looks up a localized string similar to {0} day. /// @@ -2551,7 +2578,8 @@ public static System.Drawing.Icon TrayIcon { } /// - /// Looks up a localized string similar to Usage: {0} [OPTIONS] [<input>] + /// Looks up a localized string similar to Usage: hourglass.exe [OPTIONS] [<input>] + /// ///A simple countdown timer for Windows. /// /// <input> @@ -2563,7 +2591,7 @@ public static System.Drawing.Icon TrayIcon { /// "7:30:00" count down for 7 hours 30 minutes /// "5 minutes" count down for 5 minutes /// "5 minutes 30 seconds" count down for 5 minutes 30 seconds - /// "7 hours 30 minutes" count d [rest of string was truncated]";. + /// "7 hours 30 minutes [rest of string was truncated]";. /// public static string Usage { get { diff --git a/Hourglass/Properties/Resources.resx b/Hourglass/Properties/Resources.resx index 0c5e4a4..26c52b1 100644 --- a/Hourglass/Properties/Resources.resx +++ b/Hourglass/Properties/Resources.resx @@ -291,7 +291,7 @@ A format string for N years with units, where {0} is the number of years with a possible fraction part (e.g., "{0} years" => "5 years") - ..\Resources\Usage.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;UTF-8 + ..\Resources\Usage.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 @@ -924,7 +924,7 @@ $ _Update The text for the update button in the timer window, where the character following the optional underscore (_) is the access key - + New timer: {0} A format string for the string representation of a timer window used when the timer window does not have a running timer, where {0} is the current user input into the window @@ -1313,6 +1313,10 @@ $ Prefer 24-_hour time when parsing The text for the prefer 24-hour time when parsing menu item, where the character following the optional underscore (_) is the access key + + Display time in the digital _clock format + The text for the dispay time in the digital clock format when displaying menu item, where the character following the optional underscore (_) is the access key + FA_Q The text for the FAQ menu item, where the character following the optional underscore (_) is the access key @@ -1368,4 +1372,12 @@ $ https://chris.dziemborowicz.com/apps/hourglass/#downloads FAQ URL + + d'.'hh':'mm':'ss + Compact date with days format + + + hh':'mm':'ss + Compact date without days format + \ No newline at end of file diff --git a/Hourglass/Properties/Settings.Designer.cs b/Hourglass/Properties/Settings.Designer.cs index 329597c..388d12a 100644 --- a/Hourglass/Properties/Settings.Designer.cs +++ b/Hourglass/Properties/Settings.Designer.cs @@ -137,5 +137,17 @@ public bool Prefer24HourTime { this["Prefer24HourTime"] = value; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool DigitalClockTime { + get { + return ((bool)(this["DigitalClockTime"])); + } + set { + this["DigitalClockTime"] = value; + } + } } } diff --git a/Hourglass/Properties/Settings.settings b/Hourglass/Properties/Settings.settings index 016d0f4..095e6c4 100644 --- a/Hourglass/Properties/Settings.settings +++ b/Hourglass/Properties/Settings.settings @@ -32,5 +32,8 @@ False + + False + \ No newline at end of file diff --git a/Hourglass/Resources/License.txt b/Hourglass/Resources/License.txt index 7222e5f..eb3795f 100644 --- a/Hourglass/Resources/License.txt +++ b/Hourglass/Resources/License.txt @@ -1,4 +1,4 @@ -MIT License +MIT License Copyright © 2021 Chris Dziemborowicz, 2024 Ivan Ivon diff --git a/Hourglass/Resources/Usage.txt b/Hourglass/Resources/Usage.txt index 533d290..1a2e3c6 100644 --- a/Hourglass/Resources/Usage.txt +++ b/Hourglass/Resources/Usage.txt @@ -1,4 +1,5 @@ -Usage: {0} [OPTIONS] [] +Usage: hourglass.exe [OPTIONS] [] + A simple countdown timer for Windows. @@ -26,6 +27,7 @@ A simple countdown timer for Windows. Variations of some of these formats are also supported. Options: + --title Sets the title for the timer. @@ -82,6 +84,15 @@ Options: Default value last Alias -g + --digital-clock-time on|off|last + Displays time in the digital clock format: + - d.hh:mm:ss if a time is longer than a day; + - hh:mm:ss otherwise. + + Required no + Default value last + Alias -c + --show-time-elapsed on|off|last Shows the time elapsed since the timer was started, rather than the time left until the timer expires. @@ -250,6 +261,7 @@ Options: --do-not-keep-awake -k off --show-in-notification-area -n off --reverse-progress-bar -g off + --digital-clock-time -c off --show-time-elapsed -u off --loop-timer -l off --pop-up-when-expired -p on @@ -262,7 +274,7 @@ Options: --prefer-24h-time -j off --window-title -i app --window-state -w normal - --window-bounds -b auto,auto,350,150 + --window-bounds -b auto,auto,355,160 --lock-interface -z off Required no diff --git a/Hourglass/Serialization/TimerOptionsInfo.cs b/Hourglass/Serialization/TimerOptionsInfo.cs index 45a987f..a9ddcfe 100644 --- a/Hourglass/Serialization/TimerOptionsInfo.cs +++ b/Hourglass/Serialization/TimerOptionsInfo.cs @@ -44,6 +44,11 @@ public sealed class TimerOptionsInfo /// </summary> public bool ReverseProgressBar { get; set; } + /// <summary> + /// Gets or sets a value indicating whether to display time in the digital clock format. + /// </summary> + public bool DigitalClockTime { get; set; } + /// <summary> /// Gets or sets a value indicating whether to show the time elapsed rather than the time left. /// </summary> diff --git a/Hourglass/Timing/Timer.cs b/Hourglass/Timing/Timer.cs index c8df93d..a901c10 100644 --- a/Hourglass/Timing/Timer.cs +++ b/Hourglass/Timing/Timer.cs @@ -203,7 +203,7 @@ public override string ToString() return string.Format( Resources.ResourceManager.GetEffectiveProvider(), Resources.ResourceManager.GetString(resourceName) ?? GetType().ToString(), - Options.ShowTimeElapsed ? TimeElapsed.ToNaturalString() : TimeLeft.RoundUp().ToNaturalString(), + Options.ShowTimeElapsed ? TimeElapsed.ToNaturalString(Options.DigitalClockTime) : TimeLeft.RoundUp().ToNaturalString(Options.DigitalClockTime), TimerStart, Options.Title); } @@ -324,9 +324,9 @@ private void UpdateHourglassTimer() TimerStart = State != TimerState.Stopped ? TimerStart : null; TimeLeftAsPercentage = GetTimeLeftAsPercentage(); TimeElapsedAsPercentage = GetTimeElapsedAsPercentage(); - TimeLeftAsString = GetTimeLeftAsString(); - TimeElapsedAsString = GetTimeElapsedAsString(); - TimeExpiredAsString = GetTimeExpiredAsString(); + TimeLeftAsString = GetTimeLeftAsString(Options.DigitalClockTime); + TimeElapsedAsString = GetTimeElapsedAsString(Options.DigitalClockTime); + TimeExpiredAsString = GetTimeExpiredAsString(false); // Always human-readable. OnPropertyChanged( nameof(TimerStart), @@ -394,8 +394,9 @@ private void UpdateHourglassTimer() /// <summary> /// Returns the string representation of the time left until the timer expires. /// </summary> + /// <param name="compact">Use compact time format.</param> /// <returns>The string representation of the time left until the timer expires.</returns> - private string GetTimeLeftAsString() + private string GetTimeLeftAsString(bool compact) { if (State == TimerState.Stopped) { @@ -407,14 +408,15 @@ private string GetTimeLeftAsString() return Resources.TimerTimerExpired; } - return TimeLeft.RoundUp().ToNaturalString(); + return TimeLeft.RoundUp().ToNaturalString(compact); } /// <summary> /// Returns the string representation of the time elapsed since the timer was started. /// </summary> + /// <param name="compact">Use compact time format.</param> /// <returns>The string representation of the time elapsed since the timer was started.</returns> - private string GetTimeElapsedAsString() + private string GetTimeElapsedAsString(bool compact) { if (!SupportsTimeElapsed) { @@ -431,14 +433,15 @@ private string GetTimeElapsedAsString() return Resources.TimerTimerExpired; } - return TimeElapsed.ToNaturalString(); + return TimeElapsed.ToNaturalString(compact); } /// <summary> /// Returns the string representation of the time since the timer expired. /// </summary> + /// <param name="compact">Use compact time format.</param> /// <returns>The string representation of the time since the timer expired.</returns> - private string GetTimeExpiredAsString() + private string GetTimeExpiredAsString(bool compact) { if (State != TimerState.Expired) { @@ -448,7 +451,7 @@ private string GetTimeExpiredAsString() return string.Format( Resources.ResourceManager.GetEffectiveProvider(), Resources.TimerTimeExpiredFormatString, - TimeExpired.ToNaturalString()); + TimeExpired.ToNaturalString(compact)); } #endregion diff --git a/Hourglass/Timing/TimerOptions.cs b/Hourglass/Timing/TimerOptions.cs index 1e84fad..36d57d0 100644 --- a/Hourglass/Timing/TimerOptions.cs +++ b/Hourglass/Timing/TimerOptions.cs @@ -160,6 +160,11 @@ public sealed class TimerOptions : INotifyPropertyChanged /// </summary> private bool _lockInterface; + /// <summary> + /// A value indicating whether to display time in the digital clock format. + /// </summary> + private bool _digitalClockTime; + #endregion #region Constructors @@ -175,6 +180,7 @@ public TimerOptions() _showProgressInTaskbar = true; _doNotKeepComputerAwake = false; _reverseProgressBar = false; + _digitalClockTime = false; _showTimeElapsed = false; _loopTimer = false; _popUpWhenExpired = true; @@ -335,6 +341,25 @@ public bool ReverseProgressBar } } + /// <summary> + /// Gets or sets a value indicating whether to display time in the digital clock format. + /// </summary> + public bool DigitalClockTime + { + get => _digitalClockTime; + + set + { + if (_digitalClockTime == value) + { + return; + } + + _digitalClockTime = value; + PropertyChanged.Notify(this); + } + } + /// <summary> /// Gets or sets a value indicating whether to show the time elapsed rather than the time left. /// </summary> @@ -592,6 +617,7 @@ public void Set(TimerOptions options) _showProgressInTaskbar = options._showProgressInTaskbar; _doNotKeepComputerAwake = options._doNotKeepComputerAwake; _reverseProgressBar = options._reverseProgressBar; + _digitalClockTime = options._digitalClockTime; _showTimeElapsed = options._showTimeElapsed; _loopTimer = options._loopTimer; _popUpWhenExpired = options._popUpWhenExpired; @@ -613,6 +639,7 @@ public void Set(TimerOptions options) nameof(ShowProgressInTaskbar), nameof(DoNotKeepComputerAwake), nameof(ReverseProgressBar), + nameof(DigitalClockTime), nameof(ShowTimeElapsed), nameof(LoopTimer), nameof(PopUpWhenExpired), @@ -641,6 +668,7 @@ public void Set(TimerOptionsInfo info) _showProgressInTaskbar = info.ShowProgressInTaskbar; _doNotKeepComputerAwake = info.DoNotKeepComputerAwake; _reverseProgressBar = info.ReverseProgressBar; + _digitalClockTime = info.DigitalClockTime; _showTimeElapsed = info.ShowTimeElapsed; _loopTimer = info.LoopTimer; _popUpWhenExpired = info.PopUpWhenExpired; @@ -662,6 +690,7 @@ public void Set(TimerOptionsInfo info) nameof(ShowProgressInTaskbar), nameof(DoNotKeepComputerAwake), nameof(ReverseProgressBar), + nameof(DigitalClockTime), nameof(ShowTimeElapsed), nameof(LoopTimer), nameof(PopUpWhenExpired), @@ -687,6 +716,7 @@ public TimerOptionsInfo ToTimerOptionsInfo() ShowProgressInTaskbar = _showProgressInTaskbar, DoNotKeepComputerAwake = _doNotKeepComputerAwake, ReverseProgressBar = _reverseProgressBar, + DigitalClockTime = _digitalClockTime, ShowTimeElapsed = _showTimeElapsed, LoopTimer = _loopTimer, PopUpWhenExpired = _popUpWhenExpired, diff --git a/Hourglass/Windows/ContextMenu.cs b/Hourglass/Windows/ContextMenu.cs index 16328c5..63e6e88 100644 --- a/Hourglass/Windows/ContextMenu.cs +++ b/Hourglass/Windows/ContextMenu.cs @@ -151,6 +151,11 @@ public sealed class ContextMenu : System.Windows.Controls.ContextMenu /// </summary> private MenuItem _openSavedTimersOnStartupMenuItem; + /// <summary> + /// The "Display time in the digital clock format" <see cref="MenuItem"/>. + /// </summary> + private MenuItem _digitalClockTimeMenuItem; + /// <summary> /// The "Prefer 24-hour time when parsing" <see cref="MenuItem"/>. /// </summary> @@ -380,6 +385,9 @@ private void UpdateMenuFromOptions() // Prefer 24-hour time when parsing _prefer24HourTimeMenuItem.IsChecked = Settings.Default.Prefer24HourTime; + // Display time in the digital clock format + _digitalClockTimeMenuItem.IsChecked = _timerWindow.Options.DigitalClockTime; + // Reverse progress bar _reverseProgressBarMenuItem.IsChecked = _timerWindow.Options.ReverseProgressBar; @@ -457,9 +465,15 @@ private void UpdateOptionsFromMenu() // Prefer 24-hour time when parsing Settings.Default.Prefer24HourTime = _prefer24HourTimeMenuItem.IsChecked; + // Display time in the digital clock format + Settings.Default.DigitalClockTime = _digitalClockTimeMenuItem.IsChecked; + // Reverse progress bar _timerWindow.Options.ReverseProgressBar = _reverseProgressBarMenuItem.IsChecked; + // Display time in the digital clock format + _timerWindow.Options.DigitalClockTime = _digitalClockTimeMenuItem.IsChecked; + // Show time elapsed _timerWindow.Options.ShowTimeElapsed = _showTimeElapsedMenuItem.IsChecked; @@ -757,14 +771,34 @@ private void BuildMenu() }; Items.Add(advancedOptionsMenuItem); - // Do not keep computer awake - _doNotKeepComputerAwakeMenuItem = new() + // Display time in the digital clock format + _digitalClockTimeMenuItem = new() { - Header = Properties.Resources.ContextMenuDoNotKeepComputerAwakeMenuItem, + Header = Properties.Resources.ContextMenuDigitalClockTimeMenuItem, IsCheckable = true }; - _doNotKeepComputerAwakeMenuItem.Click += CheckableMenuItemClick; - advancedOptionsMenuItem.Items.Add(_doNotKeepComputerAwakeMenuItem); + _digitalClockTimeMenuItem.Click += CheckableMenuItemClick; + advancedOptionsMenuItem.Items.Add(_digitalClockTimeMenuItem); + + // Show time elapsed + _showTimeElapsedMenuItem = new() + { + Header = Properties.Resources.ContextMenuShowTimeElapsedMenuItem, + IsCheckable = true + }; + _showTimeElapsedMenuItem.Click += CheckableMenuItemClick; + advancedOptionsMenuItem.Items.Add(_showTimeElapsedMenuItem); + + // Reverse progress bar + _reverseProgressBarMenuItem = new() + { + Header = Properties.Resources.ContextMenuReverseProgressBarMenuItem, + IsCheckable = true + }; + _reverseProgressBarMenuItem.Click += CheckableMenuItemClick; + advancedOptionsMenuItem.Items.Add(_reverseProgressBarMenuItem); + + advancedOptionsMenuItem.Items.Add(new Separator()); // Open saved timers on startup _openSavedTimersOnStartupMenuItem = new() @@ -784,23 +818,16 @@ private void BuildMenu() _prefer24HourTimeMenuItem.Click += CheckableMenuItemClick; advancedOptionsMenuItem.Items.Add(_prefer24HourTimeMenuItem); - // Reverse progress bar - _reverseProgressBarMenuItem = new() - { - Header = Properties.Resources.ContextMenuReverseProgressBarMenuItem, - IsCheckable = true - }; - _reverseProgressBarMenuItem.Click += CheckableMenuItemClick; - advancedOptionsMenuItem.Items.Add(_reverseProgressBarMenuItem); + advancedOptionsMenuItem.Items.Add(new Separator()); - // Show time elapsed - _showTimeElapsedMenuItem = new() + // Do not keep computer awake + _doNotKeepComputerAwakeMenuItem = new() { - Header = Properties.Resources.ContextMenuShowTimeElapsedMenuItem, + Header = Properties.Resources.ContextMenuDoNotKeepComputerAwakeMenuItem, IsCheckable = true }; - _showTimeElapsedMenuItem.Click += CheckableMenuItemClick; - advancedOptionsMenuItem.Items.Add(_showTimeElapsedMenuItem); + _doNotKeepComputerAwakeMenuItem.Click += CheckableMenuItemClick; + advancedOptionsMenuItem.Items.Add(_doNotKeepComputerAwakeMenuItem); // Shut down when expired _shutDownWhenExpiredMenuItem = new() @@ -1375,15 +1402,15 @@ private void ThemeMenuItemClick(object sender, RoutedEventArgs e) private void ManageThemesMenuItemClick(object sender, RoutedEventArgs e) { ThemeManagerWindow window = Application.Current.Windows.OfType<ThemeManagerWindow>().FirstOrDefault(); - if (window is not null) + if (window is null) { - window.SetTimerWindow(_timerWindow); - window.BringToFrontAndActivate(); + window = new(_timerWindow); + window.Show(); } else { - window = new(_timerWindow); - window.Show(); + window.SetTimerWindow(_timerWindow); + window.BringToFrontAndActivate(); } } diff --git a/Hourglass/Windows/InterfaceScaler.cs b/Hourglass/Windows/InterfaceScaler.cs index e4a8914..74092da 100644 --- a/Hourglass/Windows/InterfaceScaler.cs +++ b/Hourglass/Windows/InterfaceScaler.cs @@ -82,7 +82,7 @@ public sealed class InterfaceScaler /// <summary> /// The default width of a <see cref="_timerWindow"/>. /// </summary> - public const double BaseWindowWidth = 370; + public const double BaseWindowWidth = 355; /// <summary> /// The default height of a <see cref="_timerWindow"/>. @@ -92,7 +92,7 @@ public sealed class InterfaceScaler /// <summary> /// The reduction factor that relates the base scale factor with the reduced scale factor. /// </summary> - private const double ReductionFactor = 0.5; + private const double ReductionFactor = 0.55; /// <summary> /// A <see cref="TimerWindow"/>. diff --git a/Hourglass/Windows/NotificationAreaIcon.cs b/Hourglass/Windows/NotificationAreaIcon.cs index 0fd8f73..6acf300 100644 --- a/Hourglass/Windows/NotificationAreaIcon.cs +++ b/Hourglass/Windows/NotificationAreaIcon.cs @@ -135,12 +135,14 @@ protected virtual void Dispose(bool disposing) /// </summary> private void RestoreAllTimerWindows() { - if (Application.Current is not null) + if (Application.Current is null) { - foreach (TimerWindow window in Application.Current.Windows.OfType<TimerWindow>().ArrangeDescending()) - { - window.BringToFrontAndActivate(); - } + return; + } + + foreach (TimerWindow window in Application.Current.Windows.OfType<TimerWindow>().ArrangeDescending()) + { + window.BringToFrontAndActivate(); } } @@ -149,12 +151,14 @@ private void RestoreAllTimerWindows() /// </summary> private void RestoreAllExpiredTimerWindows() { - if (Application.Current is not null) + if (Application.Current is null) { - foreach (TimerWindow window in Application.Current.Windows.OfType<TimerWindow>().Where(static w => w.Timer.State == TimerState.Expired)) - { - window.BringToFrontAndActivate(); - } + return; + } + + foreach (TimerWindow window in Application.Current.Windows.OfType<TimerWindow>().Where(static w => w.Timer.State == TimerState.Expired)) + { + window.BringToFrontAndActivate(); } } diff --git a/Hourglass/Windows/TimerWindow.xaml.cs b/Hourglass/Windows/TimerWindow.xaml.cs index 0213475..222c5e8 100644 --- a/Hourglass/Windows/TimerWindow.xaml.cs +++ b/Hourglass/Windows/TimerWindow.xaml.cs @@ -619,7 +619,7 @@ public override string ToString() string title = TitleTextBox.Text; string format = string.IsNullOrWhiteSpace(title) - ? Properties.Resources.TimerWindwoNewTimerFormatString + ? Properties.Resources.TimerWindowNewTimerFormatString : Properties.Resources.TimerWindowNewTimerWithTitleFormatString; return string.Format(format, input, title); diff --git a/Hourglass/Windows/UsageDialog.xaml.cs b/Hourglass/Windows/UsageDialog.xaml.cs index dd9281b..c0e8c4a 100644 --- a/Hourglass/Windows/UsageDialog.xaml.cs +++ b/Hourglass/Windows/UsageDialog.xaml.cs @@ -56,13 +56,13 @@ public static void ShowOrActivate(string errorMessage = null) ErrorMessage = errorMessage }; - if (Application.Current?.Dispatcher is not null) + if (Application.Current?.Dispatcher is null) { - _instance.Show(); + _instance.ShowDialog(); } else { - _instance.ShowDialog(); + _instance.Show(); } } diff --git a/Hourglass/Windows/WindowSize.cs b/Hourglass/Windows/WindowSize.cs index 7bba993..eb509e8 100644 --- a/Hourglass/Windows/WindowSize.cs +++ b/Hourglass/Windows/WindowSize.cs @@ -6,6 +6,7 @@ namespace Hourglass.Windows; +using System; using System.Linq; using System.Windows; @@ -143,19 +144,19 @@ public static WindowSize FromWindowOfType<T>() T window = Application.Current.Windows .OfType<T>() - .LastOrDefault(w => w.IsVisible); + .LastOrDefault(static w => w.IsVisible); return FromWindow(window); } /// <summary> - /// Returns a <see cref="WindowSize"/> for another visible window of the same type, or <c>null</c> if there is - /// no other visible window of the same type. + /// Returns a <see cref="WindowSize"/> for another visible or last window of the same type, or <c>null</c> if there is + /// no other visible or last window of the same type. /// </summary> /// <typeparam name="T">The type of the window.</typeparam> /// <param name="window">A window.</param> - /// <returns>A <see cref="WindowSize"/> for another visible window of the same type, or <c>null</c> if there is - /// no other visible window of the same type.</returns> + /// <returns>A <see cref="WindowSize"/> for another visible or last window of the same type, or <c>null</c> if there is + /// no other visible or last window of the same type.</returns> public static WindowSize FromSiblingOfWindow<T>(T window) where T : Window, IRestorableWindow { @@ -164,11 +165,12 @@ public static WindowSize FromSiblingOfWindow<T>(T window) return null; } - T otherWindow = Application.Current.Windows + T[] otherWindows = Application.Current.Windows .OfType<T>() - .LastOrDefault(w => !w.Equals(window) && w.IsVisible); + .Where(w => !ReferenceEquals(w, window)) + .ToArray(); - return FromWindow(otherWindow); + return FromWindow(Array.FindLast(otherWindows, static w => w.IsVisible) ?? otherWindows.LastOrDefault()); } /// <summary> diff --git a/README.md b/README.md index f57cad8..feaa645 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # Hourglass +[![Latest build](https://github.com/i2van/hourglass/workflows/build/badge.svg)](https://github.com/i2van/hourglass/actions) +[![Latest release](https://img.shields.io/github/downloads/i2van/hourglass/total.svg)](https://github.com/i2van/hourglass/releases/latest) +[![License](https://img.shields.io/badge/license-MIT-yellow)](https://opensource.org/licenses/MIT) + This project is the modified [simple countdown timer for Windows](https://github.com/dziemborowicz/hourglass). The changes were made to the original **Hourglass** can be found [here](#hourglass-changes). The latest **Hourglass** installer or portable distribution can be downloaded [here](https://github.com/i2van/hourglass/releases/latest). Visit the [original Hourglass site](https://chris.dziemborowicz.com/apps/hourglass) to learn more. **Hourglass** FAQ can be found [here](https://chris.dziemborowicz.com/apps/hourglass/#downloads). @@ -7,7 +11,7 @@ Visit the [original Hourglass site](https://chris.dziemborowicz.com/apps/hourgla ## Hourglass Command-line Example ```shell -hourglass -n on -a on -g on -w minimized -i left+title -t "Timer 1" 1h23 +hourglass -n on -a on -g on -c on -w minimized -i left+title -t "Timer 1" 1h23 ``` creates @@ -18,10 +22,11 @@ creates - with the timer window: - which is always on top: `-a on` - shows the reversed progress bar: `-g on` + - displays time in the digital clock format: `-c on` - initially minimized: `-w minimized` - has the time left and the timer name displayed in the title: `-i left+title` -Run `hourglass -h` to display the **Hourglass** command-line reference or select **Command-line usage** from notification area context menu. +Run `hourglass -h` to display the **Hourglass** [command-line reference](https://github.com/i2van/hourglass/blob/develop/Hourglass/Resources/Usage.txt) or select **Command-line usage** from the notification area context menu. ## Prerequisites @@ -56,14 +61,15 @@ Run `hourglass -h` to display the **Hourglass** command-line reference or select - All the timer windows are arranged by the time left. The order of timer windows is new, expired, paused, running. - When the timer window is minimized or closed the next visible non-minimized timer window is activated. - The **Window title** submenu is available directly from the timer window context menu. -- The **Reset bounds** submenu sets the timer window default position and size. +- The **Reset bounds** menu item sets the timer window default position and size. - The **Restore**, **Minimize** and **Maximize** timer window commands are always present in the timer window context menu. - All the timer window commands are available in the timer window context menu. - Shortcuts are displayed in the timer window context menu. - The progress bar changes direction to vertical when the height is more than the width and vice versa. - The switching between light and dark themes is improved. +- The **Display time in the digital clock format** menu item toggles the displayed time digital clock time format. It can be found under the **Advanced options** submenu of the timer window context menu. Command-line option is `--digital-clock-time` / `-c` -### Mics +### Misc - The **Hourglass** is built deterministically using the [GitHub Actions](https://github.com/i2van/hourglass/actions). diff --git a/latest.xml b/latest.xml index 74e9c1c..f68efb1 100644 --- a/latest.xml +++ b/latest.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> <UpdateInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> - <LatestVersion>1.15.14.0</LatestVersion> + <LatestVersion>1.15.15.0</LatestVersion> <UpdateUrl>https://github.com/i2van/hourglass/releases/latest</UpdateUrl> </UpdateInfo> \ No newline at end of file