diff --git a/Hourglass.Bundle/Bundle.wxs b/Hourglass.Bundle/Bundle.wxs index b96e2d1..38d29fa 100644 --- a/Hourglass.Bundle/Bundle.wxs +++ b/Hourglass.Bundle/Bundle.wxs @@ -1,7 +1,7 @@ - + diff --git a/Hourglass.Setup/Product.wxs b/Hourglass.Setup/Product.wxs index cd1f9c0..45a11bb 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 a240fad..7445d7a 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.5.0.0")] -[assembly: AssemblyFileVersion("1.5.0.0")] +[assembly: AssemblyVersion("1.6.0.0")] +[assembly: AssemblyFileVersion("1.6.0.0")] diff --git a/Hourglass/CommandLineArguments.cs b/Hourglass/CommandLineArguments.cs index 76117fd..b387e58 100644 --- a/Hourglass/CommandLineArguments.cs +++ b/Hourglass/CommandLineArguments.cs @@ -119,9 +119,9 @@ public static string Usage public bool ShutDownWhenExpired { get; private set; } /// - /// Gets the color of the timer progress bar. + /// Gets the theme of the timer window. /// - public Color Color { get; private set; } + public Theme Theme { get; private set; } /// /// Gets the sound to play when the timer expires, or null if no sound is to be played. @@ -139,6 +139,11 @@ public static string Usage /// public bool OpenSavedTimers { get; private set; } + /// + /// Gets or sets a value indicating what information to display in the timer window title. + /// + public WindowTitleMode WindowTitleMode { get; set; } + /// /// Gets a value that indicates whether the timer window is restored, minimized, or maximized. /// @@ -215,9 +220,10 @@ public TimerOptions GetTimerOptions() PopUpWhenExpired = this.PopUpWhenExpired, CloseWhenExpired = this.CloseWhenExpired, ShutDownWhenExpired = this.ShutDownWhenExpired, - Color = this.Color, + Theme = this.Theme, Sound = this.Sound, LoopSound = this.LoopSound, + WindowTitleMode = this.WindowTitleMode, WindowSize = this.GetWindowSize() }; } @@ -261,10 +267,11 @@ private static CommandLineArguments GetArgumentsFromMostRecentOptions() PopUpWhenExpired = options.PopUpWhenExpired, CloseWhenExpired = options.CloseWhenExpired, ShutDownWhenExpired = options.ShutDownWhenExpired, - Color = options.Color, + Theme = options.Theme, Sound = options.Sound, LoopSound = options.LoopSound, OpenSavedTimers = Settings.Default.OpenSavedTimersOnStartup, + WindowTitleMode = options.WindowTitleMode, WindowState = windowSize.WindowState != WindowState.Minimized ? windowSize.WindowState : windowSize.RestoreWindowState, RestoreWindowState = windowSize.RestoreWindowState, WindowBounds = windowSize.RestoreBounds @@ -296,10 +303,11 @@ private static CommandLineArguments GetArgumentsFromFactoryDefaults() PopUpWhenExpired = defaultOptions.PopUpWhenExpired, CloseWhenExpired = defaultOptions.CloseWhenExpired, ShutDownWhenExpired = defaultOptions.ShutDownWhenExpired, - Color = defaultOptions.Color, + Theme = defaultOptions.Theme, Sound = defaultOptions.Sound, LoopSound = defaultOptions.LoopSound, OpenSavedTimers = false, + WindowTitleMode = WindowTitleMode.ApplicationName, WindowState = defaultOptions.WindowSize.WindowState, RestoreWindowState = defaultOptions.WindowSize.RestoreWindowState, WindowBounds = defaultWindowBoundsWithLocation @@ -476,17 +484,17 @@ private static CommandLineArguments GetCommandLineArguments(IEnumerable argumentsBasedOnFactoryDefaults.ShutDownWhenExpired = shutDownWhenExpired; break; - case "--color": - case "-c": - ThrowIfDuplicateSwitch(specifiedSwitches, "--color"); + case "--theme": + case "-m": + ThrowIfDuplicateSwitch(specifiedSwitches, "--theme"); - Color color = GetColorValue( + Theme theme = GetThemeValue( arg, remainingArgs, - argumentsBasedOnMostRecentOptions.Color); + argumentsBasedOnMostRecentOptions.Theme); - argumentsBasedOnMostRecentOptions.Color = color; - argumentsBasedOnFactoryDefaults.Color = color; + argumentsBasedOnMostRecentOptions.Theme = theme; + argumentsBasedOnFactoryDefaults.Theme = theme; break; case "--sound": @@ -528,17 +536,17 @@ private static CommandLineArguments GetCommandLineArguments(IEnumerable argumentsBasedOnFactoryDefaults.OpenSavedTimers = openSavedTimers; break; - case "--window-bounds": - case "-b": - ThrowIfDuplicateSwitch(specifiedSwitches, "--window-bounds"); + case "--window-title": + case "-i": + ThrowIfDuplicateSwitch(specifiedSwitches, "--window-title"); - Rect windowBounds = GetRectValue( + WindowTitleMode windowTitleMode = GetWindowTitleModeValue( arg, remainingArgs, - argumentsBasedOnMostRecentOptions.WindowBounds); + argumentsBasedOnMostRecentOptions.WindowTitleMode); - argumentsBasedOnMostRecentOptions.WindowBounds = argumentsBasedOnMostRecentOptions.WindowBounds.Merge(windowBounds); - argumentsBasedOnFactoryDefaults.WindowBounds = argumentsBasedOnFactoryDefaults.WindowBounds.Merge(windowBounds); + argumentsBasedOnMostRecentOptions.WindowTitleMode = windowTitleMode; + argumentsBasedOnFactoryDefaults.WindowTitleMode = windowTitleMode; break; case "--window-state": @@ -554,6 +562,19 @@ private static CommandLineArguments GetCommandLineArguments(IEnumerable argumentsBasedOnFactoryDefaults.WindowState = windowState; break; + case "--window-bounds": + case "-b": + ThrowIfDuplicateSwitch(specifiedSwitches, "--window-bounds"); + + Rect windowBounds = GetRectValue( + arg, + remainingArgs, + argumentsBasedOnMostRecentOptions.WindowBounds); + + argumentsBasedOnMostRecentOptions.WindowBounds = argumentsBasedOnMostRecentOptions.WindowBounds.Merge(windowBounds); + argumentsBasedOnFactoryDefaults.WindowBounds = argumentsBasedOnFactoryDefaults.WindowBounds.Merge(windowBounds); + break; + case "--use-factory-defaults": case "-d": ThrowIfDuplicateSwitch(specifiedSwitches, "--use-factory-defaults"); @@ -651,7 +672,7 @@ private static bool GetBoolValue(string arg, Queue remainingArgs) { string value = GetRequiredValue(arg, remainingArgs); - switch (value) + switch (value.ToLowerInvariant()) { case "on": return true; @@ -684,7 +705,7 @@ private static bool GetBoolValue(string arg, Queue remainingArgs, bool l { string value = GetRequiredValue(arg, remainingArgs); - switch (value) + switch (value.ToLowerInvariant()) { case "on": return true; @@ -707,48 +728,41 @@ private static bool GetBoolValue(string arg, Queue remainingArgs, bool l } /// - /// Returns the next value in , or throws an exception if + /// Returns the next value in , or throws an exception if /// is empty or the next argument is not "last" or a valid representation of a - /// . + /// . /// /// The name of the argument for which the value is to be returned. /// The unparsed arguments. /// The value of the argument returned when the user specifies "last". - /// The next value in + /// The next value in /// If is empty or the next argument is not - /// "last" or a valid representation of a . - private static Color GetColorValue(string arg, Queue remainingArgs, Color last) + /// "last" or a valid representation of a . + private static Theme GetThemeValue(string arg, Queue remainingArgs, Theme last) { string value = GetRequiredValue(arg, remainingArgs); - switch (value) + switch (value.ToLowerInvariant()) { case "last": return last; default: - Color color = ColorManager.Instance.GetColorByName(value, StringComparison.CurrentCultureIgnoreCase); + Theme theme = ThemeManager.Instance.GetThemeByIdentifier(value.ToLowerInvariant()) ?? + ThemeManager.Instance.GetThemeByName(value, StringComparison.CurrentCultureIgnoreCase); - if (color == null) + if (theme == null) { - try - { - color = new Color(value); - ColorManager.Instance.Add(color); - } - catch - { - string message = string.Format( - Resources.ResourceManager.GetEffectiveProvider(), - Resources.CommandLineArgumentsParseExceptionInvalidValueForSwitchFormatString, - arg, - value); + string message = string.Format( + Resources.ResourceManager.GetEffectiveProvider(), + Resources.CommandLineArgumentsParseExceptionInvalidValueForSwitchFormatString, + arg, + value); - throw new ParseException(message); - } + throw new ParseException(message); } - return color; + return theme; } } @@ -767,7 +781,7 @@ private static Sound GetSoundValue(string arg, Queue remainingArgs, Soun { string value = GetRequiredValue(arg, remainingArgs); - switch (value) + switch (value.ToLowerInvariant()) { case "none": return null; @@ -793,6 +807,49 @@ private static Sound GetSoundValue(string arg, Queue remainingArgs, Soun } } + /// + /// Returns the next value in , or throws an + /// exception if is empty or the next argument is not "app", "left", "elapsed", + /// "title", or "last". + /// + /// The name of the argument for which the value is to be returned. + /// The unparsed arguments. + /// The value of the argument returned when the user specifies "last". + /// The next value in . + /// If is empty or the next argument is not + /// "app", "left", "elapsed", "title", or "last". + private static WindowTitleMode GetWindowTitleModeValue(string arg, Queue remainingArgs, WindowTitleMode last) + { + string value = GetRequiredValue(arg, remainingArgs); + + switch (value.ToLowerInvariant()) + { + case "app": + return WindowTitleMode.ApplicationName; + + case "left": + return WindowTitleMode.TimeLeft; + + case "elapsed": + return WindowTitleMode.TimeElapsed; + + case "title": + return WindowTitleMode.TimerTitle; + + case "last": + return last; + + default: + string message = string.Format( + Resources.ResourceManager.GetEffectiveProvider(), + Resources.CommandLineArgumentsParseExceptionInvalidValueForSwitchFormatString, + arg, + value); + + throw new ParseException(message); + } + } + /// /// Returns the next value in , or throws an /// exception if is empty or the next argument is not "normal", "maximized", @@ -808,7 +865,7 @@ private static WindowState GetWindowStateValue(string arg, Queue remaini { string value = GetRequiredValue(arg, remainingArgs); - switch (value) + switch (value.ToLowerInvariant()) { case "normal": return WindowState.Normal; diff --git a/Hourglass/Extensions/ColorExtensions.cs b/Hourglass/Extensions/ColorExtensions.cs new file mode 100644 index 0000000..1c88229 --- /dev/null +++ b/Hourglass/Extensions/ColorExtensions.cs @@ -0,0 +1,44 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) Chris Dziemborowicz. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Hourglass.Extensions +{ + using System; + using System.Windows.Media; + + /// + /// Provides utility methods for the struct. + /// + public static class ColorExtensions + { + /// + /// Converts a to an representation. + /// + /// A . + /// An for . + public static int ToInt(this Color color) + { + return (color.R << 0) | (color.G << 8) | (color.B << 16); + } + + /// + /// Converts a representation of a into a . + /// + /// A representation of a . + /// A . + public static Color FromString(string colorString) + { + object color = ColorConverter.ConvertFromString(colorString); + + if (color == null) + { + throw new ArgumentException("colorString"); + } + + return (Color)color; + } + } +} diff --git a/Hourglass/Hourglass.csproj b/Hourglass/Hourglass.csproj index f492a23..287031d 100644 --- a/Hourglass/Hourglass.csproj +++ b/Hourglass/Hourglass.csproj @@ -87,6 +87,7 @@ + @@ -100,13 +101,13 @@ - + @@ -126,6 +127,8 @@ + + @@ -133,22 +136,27 @@ - + + + ColorControl.xaml + ErrorDialog.xaml - + + ThemeManagerWindow.xaml + TimerWindow.xaml @@ -158,10 +166,18 @@ + + Designer + MSBuild:Compile + Designer MSBuild:Compile + + Designer + MSBuild:Compile + Designer MSBuild:Compile diff --git a/Hourglass/Managers/AppManager.cs b/Hourglass/Managers/AppManager.cs index 913bf10..d18326b 100644 --- a/Hourglass/Managers/AppManager.cs +++ b/Hourglass/Managers/AppManager.cs @@ -26,7 +26,7 @@ public class AppManager : Manager KeepAwakeManager.Instance, WakeUpManager.Instance, NotificationAreaIconManager.Instance, - ColorManager.Instance, + ThemeManager.Instance, SoundManager.Instance, TimerStartManager.Instance, TimerOptionsManager.Instance, diff --git a/Hourglass/Managers/ColorManager.cs b/Hourglass/Managers/ColorManager.cs deleted file mode 100644 index 09b2eb4..0000000 --- a/Hourglass/Managers/ColorManager.cs +++ /dev/null @@ -1,283 +0,0 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) Chris Dziemborowicz. All rights reserved. -// -// -------------------------------------------------------------------------------------------------------------------- - -namespace Hourglass.Managers -{ - using System; - using System.Collections.Generic; - using System.Linq; - - using Hourglass.Properties; - using Hourglass.Timing; - - /// - /// Manages colors. - /// - public class ColorManager : Manager - { - /// - /// Singleton instance of the class. - /// - public static readonly ColorManager Instance = new ColorManager(); - - /// - /// A collection of colors. - /// - private readonly List colors = new List(); - - /// - /// Prevents a default instance of the class from being created. - /// - private ColorManager() - { - } - - /// - /// Gets the default color. - /// - public Color DefaultColor - { - get { return this.GetColorByIdentifier("resource:Default color"); } - } - - /// - /// Gets a collection of all colors. - /// - public IList AllColors - { - get { return this.colors.ToList(); } - } - - /// - /// Gets a collection of the colors stored in the assembly. - /// - public IList BuiltInColors - { - get { return this.colors.Where(c => c.IsBuiltIn).ToList(); } - } - - /// - /// Gets a collection of the colors defined by the user. - /// - public IList UserProvidedColors - { - get { return this.colors.Where(c => !c.IsBuiltIn).ToList(); } - } - - /// - /// Initializes the class. - /// - public override void Initialize() - { - this.colors.Clear(); - this.AddRange(this.GetBuiltInColors()); - this.AddRange(this.GetUserProvidedColors()); - } - - /// - /// Persists the state of the class. - /// - public override void Persist() - { - Settings.Default.UserProvidedColors = this.UserProvidedColors; - } - - /// - /// Adds a color. - /// - /// A . - public void Add(Color color) - { - if (this.GetColorByIdentifier(color.Identifier) == null) - { - this.colors.Add(color); - } - } - - /// - /// Adds the colors of the specified collection. - /// - /// A collection of s. - public void AddRange(IEnumerable collection) - { - foreach (Color color in collection) - { - this.Add(color); - } - } - - /// - /// Clears all . - /// - public void ClearUserProvidedColors() - { - this.colors.RemoveAll(c => !c.IsBuiltIn); - } - - /// - /// Returns the color for the specified identifier, or null if no such color is loaded. - /// - /// The identifier for the color. - /// The color for the specified identifier, or null if no such color is loaded. - public Color GetColorByIdentifier(string identifier) - { - if (string.IsNullOrEmpty(identifier)) - { - return null; - } - - return this.colors.FirstOrDefault(c => c.Identifier == identifier); - } - - /// - /// Returns the color for the specified identifier, or if no such color is loaded. - /// - /// The identifier for the color. - /// The color for the specified identifier, or if no such color is loaded. - /// - public Color GetColorOrDefaultByIdentifier(string identifier) - { - if (string.IsNullOrEmpty(identifier)) - { - return null; - } - - return this.GetColorByIdentifier(identifier) ?? this.DefaultColor; - } - - /// - /// Returns the first color for the specified name, or null if no such color is loaded. - /// - /// The name for the color. - /// One of the enumeration values that specifies how the strings will be - /// compared. - /// The first color for the specified name, or null if no such color is loaded. - public Color GetColorByName(string name, StringComparison stringComparison = StringComparison.Ordinal) - { - if (string.IsNullOrEmpty(name)) - { - return null; - } - - return this.colors.FirstOrDefault(c => string.Equals(c.Name, name, stringComparison)); - } - - /// - /// Returns the first color for the specified name, or if no such color is loaded. - /// - /// The name for the color. - /// One of the enumeration values that specifies how the strings will be - /// compared. - /// The first color for the specified name, or if no such color is loaded. - /// - public Color GetColorOrDefaultByName(string name, StringComparison stringComparison = StringComparison.Ordinal) - { - if (string.IsNullOrEmpty(name)) - { - return null; - } - - return this.GetColorByName(name, stringComparison) ?? this.DefaultColor; - } - - /// - /// Returns the first color for the specified , or null if no - /// such color is loaded. - /// - /// The for the color. - /// The first color for the specified , or null if no - /// such color is loaded. - public Color GetColorByMediaColor(System.Windows.Media.Color mediaColor) - { - return this.colors.FirstOrDefault(c => object.Equals(c.MediaColor, mediaColor)); - } - - /// - /// Returns the first color for the specified , or if no such color is loaded. - /// - /// The for the color. - /// The first color for the specified , or if no such color is loaded. - public Color GetColorOrDefaultByMediaColor(System.Windows.Media.Color mediaColor) - { - return this.GetColorByMediaColor(mediaColor) ?? this.DefaultColor; - } - - /// - /// Loads the collection of colors defined in the assembly. - /// - /// A collection of colors defined in the assembly. - private IList GetBuiltInColors() - { - return new List - { - new Color( - "#3665B3", - "Default color" /* invariantName */, - Resources.ColorManagerDefaultColor /* name */, - true /* isBuiltIn */), - - new Color( - "#C75050", - "Red" /* invariantName */, - Resources.ColorManagerRed /* name */, - true /* isBuiltIn */), - - new Color( - "#FF7F50", - "Orange" /* invariantName */, - Resources.ColorManagerOrange /* name */, - true /* isBuiltIn */), - - new Color( - "#FFC800", - "Yellow" /* invariantName */, - Resources.ColorManagerYellow /* name */, - true /* isBuiltIn */), - - new Color( - "#57A64A", - "Green" /* invariantName */, - Resources.ColorManagerGreen /* name */, - true /* isBuiltIn */), - - new Color( - "#3665B3", - "Blue" /* invariantName */, - Resources.ColorManagerBlue /* name */, - true /* isBuiltIn */), - - new Color( - "#843179", - "Purple" /* invariantName */, - Resources.ColorManagerPurple /* name */, - true /* isBuiltIn */), - - new Color( - "#666666", - "Gray" /* invariantName */, - Resources.ColorManagerGray /* name */, - true /* isBuiltIn */), - - new Color( - "#000000", - "Black" /* invariantName */, - Resources.ColorManagerBlack /* name */, - true /* isBuiltIn */) - }; - } - - /// - /// Loads the collection of colors defined by the user. - /// - /// A collection of sounds defined by the user. - private IList GetUserProvidedColors() - { - return Settings.Default.UserProvidedColors; - } - } -} diff --git a/Hourglass/Managers/ThemeManager.cs b/Hourglass/Managers/ThemeManager.cs new file mode 100644 index 0000000..c96805d --- /dev/null +++ b/Hourglass/Managers/ThemeManager.cs @@ -0,0 +1,513 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) Chris Dziemborowicz. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Hourglass.Managers +{ + using System; + using System.Collections.Generic; + using System.Linq; + + using Hourglass.Properties; + using Hourglass.Timing; + + /// + /// Manages themes. + /// + public class ThemeManager : Manager + { + /// + /// Singleton instance of the class. + /// + public static readonly ThemeManager Instance = new ThemeManager(); + + /// + /// A collection of themes. + /// + private readonly List themes = new List(); + + /// + /// Prevents a default instance of the class from being created. + /// + private ThemeManager() + { + } + + /// + /// Gets the default theme. + /// + public Theme DefaultTheme + { + get { return this.GetThemeByIdentifier("blue"); } + } + + /// + /// Gets the default dark theme. + /// + public Theme DefaultDarkTheme + { + get { return this.GetThemeByIdentifier("blue-dark"); } + } + + /// + /// Gets a collection of all themes. + /// + public IList AllThemes + { + get { return this.themes.ToList(); } + } + + /// + /// Gets a collection of the themes stored in the assembly. + /// + public IList BuiltInThemes + { + get { return this.themes.Where(t => t.Type != ThemeType.UserProvided).ToList(); } + } + + /// + /// Gets a collection of the light themes stored in the assembly. + /// + public IList BuiltInLightThemes + { + get { return this.themes.Where(t => t.Type == ThemeType.BuiltInLight).ToList(); } + } + + /// + /// Gets a collection of the dark themes stored in the assembly. + /// + public IList BuiltInDarkThemes + { + get { return this.themes.Where(t => t.Type == ThemeType.BuiltInDark).ToList(); } + } + + /// + /// Gets a collection of the themes defined by the user ordered by name. + /// + public IList UserProvidedThemes + { + get { return this.themes.Where(t => t.Type == ThemeType.UserProvided).OrderBy(t => t.Name).ToList(); } + } + + /// + /// Initializes the class. + /// + public override void Initialize() + { + this.themes.Clear(); + this.AddRange(this.GetBuiltInThemes()); + this.AddRange(this.GetUserProvidedThemes()); + } + + /// + /// Persists the state of the class. + /// + public override void Persist() + { + Settings.Default.UserProvidedThemes = this.UserProvidedThemes; + } + + /// + /// Adds a theme. + /// + /// A . + public void Add(Theme theme) + { + if (this.GetThemeByIdentifier(theme.Identifier) == null) + { + this.themes.Add(theme); + } + } + + /// + /// Adds the themes of the specified collection. + /// + /// A collection of s. + public void AddRange(IEnumerable collection) + { + foreach (Theme theme in collection) + { + this.Add(theme); + } + } + + /// + /// Adds a theme based on another theme. + /// + /// A . + /// The newly added theme. + public Theme AddThemeBasedOnTheme(Theme theme) + { + string identifier = Guid.NewGuid().ToString(); + string name = Resources.ThemeManagerNewTheme; + Theme newTheme = Theme.FromTheme(ThemeType.UserProvided, identifier, name, theme); + this.Add(newTheme); + return newTheme; + } + + /// + /// Returns the theme for the specified identifier, or null if no such theme is loaded. + /// + /// The identifier for the theme. + /// The theme for the specified identifier, or null if no such theme is loaded. + public Theme GetThemeByIdentifier(string identifier) + { + if (string.IsNullOrEmpty(identifier)) + { + return null; + } + + return this.themes.FirstOrDefault(t => t.Identifier == identifier); + } + + /// + /// Returns the theme for the specified identifier, or if no such theme is loaded. + /// + /// The identifier for the theme. + /// The theme for the specified identifier, or if no such theme is loaded. + /// + public Theme GetThemeOrDefaultByIdentifier(string identifier) + { + return this.GetThemeByIdentifier(identifier) ?? this.DefaultTheme; + } + + /// + /// Returns the first theme for the specified name, or null if no such theme is loaded. + /// + /// The name for the theme. + /// One of the enumeration values that specifies how the strings will be + /// compared. + /// The first theme for the specified name, or null if no such theme is loaded. + public Theme GetThemeByName(string name, StringComparison stringComparison = StringComparison.Ordinal) + { + if (string.IsNullOrEmpty(name)) + { + return null; + } + + return this.themes.FirstOrDefault(t => string.Equals(t.Name, name, stringComparison)); + } + + /// + /// Returns the first theme for the specified name, or if no such theme is loaded. + /// + /// The name for the theme. + /// One of the enumeration values that specifies how the strings will be + /// compared. + /// The first theme for the specified name, or if no such theme is loaded. + /// + public Theme GetThemeOrDefaultByName(string name, StringComparison stringComparison = StringComparison.Ordinal) + { + return this.GetThemeByName(name, stringComparison) ?? this.DefaultTheme; + } + + /// + /// Returns the light variant of a theme. + /// + /// A theme. + /// The light variant of the . + public Theme GetLightVariantForTheme(Theme theme) + { + switch (theme.Type) + { + case ThemeType.BuiltInLight: + return theme; + + case ThemeType.BuiltInDark: + return this.GetThemeOrDefaultByIdentifier(theme.Identifier.Replace("-dark", string.Empty)); + + case ThemeType.UserProvided: + return this.DefaultTheme; + + default: + throw new ArgumentException(); + } + } + + /// + /// Returns the dark variant of a theme. + /// + /// A theme. + /// The dark variant of the . + public Theme GetDarkVariantForTheme(Theme theme) + { + switch (theme.Type) + { + case ThemeType.BuiltInLight: + return this.GetThemeOrDefaultByIdentifier(theme.Identifier + "-dark"); + + case ThemeType.BuiltInDark: + return theme; + + case ThemeType.UserProvided: + return this.DefaultDarkTheme; + + default: + throw new ArgumentException(); + } + } + + /// + /// Removes a theme, and updates any timers using the theme to use the default theme. + /// + /// A . + public void Remove(Theme theme) + { + foreach (Timer timer in TimerManager.Instance.Timers.Where(t => t.Options.Theme == theme)) + { + timer.Options.Theme = this.DefaultTheme; + } + + this.themes.Remove(theme); + } + + /// + /// Loads the collection of themes defined in the assembly. + /// + /// A collection of themes defined in the assembly. + private IList GetBuiltInThemes() + { + return new List + { + // Light themes + new Theme( + ThemeType.BuiltInLight, + "red" /* identifier */, + Resources.ThemeManagerRedLightTheme /* name */, + "#FFFFFF" /* backgroundColor */, + "#C75050" /* progressBarColor */, + "#EEEEEE" /* progressBackgroundColor */, + "#C75050" /* expirationFlashColor */, + "#000000" /* mainTextColor */, + "#808080" /* mainHintColor */, + "#808080" /* secondaryTextColor */, + "#808080" /* secondaryHintColor */, + "#0066CC" /* buttonColor */, + "#FF0000" /* buttonHoverColor */), + new Theme( + ThemeType.BuiltInLight, + "orange" /* identifier */, + Resources.ThemeManagerOrangeLightTheme /* name */, + "#FFFFFF" /* backgroundColor */, + "#FF7F50" /* progressBarColor */, + "#EEEEEE" /* progressBackgroundColor */, + "#C75050" /* expirationFlashColor */, + "#000000" /* mainTextColor */, + "#808080" /* mainHintColor */, + "#808080" /* secondaryTextColor */, + "#808080" /* secondaryHintColor */, + "#0066CC" /* buttonColor */, + "#FF0000" /* buttonHoverColor */), + new Theme( + ThemeType.BuiltInLight, + "yellow" /* identifier */, + Resources.ThemeManagerYellowLightTheme /* name */, + "#FFFFFF" /* backgroundColor */, + "#FFC800" /* progressBarColor */, + "#EEEEEE" /* progressBackgroundColor */, + "#C75050" /* expirationFlashColor */, + "#000000" /* mainTextColor */, + "#808080" /* mainHintColor */, + "#808080" /* secondaryTextColor */, + "#808080" /* secondaryHintColor */, + "#0066CC" /* buttonColor */, + "#FF0000" /* buttonHoverColor */), + new Theme( + ThemeType.BuiltInLight, + "green" /* identifier */, + Resources.ThemeManagerGreenLightTheme /* name */, + "#FFFFFF" /* backgroundColor */, + "#57A64A" /* progressBarColor */, + "#EEEEEE" /* progressBackgroundColor */, + "#C75050" /* expirationFlashColor */, + "#000000" /* mainTextColor */, + "#808080" /* mainHintColor */, + "#808080" /* secondaryTextColor */, + "#808080" /* secondaryHintColor */, + "#0066CC" /* buttonColor */, + "#FF0000" /* buttonHoverColor */), + new Theme( + ThemeType.BuiltInLight, + "blue" /* identifier */, + Resources.ThemeManagerBlueLightTheme /* name */, + "#FFFFFF" /* backgroundColor */, + "#3665B3" /* progressBarColor */, + "#EEEEEE" /* progressBackgroundColor */, + "#C75050" /* expirationFlashColor */, + "#000000" /* mainTextColor */, + "#808080" /* mainHintColor */, + "#808080" /* secondaryTextColor */, + "#808080" /* secondaryHintColor */, + "#0066CC" /* buttonColor */, + "#FF0000" /* buttonHoverColor */), + new Theme( + ThemeType.BuiltInLight, + "purple" /* identifier */, + Resources.ThemeManagerPurpleLightTheme /* name */, + "#FFFFFF" /* backgroundColor */, + "#843179" /* progressBarColor */, + "#EEEEEE" /* progressBackgroundColor */, + "#C75050" /* expirationFlashColor */, + "#000000" /* mainTextColor */, + "#808080" /* mainHintColor */, + "#808080" /* secondaryTextColor */, + "#808080" /* secondaryHintColor */, + "#0066CC" /* buttonColor */, + "#FF0000" /* buttonHoverColor */), + new Theme( + ThemeType.BuiltInLight, + "gray" /* identifier */, + Resources.ThemeManagerGrayLightTheme /* name */, + "#FFFFFF" /* backgroundColor */, + "#666666" /* progressBarColor */, + "#EEEEEE" /* progressBackgroundColor */, + "#C75050" /* expirationFlashColor */, + "#000000" /* mainTextColor */, + "#808080" /* mainHintColor */, + "#808080" /* secondaryTextColor */, + "#808080" /* secondaryHintColor */, + "#0066CC" /* buttonColor */, + "#FF0000" /* buttonHoverColor */), + new Theme( + ThemeType.BuiltInLight, + "black" /* identifier */, + Resources.ThemeManagerBlackLightTheme /* name */, + "#FFFFFF" /* backgroundColor */, + "#000000" /* progressBarColor */, + "#EEEEEE" /* progressBackgroundColor */, + "#C75050" /* expirationFlashColor */, + "#000000" /* mainTextColor */, + "#808080" /* mainHintColor */, + "#808080" /* secondaryTextColor */, + "#808080" /* secondaryHintColor */, + "#0066CC" /* buttonColor */, + "#FF0000" /* buttonHoverColor */), + + // Dark themes + new Theme( + ThemeType.BuiltInDark, + "red-dark" /* identifier */, + Resources.ThemeManagerRedDarkTheme /* name */, + "#1E1E1E" /* backgroundColor */, + "#C75050" /* progressBarColor */, + "#2D2D30" /* progressBackgroundColor */, + "#C75050" /* expirationFlashColor */, + "#808080" /* mainTextColor */, + "#505050" /* mainHintColor */, + "#505050" /* secondaryTextColor */, + "#505050" /* secondaryHintColor */, + "#0066CC" /* buttonColor */, + "#FF0000" /* buttonHoverColor */), + new Theme( + ThemeType.BuiltInDark, + "orange-dark" /* identifier */, + Resources.ThemeManagerOrangeDarkTheme /* name */, + "#1E1E1E" /* backgroundColor */, + "#FF7F50" /* progressBarColor */, + "#2D2D30" /* progressBackgroundColor */, + "#C75050" /* expirationFlashColor */, + "#808080" /* mainTextColor */, + "#505050" /* mainHintColor */, + "#505050" /* secondaryTextColor */, + "#505050" /* secondaryHintColor */, + "#0066CC" /* buttonColor */, + "#FF0000" /* buttonHoverColor */), + new Theme( + ThemeType.BuiltInDark, + "yellow-dark" /* identifier */, + Resources.ThemeManagerYellowDarkTheme /* name */, + "#1E1E1E" /* backgroundColor */, + "#FFC800" /* progressBarColor */, + "#2D2D30" /* progressBackgroundColor */, + "#C75050" /* expirationFlashColor */, + "#808080" /* mainTextColor */, + "#505050" /* mainHintColor */, + "#505050" /* secondaryTextColor */, + "#505050" /* secondaryHintColor */, + "#0066CC" /* buttonColor */, + "#FF0000" /* buttonHoverColor */), + new Theme( + ThemeType.BuiltInDark, + "green-dark" /* identifier */, + Resources.ThemeManagerGreenDarkTheme /* name */, + "#1E1E1E" /* backgroundColor */, + "#57A64A" /* progressBarColor */, + "#2D2D30" /* progressBackgroundColor */, + "#C75050" /* expirationFlashColor */, + "#808080" /* mainTextColor */, + "#505050" /* mainHintColor */, + "#505050" /* secondaryTextColor */, + "#505050" /* secondaryHintColor */, + "#0066CC" /* buttonColor */, + "#FF0000" /* buttonHoverColor */), + new Theme( + ThemeType.BuiltInDark, + "blue-dark" /* identifier */, + Resources.ThemeManagerBlueDarkTheme /* name */, + "#1E1E1E" /* backgroundColor */, + "#3665B3" /* progressBarColor */, + "#2D2D30" /* progressBackgroundColor */, + "#C75050" /* expirationFlashColor */, + "#808080" /* mainTextColor */, + "#505050" /* mainHintColor */, + "#505050" /* secondaryTextColor */, + "#505050" /* secondaryHintColor */, + "#0066CC" /* buttonColor */, + "#FF0000" /* buttonHoverColor */), + new Theme( + ThemeType.BuiltInDark, + "purple-dark" /* identifier */, + Resources.ThemeManagerPurpleDarkTheme /* name */, + "#1E1E1E" /* backgroundColor */, + "#843179" /* progressBarColor */, + "#2D2D30" /* progressBackgroundColor */, + "#C75050" /* expirationFlashColor */, + "#808080" /* mainTextColor */, + "#505050" /* mainHintColor */, + "#505050" /* secondaryTextColor */, + "#505050" /* secondaryHintColor */, + "#0066CC" /* buttonColor */, + "#FF0000" /* buttonHoverColor */), + new Theme( + ThemeType.BuiltInDark, + "gray-dark" /* identifier */, + Resources.ThemeManagerGrayDarkTheme /* name */, + "#1E1E1E" /* backgroundColor */, + "#666666" /* progressBarColor */, + "#2D2D30" /* progressBackgroundColor */, + "#C75050" /* expirationFlashColor */, + "#808080" /* mainTextColor */, + "#505050" /* mainHintColor */, + "#505050" /* secondaryTextColor */, + "#505050" /* secondaryHintColor */, + "#0066CC" /* buttonColor */, + "#FF0000" /* buttonHoverColor */), + new Theme( + ThemeType.BuiltInDark, + "black-dark" /* identifier */, + Resources.ThemeManagerBlackDarkTheme /* name */, + "#1E1E1E" /* backgroundColor */, + "#000000" /* progressBarColor */, + "#2D2D30" /* progressBackgroundColor */, + "#C75050" /* expirationFlashColor */, + "#808080" /* mainTextColor */, + "#505050" /* mainHintColor */, + "#505050" /* secondaryTextColor */, + "#505050" /* secondaryHintColor */, + "#0066CC" /* buttonColor */, + "#FF0000" /* buttonHoverColor */) + }; + } + + /// + /// Loads the collection of themes defined by the user. + /// + /// A collection of sounds defined by the user. + private IList GetUserProvidedThemes() + { + return Settings.Default.UserProvidedThemes; + } + } +} diff --git a/Hourglass/Properties/App.manifest b/Hourglass/Properties/App.manifest index bc80def..4056a8c 100644 --- a/Hourglass/Properties/App.manifest +++ b/Hourglass/Properties/App.manifest @@ -1,6 +1,6 @@  - + diff --git a/Hourglass/Properties/AssemblyInfo.cs b/Hourglass/Properties/AssemblyInfo.cs index 49d5d78..052c911 100644 --- a/Hourglass/Properties/AssemblyInfo.cs +++ b/Hourglass/Properties/AssemblyInfo.cs @@ -17,8 +17,8 @@ [assembly: AssemblyCopyright("Copyright © 2016 Chris Dziemborowicz")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("1.5.0.0")] -[assembly: AssemblyFileVersion("1.5.0.0")] +[assembly: AssemblyVersion("1.6.0.0")] +[assembly: AssemblyFileVersion("1.6.0.0")] [assembly: NeutralResourcesLanguageAttribute("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 ea0fabd..8ebf02e 100644 --- a/Hourglass/Properties/Resources.Designer.cs +++ b/Hourglass/Properties/Resources.Designer.cs @@ -97,87 +97,6 @@ internal static System.IO.UnmanagedMemoryStream BeepQuiet { } } - /// - /// Looks up a localized string similar to Black. - /// - internal static string ColorManagerBlack { - get { - return ResourceManager.GetString("ColorManagerBlack", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Blue. - /// - internal static string ColorManagerBlue { - get { - return ResourceManager.GetString("ColorManagerBlue", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Default color. - /// - internal static string ColorManagerDefaultColor { - get { - return ResourceManager.GetString("ColorManagerDefaultColor", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Gray. - /// - internal static string ColorManagerGray { - get { - return ResourceManager.GetString("ColorManagerGray", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Green. - /// - internal static string ColorManagerGreen { - get { - return ResourceManager.GetString("ColorManagerGreen", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Orange. - /// - internal static string ColorManagerOrange { - get { - return ResourceManager.GetString("ColorManagerOrange", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Purple. - /// - internal static string ColorManagerPurple { - get { - return ResourceManager.GetString("ColorManagerPurple", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Red. - /// - internal static string ColorManagerRed { - get { - return ResourceManager.GetString("ColorManagerRed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Yellow. - /// - internal static string ColorManagerYellow { - get { - return ResourceManager.GetString("ColorManagerYellow", resourceCulture); - } - } - /// /// Looks up a localized string similar to Duplicate switch "{0}". /// @@ -223,15 +142,6 @@ internal static string CommandLineArgumentsParseExceptionUnrecognizedSwitchForma } } - /// - /// Looks up a localized string similar to _Add custom color.... - /// - internal static string ContextMenuAddCustomColorMenuItem { - get { - return ResourceManager.GetString("ContextMenuAddCustomColorMenuItem", resourceCulture); - } - } - /// /// Looks up a localized string similar to Advanced options. /// @@ -251,11 +161,11 @@ internal static string ContextMenuAlwaysOnTopMenuItem { } /// - /// Looks up a localized string similar to _Clear custom colors. + /// Looks up a localized string similar to _Application name. /// - internal static string ContextMenuClearCustomColorsMenuItem { + internal static string ContextMenuApplicationNameWindowTitleMenuItem { get { - return ResourceManager.GetString("ContextMenuClearCustomColorsMenuItem", resourceCulture); + return ResourceManager.GetString("ContextMenuApplicationNameWindowTitleMenuItem", resourceCulture); } } @@ -296,20 +206,11 @@ internal static string ContextMenuCloseWhenExpiredMenuItem { } /// - /// Looks up a localized string similar to C_olor. + /// Looks up a localized string similar to _Dark theme. /// - internal static string ContextMenuColorMenuItem { + internal static string ContextMenuDarkThemeMenuItem { get { - return ResourceManager.GetString("ContextMenuColorMenuItem", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Custom color. - /// - internal static string ContextMenuCustomColorMenuItem { - get { - return ResourceManager.GetString("ContextMenuCustomColorMenuItem", resourceCulture); + return ResourceManager.GetString("ContextMenuDarkThemeMenuItem", resourceCulture); } } @@ -331,6 +232,15 @@ internal static string ContextMenuFullScreenMenuItem { } } + /// + /// Looks up a localized string similar to _Light theme. + /// + internal static string ContextMenuLightThemeMenuItem { + get { + return ResourceManager.GetString("ContextMenuLightThemeMenuItem", resourceCulture); + } + } + /// /// Looks up a localized string similar to _Loop sound. /// @@ -349,6 +259,15 @@ internal static string ContextMenuLoopTimerMenuItem { } } + /// + /// Looks up a localized string similar to _Manage themes.... + /// + internal static string ContextMenuManageThemesMenuItem { + get { + return ResourceManager.GetString("ContextMenuManageThemesMenuItem", resourceCulture); + } + } + /// /// Looks up a localized string similar to _New timer. /// @@ -431,7 +350,7 @@ internal static string ContextMenuRecentInputsMenuItem { } /// - /// Looks up a localized string similar to Saved _timers. + /// Looks up a localized string similar to Sa_ved timers. /// internal static string ContextMenuSavedTimersMenuItem { get { @@ -475,6 +394,60 @@ internal static string ContextMenuSoundMenuItem { } } + /// + /// Looks up a localized string similar to _Theme. + /// + internal static string ContextMenuThemeMenuItem { + get { + return ResourceManager.GetString("ContextMenuThemeMenuItem", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Time _elapsed. + /// + internal static string ContextMenuTimeElapsedWindowTitleMenuItem { + get { + return ResourceManager.GetString("ContextMenuTimeElapsedWindowTitleMenuItem", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Time _left. + /// + internal static string ContextMenuTimeLeftWindowTitleMenuItem { + get { + return ResourceManager.GetString("ContextMenuTimeLeftWindowTitleMenuItem", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Timer _title. + /// + internal static string ContextMenuTimerTitleWindowTitleMenuItem { + get { + return ResourceManager.GetString("ContextMenuTimerTitleWindowTitleMenuItem", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unnamed theme. + /// + internal static string ContextMenuUnnamedTheme { + get { + return ResourceManager.GetString("ContextMenuUnnamedTheme", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to _Window title. + /// + internal static string ContextMenuWindowTitleMenuItem { + get { + return ResourceManager.GetString("ContextMenuWindowTitleMenuItem", resourceCulture); + } + } + /// /// Looks up a localized string similar to en-US. /// @@ -1384,6 +1357,204 @@ internal static string SpecialTimeTokenMidnightPattern { } } + /// + /// Looks up a localized string similar to Black. + /// + internal static string ThemeManagerBlackDarkTheme { + get { + return ResourceManager.GetString("ThemeManagerBlackDarkTheme", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Black. + /// + internal static string ThemeManagerBlackLightTheme { + get { + return ResourceManager.GetString("ThemeManagerBlackLightTheme", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Blue. + /// + internal static string ThemeManagerBlueDarkTheme { + get { + return ResourceManager.GetString("ThemeManagerBlueDarkTheme", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Blue. + /// + internal static string ThemeManagerBlueLightTheme { + get { + return ResourceManager.GetString("ThemeManagerBlueLightTheme", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Gray. + /// + internal static string ThemeManagerGrayDarkTheme { + get { + return ResourceManager.GetString("ThemeManagerGrayDarkTheme", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Gray. + /// + internal static string ThemeManagerGrayLightTheme { + get { + return ResourceManager.GetString("ThemeManagerGrayLightTheme", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Green. + /// + internal static string ThemeManagerGreenDarkTheme { + get { + return ResourceManager.GetString("ThemeManagerGreenDarkTheme", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Green. + /// + internal static string ThemeManagerGreenLightTheme { + get { + return ResourceManager.GetString("ThemeManagerGreenLightTheme", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to New theme. + /// + internal static string ThemeManagerNewTheme { + get { + return ResourceManager.GetString("ThemeManagerNewTheme", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Orange. + /// + internal static string ThemeManagerOrangeDarkTheme { + get { + return ResourceManager.GetString("ThemeManagerOrangeDarkTheme", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Orange. + /// + internal static string ThemeManagerOrangeLightTheme { + get { + return ResourceManager.GetString("ThemeManagerOrangeLightTheme", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Purple. + /// + internal static string ThemeManagerPurpleDarkTheme { + get { + return ResourceManager.GetString("ThemeManagerPurpleDarkTheme", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Purple. + /// + internal static string ThemeManagerPurpleLightTheme { + get { + return ResourceManager.GetString("ThemeManagerPurpleLightTheme", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Red. + /// + internal static string ThemeManagerRedDarkTheme { + get { + return ResourceManager.GetString("ThemeManagerRedDarkTheme", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Red. + /// + internal static string ThemeManagerRedLightTheme { + get { + return ResourceManager.GetString("ThemeManagerRedLightTheme", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Dark themes. + /// + internal static string ThemeManagerWindowDarkThemesSectionHeader { + get { + return ResourceManager.GetString("ThemeManagerWindowDarkThemesSectionHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Are you sure you want to delete the selected theme?. + /// + internal static string ThemeManagerWindowDeletePrompt { + get { + return ResourceManager.GetString("ThemeManagerWindowDeletePrompt", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Light themes. + /// + internal static string ThemeManagerWindowLightThemesSectionHeader { + get { + return ResourceManager.GetString("ThemeManagerWindowLightThemesSectionHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Do you want to save the changes to the current theme?. + /// + internal static string ThemeManagerWindowSavePrompt { + get { + return ResourceManager.GetString("ThemeManagerWindowSavePrompt", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Custom themes. + /// + internal static string ThemeManagerWindowUserProvidedThemesSectionHeader { + get { + return ResourceManager.GetString("ThemeManagerWindowUserProvidedThemesSectionHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Yellow. + /// + internal static string ThemeManagerYellowDarkTheme { + get { + return ResourceManager.GetString("ThemeManagerYellowDarkTheme", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Yellow. + /// + internal static string ThemeManagerYellowLightTheme { + get { + return ResourceManager.GetString("ThemeManagerYellowLightTheme", resourceCulture); + } + } + /// /// Looks up a localized string similar to {1} (Expired). /// diff --git a/Hourglass/Properties/Resources.resx b/Hourglass/Properties/Resources.resx index 53a7796..45c1b99 100644 --- a/Hourglass/Properties/Resources.resx +++ b/Hourglass/Properties/Resources.resx @@ -732,42 +732,6 @@ $ The separator between the units of a time span (e.g., " " => "5 minutes 30 seconds", " : " => "5 minutes : 30 seconds") - - Black - The string for the gray color - - - Blue - The string for the blue color - - - Default color - The string for the default color - - - Gray - The string for the gray color - - - Green - The string for the green color - - - Orange - The string for the orange color - - - Purple - The string for the purple color - - - Red - The string for the red color - - - Yellow - The string for the yellow color - Loud beep The string for the loud beep sound @@ -812,18 +776,10 @@ $ Unrecognized switch "{0}" A format string for the error message displayed when a command-line argument switch is unrecognized, where {0} is the unrecognized switch (e.g., "Unrecognized switch '{0}'." => "Unrecognized switch '--junk'.") - - _Add custom color... - The text for the add custom color menu item, where the character following the optional underscore (_) is the access key - _Always on top The text for the always on top menu item, where the character following the optional underscore (_) is the access key - - _Clear custom colors - The text for the clear custom colors menu item, where the character following the optional underscore (_) is the access key - _Clear recent inputs The text for the clear recent inputs menu item, where the character following the optional underscore (_) is the access key @@ -840,14 +796,6 @@ $ Close when _expired The text for the close when expired menu item, where the character following the optional underscore (_) is the access key - - C_olor - The text for the color menu item, where the character following the optional underscore (_) is the access key - - - Custom color - The text for a custom color menu item, where the character following the optional underscore (_) is the access key - _Full screen The text for the full screen menu item, where the character following the optional underscore (_) is the access key @@ -885,7 +833,7 @@ $ The text for the recent inputs menu item, where the character following the optional underscore (_) is the access key - Saved _timers + Sa_ved timers The text for the saved timers menu item, where the character following the optional underscore (_) is the access key @@ -1125,4 +1073,132 @@ $ Do not _keep computer awake The text for the do not keep computer awake menu item, where the character following the optional underscore (_) is the access key + + _Theme + The text for the theme menu item, where the character following the optional underscore (_) is the access key + + + Black + The name of the black theme with a dark background + + + Black + The name of the black theme with a light background + + + Blue + The name of the blue theme with a dark background + + + Blue + The name of the blue theme with a light background + + + Gray + The name of the gray theme with a dark background + + + Gray + The name of the gray theme with a light background + + + Green + The name of the green theme with a dark background + + + Green + The name of the green theme with a light background + + + Orange + The name of the orange theme with a dark background + + + Orange + The name of the orange theme with a light background + + + Purple + The name of the purple theme with a dark background + + + Purple + The name of the purple theme with a light background + + + Red + The name of the red theme with a dark background + + + Red + The name of the red theme with a light background + + + Yellow + The name of the yellow theme with a dark background + + + Yellow + The name of the yellow theme with a light background + + + Unnamed theme + The text for the unnamed theme menu items, where the character following the optional underscore (_) is the access key + + + _Dark theme + The text for the dark theme menu item, where the character following the optional underscore (_) is the access key + + + _Light theme + The text for the light theme menu item, where the character following the optional underscore (_) is the access key + + + _Manage themes... + The text for the manage themes menu item, where the character following the optional underscore (_) is the access key + + + New theme + The name of a new theme + + + Do you want to save the changes to the current theme? + The prompt displayed when the user tries to close the window or navigate away while there are unsaved changes to a theme + + + Dark themes + The section header text for dark themes + + + Light themes + The section header text for light themes + + + Custom themes + The section header text for user-provided themes + + + Are you sure you want to delete the selected theme? + The prompt displayed when the user deletes a theme + + + _Application name + The text for the application name menu item, where the character following the optional underscore (_) is the access key + + + Time _elapsed + The text for the time elapsed menu item, where the character following the optional underscore (_) is the access key + + + Time _left + The text for the time left menu item, where the character following the optional underscore (_) is the access key + + + Timer _title + The text for the timer title menu item, where the character following the optional underscore (_) is the access key + + + _Window title + The text for the window title menu item, where the character following the optional underscore (_) is the access key + \ No newline at end of file diff --git a/Hourglass/Properties/Settings.Designer.cs b/Hourglass/Properties/Settings.Designer.cs index 9564d9a..120736b 100644 --- a/Hourglass/Properties/Settings.Designer.cs +++ b/Hourglass/Properties/Settings.Designer.cs @@ -34,6 +34,18 @@ public static Settings Default { } } + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool OpenSavedTimersOnStartup { + get { + return ((bool)(this["OpenSavedTimersOnStartup"])); + } + set { + this["OpenSavedTimersOnStartup"] = value; + } + } + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("False")] @@ -82,12 +94,12 @@ public bool ShowInNotificationArea { [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - public global::System.Collections.Specialized.StringCollection UserProvidedColorIdentifiers { + public global::Hourglass.Serialization.ThemeInfoList UserProvidedThemeInfos { get { - return ((global::System.Collections.Specialized.StringCollection)(this["UserProvidedColorIdentifiers"])); + return ((global::Hourglass.Serialization.ThemeInfoList)(this["UserProvidedThemeInfos"])); } set { - this["UserProvidedColorIdentifiers"] = value; + this["UserProvidedThemeInfos"] = value; } } @@ -101,17 +113,5 @@ public bool ShowInNotificationArea { this["WindowSizeInfo"] = value; } } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("False")] - public bool OpenSavedTimersOnStartup { - get { - return ((bool)(this["OpenSavedTimersOnStartup"])); - } - set { - this["OpenSavedTimersOnStartup"] = value; - } - } } } diff --git a/Hourglass/Properties/Settings.cs b/Hourglass/Properties/Settings.cs index 859de32..751ce7e 100644 --- a/Hourglass/Properties/Settings.cs +++ b/Hourglass/Properties/Settings.cs @@ -7,7 +7,6 @@ namespace Hourglass.Properties { using System.Collections.Generic; - using System.Collections.Specialized; using System.Linq; using Hourglass.Serialization; @@ -68,31 +67,20 @@ public IList TimerStarts } /// - /// Gets or sets the collection of the colors defined by the user. + /// Gets or sets the collection of the themes defined by the user. /// - public IList UserProvidedColors + public IList UserProvidedThemes { get { - List userProvidedColors = new List(); - - StringCollection userProvidedColorIdentifiers = this.UserProvidedColorIdentifiers; - if (userProvidedColorIdentifiers != null) - { - foreach (string identifier in userProvidedColorIdentifiers) - { - userProvidedColors.Add(Color.FromIdentifier(identifier)); - } - } - - return userProvidedColors; + IEnumerable userProvidedThemeInfos = this.UserProvidedThemeInfos ?? new ThemeInfoList(); + return userProvidedThemeInfos.Select(Theme.FromThemeInfo).ToList(); } set { - StringCollection userProvidedColorIdentifiers = new StringCollection(); - userProvidedColorIdentifiers.AddRange(value.Select(c => c.Identifier).ToArray()); - this.UserProvidedColorIdentifiers = userProvidedColorIdentifiers; + IEnumerable userProvidedThemeInfos = value.Select(ThemeInfo.FromTheme); + this.UserProvidedThemeInfos = new ThemeInfoList(userProvidedThemeInfos); } } diff --git a/Hourglass/Properties/Settings.settings b/Hourglass/Properties/Settings.settings index 93c77f9..d914c10 100644 --- a/Hourglass/Properties/Settings.settings +++ b/Hourglass/Properties/Settings.settings @@ -20,7 +20,7 @@ 00000000-0000-0000-0000-000000000000 - + diff --git a/Hourglass/Resources/Usage.txt b/Hourglass/Resources/Usage.txt index 93ac822..e7f7cc8 100644 --- a/Hourglass/Resources/Usage.txt +++ b/Hourglass/Resources/Usage.txt @@ -69,7 +69,8 @@ Options: Alias -n --show-time-elapsed on|off|last - Shows the time elapsed since the timer was started, rather than the time left until the timer expires. + Shows the time elapsed since the timer was started, rather than the + time left until the timer expires. Required no Default value last @@ -103,12 +104,26 @@ Options: Default value off Alias -x - --color last| - Sets the color of the timer window. + --theme last| + Sets the theme of the timer window. + + You can specify the name of one of the built-in themes, or the name of + one of the themes that you have defined yourself. + + The built-in themes are: + + red red-dark + orange orange-dark + yellow yellow-dark + green green-dark + blue blue-dark + purple purple-dark + gray gray-dark + black black-dark Required no Default value last - Alias -c + Alias -m --sound none|last| Plays a sound when the timer expires. @@ -131,6 +146,20 @@ Options: Default value last Alias -v + --window-title app|left|elapsed|title|last + Sets the timer window title. + + The options are: + + app Application name + left Time remaining + elapsed Time elapsed + title Timer title + + Required no + Default value last + Alias -i + --window-state normal|maximized|minimized|last Sets the state of the timer window. @@ -172,7 +201,7 @@ Options: --pop-up-when-expired on --close-when-expired off --shut-down-when-expired off - --color blue + --theme blue --sound normal beep --loop-sound off --open-saved-timers off diff --git a/Hourglass/Serialization/ThemeInfo.cs b/Hourglass/Serialization/ThemeInfo.cs new file mode 100644 index 0000000..13a9af3 --- /dev/null +++ b/Hourglass/Serialization/ThemeInfo.cs @@ -0,0 +1,93 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) Chris Dziemborowicz. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Hourglass.Serialization +{ + using System.Windows.Media; + + using Hourglass.Timing; + + /// + /// The representation of a used for XML serialization. + /// + public class ThemeInfo + { + /// + /// Gets or sets the unique identifier for this theme. + /// + public string Identifier { get; set; } + + /// + /// Gets or sets the friendly name for this theme, or null if no friendly name is specified. + /// + public string Name { get; set; } + + /// + /// Gets or sets the background color of the window. + /// + public Color BackgroundColor { get; set; } + + /// + /// Gets or sets the color of the progress bar. + /// + public Color ProgressBarColor { get; set; } + + /// + /// Gets or sets the background color of the progress bar. + /// + public Color ProgressBackgroundColor { get; set; } + + /// + /// Gets or sets the color that is flashed on expiration. + /// + public Color ExpirationFlashColor { get; set; } + + /// + /// Gets or sets the color of the primary text. + /// + public Color PrimaryTextColor { get; set; } + + /// + /// Gets or sets the color of the watermark in the primary text box. + /// + public Color PrimaryHintColor { get; set; } + + /// + /// Gets or sets the color of any secondary text. + /// + public Color SecondaryTextColor { get; set; } + + /// + /// Gets or sets the color of the watermark in any secondary text box. + /// + public Color SecondaryHintColor { get; set; } + + /// + /// Gets or sets the color of the button text. + /// + public Color ButtonColor { get; set; } + + /// + /// Gets or sets the color of the button text when the user hovers over the button. + /// + public Color ButtonHoverColor { get; set; } + + /// + /// Returns a for the specified . + /// + /// A . + /// A for the specified . + public static ThemeInfo FromTheme(Theme options) + { + if (options == null) + { + return null; + } + + return options.ToThemeInfo(); + } + } +} diff --git a/Hourglass/Serialization/ThemeInfoList.cs b/Hourglass/Serialization/ThemeInfoList.cs new file mode 100644 index 0000000..98f3471 --- /dev/null +++ b/Hourglass/Serialization/ThemeInfoList.cs @@ -0,0 +1,44 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) Chris Dziemborowicz. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Hourglass.Serialization +{ + using System.Collections.Generic; + + /// + /// A list of objects used for XML serialization. + /// + public class ThemeInfoList : List + { + /// + /// Initializes a new instance of the class that is empty and has the default + /// initial capacity. + /// + public ThemeInfoList() + { + } + + /// + /// Initializes a new instance of the class that contains elements copied from the + /// specified collection and has sufficient capacity to accommodate the number of elements copied. + /// + /// The collection whose elements are copied to the new list. + public ThemeInfoList(IEnumerable collection) + : base(collection) + { + } + + /// + /// Initializes a new instance of the class that is empty and has the specified + /// initial capacity. + /// + /// The number of elements that the new list can initially store. + public ThemeInfoList(int capacity) + : base(capacity) + { + } + } +} diff --git a/Hourglass/Serialization/TimerOptionsInfo.cs b/Hourglass/Serialization/TimerOptionsInfo.cs index 48c3b1b..26fa81d 100644 --- a/Hourglass/Serialization/TimerOptionsInfo.cs +++ b/Hourglass/Serialization/TimerOptionsInfo.cs @@ -61,9 +61,9 @@ public class TimerOptionsInfo public bool ShutDownWhenExpired { get; set; } /// - /// Gets or sets the identifier of the color of the timer progress bar. + /// Gets or sets the identifier of the theme of the timer window. /// - public string ColorIdentifier { get; set; } + public string ThemeIdentifier { get; set; } /// /// Gets or sets the identifier of the sound to play when the timer expires, or null if no sound is to @@ -77,6 +77,11 @@ public class TimerOptionsInfo /// public bool LoopSound { get; set; } + /// + /// Gets or sets a value indicating what information to display in the timer window title. + /// + public WindowTitleMode WindowTitleMode { get; set; } + /// /// Gets or sets the size, position, and state of the timer window. /// diff --git a/Hourglass/Timing/Color.cs b/Hourglass/Timing/Color.cs deleted file mode 100644 index a97c1b6..0000000 --- a/Hourglass/Timing/Color.cs +++ /dev/null @@ -1,196 +0,0 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) Chris Dziemborowicz. All rights reserved. -// -// -------------------------------------------------------------------------------------------------------------------- - -namespace Hourglass.Timing -{ - using System; - using System.Windows.Media; - - using Hourglass.Managers; - - /// - /// A color for the timer progress bar. - /// - public class Color - { - /// - /// The friendly name for this color, or null if no friendly name is specified. - /// - private readonly string name; - - /// - /// A unique identifier for this color. - /// - private readonly string identifier; - - /// - /// A value indicating whether this color is defined in the assembly. - /// - private readonly bool isBuiltIn; - - /// - /// The representation of this color. - /// - private readonly System.Windows.Media.Color mediaColor; - - /// - /// The used to paint the this color. - /// - private Brush brush; - - /// - /// Initializes a new instance of the class. - /// - /// The color. - /// The culture-insensitive name of the color. (Optional.) - /// The localized friendly name of the color. (Optional.) - /// A value indicating whether this color is defined in the assembly. (Optional.) - public Color(System.Windows.Media.Color mediaColor, string invariantName = null, string name = null, bool isBuiltIn = false) - { - this.name = name; - this.identifier = isBuiltIn ? ("resource:" + invariantName) : ("color:" + mediaColor); - this.isBuiltIn = isBuiltIn; - this.mediaColor = mediaColor; - } - - /// - /// Initializes a new instance of the class. - /// - /// The sRGB red channel, R, of the color. - /// The sRGB green channel, G, of the color. - /// The sRGB blue channel, B, of the color. - /// The culture-insensitive name of the color. (Optional.) - /// The localized friendly name of the color. (Optional.) - /// A value indicating whether this color is defined in the assembly. (Optional.) - public Color(byte r, byte g, byte b, string invariantName = null, string name = null, bool isBuiltIn = false) - : this(System.Windows.Media.Color.FromRgb(r, g, b), invariantName, name, isBuiltIn) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// A string representation of a color. - /// The culture-insensitive name of the color. (Optional.) - /// The localized friendly name of the color. (Optional.) - /// A value indicating whether this color is defined in the assembly. (Optional.) - public Color(string colorString, string invariantName = null, string name = null, bool isBuiltIn = false) - : this(GetMediaColorFromString(colorString), invariantName, name, isBuiltIn) - { - } - - /// - /// Gets the default color. - /// - public static Color DefaultColor - { - get { return ColorManager.Instance.DefaultColor; } - } - - /// - /// Gets the friendly name of this color, or null if no friendly name is specified. - /// - public string Name - { - get { return this.name; } - } - - /// - /// Gets the unique identifier for this color. - /// - public string Identifier - { - get { return this.identifier; } - } - - /// - /// Gets a value indicating whether this color is defined in the assembly. - /// - public bool IsBuiltIn - { - get { return this.isBuiltIn; } - } - - /// - /// Gets the representation of this color. - /// - public System.Windows.Media.Color MediaColor - { - get { return this.mediaColor; } - } - - /// - /// Gets the used to paint the this color. - /// - public Brush Brush - { - get - { - if (this.brush == null) - { - this.brush = new SolidColorBrush(this.mediaColor); - } - - return this.brush; - } - } - - /// - /// Returns a for the specified identifier, or null if the identifier is null - /// or empty. - /// - /// The identifier for the sound. - /// A for the specified identifier, or null if the identifier is - /// null or empty. - public static Color FromIdentifier(string identifier) - { - if (identifier.StartsWith("color:", StringComparison.Ordinal)) - { - Color color = ColorManager.Instance.GetColorByIdentifier(identifier); - - // If the color is not registered in the ColorManager, register it - if (color == null) - { - string colorString = identifier.Substring("color:".Length); - color = new Color(colorString); - ColorManager.Instance.Add(color); - } - - return color; - } - else - { - return ColorManager.Instance.GetColorOrDefaultByIdentifier(identifier); - } - } - - /// - /// Returns an for this color. - /// - /// An for this color. - public int ToInt() - { - return (this.mediaColor.R << 0) | (this.mediaColor.G << 8) | (this.mediaColor.B << 16); - } - - /// - /// Converts a representation of a into a . - /// - /// A representation of a . - /// A . - private static System.Windows.Media.Color GetMediaColorFromString(string colorString) - { - object color = ColorConverter.ConvertFromString(colorString); - - if (color == null) - { - throw new ArgumentException("colorString"); - } - - return (System.Windows.Media.Color)color; - } - } -} diff --git a/Hourglass/Timing/Theme.cs b/Hourglass/Timing/Theme.cs new file mode 100644 index 0000000..11118c8 --- /dev/null +++ b/Hourglass/Timing/Theme.cs @@ -0,0 +1,912 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) Chris Dziemborowicz. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Hourglass.Timing +{ + using System.ComponentModel; + using System.Linq; + using System.Windows.Media; + + using Hourglass.Extensions; + using Hourglass.Managers; + using Hourglass.Serialization; + + /// + /// The type of theme. + /// + public enum ThemeType + { + /// + /// A built-in theme with a light background. + /// + BuiltInLight, + + /// + /// A built-in theme with a dark background. + /// + BuiltInDark, + + /// + /// A theme that is provided by the user. + /// + UserProvided + } + + /// + /// A theme for the timer window. + /// + public class Theme : INotifyPropertyChanged + { + #region Private Members + + /// + /// The type of this theme. + /// + private readonly ThemeType type; + + /// + /// A unique identifier for this theme. + /// + private readonly string identifier; + + /// + /// The friendly name for this theme, or null if no friendly name is specified. + /// + private string name; + + /// + /// The background color of the window. + /// + private Color backgroundColor; + + /// + /// The brush used to paint the background color of the window. + /// + private Brush backgroundBrush; + + /// + /// The color of the progress bar. + /// + private Color progressBarColor; + + /// + /// The brush used to paint the color of the progress bar. + /// + private Brush progressBarBrush; + + /// + /// The background color of the progress bar. + /// + private Color progressBackgroundColor; + + /// + /// The brush used to paint the background color of the progress bar. + /// + private Brush progressBackgroundBrush; + + /// + /// The color that is flashed on expiration. + /// + private Color expirationFlashColor; + + /// + /// The brush used to paint the color that is flashed on expiration. + /// + private Brush expirationFlashBrush; + + /// + /// The color of the primary text. + /// + private Color primaryTextColor; + + /// + /// The brush used to paint the color of the primary text. + /// + private Brush primaryTextBrush; + + /// + /// The color of the watermark in the primary text box. + /// + private Color primaryHintColor; + + /// + /// The brush used to paint the color of the watermark in the primary text box. + /// + private Brush primaryHintBrush; + + /// + /// The color of any secondary text. + /// + private Color secondaryTextColor; + + /// + /// The brush used to paint the color of any secondary text. + /// + private Brush secondaryTextBrush; + + /// + /// The color of the watermark in any secondary text box. + /// + private Color secondaryHintColor; + + /// + /// The brush used to paint the color of the watermark in any secondary text box. + /// + private Brush secondaryHintBrush; + + /// + /// The color of the button text. + /// + private Color buttonColor; + + /// + /// The brush used to paint the color of the button text. + /// + private Brush buttonBrush; + + /// + /// The color of the button text when the user hovers over the button. + /// + private Color buttonHoverColor; + + /// + /// The brush used to paint the color of the button text when the user hovers over the button. + /// + private Brush buttonHoverBrush; + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The type of this theme. + /// A unique identifier for this theme. + /// The friendly name for this theme, or null if no friendly name is specified. + /// The background color of the window. + /// The color of the progress bar. + /// The background color of the progress bar. + /// The color that is flashed on expiration. + /// The color of the primary text. + /// The color of the watermark in the primary text box. + /// The color of any secondary text. + /// The color of the watermark in any secondary text box. + /// The color of the button text. + /// The color of the button text when the user hovers over the button. + public Theme( + ThemeType type, + string identifier, + string name, + Color backgroundColor, + Color progressBarColor, + Color progressBackgroundColor, + Color expirationFlashColor, + Color primaryTextColor, + Color primaryHintColor, + Color secondaryTextColor, + Color secondaryHintColor, + Color buttonColor, + Color buttonHoverColor) + { + this.type = type; + this.identifier = identifier; + this.name = name; + + this.backgroundColor = backgroundColor; + this.progressBarColor = progressBarColor; + this.progressBackgroundColor = progressBackgroundColor; + this.expirationFlashColor = expirationFlashColor; + this.primaryTextColor = primaryTextColor; + this.primaryHintColor = primaryHintColor; + this.secondaryTextColor = secondaryTextColor; + this.secondaryHintColor = secondaryHintColor; + this.buttonColor = buttonColor; + this.buttonHoverColor = buttonHoverColor; + } + + /// + /// Initializes a new instance of the class. + /// + /// The type of this theme. + /// A unique identifier for this theme. + /// The friendly name for this theme, or null if no friendly name is specified. + /// The background color of the window. + /// The color of the progress bar. + /// The background color of the progress bar. + /// The color that is flashed on expiration. + /// The color of the primary text. + /// The color of the watermark in the primary text box. + /// The color of any secondary text. + /// The color of the watermark in any secondary text box. + /// The color of the button text. + /// The color of the button text when the user hovers over the button. + public Theme( + ThemeType type, + string identifier, + string name, + string backgroundColor, + string progressBarColor, + string progressBackgroundColor, + string expirationFlashColor, + string primaryTextColor, + string primaryHintColor, + string secondaryTextColor, + string secondaryHintColor, + string buttonColor, + string buttonHoverColor) + : this( + type, + identifier, + name, + ColorExtensions.FromString(backgroundColor), + ColorExtensions.FromString(progressBarColor), + ColorExtensions.FromString(progressBackgroundColor), + ColorExtensions.FromString(expirationFlashColor), + ColorExtensions.FromString(primaryTextColor), + ColorExtensions.FromString(primaryHintColor), + ColorExtensions.FromString(secondaryTextColor), + ColorExtensions.FromString(secondaryHintColor), + ColorExtensions.FromString(buttonColor), + ColorExtensions.FromString(buttonHoverColor)) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The type of this theme. + /// A unique identifier for this theme. + /// The friendly name for this theme, or null if no friendly name is specified. + /// A theme from which to copy colors. + public Theme(ThemeType type, string identifier, string name, Theme theme) + : this( + type, + identifier, + name, + theme.backgroundColor, + theme.progressBarColor, + theme.progressBackgroundColor, + theme.expirationFlashColor, + theme.primaryTextColor, + theme.primaryHintColor, + theme.secondaryTextColor, + theme.secondaryHintColor, + theme.buttonColor, + theme.buttonHoverColor) + { + } + + /// + /// Initializes a new instance of the class from a . + /// + /// A . + public Theme(ThemeInfo info) + : this( + ThemeType.UserProvided, + info.Identifier, + info.Name, + info.BackgroundColor, + info.ProgressBarColor, + info.ProgressBackgroundColor, + info.ExpirationFlashColor, + info.PrimaryTextColor, + info.PrimaryHintColor, + info.SecondaryTextColor, + info.SecondaryHintColor, + info.ButtonColor, + info.ButtonHoverColor) + { + } + + #endregion + + #region Events + + /// + /// Raised when a property value changes. + /// + public event PropertyChangedEventHandler PropertyChanged; + + #endregion + + #region Properties + + /// + /// Gets the default theme. + /// + public static Theme DefaultTheme + { + get { return ThemeManager.Instance.DefaultTheme; } + } + + /// + /// Gets the type of this theme. + /// + public ThemeType Type + { + get { return this.type; } + } + + /// + /// Gets the unique identifier for this theme. + /// + public string Identifier + { + get { return this.identifier; } + } + + /// + /// Gets or sets the friendly name of this theme, or null if no friendly name is specified. + /// + public string Name + { + get + { + return this.name; + } + + set + { + if (this.name == value) + { + return; + } + + this.name = value; + this.OnPropertyChanged(nameof(this.Name)); + } + } + + /// + /// Gets or sets the background color of the window. + /// + public Color BackgroundColor + { + get + { + return this.backgroundColor; + } + + set + { + if (this.backgroundColor == value) + { + return; + } + + this.backgroundColor = value; + this.backgroundBrush = null; + this.OnPropertyChanged(nameof(this.BackgroundColor)); + this.OnPropertyChanged(nameof(this.BackgroundBrush)); + } + } + + /// + /// Gets the brush used to paint the background color of the window. + /// + public Brush BackgroundBrush + { + get + { + if (this.backgroundBrush == null) + { + this.backgroundBrush = new SolidColorBrush(this.backgroundColor); + } + + return this.backgroundBrush; + } + } + + /// + /// Gets or sets the color of the progress bar. + /// + public Color ProgressBarColor + { + get + { + return this.progressBarColor; + } + + set + { + if (this.progressBarColor == value) + { + return; + } + + this.progressBarColor = value; + this.progressBarBrush = null; + this.OnPropertyChanged(nameof(this.ProgressBarColor)); + this.OnPropertyChanged(nameof(this.ProgressBarBrush)); + } + } + + /// + /// Gets the brush used to paint the color of the progress bar. + /// + public Brush ProgressBarBrush + { + get + { + if (this.progressBarBrush == null) + { + this.progressBarBrush = new SolidColorBrush(this.progressBarColor); + } + + return this.progressBarBrush; + } + } + + /// + /// Gets or sets the background color of the progress bar. + /// + public Color ProgressBackgroundColor + { + get + { + return this.progressBackgroundColor; + } + + set + { + if (this.progressBackgroundColor == value) + { + return; + } + + this.progressBackgroundColor = value; + this.progressBackgroundBrush = null; + this.OnPropertyChanged(nameof(this.ProgressBackgroundColor)); + this.OnPropertyChanged(nameof(this.ProgressBackgroundBrush)); + } + } + + /// + /// Gets the brush used to paint the background color of the progress bar. + /// + public Brush ProgressBackgroundBrush + { + get + { + if (this.progressBackgroundBrush == null) + { + this.progressBackgroundBrush = new SolidColorBrush(this.progressBackgroundColor); + } + + return this.progressBackgroundBrush; + } + } + + /// + /// Gets or sets the color that is flashed on expiration. + /// + public Color ExpirationFlashColor + { + get + { + return this.expirationFlashColor; + } + + set + { + if (this.expirationFlashColor == value) + { + return; + } + + this.expirationFlashColor = value; + this.expirationFlashBrush = null; + this.OnPropertyChanged(nameof(this.ExpirationFlashColor)); + this.OnPropertyChanged(nameof(this.ExpirationFlashBrush)); + } + } + + /// + /// Gets the brush used to paint the color that is flashed on expiration. + /// + public Brush ExpirationFlashBrush + { + get + { + if (this.expirationFlashBrush == null) + { + this.expirationFlashBrush = new SolidColorBrush(this.expirationFlashColor); + } + + return this.expirationFlashBrush; + } + } + + /// + /// Gets or sets the color of the primary text. + /// + public Color PrimaryTextColor + { + get + { + return this.primaryTextColor; + } + + set + { + if (this.primaryTextColor == value) + { + return; + } + + this.primaryTextColor = value; + this.primaryTextBrush = null; + this.OnPropertyChanged(nameof(this.PrimaryTextColor)); + this.OnPropertyChanged(nameof(this.PrimaryTextBrush)); + } + } + + /// + /// Gets the brush used to paint the color of the primary text. + /// + public Brush PrimaryTextBrush + { + get + { + if (this.primaryTextBrush == null) + { + this.primaryTextBrush = new SolidColorBrush(this.primaryTextColor); + } + + return this.primaryTextBrush; + } + } + + /// + /// Gets or sets the color of the watermark in the primary text box. + /// + public Color PrimaryHintColor + { + get + { + return this.primaryHintColor; + } + + set + { + if (this.primaryHintColor == value) + { + return; + } + + this.primaryHintColor = value; + this.primaryHintBrush = null; + this.OnPropertyChanged(nameof(this.PrimaryHintColor)); + this.OnPropertyChanged(nameof(this.PrimaryHintBrush)); + } + } + + /// + /// Gets the brush used to paint the color of the watermark in the primary text box. + /// + public Brush PrimaryHintBrush + { + get + { + if (this.primaryHintBrush == null) + { + this.primaryHintBrush = new SolidColorBrush(this.primaryHintColor); + } + + return this.primaryHintBrush; + } + } + + /// + /// Gets or sets the color of any secondary text. + /// + public Color SecondaryTextColor + { + get + { + return this.secondaryTextColor; + } + + set + { + if (this.secondaryTextColor == value) + { + return; + } + + this.secondaryTextColor = value; + this.secondaryTextBrush = null; + this.OnPropertyChanged(nameof(this.SecondaryTextColor)); + this.OnPropertyChanged(nameof(this.SecondaryTextBrush)); + } + } + + /// + /// Gets the brush used to paint the color of any secondary text. + /// + public Brush SecondaryTextBrush + { + get + { + if (this.secondaryTextBrush == null) + { + this.secondaryTextBrush = new SolidColorBrush(this.secondaryTextColor); + } + + return this.secondaryTextBrush; + } + } + + /// + /// Gets or sets the color of the watermark in any secondary text box. + /// + public Color SecondaryHintColor + { + get + { + return this.secondaryHintColor; + } + + set + { + if (this.secondaryHintColor == value) + { + return; + } + + this.secondaryHintColor = value; + this.secondaryHintBrush = null; + this.OnPropertyChanged(nameof(this.SecondaryHintColor)); + this.OnPropertyChanged(nameof(this.SecondaryHintBrush)); + } + } + + /// + /// Gets the brush used to paint the color of the watermark in any secondary text box. + /// + public Brush SecondaryHintBrush + { + get + { + if (this.secondaryHintBrush == null) + { + this.secondaryHintBrush = new SolidColorBrush(this.secondaryHintColor); + } + + return this.secondaryHintBrush; + } + } + + /// + /// Gets or sets the color of the button text. + /// + public Color ButtonColor + { + get + { + return this.buttonColor; + } + + set + { + if (this.buttonColor == value) + { + return; + } + + this.buttonColor = value; + this.buttonBrush = null; + this.OnPropertyChanged(nameof(this.ButtonColor)); + this.OnPropertyChanged(nameof(this.ButtonBrush)); + } + } + + /// + /// Gets the brush used to paint the color of the button text. + /// + public Brush ButtonBrush + { + get + { + if (this.buttonBrush == null) + { + this.buttonBrush = new SolidColorBrush(this.buttonColor); + } + + return this.buttonBrush; + } + } + + /// + /// Gets or sets the color of the button text when the user hovers over the button. + /// + public Color ButtonHoverColor + { + get + { + return this.buttonHoverColor; + } + + set + { + if (this.buttonHoverColor == value) + { + return; + } + + this.buttonHoverColor = value; + this.buttonHoverBrush = null; + this.OnPropertyChanged(nameof(this.ButtonHoverColor)); + this.OnPropertyChanged(nameof(this.ButtonHoverBrush)); + } + } + + /// + /// Gets the brush used to paint the color of the button text when the user hovers over the button. + /// + public Brush ButtonHoverBrush + { + get + { + if (this.buttonHoverBrush == null) + { + this.buttonHoverBrush = new SolidColorBrush(this.buttonHoverColor); + } + + return this.buttonHoverBrush; + } + } + + /// + /// Gets the light variant of this theme. + /// + public Theme LightVariant + { + get { return ThemeManager.Instance.GetLightVariantForTheme(this); } + } + + /// + /// Gets the dark variant of this theme. + /// + public Theme DarkVariant + { + get { return ThemeManager.Instance.GetDarkVariantForTheme(this); } + } + + #endregion + + #region Public Static Methods + + /// + /// Returns the theme for the specified identifier, or null if no such theme is loaded. + /// + /// The identifier for the theme. + /// The theme for the specified identifier, or null if no such theme is loaded. + public static Theme FromIdentifier(string identifier) + { + return ThemeManager.Instance.GetThemeOrDefaultByIdentifier(identifier); + } + + /// + /// Returns a that is a copy of another . + /// + /// The type of this theme. + /// A unique identifier for this theme. + /// The friendly name for this theme, or null if no friendly name is specified. + /// A theme from which to copy colors. + /// A that is a copy of another . + public static Theme FromTheme(ThemeType type, string identifier, string name, Theme theme) + { + return new Theme(type, identifier, name, theme); + } + + /// + /// Returns a for the specified , or null if the specified + /// is null. + /// + /// A . + /// A for the specified , or null if the specified + /// is null. + public static Theme FromThemeInfo(ThemeInfo info) + { + return info != null ? new Theme(info) : null; + } + + #endregion + + #region Public Methods + + /// + /// Returns the unique colors used in this theme. + /// + /// The unique colors used in this theme. + public Color[] GetPalette() + { + Color[] allColors = + { + this.ProgressBarColor, + this.ProgressBackgroundColor, + this.BackgroundColor, + this.ExpirationFlashColor, + this.PrimaryTextColor, + this.PrimaryHintColor, + this.SecondaryTextColor, + this.SecondaryHintColor, + this.ButtonColor, + this.ButtonHoverColor + }; + + return allColors.Distinct().ToArray(); + } + + /// + /// Sets all of the properties, except for and , from another + /// instance of the class. + /// + /// Another instance of the class. + public void Set(Theme theme) + { + this.Name = theme.Name; + this.BackgroundColor = theme.BackgroundColor; + this.ProgressBarColor = theme.ProgressBarColor; + this.ProgressBackgroundColor = theme.ProgressBackgroundColor; + this.ExpirationFlashColor = theme.ExpirationFlashColor; + this.PrimaryTextColor = theme.PrimaryTextColor; + this.PrimaryHintColor = theme.PrimaryHintColor; + this.SecondaryTextColor = theme.SecondaryTextColor; + this.SecondaryHintColor = theme.SecondaryHintColor; + this.ButtonColor = theme.ButtonColor; + this.ButtonHoverColor = theme.ButtonHoverColor; + } + + /// + /// Returns the representation of the used for XML serialization. + /// + /// The representation of the used for XML serialization. + public ThemeInfo ToThemeInfo() + { + return new ThemeInfo + { + Identifier = this.identifier, + Name = this.name, + BackgroundColor = this.backgroundColor, + ProgressBarColor = this.progressBarColor, + ProgressBackgroundColor = this.progressBackgroundColor, + ExpirationFlashColor = this.expirationFlashColor, + PrimaryTextColor = this.primaryTextColor, + PrimaryHintColor = this.primaryHintColor, + SecondaryTextColor = this.secondaryTextColor, + SecondaryHintColor = this.secondaryHintColor, + ButtonColor = this.buttonColor, + ButtonHoverColor = this.buttonHoverColor + }; + } + + #endregion + + #region Protected Methods + + /// + /// Raises the event. + /// + /// One or more property names. + protected void OnPropertyChanged(params string[] propertyNames) + { + PropertyChangedEventHandler eventHandler = this.PropertyChanged; + + if (eventHandler != null) + { + foreach (string propertyName in propertyNames) + { + eventHandler(this, new PropertyChangedEventArgs(propertyName)); + } + } + } + + #endregion + } +} diff --git a/Hourglass/Timing/Timer.cs b/Hourglass/Timing/Timer.cs index b815f37..63049a5 100644 --- a/Hourglass/Timing/Timer.cs +++ b/Hourglass/Timing/Timer.cs @@ -8,7 +8,6 @@ namespace Hourglass.Timing { using System; using System.Globalization; - using System.Resources; using Hourglass.Extensions; using Hourglass.Properties; diff --git a/Hourglass/Timing/TimerOptions.cs b/Hourglass/Timing/TimerOptions.cs index 7ba8330..8df5650 100644 --- a/Hourglass/Timing/TimerOptions.cs +++ b/Hourglass/Timing/TimerOptions.cs @@ -13,6 +13,32 @@ namespace Hourglass.Timing using Hourglass.Serialization; using Hourglass.Windows; + /// + /// Modes indicating what information to display in the timer window title. + /// + public enum WindowTitleMode + { + /// + /// The timer window title is set to show the application name. + /// + ApplicationName, + + /// + /// The timer window title is set to show the time left. + /// + TimeLeft, + + /// + /// The timer window title is set to show the time elapsed. + /// + TimeElapsed, + + /// + /// The timer window title is set to show the timer title. + /// + TimerTitle + } + /// /// Configuration data for a timer. /// @@ -78,9 +104,14 @@ public class TimerOptions : INotifyPropertyChanged private bool loopSound; /// - /// The color of the timer progress bar. + /// The theme of the timer window. + /// + private Theme theme; + + /// + /// A value indicating what information to display in the timer window title. /// - private Color color; + private WindowTitleMode windowTitleMode; /// /// The size, position, and state of the timer window. @@ -105,9 +136,10 @@ public TimerOptions() this.popUpWhenExpired = true; this.closeWhenExpired = false; this.shutDownWhenExpired = false; - this.color = Color.DefaultColor; + this.theme = Theme.DefaultTheme; this.sound = Sound.DefaultSound; this.loopSound = false; + this.windowTitleMode = WindowTitleMode.ApplicationName; this.windowSize = new WindowSize( new Rect(double.PositiveInfinity, double.PositiveInfinity, 350, 150), WindowState.Normal, @@ -348,24 +380,24 @@ public bool ShutDownWhenExpired } /// - /// Gets or sets the color of the timer progress bar. + /// Gets or sets the theme of the timer window. /// - public Color Color + public Theme Theme { get { - return this.color; + return this.theme; } set { - if (object.ReferenceEquals(this.color, value)) + if (object.ReferenceEquals(this.theme, value)) { return; } - this.color = value; - this.OnPropertyChanged("Color"); + this.theme = value; + this.OnPropertyChanged("Theme"); } } @@ -414,6 +446,28 @@ public bool LoopSound } } + /// + /// Gets or sets a value indicating what information to display in the timer window title. + /// + public WindowTitleMode WindowTitleMode + { + get + { + return this.windowTitleMode; + } + + set + { + if (this.windowTitleMode == value) + { + return; + } + + this.windowTitleMode = value; + this.OnPropertyChanged("WindowTitleMode"); + } + } + /// /// Gets or sets the size, position, and state of the timer window. /// @@ -484,10 +538,27 @@ public void Set(TimerOptions options) this.popUpWhenExpired = options.popUpWhenExpired; this.closeWhenExpired = options.closeWhenExpired; this.shutDownWhenExpired = options.shutDownWhenExpired; - this.color = options.color; + this.theme = options.theme; this.sound = options.sound; this.loopSound = options.loopSound; + this.windowTitleMode = options.windowTitleMode; this.windowSize = WindowSize.FromWindowSize(options.WindowSize); + + this.OnPropertyChanged( + "Title", + "AlwaysOnTop", + "PromptOnExit", + "DoNotKeepComputerAwake", + "ShowTimeElapsed", + "LoopTimer", + "PopUpWhenExpired", + "CloseWhenExpired", + "ShutDownWhenExpired", + "Theme", + "Sound", + "LoopSound", + "WindowTitleMode", + "WindowSize"); } /// @@ -510,10 +581,27 @@ public void Set(TimerOptionsInfo info) this.popUpWhenExpired = info.PopUpWhenExpired; this.closeWhenExpired = info.CloseWhenExpired; this.shutDownWhenExpired = info.ShutDownWhenExpired; - this.color = Color.FromIdentifier(info.ColorIdentifier); + this.theme = Theme.FromIdentifier(info.ThemeIdentifier); this.sound = Sound.FromIdentifier(info.SoundIdentifier); this.loopSound = info.LoopSound; + this.windowTitleMode = info.WindowTitleMode; this.windowSize = WindowSize.FromWindowSizeInfo(info.WindowSize); + + this.OnPropertyChanged( + "Title", + "AlwaysOnTop", + "PromptOnExit", + "DoNotKeepComputerAwake", + "ShowTimeElapsed", + "LoopTimer", + "PopUpWhenExpired", + "CloseWhenExpired", + "ShutDownWhenExpired", + "Theme", + "Sound", + "LoopSound", + "WindowTitleMode", + "WindowSize"); } /// @@ -533,9 +621,10 @@ public TimerOptionsInfo ToTimerOptionsInfo() PopUpWhenExpired = this.popUpWhenExpired, CloseWhenExpired = this.closeWhenExpired, ShutDownWhenExpired = this.shutDownWhenExpired, - ColorIdentifier = this.color?.Identifier, + ThemeIdentifier = this.theme?.Identifier, SoundIdentifier = this.sound?.Identifier, LoopSound = this.loopSound, + WindowTitleMode = this.windowTitleMode, WindowSize = WindowSizeInfo.FromWindowSize(this.windowSize) }; } diff --git a/Hourglass/Windows/ColorControl.xaml b/Hourglass/Windows/ColorControl.xaml new file mode 100644 index 0000000..0d724c7 --- /dev/null +++ b/Hourglass/Windows/ColorControl.xaml @@ -0,0 +1,37 @@ + + + diff --git a/Hourglass/Windows/ColorControl.xaml.cs b/Hourglass/Windows/ColorControl.xaml.cs new file mode 100644 index 0000000..fe8687d --- /dev/null +++ b/Hourglass/Windows/ColorControl.xaml.cs @@ -0,0 +1,103 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) Chris Dziemborowicz. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Hourglass.Windows +{ + using System; + using System.Linq; + using System.Windows; + using System.Windows.Forms; + using System.Windows.Media; + + using Hourglass.Extensions; + using Hourglass.Timing; + + /// + /// A control for displaying and selecting a . + /// + public partial class ColorControl + { + /// + /// A that specifies the text label. + /// + public static readonly DependencyProperty TextProperty + = DependencyProperty.Register("Text", typeof(string), typeof(ColorControl)); + + /// + /// A that specifies the color. + /// + public static readonly DependencyProperty ColorProperty + = DependencyProperty.Register("Color", typeof(Color), typeof(ColorControl)); + + /// + /// A that specifies the theme to which the color belongs. + /// + public static readonly DependencyProperty ThemeProperty + = DependencyProperty.Register("Theme", typeof(Theme), typeof(ColorControl)); + + /// + /// Initializes a new instance of the class. + /// + public ColorControl() + { + this.InitializeComponent(); + } + + /// + /// Occurs when the property changes. + /// + public event EventHandler ColorChanged; + + /// + /// Gets or sets the text label. + /// + public string Text + { + get { return (string)this.GetValue(TextProperty); } + set { this.SetValue(TextProperty, value); } + } + + /// + /// Gets or sets the color. + /// + public Color Color + { + get { return (Color)this.GetValue(ColorProperty); } + set { this.SetValue(ColorProperty, value); } + } + + /// + /// Gets or sets the theme to which the color belongs. + /// + public Theme Theme + { + get { return (Theme)this.GetValue(ThemeProperty); } + set { this.SetValue(ThemeProperty, value); } + } + + /// + /// Invoked when the is clicked. + /// + /// The . + /// The event data. + private void ButtonClick(object sender, RoutedEventArgs e) + { + ColorDialog dialog = new ColorDialog { AnyColor = true, FullOpen = true }; + + if (this.Theme != null) + { + dialog.CustomColors = this.Theme.GetPalette().Select(c => c.ToInt()).ToArray(); + } + + DialogResult result = dialog.ShowDialog(); + if (result == DialogResult.OK) + { + this.Color = Color.FromRgb(dialog.Color.R, dialog.Color.G, dialog.Color.B); + this.ColorChanged?.Invoke(this /* sender */, EventArgs.Empty); + } + } + } +} diff --git a/Hourglass/Windows/ContextMenu.cs b/Hourglass/Windows/ContextMenu.cs index 9c7265a..470b654 100644 --- a/Hourglass/Windows/ContextMenu.cs +++ b/Hourglass/Windows/ContextMenu.cs @@ -11,7 +11,6 @@ namespace Hourglass.Windows using System.Linq; using System.Windows; using System.Windows.Controls; - using System.Windows.Input; using System.Windows.Media; using System.Windows.Threading; @@ -20,10 +19,6 @@ namespace Hourglass.Windows using Hourglass.Properties; using Hourglass.Timing; - using Color = Hourglass.Timing.Color; - using ColorDialog = System.Windows.Forms.ColorDialog; - using DialogResult = System.Windows.Forms.DialogResult; - /// /// A for the . /// @@ -107,24 +102,29 @@ public class ContextMenu : System.Windows.Controls.ContextMenu private MenuItem clearSavedTimersMenuItem; /// - /// The "Color" . + /// The "Theme" . + /// + private MenuItem themeMenuItem; + + /// + /// The "Light theme" . /// - private MenuItem colorMenuItem; + private MenuItem lightThemeMenuItem; /// - /// The "Color" s associated with s. + /// The "Dark theme" . /// - private IList selectableColorMenuItems; + private MenuItem darkThemeMenuItem; /// - /// The "Add custom color..." . + /// The "Manage themes" . /// - private MenuItem addCustomColorMenuItem; + private MenuItem manageThemesMenuItem; /// - /// The "Clear custom colors" . + /// The "Theme" s associated with s. /// - private MenuItem clearCustomColorsMenuItem; + private IList selectableThemeMenuItems; /// /// The "Sound" . @@ -166,6 +166,36 @@ public class ContextMenu : System.Windows.Controls.ContextMenu /// private MenuItem shutDownWhenExpiredMenuItem; + /// + /// The "Window title" . + /// + private MenuItem windowTitleMenuItem; + + /// + /// The "Application name" window title . + /// + private MenuItem applicationNameWindowTitleMenuItem; + + /// + /// The "Time left" window title . + /// + private MenuItem timeLeftWindowTitleMenuItem; + + /// + /// The "Time elapsed" window title . + /// + private MenuItem timeElapsedWindowTitleMenuItem; + + /// + /// The "Timer title" window title . + /// + private MenuItem timerTitleWindowTitleMenuItem; + + /// + /// The "Window title" s associated with s. + /// + private IList selectableWindowTitleMenuItems; + /// /// The "Close" . /// @@ -221,8 +251,9 @@ public void Bind(TimerWindow window) this.dispatcherTimer.Interval = TimeSpan.FromSeconds(1); this.dispatcherTimer.Tick += this.DispatcherTimerTick; - this.selectableColorMenuItems = new List(); + this.selectableThemeMenuItems = new List(); this.selectableSoundMenuItems = new List(); + this.selectableWindowTitleMenuItems = new List(); // Build the menu this.BuildMenu(); @@ -242,7 +273,7 @@ private void WindowContextMenuOpening(object sender, ContextMenuEventArgs e) // Update dynamic items this.UpdateRecentInputsMenuItem(); this.UpdateSavedTimersMenuItem(); - this.UpdateColorMenuItem(); + this.UpdateThemeMenuItem(); this.UpdateSoundMenuItem(); // Update binding @@ -326,12 +357,28 @@ private void UpdateMenuFromOptions() this.closeWhenExpiredMenuItem.IsEnabled = false; } - // Color - foreach (MenuItem menuItem in this.selectableColorMenuItems) + // Theme + foreach (MenuItem menuItem in this.selectableThemeMenuItems) { - menuItem.IsChecked = object.Equals(menuItem.Tag, this.timerWindow.Options.Color); + Theme menuItemTheme = (Theme)menuItem.Tag; + menuItem.IsChecked = menuItemTheme == this.timerWindow.Options.Theme; + if (this.timerWindow.Options.Theme.Type == ThemeType.UserProvided) + { + menuItem.Visibility = menuItemTheme.Type == ThemeType.BuiltInLight || menuItemTheme.Type == ThemeType.UserProvided + ? Visibility.Visible + : Visibility.Collapsed; + } + else + { + menuItem.Visibility = menuItemTheme.Type == this.timerWindow.Options.Theme.Type || menuItemTheme.Type == ThemeType.UserProvided + ? Visibility.Visible + : Visibility.Collapsed; + } } + this.lightThemeMenuItem.IsChecked = this.timerWindow.Options.Theme.Type == ThemeType.BuiltInLight; + this.darkThemeMenuItem.IsChecked = this.timerWindow.Options.Theme.Type == ThemeType.BuiltInDark; + // Sound foreach (MenuItem menuItem in this.selectableSoundMenuItems) { @@ -361,6 +408,13 @@ private void UpdateMenuFromOptions() this.shutDownWhenExpiredMenuItem.IsChecked = false; this.shutDownWhenExpiredMenuItem.IsEnabled = false; } + + // Window title + foreach (MenuItem menuItem in this.selectableWindowTitleMenuItems) + { + WindowTitleMode windowTitleMode = (WindowTitleMode)menuItem.Tag; + menuItem.IsChecked = windowTitleMode == this.timerWindow.Options.WindowTitleMode; + } } /// @@ -373,7 +427,7 @@ private void UpdateOptionsFromMenu() // Full screen this.timerWindow.IsFullScreen = this.fullScreenMenuItem.IsChecked; - + // Prompt on exit this.timerWindow.Options.PromptOnExit = this.promptOnExitMenuItem.IsChecked; @@ -395,10 +449,6 @@ private void UpdateOptionsFromMenu() this.timerWindow.Options.CloseWhenExpired = this.closeWhenExpiredMenuItem.IsChecked; } - // Color - MenuItem selectedColorMenuItem = this.selectableColorMenuItems.FirstOrDefault(mi => mi.IsChecked); - this.timerWindow.Options.Color = selectedColorMenuItem != null ? selectedColorMenuItem.Tag as Color : Color.DefaultColor; - // Sound MenuItem selectedSoundMenuItem = this.selectableSoundMenuItems.FirstOrDefault(mi => mi.IsChecked); this.timerWindow.Options.Sound = selectedSoundMenuItem != null ? selectedSoundMenuItem.Tag as Sound : null; @@ -420,6 +470,12 @@ private void UpdateOptionsFromMenu() { this.timerWindow.Options.ShutDownWhenExpired = this.shutDownWhenExpiredMenuItem.IsChecked; } + + // Window title + MenuItem selectedWindowTitleMenuItem = this.selectableWindowTitleMenuItems.FirstOrDefault(mi => mi.IsChecked); + this.timerWindow.Options.WindowTitleMode = selectedWindowTitleMenuItem != null + ? (WindowTitleMode)selectedWindowTitleMenuItem.Tag + : WindowTitleMode.ApplicationName; } /// @@ -444,6 +500,7 @@ private void BuildMenu() { this.Items.Clear(); + // New timer this.newTimerMenuItem = new MenuItem(); this.newTimerMenuItem.Header = Properties.Resources.ContextMenuNewTimerMenuItem; this.newTimerMenuItem.Click += this.NewTimerMenuItemClick; @@ -451,24 +508,28 @@ private void BuildMenu() this.Items.Add(new Separator()); + // Always on top this.alwaysOnTopMenuItem = new MenuItem(); this.alwaysOnTopMenuItem.Header = Properties.Resources.ContextMenuAlwaysOnTopMenuItem; this.alwaysOnTopMenuItem.IsCheckable = true; this.alwaysOnTopMenuItem.Click += this.CheckableMenuItemClick; this.Items.Add(this.alwaysOnTopMenuItem); + // Full screen this.fullScreenMenuItem = new MenuItem(); this.fullScreenMenuItem.Header = Properties.Resources.ContextMenuFullScreenMenuItem; this.fullScreenMenuItem.IsCheckable = true; this.fullScreenMenuItem.Click += this.CheckableMenuItemClick; this.Items.Add(this.fullScreenMenuItem); + // Prompt on exit this.promptOnExitMenuItem = new MenuItem(); this.promptOnExitMenuItem.Header = Properties.Resources.ContextMenuPromptOnExitMenuItem; this.promptOnExitMenuItem.IsCheckable = true; this.promptOnExitMenuItem.Click += this.CheckableMenuItemClick; this.Items.Add(this.promptOnExitMenuItem); + // Show in notification area this.showInNotificationAreaMenuItem = new MenuItem(); this.showInNotificationAreaMenuItem.Header = Properties.Resources.ContextMenuShowInNotificationAreaMenuItem; this.showInNotificationAreaMenuItem.IsCheckable = true; @@ -477,18 +538,21 @@ private void BuildMenu() this.Items.Add(new Separator()); + // Loop timer this.loopTimerMenuItem = new MenuItem(); this.loopTimerMenuItem.Header = Properties.Resources.ContextMenuLoopTimerMenuItem; this.loopTimerMenuItem.IsCheckable = true; this.loopTimerMenuItem.Click += this.CheckableMenuItemClick; this.Items.Add(this.loopTimerMenuItem); + // Pop up when expired this.popUpWhenExpiredMenuItem = new MenuItem(); this.popUpWhenExpiredMenuItem.Header = Properties.Resources.ContextMenuPopUpWhenExpiredMenuItem; this.popUpWhenExpiredMenuItem.IsCheckable = true; this.popUpWhenExpiredMenuItem.Click += this.CheckableMenuItemClick; this.Items.Add(this.popUpWhenExpiredMenuItem); + // Close when expired this.closeWhenExpiredMenuItem = new MenuItem(); this.closeWhenExpiredMenuItem.Header = Properties.Resources.ContextMenuCloseWhenExpiredMenuItem; this.closeWhenExpiredMenuItem.IsCheckable = true; @@ -497,20 +561,24 @@ private void BuildMenu() this.Items.Add(new Separator()); + // Recent inputs this.recentInputsMenuItem = new MenuItem(); this.recentInputsMenuItem.Header = Properties.Resources.ContextMenuRecentInputsMenuItem; this.Items.Add(this.recentInputsMenuItem); + // Saved timers this.savedTimersMenuItem = new MenuItem(); this.savedTimersMenuItem.Header = Properties.Resources.ContextMenuSavedTimersMenuItem; this.Items.Add(this.savedTimersMenuItem); this.Items.Add(new Separator()); - this.colorMenuItem = new MenuItem(); - this.colorMenuItem.Header = Properties.Resources.ContextMenuColorMenuItem; - this.Items.Add(this.colorMenuItem); + // Theme + this.themeMenuItem = new MenuItem(); + this.themeMenuItem.Header = Properties.Resources.ContextMenuThemeMenuItem; + this.Items.Add(this.themeMenuItem); + // Sound this.soundMenuItem = new MenuItem(); this.soundMenuItem.Header = Properties.Resources.ContextMenuSoundMenuItem; this.Items.Add(this.soundMenuItem); @@ -518,36 +586,87 @@ private void BuildMenu() Separator separator = new Separator(); this.Items.Add(separator); + // Advanced options this.advancedOptionsMenuItem = new MenuItem(); this.advancedOptionsMenuItem.Header = Properties.Resources.ContextMenuAdvancedOptionsMenuItem; this.Items.Add(this.advancedOptionsMenuItem); + // Do not keep computer awake this.doNotKeepComputerAwakeMenuItem = new MenuItem(); this.doNotKeepComputerAwakeMenuItem.Header = Properties.Resources.ContextMenuDoNotKeepComputerAwakeMenuItem; this.doNotKeepComputerAwakeMenuItem.IsCheckable = true; this.doNotKeepComputerAwakeMenuItem.Click += this.CheckableMenuItemClick; this.advancedOptionsMenuItem.Items.Add(this.doNotKeepComputerAwakeMenuItem); + // Open saved timers on startup this.openSavedTimersOnStartupMenuItem = new MenuItem(); this.openSavedTimersOnStartupMenuItem.Header = Properties.Resources.ContextMenuOpenSavedTimersOnStartupMenuItem; this.openSavedTimersOnStartupMenuItem.IsCheckable = true; this.openSavedTimersOnStartupMenuItem.Click += this.CheckableMenuItemClick; this.advancedOptionsMenuItem.Items.Add(this.openSavedTimersOnStartupMenuItem); + // Show time elapsed this.showTimeElapsedMenuItem = new MenuItem(); this.showTimeElapsedMenuItem.Header = Properties.Resources.ContextMenuShowTimeElapsedMenuItem; this.showTimeElapsedMenuItem.IsCheckable = true; this.showTimeElapsedMenuItem.Click += this.CheckableMenuItemClick; this.advancedOptionsMenuItem.Items.Add(this.showTimeElapsedMenuItem); + // Shut down when expired this.shutDownWhenExpiredMenuItem = new MenuItem(); this.shutDownWhenExpiredMenuItem.Header = Properties.Resources.ContextMenuShutDownWhenExpiredMenuItem; this.shutDownWhenExpiredMenuItem.IsCheckable = true; this.shutDownWhenExpiredMenuItem.Click += this.CheckableMenuItemClick; this.advancedOptionsMenuItem.Items.Add(this.shutDownWhenExpiredMenuItem); + // Window title + this.windowTitleMenuItem = new MenuItem(); + this.windowTitleMenuItem.Header = Properties.Resources.ContextMenuWindowTitleMenuItem; + this.advancedOptionsMenuItem.Items.Add(this.windowTitleMenuItem); + + // Application name (window title) + this.applicationNameWindowTitleMenuItem = new MenuItem(); + this.applicationNameWindowTitleMenuItem.Header = Properties.Resources.ContextMenuApplicationNameWindowTitleMenuItem; + this.applicationNameWindowTitleMenuItem.IsCheckable = true; + this.applicationNameWindowTitleMenuItem.Tag = WindowTitleMode.ApplicationName; + this.applicationNameWindowTitleMenuItem.Click += this.WindowTitleMenuItemClick; + this.applicationNameWindowTitleMenuItem.Click += this.CheckableMenuItemClick; + this.windowTitleMenuItem.Items.Add(this.applicationNameWindowTitleMenuItem); + this.selectableWindowTitleMenuItems.Add(this.applicationNameWindowTitleMenuItem); + + // Time left (window title) + this.timeLeftWindowTitleMenuItem = new MenuItem(); + this.timeLeftWindowTitleMenuItem.Header = Properties.Resources.ContextMenuTimeLeftWindowTitleMenuItem; + this.timeLeftWindowTitleMenuItem.IsCheckable = true; + this.timeLeftWindowTitleMenuItem.Tag = WindowTitleMode.TimeLeft; + this.timeLeftWindowTitleMenuItem.Click += this.WindowTitleMenuItemClick; + this.timeLeftWindowTitleMenuItem.Click += this.CheckableMenuItemClick; + this.windowTitleMenuItem.Items.Add(this.timeLeftWindowTitleMenuItem); + this.selectableWindowTitleMenuItems.Add(this.timeLeftWindowTitleMenuItem); + + // Time elapsed (window title) + this.timeElapsedWindowTitleMenuItem = new MenuItem(); + this.timeElapsedWindowTitleMenuItem.Header = Properties.Resources.ContextMenuTimeElapsedWindowTitleMenuItem; + this.timeElapsedWindowTitleMenuItem.IsCheckable = true; + this.timeElapsedWindowTitleMenuItem.Tag = WindowTitleMode.TimeElapsed; + this.timeElapsedWindowTitleMenuItem.Click += this.WindowTitleMenuItemClick; + this.timeElapsedWindowTitleMenuItem.Click += this.CheckableMenuItemClick; + this.windowTitleMenuItem.Items.Add(this.timeElapsedWindowTitleMenuItem); + this.selectableWindowTitleMenuItems.Add(this.timeElapsedWindowTitleMenuItem); + + // Timer title (window title) + this.timerTitleWindowTitleMenuItem = new MenuItem(); + this.timerTitleWindowTitleMenuItem.Header = Properties.Resources.ContextMenuTimerTitleWindowTitleMenuItem; + this.timerTitleWindowTitleMenuItem.IsCheckable = true; + this.timerTitleWindowTitleMenuItem.Tag = WindowTitleMode.TimerTitle; + this.timerTitleWindowTitleMenuItem.Click += this.WindowTitleMenuItemClick; + this.timerTitleWindowTitleMenuItem.Click += this.CheckableMenuItemClick; + this.windowTitleMenuItem.Items.Add(this.timerTitleWindowTitleMenuItem); + this.selectableWindowTitleMenuItems.Add(this.timerTitleWindowTitleMenuItem); + this.Items.Add(new Separator()); + // Close this.closeMenuItem = new MenuItem(); this.closeMenuItem.Header = Properties.Resources.ContextMenuCloseMenuItem; this.closeMenuItem.Click += this.CloseMenuItemClick; @@ -752,7 +871,7 @@ private object GetIconForTimer(Timer timer) if (timer.State == TimerState.Expired) { Border progress = new Border(); - progress.Background = new SolidColorBrush(System.Windows.Media.Color.FromRgb(199, 80, 80)); + progress.Background = new SolidColorBrush(Color.FromRgb(199, 80, 80)); progress.Width = 16; progress.Height = 6; @@ -761,7 +880,7 @@ private object GetIconForTimer(Timer timer) else if (timer.TimeLeftAsPercentage.HasValue) { Border progress = new Border(); - progress.Background = timer.Options.Color.Brush; + progress.Background = timer.Options.Theme.ProgressBarBrush; progress.HorizontalAlignment = HorizontalAlignment.Left; progress.Width = timer.TimeLeftAsPercentage.Value / 100.0 * 16.0; progress.Height = 6; @@ -860,98 +979,106 @@ private void ClearSavedTimersMenuItemClick(object sender, RoutedEventArgs e) #endregion - #region Private Methods (Color) + #region Private Methods (Theme) /// - /// Updates the . + /// Updates the . /// - private void UpdateColorMenuItem() + private void UpdateThemeMenuItem() { - this.colorMenuItem.Items.Clear(); - this.selectableColorMenuItems.Clear(); + this.themeMenuItem.Items.Clear(); + this.selectableThemeMenuItems.Clear(); + + // Switch between light and dark themes + if (this.lightThemeMenuItem == null) + { + this.lightThemeMenuItem = new MenuItem(); + this.lightThemeMenuItem.Header = Properties.Resources.ContextMenuLightThemeMenuItem; + this.lightThemeMenuItem.Tag = ThemeType.BuiltInLight; + this.lightThemeMenuItem.Click += this.ThemeTypeMenuItemClick; + } + + this.themeMenuItem.Items.Add(this.lightThemeMenuItem); - // Ensure the current timer color is registered - if (!this.timerWindow.Options.Color.IsBuiltIn) + if (this.darkThemeMenuItem == null) { - ColorManager.Instance.Add(this.timerWindow.Options.Color); + this.darkThemeMenuItem = new MenuItem(); + this.darkThemeMenuItem.Header = Properties.Resources.ContextMenuDarkThemeMenuItem; + this.darkThemeMenuItem.Tag = ThemeType.BuiltInDark; + this.darkThemeMenuItem.Click += this.ThemeTypeMenuItemClick; } - // Colors - this.CreateColorMenuItem(Color.DefaultColor); - this.CreateColorMenuItemsFromList(ColorManager.Instance.BuiltInColors.Where(c => c != Color.DefaultColor).ToList()); - this.CreateColorMenuItemsFromList(ColorManager.Instance.UserProvidedColors); + this.themeMenuItem.Items.Add(this.darkThemeMenuItem); - // Custom color actions - this.colorMenuItem.Items.Add(new Separator()); + // Built-in themes + this.CreateThemeMenuItemsFromList(ThemeManager.Instance.BuiltInThemes); - if (this.addCustomColorMenuItem == null) + // User-provided themes + if (ThemeManager.Instance.UserProvidedThemes.Count > 0) { - this.addCustomColorMenuItem = new MenuItem(); - this.addCustomColorMenuItem.Header = Properties.Resources.ContextMenuAddCustomColorMenuItem; - this.addCustomColorMenuItem.Click += this.AddCustomColorMenuItemClick; + this.CreateThemeMenuItemsFromList(ThemeManager.Instance.UserProvidedThemes); } - this.colorMenuItem.Items.Add(this.addCustomColorMenuItem); + // Manage themes + this.themeMenuItem.Items.Add(new Separator()); - if (this.clearCustomColorsMenuItem == null) + if (this.manageThemesMenuItem == null) { - this.clearCustomColorsMenuItem = new MenuItem(); - this.clearCustomColorsMenuItem.Header = Properties.Resources.ContextMenuClearCustomColorsMenuItem; - this.clearCustomColorsMenuItem.Click += this.ClearCustomColorsMenuItemClick; + this.manageThemesMenuItem = new MenuItem(); + this.manageThemesMenuItem.Header = Properties.Resources.ContextMenuManageThemesMenuItem; + this.manageThemesMenuItem.Click += this.ManageThemesMenuItemClick; } - this.colorMenuItem.Items.Add(this.clearCustomColorsMenuItem); + this.themeMenuItem.Items.Add(this.manageThemesMenuItem); } /// - /// Creates a for a . + /// Creates a for each in the collection. /// - /// A . - private void CreateColorMenuItem(Color color) + /// A collection of s. + private void CreateThemeMenuItemsFromList(IList themes) { - MenuItem menuItem = new MenuItem(); - menuItem.Header = this.GetHeaderForColor(color); - menuItem.Tag = color; - menuItem.IsCheckable = true; - menuItem.Click += this.ColorMenuItemClick; - menuItem.Click += this.CheckableMenuItemClick; + this.themeMenuItem.Items.Add(new Separator()); - this.colorMenuItem.Items.Add(menuItem); - this.selectableColorMenuItems.Add(menuItem); + foreach (Theme theme in themes) + { + this.CreateThemeMenuItem(theme); + } } /// - /// Creates a for each in the collection. + /// Creates a for a . /// - /// A collection of s. - private void CreateColorMenuItemsFromList(IList colors) + /// A . + private void CreateThemeMenuItem(Theme theme) { - if (colors.Count > 0) - { - this.colorMenuItem.Items.Add(new Separator()); - foreach (Color color in colors) - { - this.CreateColorMenuItem(color); - } - } + MenuItem menuItem = new MenuItem(); + menuItem.Header = this.GetHeaderForTheme(theme); + menuItem.Tag = theme; + menuItem.IsCheckable = true; + menuItem.Click += this.ThemeMenuItemClick; + menuItem.Click += this.CheckableMenuItemClick; + + this.themeMenuItem.Items.Add(menuItem); + this.selectableThemeMenuItems.Add(menuItem); } /// /// Returns an object that can be set for the of a that - /// displays a . + /// displays a . /// - /// A . + /// A . /// An object that can be set for the . - private object GetHeaderForColor(Color color) + private object GetHeaderForTheme(Theme theme) { Border border = new Border(); - border.Background = color.Brush; + border.Background = theme.ProgressBarBrush; border.CornerRadius = new CornerRadius(2); border.Width = 8; border.Height = 8; TextBlock textBlock = new TextBlock(); - textBlock.Text = color.Name ?? Properties.Resources.ContextMenuCustomColorMenuItem; + textBlock.Text = theme.Name ?? Properties.Resources.ContextMenuUnnamedTheme; textBlock.Margin = new Thickness(5, 0, 0, 0); StackPanel stackPanel = new StackPanel(); @@ -962,50 +1089,59 @@ private object GetHeaderForColor(Color color) } /// - /// Invoked when a color is clicked. + /// Invoked when a theme type is clicked. /// /// The where the event handler is attached. /// The event data. - private void ColorMenuItemClick(object sender, RoutedEventArgs e) + private void ThemeTypeMenuItemClick(object sender, RoutedEventArgs e) { - foreach (MenuItem menuItem in this.selectableColorMenuItems) + MenuItem clickedMenuItem = (MenuItem)sender; + ThemeType type = (ThemeType)clickedMenuItem.Tag; + + if (type == ThemeType.BuiltInDark) { - menuItem.IsChecked = object.ReferenceEquals(menuItem, sender); + this.timerWindow.Options.Theme = this.timerWindow.Options.Theme.DarkVariant; + } + else + { + this.timerWindow.Options.Theme = this.timerWindow.Options.Theme.LightVariant; } } /// - /// Invoked when the is clicked. + /// Invoked when a theme is clicked. /// /// The where the event handler is attached. /// The event data. - private void AddCustomColorMenuItemClick(object sender, RoutedEventArgs e) + private void ThemeMenuItemClick(object sender, RoutedEventArgs e) { - ColorDialog dialog = new ColorDialog(); - dialog.AnyColor = true; - dialog.FullOpen = true; - dialog.CustomColors = ColorManager.Instance.AllColors - .Where(c => c != Color.DefaultColor) - .Select(c => c.ToInt()) - .ToArray(); - - DialogResult result = dialog.ShowDialog(); - if (result == DialogResult.OK) + foreach (MenuItem menuItem in this.selectableThemeMenuItems) { - Color color = new Color(dialog.Color.R, dialog.Color.G, dialog.Color.B); - ColorManager.Instance.Add(color); - this.timerWindow.Options.Color = color; + menuItem.IsChecked = object.ReferenceEquals(menuItem, sender); } + + MenuItem selectedMenuItem = (MenuItem)sender; + this.timerWindow.Options.Theme = (Theme)selectedMenuItem.Tag; } /// - /// Invoked when the is clicked. + /// Invoked when the "Manage themes" is clicked. /// /// The where the event handler is attached. /// The event data. - private void ClearCustomColorsMenuItemClick(object sender, RoutedEventArgs e) + private void ManageThemesMenuItemClick(object sender, RoutedEventArgs e) { - ColorManager.Instance.ClearUserProvidedColors(); + ThemeManagerWindow window = Application.Current.Windows.OfType().FirstOrDefault(); + if (window != null) + { + window.SetTimerWindow(this.timerWindow); + window.BringToFrontAndActivate(); + } + else + { + window = new ThemeManagerWindow(this.timerWindow); + window.Show(); + } } #endregion @@ -1087,6 +1223,23 @@ private void SoundMenuItemClick(object sender, RoutedEventArgs e) #endregion + #region Private Methods (Window Title) + + /// + /// Invoked when a window title is clicked. + /// + /// The where the event handler is attached. + /// The event data. + private void WindowTitleMenuItemClick(object sender, RoutedEventArgs e) + { + foreach (MenuItem menuItem in this.selectableWindowTitleMenuItems) + { + menuItem.IsChecked = object.ReferenceEquals(menuItem, sender); + } + } + + #endregion + #region Private Methods (Close) /// diff --git a/Hourglass/Windows/MultiplierConverter.cs b/Hourglass/Windows/MultiplierConverter.cs deleted file mode 100644 index af21734..0000000 --- a/Hourglass/Windows/MultiplierConverter.cs +++ /dev/null @@ -1,69 +0,0 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) Chris Dziemborowicz. All rights reserved. -// -// -------------------------------------------------------------------------------------------------------------------- - -namespace Hourglass.Windows -{ - using System; - using System.Globalization; - using System.Windows.Data; - - /// - /// Multiplies values by a constant value. - /// - [ValueConversion(typeof(double), typeof(double))] - public class MultiplierConverter : IValueConverter - { - /// - /// The constant value to multiply by when converting. - /// - private readonly double multiplier; - - /// - /// Initializes a new instance of the class with the specified multiplier. - /// - /// The constant value to multiply by when converting. - public MultiplierConverter(double multiplier) - { - this.multiplier = multiplier; - } - - /// - /// Converts a value. - /// - /// The value produced by the binding source. - /// The type of the binding target property. - /// The converter parameter to use. - /// The culture to use in the converter. - /// The value multiplied by the multiplier, or null if the value was null. - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - if (value == null) - { - return null; - } - - return (double)value * this.multiplier; - } - - /// - /// Converts a value back. - /// - /// The value that is produced by the binding target. - /// The type to convert to. - /// The converter parameter to use. - /// The culture to use in the converter. - /// The value divided by the multiplier, or null if the value was null. - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - if (value == null) - { - return null; - } - - return (double)value / this.multiplier; - } - } -} diff --git a/Hourglass/Windows/ThemeManagerWindow.xaml b/Hourglass/Windows/ThemeManagerWindow.xaml new file mode 100644 index 0000000..276579d --- /dev/null +++ b/Hourglass/Windows/ThemeManagerWindow.xaml @@ -0,0 +1,57 @@ + + + + + + + + + + + +