diff options
-rw-r--r-- | WarframeClock.sln | 7 | ||||
-rw-r--r-- | WarframeClock/App.xaml.cs | 30 | ||||
-rw-r--r-- | WarframeClock/BindableBase.cs | 15 | ||||
-rw-r--r-- | WarframeClock/Native.cs | 4 | ||||
-rw-r--r-- | WarframeClock/OverlayWindow.xaml | 73 | ||||
-rw-r--r-- | WarframeClock/OverlayWindow.xaml.cs | 200 | ||||
-rw-r--r-- | WarframeClock/OverlayWindowBase.cs | 141 | ||||
-rw-r--r-- | WarframeClock/OverlayWindowViewModel.cs | 66 | ||||
-rw-r--r-- | WarframeClock/Settings.cs | 92 | ||||
-rw-r--r-- | WarframeClock/WarframeClock.csproj | 5 |
10 files changed, 488 insertions, 145 deletions
diff --git a/WarframeClock.sln b/WarframeClock.sln index 26ab426..7c5ca3d 100644 --- a/WarframeClock.sln +++ b/WarframeClock.sln @@ -1,13 +1,14 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.28307.421 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.28705.295 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WarframeClock", "WarframeClock\WarframeClock.csproj", "{B6F30BD0-5917-4C89-8C8D-DEF0029E08A3}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{BDF96AA8-18DB-4063-BD9A-CDAE76F35F43}" ProjectSection(SolutionItems) = preProject - README.txt = README.txt + COPYING = COPYING + README = README EndProjectSection EndProject Global diff --git a/WarframeClock/App.xaml.cs b/WarframeClock/App.xaml.cs index 81b9ba0..b3393d3 100644 --- a/WarframeClock/App.xaml.cs +++ b/WarframeClock/App.xaml.cs @@ -10,11 +10,12 @@ namespace WarframeClock /// </summary> public partial class App : Application { - public static readonly string VersionString = "Warframe Clock Overlay 2019-02-14"; + public static readonly string VersionString = "Warframe Clock Overlay 2019-03-12"; + public static readonly string WebsiteUriString = "https://poepoe.org/warframe/clock/"; + public static readonly Uri WebsiteUri = new Uri(WebsiteUriString); public static readonly bool UseWorldStatePhp = true; - private Timer worldStateFetchTimer; - private System.Windows.Forms.NotifyIcon notifyIcon; + private Timer _worldStateFetchTimer; protected override void OnStartup(StartupEventArgs e) { @@ -22,12 +23,11 @@ namespace WarframeClock if (UseWorldStatePhp) StartWorldStateFetch(); - StartNotifyIcon(); } private void StartWorldStateFetch() { - worldStateFetchTimer = new Timer(state => + _worldStateFetchTimer = new Timer(state => { try { @@ -52,25 +52,9 @@ namespace WarframeClock }, null, TimeSpan.Zero, TimeSpan.FromMinutes(10)); } - private void StartNotifyIcon() + internal void Terminate() { - var iconMenu = new System.Windows.Forms.ContextMenu(); - iconMenu.MenuItems.Add(new System.Windows.Forms.MenuItem(VersionString) { Enabled = false }); - iconMenu.MenuItems.Add(new System.Windows.Forms.MenuItem("Quit", (_, __) => Terminate())); - - notifyIcon = new System.Windows.Forms.NotifyIcon - { - Text = "Warframe Clock", - ContextMenu = iconMenu, - Icon = WarframeClock.Properties.Resources.TrayIcon, - Visible = true - }; - } - - private void Terminate() - { - notifyIcon.Dispose(); - worldStateFetchTimer.Change(Timeout.Infinite, Timeout.Infinite); + _worldStateFetchTimer.Change(Timeout.Infinite, Timeout.Infinite); MainWindow?.Close(); } } diff --git a/WarframeClock/BindableBase.cs b/WarframeClock/BindableBase.cs new file mode 100644 index 0000000..3f81f24 --- /dev/null +++ b/WarframeClock/BindableBase.cs @@ -0,0 +1,15 @@ +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace WarframeClock +{ + internal abstract class BindableBase : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + protected void NotifyPropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + } +} diff --git a/WarframeClock/Native.cs b/WarframeClock/Native.cs index 8fccd5c..cebe362 100644 --- a/WarframeClock/Native.cs +++ b/WarframeClock/Native.cs @@ -3,7 +3,7 @@ using System.Runtime.InteropServices; namespace WarframeClock { - class Native + internal class Native { [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] @@ -26,4 +26,4 @@ namespace WarframeClock public int Bottom; } } -}
\ No newline at end of file +} diff --git a/WarframeClock/OverlayWindow.xaml b/WarframeClock/OverlayWindow.xaml index 2888f7d..3e14ba8 100644 --- a/WarframeClock/OverlayWindow.xaml +++ b/WarframeClock/OverlayWindow.xaml @@ -1,10 +1,11 @@ -<Window x:Class="WarframeClock.OverlayWindow" +<local:OverlayWindowBase x:Class="WarframeClock.OverlayWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WarframeClock" mc:Ignorable="d" + d:DataContext="{d:DesignInstance local:OverlayWindowViewModel, IsDesignTimeCreatable=True}" Title="WarframeClock OverlayWindow" WindowStyle="None" ShowInTaskbar="false" @@ -12,12 +13,64 @@ <Window.Background> <SolidColorBrush Opacity="0.0" Color="Black" /> </Window.Background> - <Label x:Name="OverlayLabel" - HorizontalAlignment="Left" - VerticalAlignment="Bottom" - FontFamily="Segoe UI" - FontSize="13" - Content="!!PLACEHOLDER!!" - Foreground="White" - Margin="20,0,0,20" /> -</Window> + <Grid> + <TextBlock x:Name="OverlayLabel" + HorizontalAlignment="{Binding HorizontalAlignment}" + VerticalAlignment="{Binding VerticalAlignment}" + Margin="{Binding Margin}" + FontFamily="{Binding FontFamily}" + FontSize="{Binding FontSize}" + Text="{Binding OverlayText}" + Foreground="White" /> + <Grid Visibility="{Binding SettingsControlVisibility}"> + <StackPanel Background="White" Orientation="Horizontal" + HorizontalAlignment="Center" VerticalAlignment="Center"> + <StackPanel> + <GroupBox Header="Settings"> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="Auto" /> + <ColumnDefinition Width="5" /> + <ColumnDefinition Width="*" /> + </Grid.ColumnDefinitions> + <Grid.RowDefinitions> + <RowDefinition /> + <RowDefinition /> + </Grid.RowDefinitions> + <TextBlock Grid.Row="0" Grid.Column="0">Font Settings:</TextBlock> + <Button Grid.Row="0" Grid.Column="2" Click="FontSettingsButton_Click">...</Button> + <TextBlock Grid.Row="1" Grid.Column="0">Position:</TextBlock> + <TextBlock Grid.Row="1" Grid.Column="2">Drag the clock overlay to reposition it.</TextBlock> + </Grid> + </GroupBox> + <GroupBox Header="About"> + <StackPanel> + <TextBlock> + <TextBlock Text="{x:Static local:App.VersionString}" /> + </TextBlock> + <TextBlock> + <Hyperlink NavigateUri="{x:Static local:App.WebsiteUri}" + RequestNavigate="Hyperlink_RequestNavigate"> + <TextBlock Text="{x:Static local:App.WebsiteUriString}" /> + </Hyperlink> + </TextBlock> + </StackPanel> + </GroupBox> + <Button Click="DoneSettingsButton_Click">Close</Button> + </StackPanel> + </StackPanel> + <TextBlock x:Name="OverlayLabelOverlay" + HorizontalAlignment="{Binding HorizontalAlignment}" + VerticalAlignment="{Binding VerticalAlignment}" + Margin="{Binding Margin}" + FontFamily="{Binding FontFamily}" + FontSize="{Binding FontSize}" + Text="{Binding OverlayText}" + Background="White" + Foreground="Black" + MouseMove="OverlayLabelOverlay_MouseMove" + MouseLeftButtonDown="OverlayLabelOverlay_MouseLeftButtonDown" + MouseLeftButtonUp="OverlayLabelOverlay_MouseLeftButtonUp" /> + </Grid> + </Grid> +</local:OverlayWindowBase> diff --git a/WarframeClock/OverlayWindow.xaml.cs b/WarframeClock/OverlayWindow.xaml.cs index 520828e..d5b0562 100644 --- a/WarframeClock/OverlayWindow.xaml.cs +++ b/WarframeClock/OverlayWindow.xaml.cs @@ -1,7 +1,8 @@ using System; using System.Diagnostics; using System.Windows; -using System.Windows.Interop; +using System.Windows.Input; +using System.Windows.Navigation; using System.Windows.Threading; namespace WarframeClock @@ -9,148 +10,133 @@ namespace WarframeClock /// <summary> /// Interaction logic for OverlayWindow.xaml /// </summary> - public partial class OverlayWindow : Window + public partial class OverlayWindow : OverlayWindowBase { - private readonly IntPtr windowHandle; - private readonly DispatcherTimer updateTimer; - private bool xTopmost; - private IntPtr targetWindowHandle; - private Native.Rect parentBounds; + private readonly OverlayWindowViewModel _vm; + private readonly System.Windows.Forms.NotifyIcon _notifyIcon; + private readonly DispatcherTimer _updateTimer; public OverlayWindow() { InitializeComponent(); - windowHandle = new WindowInteropHelper(this).EnsureHandle(); - xTopmost = false; + DataContext = _vm = new OverlayWindowViewModel(); - updateTimer = new DispatcherTimer(TimeSpan.FromMilliseconds(100), DispatcherPriority.Background, + var iconMenu = new System.Windows.Forms.ContextMenu(); + iconMenu.MenuItems.Add(new System.Windows.Forms.MenuItem(App.VersionString) { Enabled = false }); + iconMenu.MenuItems.Add(new System.Windows.Forms.MenuItem("Configure", + (_, __) => _vm.SettingsMode = true)); + iconMenu.MenuItems.Add(new System.Windows.Forms.MenuItem("Quit", + (_, __) => ((App)Application.Current).Terminate())); + + _notifyIcon = new System.Windows.Forms.NotifyIcon + { + ContextMenu = iconMenu, + Icon = Properties.Resources.TrayIcon, + Visible = true + }; + + _updateTimer = new DispatcherTimer(TimeSpan.FromMilliseconds(100), DispatcherPriority.Background, UpdateTimerTick, Dispatcher.CurrentDispatcher); - updateTimer.Start(); + _updateTimer.Start(); } protected override void OnClosed(EventArgs e) { base.OnClosed(e); - updateTimer.Stop(); + _notifyIcon.Dispose(); + _updateTimer.Stop(); } private void UpdateTimerTick(object sender, EventArgs e) { - if (!GetTargetWindow(out var newBounds)) - { - if (IsVisible) - { - Logging("Hide overlay; target window disappeared"); - Hide(); - } - return; - } + var str = Clock.OverlayString(); + _notifyIcon.Text = $"Warframe Clock - {str}"; - // Set top-most flag if the target window is foreground. Move to - // behind the current foreground window otherwise. - var currentActiveWindow = Native.GetForegroundWindow(); - if (currentActiveWindow != IntPtr.Zero && - (!IsVisible || currentActiveWindow == targetWindowHandle != xTopmost)) + var isActive = AdjustOverlay(); + if (isActive) { - // Setting Topmost to false brings the overlay to just - // behind the other top-most window -- above the foreground - // window. This is not what we want to do here. - // So, let's use SetWindowPos directly instead. - if (currentActiveWindow == targetWindowHandle) - { - Logging("Overlay is now top-most"); - xTopmost = true; - } - else - { - Logging($"Overlay is now non-top-most, behind {currentActiveWindow}"); - xTopmost = false; - } - - uint flags = - 0x0001 /* SWP_NOSIZE */ | - 0x0002 /* SWP_NOMOVE */ | - 0x0008 /* SWP_NOREDRAW */ | - 0x0010 /* SWP_NOACTIVATE */ | - 0x0200 /* SWP_NOOWNERZORDER */ | - 0x0400 /* SWP_NOSENDCHANGING */; - Native.SetWindowPos(windowHandle, xTopmost ? (IntPtr) (-1) : currentActiveWindow, 0, 0, 0, 0, flags); + _vm.OverlayText = str; } + } - // Adjust window position - if (!IsVisible || - parentBounds.Left != newBounds.Left || parentBounds.Right != newBounds.Right || - parentBounds.Top != newBounds.Top || parentBounds.Bottom != newBounds.Bottom) - { - parentBounds = newBounds; + private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e) + { + Process.Start(e.Uri.AbsoluteUri); + } - var width = parentBounds.Right - parentBounds.Left; - var height = parentBounds.Bottom - parentBounds.Top; + private void DoneSettingsButton_Click(object sender, RoutedEventArgs e) + { + Settings.Save(); + _vm.SettingsMode = false; + } - // Do not use window.Left (and so on) because they're DPI-aware. - // We don't want that.... - Logging($"Move overlay to: l={parentBounds.Left};t={parentBounds.Top};w={width};h={height}"); - Native.SetWindowPos(windowHandle, IntPtr.Zero, parentBounds.Left, parentBounds.Top, width, height, - 0x0004 /* SWP_NOZORDER */); - } + private Point _dragOriginalPosition; + private bool _dragStarted; + private Point _dragOffset; - // Render text - OverlayLabel.Content = Clock.OverlayString(); - if (!IsVisible) + private void OverlayLabelOverlay_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) + { + _dragStarted = true; + _dragOriginalPosition = OverlayLabelOverlay.TranslatePoint(new Point(0, 0), this); + _dragOffset = e.GetPosition(this); + ((UIElement)sender).CaptureMouse(); + } + + private void OverlayLabelOverlay_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) + { + if (_dragStarted) { - Logging("Show overlay"); - Show(); + ((UIElement)sender).ReleaseMouseCapture(); + _dragStarted = false; } } - private bool GetTargetWindow(out Native.Rect bounds) + private void OverlayLabelOverlay_MouseMove(object sender, MouseEventArgs e) { - if (targetWindowHandle == IntPtr.Zero) + if (_dragStarted) { - foreach (var process in Process.GetProcesses()) + var p = e.GetPosition(this); + var newX = _dragOriginalPosition.X + p.X - _dragOffset.X; + if (newX <= ActualWidth / 2) { - if (targetWindowHandle == IntPtr.Zero && - (process.ProcessName == "Warframe" || process.ProcessName == "Warframe.x64")) - { - try - { - targetWindowHandle = process.MainWindowHandle; - if (targetWindowHandle != IntPtr.Zero) - { - Logging($"Found a process with name={process.ProcessName}; " + - $"MainWindowHandle={targetWindowHandle}"); - } - } - catch (Exception) - { - // Ignore errors; maybe the process is just exiting. Let's retry during the next cycle. - } - } - - process.Dispose(); + Settings.DockToX = Settings.AlignmentX.Left; + Settings.MarginX = newX; + } + else + { + Settings.DockToX = Settings.AlignmentX.Right; + Settings.MarginX = ActualWidth - newX - OverlayLabelOverlay.ActualWidth; + } + var newY = _dragOriginalPosition.Y + p.Y - _dragOffset.Y; + if (newY <= ActualHeight / 2) + { + Settings.DockToY = Settings.AlignmentY.Top; + Settings.MarginY = newY; + } + else + { + Settings.DockToY = Settings.AlignmentY.Bottom; + Settings.MarginY = ActualHeight - newY - OverlayLabelOverlay.ActualHeight; } - } - - if (targetWindowHandle == IntPtr.Zero) - { - // Suppress compile error; caller must not use the value - bounds = new Native.Rect(); - return false; - } - if (!Native.GetWindowRect(targetWindowHandle, out bounds)) - { - Logging($"GetWindowRect failed; aborting this cycle"); - targetWindowHandle = IntPtr.Zero; - return false; + _vm.RefreshPosition(); } - - return true; } - private void Logging(string message) + private void FontSettingsButton_Click(object sender, RoutedEventArgs e) { - Console.WriteLine($"ClockOverlay: {message}"); + var dialog = new System.Windows.Forms.FontDialog + { + Font = new System.Drawing.Font( + new System.Drawing.FontFamily(Settings.FontFamilyName), + (float) (Settings.FontSize * 72.0 / 96.0)) + }; + if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) + { + Settings.FontFamilyName = dialog.Font.FontFamily.Name; + Settings.FontSize = dialog.Font.SizeInPoints / 72.0 * 96.0; + _vm.RefreshFont(); + } } } } diff --git a/WarframeClock/OverlayWindowBase.cs b/WarframeClock/OverlayWindowBase.cs new file mode 100644 index 0000000..87cea8a --- /dev/null +++ b/WarframeClock/OverlayWindowBase.cs @@ -0,0 +1,141 @@ +using System; +using System.Diagnostics; +using System.Windows; +using System.Windows.Interop; + +namespace WarframeClock +{ + public class OverlayWindowBase : Window + { + private IntPtr _windowHandle; + private bool _xTopmost; + private IntPtr _targetWindowHandle; + private Native.Rect _parentBounds; + + public OverlayWindowBase() + { + _xTopmost = false; + } + + protected bool AdjustOverlay() + { + if (_windowHandle == IntPtr.Zero) + _windowHandle = new WindowInteropHelper(this).EnsureHandle(); + + if (!GetTargetWindow(out var newBounds)) + { + if (IsVisible) + { + Logging("Hide overlay; target window disappeared"); + Hide(); + } + return false; + } + + // Set top-most flag if the target window is foreground. Move to + // behind the current foreground window otherwise. + var currentActiveWindow = Native.GetForegroundWindow(); + if (currentActiveWindow != IntPtr.Zero && + (!IsVisible || currentActiveWindow == _targetWindowHandle != _xTopmost)) + { + // Setting Topmost to false brings the overlay to just + // behind the other top-most window -- above the foreground + // window. This is not what we want to do here. + // So, let's use SetWindowPos directly instead. + if (currentActiveWindow == _targetWindowHandle) + { + Logging("Overlay is now top-most"); + _xTopmost = true; + } + else + { + Logging($"Overlay is now non-top-most, behind {currentActiveWindow}"); + _xTopmost = false; + } + + uint flags = + 0x0001 /* SWP_NOSIZE */ | + 0x0002 /* SWP_NOMOVE */ | + 0x0008 /* SWP_NOREDRAW */ | + 0x0010 /* SWP_NOACTIVATE */ | + 0x0200 /* SWP_NOOWNERZORDER */ | + 0x0400 /* SWP_NOSENDCHANGING */; + Native.SetWindowPos(_windowHandle, _xTopmost ? (IntPtr)(-1) : currentActiveWindow, 0, 0, 0, 0, flags); + } + + // Adjust window position + if (!IsVisible || + _parentBounds.Left != newBounds.Left || _parentBounds.Right != newBounds.Right || + _parentBounds.Top != newBounds.Top || _parentBounds.Bottom != newBounds.Bottom) + { + _parentBounds = newBounds; + + var width = _parentBounds.Right - _parentBounds.Left; + var height = _parentBounds.Bottom - _parentBounds.Top; + + // Do not use window.Left (and so on) because they're DPI-aware. + // We don't want that.... + Logging($"Move overlay to: l={_parentBounds.Left};t={_parentBounds.Top};w={width};h={height}"); + Native.SetWindowPos(_windowHandle, IntPtr.Zero, _parentBounds.Left, _parentBounds.Top, width, height, + 0x0004 /* SWP_NOZORDER */); + } + if (!IsVisible) + { + Logging("Show overlay"); + Show(); + } + + return true; + } + + private bool GetTargetWindow(out Native.Rect bounds) + { + if (_targetWindowHandle == IntPtr.Zero) + { + foreach (var process in Process.GetProcesses()) + { + if (_targetWindowHandle == IntPtr.Zero && + (process.ProcessName == "Warframe" || process.ProcessName == "Warframe.x64")) + { + try + { + _targetWindowHandle = process.MainWindowHandle; + if (_targetWindowHandle != IntPtr.Zero) + { + Logging($"Found a process with name={process.ProcessName}; " + + $"MainWindowHandle={_targetWindowHandle}"); + } + } + catch (Exception) + { + // Ignore errors; maybe the process is just exiting. Let's retry during the next cycle. + } + } + + process.Dispose(); + } + } + + if (_targetWindowHandle == IntPtr.Zero) + { + // Suppress compile error; caller must not use the value + bounds = new Native.Rect(); + return false; + } + + if (!Native.GetWindowRect(_targetWindowHandle, out bounds)) + { + Logging("GetWindowRect failed; aborting this cycle"); + _targetWindowHandle = IntPtr.Zero; + return false; + } + + return true; + } + + private static void Logging(string message) + { + Console.WriteLine($"OverlayWindowBase: {message}"); + } + } +} diff --git a/WarframeClock/OverlayWindowViewModel.cs b/WarframeClock/OverlayWindowViewModel.cs new file mode 100644 index 0000000..e051ada --- /dev/null +++ b/WarframeClock/OverlayWindowViewModel.cs @@ -0,0 +1,66 @@ +using System.Windows; +using System.Windows.Media; + +namespace WarframeClock +{ + internal class OverlayWindowViewModel : BindableBase + { + private string _overlayText = "!!!PLACEHOLDER!!!"; + public string OverlayText + { + get => _overlayText; + set + { + _overlayText = value; + NotifyPropertyChanged(); + } + } + + private bool _settingsMode; + public bool SettingsMode + { + get => _settingsMode; + set + { + _settingsMode = value; + NotifyPropertyChanged(); + NotifyPropertyChanged(nameof(SettingsControlVisibility)); + } + } + + public Visibility SettingsControlVisibility => + SettingsMode ? Visibility.Visible : Visibility.Collapsed; + + #region "App Settings" + public FontFamily FontFamily => new FontFamily(Settings.FontFamilyName); + + public double FontSize => Settings.FontSize; + + public HorizontalAlignment HorizontalAlignment => + Settings.DockToX == Settings.AlignmentX.Left ? HorizontalAlignment.Left : HorizontalAlignment.Right; + + public VerticalAlignment VerticalAlignment => + Settings.DockToY == Settings.AlignmentY.Top ? VerticalAlignment.Top : VerticalAlignment.Bottom; + + public Thickness Margin => + new Thickness( + Settings.DockToX == Settings.AlignmentX.Left ? Settings.MarginX : 0.0, + Settings.DockToY == Settings.AlignmentY.Top ? Settings.MarginY : 0.0, + Settings.DockToX == Settings.AlignmentX.Right ? Settings.MarginX : 0.0, + Settings.DockToY == Settings.AlignmentY.Bottom ? Settings.MarginY : 0.0); + + public void RefreshPosition() + { + NotifyPropertyChanged(nameof(HorizontalAlignment)); + NotifyPropertyChanged(nameof(VerticalAlignment)); + NotifyPropertyChanged(nameof(Margin)); + } + + public void RefreshFont() + { + NotifyPropertyChanged(nameof(FontFamily)); + NotifyPropertyChanged(nameof(FontSize)); + } + #endregion + } +} diff --git a/WarframeClock/Settings.cs b/WarframeClock/Settings.cs new file mode 100644 index 0000000..88502ed --- /dev/null +++ b/WarframeClock/Settings.cs @@ -0,0 +1,92 @@ +using System; +using System.Configuration; +using System.Globalization; +using System.Runtime.CompilerServices; + +namespace WarframeClock +{ + public static class Settings + { + public enum AlignmentX + { + Left, + Right + } + + public enum AlignmentY + { + Top, + Bottom + } + + private static AlignmentX _dockToX; + public static AlignmentX DockToX + { + get => _dockToX; + set => SetValue((_dockToX = value).ToString()); + } + + private static AlignmentY _dockToY; + public static AlignmentY DockToY + { + get => _dockToY; + set => SetValue((_dockToY = value).ToString()); + } + + private static double _marginX; + public static double MarginX + { + get => _marginX; + set => SetValue((_marginX = value).ToString(CultureInfo.InvariantCulture)); + } + + private static double _marginY; + public static double MarginY + { + get => _marginY; + set => SetValue((_marginY = value).ToString(CultureInfo.InvariantCulture)); + } + + private static string _fontFamilyName; + public static string FontFamilyName + { + get => _fontFamilyName; + set => SetValue(_fontFamilyName = value); + } + + private static double _fontSize; + public static double FontSize + { + get => _fontSize; + set => SetValue((_fontSize = value).ToString(CultureInfo.InvariantCulture)); + } + + private static readonly Configuration ExeConfiguration = + ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); + + static Settings() + { + var s = ConfigurationManager.AppSettings; + _dockToX = Enum.TryParse(s.Get(nameof(DockToX)), out AlignmentX dockToX) ? dockToX : AlignmentX.Left; + _dockToY = Enum.TryParse(s.Get(nameof(DockToY)), out AlignmentY dockToY) ? dockToY : AlignmentY.Bottom; + _marginX = double.TryParse(s.Get(nameof(MarginX)), out var marginX) ? marginX : 20; + _marginY = double.TryParse(s.Get(nameof(MarginY)), out var marginY) ? marginY : 20; + _fontFamilyName = s.Get(nameof(FontFamilyName)) ?? "Segoe UI"; + _fontSize = double.TryParse(s.Get(nameof(FontSize)), out var fontSize) ? fontSize : 13; + } + + private static void SetValue(string value, [CallerMemberName] string key = null) + { + if (key == null) + throw new ArgumentNullException(nameof(key)); + ExeConfiguration.AppSettings.Settings.Remove(key); + ExeConfiguration.AppSettings.Settings.Add(key, value); + } + + public static void Save() + { + ExeConfiguration.Save(ConfigurationSaveMode.Full); + ConfigurationManager.RefreshSection("appSettings"); + } + } +} diff --git a/WarframeClock/WarframeClock.csproj b/WarframeClock/WarframeClock.csproj index aa5f885..0a22939 100644 --- a/WarframeClock/WarframeClock.csproj +++ b/WarframeClock/WarframeClock.csproj @@ -55,6 +55,7 @@ </PropertyGroup> <ItemGroup> <Reference Include="System" /> + <Reference Include="System.Configuration" /> <Reference Include="System.Data" /> <Reference Include="System.Drawing" /> <Reference Include="System.Runtime.Serialization" /> @@ -77,6 +78,10 @@ <Generator>MSBuild:Compile</Generator> <SubType>Designer</SubType> </ApplicationDefinition> + <Compile Include="BindableBase.cs" /> + <Compile Include="OverlayWindowBase.cs" /> + <Compile Include="OverlayWindowViewModel.cs" /> + <Compile Include="Settings.cs" /> <Compile Include="WorldState.cs" /> <Page Include="OverlayWindow.xaml"> <Generator>MSBuild:Compile</Generator> |