aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKazuki Yamaguchi <k@rhe.jp>2019-03-12 11:00:38 +0900
committerKazuki Yamaguchi <k@rhe.jp>2019-03-12 11:12:32 +0900
commit2e462da2a8df4307f1f7cea9587e70779eb4acca (patch)
tree3d149593b6012b2a0c6a3b290fdd838a9240fffe
parentde84c8e07dd20668108b366c853215217b886b64 (diff)
downloadwf-clock-2e462da2a8df4307f1f7cea9587e70779eb4acca.tar.gz
make position and font configurable2019-03-12
Now it may have a settings file. The file will be named <executable>.config.
-rw-r--r--WarframeClock.sln7
-rw-r--r--WarframeClock/App.xaml.cs30
-rw-r--r--WarframeClock/BindableBase.cs15
-rw-r--r--WarframeClock/Native.cs4
-rw-r--r--WarframeClock/OverlayWindow.xaml73
-rw-r--r--WarframeClock/OverlayWindow.xaml.cs200
-rw-r--r--WarframeClock/OverlayWindowBase.cs141
-rw-r--r--WarframeClock/OverlayWindowViewModel.cs66
-rw-r--r--WarframeClock/Settings.cs92
-rw-r--r--WarframeClock/WarframeClock.csproj5
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>