-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMainWindow.axaml.cs
More file actions
134 lines (116 loc) · 4.59 KB
/
Copy pathMainWindow.axaml.cs
File metadata and controls
134 lines (116 loc) · 4.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
using System;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Threading;
using Avalonia.Input;
using Avalonia.Interactivity;
using BrainFuel.Services;
using BrainFuel.ViewModels;
namespace BrainFuel;
public partial class MainWindow : Window
{
private AppSettings? _settings;
private MainViewModel? _vm;
public MainWindow()
{
InitializeComponent();
}
/// <summary>Wires up the view model and starts polling. Called once on startup.</summary>
public void Initialize(AppSettings settings, MainViewModel vm)
{
_settings = settings;
_vm = vm;
DataContext = vm;
if (settings.WindowX is int x && settings.WindowY is int y)
{
var saved = new PixelPoint(x, y);
Position = IsOnAnyScreen(saved) ? saved : EnsureOnPrimary(saved);
}
// A monitor got plugged/unplugged (or resolution changed): if the window
// is now stranded off-screen, pull it back onto a visible display.
Screens.Changed += OnScreensChanged;
vm.OnNotify = (title, msg) => Dispatcher.UIThread.Post(() =>
new NotificationWindow().ShowNotification(title, msg));
vm.Start();
}
private async void OnScreensChanged(object? sender, EventArgs e)
{
// Defer so the Screens list reflects the new topology before we test it.
// On Windows the list may still be stale right after the event fires, so
// re-check a few times over the next seconds — cheap and settles the
// "widget stranded on an unplugged monitor" case reliably.
foreach (var delay in new[] { 100, 1000, 3000 })
{
await System.Threading.Tasks.Task.Delay(delay);
await Dispatcher.UIThread.InvokeAsync(() =>
{
if (!IsOnAnyScreen(Position))
Position = EnsureOnPrimary(Position);
});
}
}
/// <summary>True if a point lies inside any currently connected screen's bounds.</summary>
private bool IsOnAnyScreen(PixelPoint p)
{
foreach (var s in Screens.All)
if (s.Bounds.Contains(p)) return true;
return false;
}
/// <summary>
/// Clamps a point into the primary screen's working area so the window is
/// always reachable. Keeps the relative corner when possible.
/// </summary>
private PixelPoint EnsureOnPrimary(PixelPoint p)
{
var primary = Screens.Primary;
if (primary is null) return p;
var wa = primary.WorkingArea;
int margin = 16;
int x = Math.Clamp(p.X, wa.X + margin, wa.Right - margin - 100);
int y = Math.Clamp(p.Y, wa.Y + margin, wa.Bottom - margin - 40);
return new PixelPoint(x, y);
}
private void Card_PointerPressed(object? sender, PointerPressedEventArgs e)
{
if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
BeginMoveDrag(e);
}
private async void Refresh_Click(object? sender, RoutedEventArgs e)
=> await (_vm?.RefreshAsync() ?? System.Threading.Tasks.Task.CompletedTask);
private void Settings_Click(object? sender, RoutedEventArgs e) => OpenSettings();
private void Quit_Click(object? sender, RoutedEventArgs e) => Close();
/// <summary>Hides the widget; polling and notifications keep running in the tray.</summary>
private void Hide_Click(object? sender, RoutedEventArgs e) => Hide();
/// <summary>Restores the window (from hide/tray or a second launch) and guarantees it lands on a connected screen.</summary>
public void EnsureVisible()
{
if (!IsOnAnyScreen(Position))
Position = EnsureOnPrimary(Position);
Show();
WindowState = WindowState.Normal;
Activate();
}
public void OpenSettings()
{
if (_settings is null) return;
var win = new SettingsWindow(_settings);
win.ShowDialog(this);
win.Closed += (_, _) => _vm?.OnSettingsChanged();
}
protected override void OnClosing(WindowClosingEventArgs e)
{
if (_settings is not null)
{
// Persist the current position only if it is still on a visible
// screen; otherwise keep whatever (valid) value was there before so
// a stranded window doesn't lock itself off-screen across restarts.
var pos = IsOnAnyScreen(Position) ? Position : EnsureOnPrimary(Position);
_settings.WindowX = pos.X;
_settings.WindowY = pos.Y;
SettingsService.Save(_settings);
}
Screens.Changed -= OnScreensChanged;
_vm?.Dispose();
base.OnClosing(e);
}
}