Skip to content

Commit

Permalink
## Hourglass Changes
Browse files Browse the repository at this point in the history
### Installer

- Adds the **Hourglass** executable path to the Windows App Paths, so the **Hourglass** command-line is available out of the box.

### Command-line

- New command `pause`
- New command `resume`
- New option `--digital-clock-time` / `-c`
- New option `--activate-next` / `-an`
- New option `--multi-timers` / `-mt`

### UI

- Uses a Windows Task Dialog instead of a message box.
- The **Command-line usage** dialog can be accessed from the **About** dialog and vice versa.

### Notification Area

- The double click shows/hides all the timer windows.
- All the timers are arranged by the time left. The order of the timers is new, expired, paused, running.
- The **Exit** menu command asks to close all the running timer windows if the **Prompt on exit** option is on.
- The **Pause all** menu command pauses all the running timers.
- The **Resume all** menu command resumes all the paused timers.

### Timer Windows

- The `Esc` shortcut minimizes the timer window.
- The `F11` shortcut makes the timer window full screen and back.
- The `Ctrl+N` shortcut creates a new timer window.
- The mouse double-click on progress border makes the timer window full screen and back.
- The minimum timer window size is limited by the Windows.
- The timer tooltip is shown if the timer window size is too small.
- All the timer windows are arranged by the time left. The order of the 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** menu command 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 **Advanced options** / **Display time in the digital clock format** timer window context menu option toggles the displayed time digital clock time format. The command-line option is `--digital-clock-time` / `-c`
- The **Advanced options** / **Activate next window when minimized or closed** timer window context menu option enables the next timer window activation when the current timer window is minimized or closed. The command-line option is `--activate-next` / `-an`
- The timer window pops up on the current virtual desktop.
- The sound file can be placed to the `%LOCALAPPDATA%\Hourglass` or `%LOCALAPPDATA%\Hourglass\Sounds` directories. It can be the symbolic link created by the `mklink` system command.
- The **Pause all** timer window context menu command pauses all the running timers. Command-line command is `pause`
- The **Resume all** timer window context menu command resumes all the paused timers. Command-line command is `resume`

### Misc

- The **Hourglass** is built deterministically using the GitHub Actions.
  • Loading branch information
i2van committed Feb 20, 2024
1 parent 65962c2 commit 2680573
Show file tree
Hide file tree
Showing 10 changed files with 102 additions and 48 deletions.
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<Project>
<PropertyGroup>
<Version>1.15.22.0</Version>
<Version>1.15.23.0</Version>
</PropertyGroup>
</Project>
2 changes: 1 addition & 1 deletion Hourglass.Bundle/Bundle.wxs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:bal="http://schemas.microsoft.com/wix/BalExtension">
<Bundle Name="Hourglass"
Version="1.15.22.0"
Version="1.15.23.0"
Manufacturer="2021 Chris Dziemborowicz, 2024 Ivan Ivon"
UpgradeCode="f1d002c9-cfc9-40fb-84af-96e7aec26e0b"
IconSourceFile="$(var.Hourglass.ProjectDir)Resources\AppIcon.ico">
Expand Down
2 changes: 1 addition & 1 deletion Hourglass.Setup/Product.wxs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:netfx="http://schemas.microsoft.com/wix/NetFxExtension">
<Product Id="*" Name="Hourglass" Language="1033" Version="1.15.22.0" Manufacturer="Chris Dziemborowicz, Ivan Ivon" UpgradeCode="172d3713-8820-4374-8195-3e2374e7724f">
<Product Id="*" Name="Hourglass" Language="1033" Version="1.15.23.0" Manufacturer="Chris Dziemborowicz, Ivan Ivon" UpgradeCode="172d3713-8820-4374-8195-3e2374e7724f">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine"/>

<Icon Id="AppIcon.exe" SourceFile="$(var.Hourglass.ProjectDir)Resources\AppIcon.ico"/>
Expand Down
23 changes: 16 additions & 7 deletions Hourglass/AppEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
namespace Hourglass;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Media.Animation;
Expand Down Expand Up @@ -116,11 +117,11 @@ private static void ShowTimerWindowsForArguments(CommandLineArguments arguments)
arguments.OpenSavedTimers && TimerManager.Instance.ResumableTimers.Any())
{
ShowSavedTimerWindows(arguments);
ShowNewTimerWindowWithTimerStart();
ShowNewTimerWindows();
}
else
{
ShowNewTimerWindowWithTimerStart(arguments is { PauseAll: false, ResumeAll: false });
ShowNewTimerWindows(arguments is { PauseAll: false, ResumeAll: false });
}

if (arguments.PauseAll)
Expand All @@ -133,11 +134,18 @@ private static void ShowTimerWindowsForArguments(CommandLineArguments arguments)
TimerManager.ResumeAll();
}

void ShowNewTimerWindowWithTimerStart(bool force = false)
void ShowNewTimerWindows(bool forceNew = false)
{
if (arguments.TimerStart is not null || force)
IEnumerable<TimerStart?> timerStarts = arguments.TimerStart;

if (forceNew)
{
timerStarts = timerStarts.DefaultIfEmpty(null);
}

foreach (TimerStart? timerStart in timerStarts)
{
ShowNewTimerWindow(arguments);
ShowNewTimerWindow(arguments, timerStart);
}
}
}
Expand All @@ -147,9 +155,10 @@ void ShowNewTimerWindowWithTimerStart(bool force = false)
/// cref="CommandLineArguments"/>, or it will display in input mode if there is no <see cref="TimerStart"/>.
/// </summary>
/// <param name="arguments">Parsed command-line arguments.</param>
private static void ShowNewTimerWindow(CommandLineArguments arguments)
/// <param name="timerStart">Timer start.</param>
private static void ShowNewTimerWindow(CommandLineArguments arguments, TimerStart? timerStart)
{
TimerWindow window = new(arguments.TimerStart);
TimerWindow window = new(timerStart);
window.Options.Set(arguments.GetTimerOptions());
window.Restore(arguments.GetWindowSize(), RestoreOptions.AllowMinimized);
window.Show();
Expand Down
101 changes: 67 additions & 34 deletions Hourglass/CommandLineArguments.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace Hourglass;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Windows;
Expand Down Expand Up @@ -55,10 +56,9 @@ public static string Usage
public bool ShouldShowUsage { get; private set; }

/// <summary>
/// Gets a <see cref="TimerStart"/>, or <c>null</c> if no <see cref="TimerStart"/> was specified on the command
/// line.
/// Gets a <see cref="TimerStart"/> values, or empty if no time was specified on the command line.
/// </summary>
public TimerStart? TimerStart { get; private set; }
public IEnumerable<TimerStart> TimerStart { get; private set; } = [];

/// <summary>
/// Gets a user-specified title for the timer.
Expand Down Expand Up @@ -178,6 +178,11 @@ public static string Usage
/// </summary>
public Rect WindowBounds { get; private set; }

/// <summary>
/// Gets a value indicating whether space separated timer command-line arguments should be processed individually.
/// </summary>
public bool MultiTimers { get; private set; }

/// <summary>
/// Gets a value indicating whether the user interface should be locked, preventing the user from taking any
/// action until the timer expires.
Expand Down Expand Up @@ -391,6 +396,26 @@ private static CommandLineArguments GetCommandLineArguments(IEnumerable<string>

switch (arg)
{
// Commands.

case "pause":
ThrowIfDuplicateSwitch(specifiedSwitches, "pause");
ThrowIfDuplicateSwitch(specifiedSwitches, "resume");

argumentsBasedOnMostRecentOptions.PauseAll = true;
argumentsBasedOnFactoryDefaults.PauseAll = true;
break;

case "resume":
ThrowIfDuplicateSwitch(specifiedSwitches, "resume");
ThrowIfDuplicateSwitch(specifiedSwitches, "pause");

argumentsBasedOnMostRecentOptions.ResumeAll = true;
argumentsBasedOnFactoryDefaults.ResumeAll = true;
break;

// Options.

case "--title":
case "-t":
case "/t":
Expand Down Expand Up @@ -709,6 +734,19 @@ private static CommandLineArguments GetCommandLineArguments(IEnumerable<string>
argumentsBasedOnFactoryDefaults.WindowBounds = argumentsBasedOnFactoryDefaults.WindowBounds.Merge(windowBounds);
break;

case "--multi-timers":
case "-mt":
case "/mt":
ThrowIfDuplicateSwitch(specifiedSwitches, "--multi-timers");

bool multiTimers = GetBoolValue(
arg,
remainingArgs);

argumentsBasedOnMostRecentOptions.MultiTimers = multiTimers;
argumentsBasedOnFactoryDefaults.MultiTimers = multiTimers;
break;

case "--lock-interface":
case "-z":
case "/z":
Expand All @@ -722,22 +760,6 @@ private static CommandLineArguments GetCommandLineArguments(IEnumerable<string>
argumentsBasedOnFactoryDefaults.LockInterface = lockInterface;
break;

case "pause":
ThrowIfDuplicateSwitch(specifiedSwitches, "pause");
ThrowIfDuplicateSwitch(specifiedSwitches, "resume");

argumentsBasedOnMostRecentOptions.PauseAll = true;
argumentsBasedOnFactoryDefaults.PauseAll = true;
break;

case "resume":
ThrowIfDuplicateSwitch(specifiedSwitches, "resume");
ThrowIfDuplicateSwitch(specifiedSwitches, "pause");

argumentsBasedOnMostRecentOptions.ResumeAll = true;
argumentsBasedOnFactoryDefaults.ResumeAll = true;
break;

case "--use-factory-defaults":
case "-d":
case "/d":
Expand Down Expand Up @@ -771,7 +793,7 @@ private static CommandLineArguments GetCommandLineArguments(IEnumerable<string>
List<string> inputArgs = [arg, ..remainingArgs];
remainingArgs.Clear();

TimerStart timerStart = GetTimerStartValue(inputArgs);
IEnumerable<TimerStart> timerStart = GetTimerStartValue(inputArgs, argumentsBasedOnMostRecentOptions.MultiTimers).ToList();

argumentsBasedOnMostRecentOptions.TimerStart = timerStart;
argumentsBasedOnFactoryDefaults.TimerStart = timerStart;
Expand Down Expand Up @@ -1113,26 +1135,37 @@ private static Rect GetRectValue(string arg, Queue<string> remainingArgs, Rect l
/// is not a valid representation of a <see cref="TimerStart"/>.
/// </summary>
/// <param name="remainingArgs">The unparsed arguments.</param>
/// <returns>The <see cref="TimerStart"/> value corresponding to the concatenation of all <paramref
/// name="remainingArgs"/></returns>
/// <param name="multiTimers">Treat each timer argument as a separate timer.</param>
/// <returns>
/// The <see cref="TimerStart"/> value corresponding to the concatenation of all <paramref name="remainingArgs"/>
/// or individual timer values if <paramref name="multiTimers"/> if <c>true</c>.
/// </returns>
/// <exception cref="ParseException">If the concatenation of all <paramref name="remainingArgs"/> is not a
/// valid representation of a <see cref="TimerStart"/>.</exception>
private static TimerStart GetTimerStartValue(IEnumerable<string> remainingArgs)
private static IEnumerable<TimerStart> GetTimerStartValue(IEnumerable<string> remainingArgs, bool multiTimers)
{
string value = string.Join(" ", remainingArgs);
TimerStart? timerStart = TimerStart.FromString(value);
if (!multiTimers)
{
string value = string.Join(" ", remainingArgs);
remainingArgs = [ value ];
}

if (timerStart is null)
foreach (string arg in remainingArgs)
{
string message = string.Format(
Resources.ResourceManager.GetEffectiveProvider(),
Resources.CommandLineArgumentsParseExceptionInvalidTimerInputFormatString,
value);
TimerStart? timerStart = Timing.TimerStart.FromString(arg);

throw new ParseException(message);
}
if (timerStart is null)
{
string message = string.Format(
Resources.ResourceManager.GetEffectiveProvider(),
Resources.CommandLineArgumentsParseExceptionInvalidTimerInputFormatString,
arg);

return timerStart;
throw new ParseException(message);
}

yield return timerStart;
}
}

/// <summary>
Expand Down Expand Up @@ -1203,7 +1236,7 @@ public ParseException(string message)
/// Initializes a new instance of the <see cref="ParseException"/> class.
/// </summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (<see langword="Nothing" /> in Visual Basic) if no inner exception is specified.</param>
/// <param name="innerException">The exception that is the cause of the current exception, or a <c>null</c> reference if no inner exception is specified.</param>
public ParseException(string message, Exception innerException)
: base(message, innerException)
{
Expand Down
4 changes: 2 additions & 2 deletions Hourglass/Managers/TimerManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,14 +141,14 @@ public void ClearResumableTimers()
/// <summary>
/// Checks whether at least one timer can be paused.
/// </summary>
/// <returns><see langword="true" /> if at least one timer can be paused.</returns>
/// <returns><c>true</c> if at least one timer can be paused.</returns>
public static bool CanPauseAll() =>
GetPausableTimers(TimerState.Running).Any();

/// <summary>
/// Checks whether at least one timer can be resumed.
/// </summary>
/// <returns><see langword="true" /> if at least one timer can be resumed.</returns>
/// <returns><c>true</c> if at least one timer can be resumed.</returns>
public static bool CanResumeAll() =>
GetPausableTimers(TimerState.Paused).Any();

Expand Down
2 changes: 1 addition & 1 deletion Hourglass/Properties/App.manifest
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<assemblyIdentity version="1.15.22.0" name="Hourglass"/>
<assemblyIdentity version="1.15.23.0" name="Hourglass"/>
<description>The simple countdown timer for Windows.</description>
<dependency>
<dependentAssembly>
Expand Down
11 changes: 11 additions & 0 deletions Hourglass/Resources/Usage.txt
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,16 @@ Options:
Default value last
Alias -u, /u

--multi-timers on|off
Treats each <input> command-line argument as an individual timer.

This option is never remembered. It must be specified as a command-line
argument each time the timer is started.

Required no
Default value off
Alias -mt, /mt

--loop-timer on|off|last
Loops the timer when it expires.

Expand Down Expand Up @@ -294,6 +304,7 @@ Options:
--activate-next -an on
--digital-clock-time -c off
--show-time-elapsed -u off
--multi-timers -mt off
--loop-timer -l off
--pop-up-when-expired -p on
--close-when-expired -e off
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Run `hourglass -h` to display the **Hourglass** [command-line reference](https:/
- New command `resume`
- New option `--digital-clock-time` / `-c`
- New option `--activate-next` / `-an`
- New option `--multi-timers` / `-mt`

See [usage](https://github.com/i2van/hourglass/blob/develop/Hourglass/Resources/Usage.txt) for details.

Expand Down
2 changes: 1 addition & 1 deletion latest.xml
Original file line number Diff line number Diff line change
@@ -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.22.0</LatestVersion>
<LatestVersion>1.15.23.0</LatestVersion>
<UpdateUrl>https://github.com/i2van/hourglass/releases/latest</UpdateUrl>
</UpdateInfo>

0 comments on commit 2680573

Please sign in to comment.