Initial commit to new repository
This commit is contained in:
141
src/AxCopilot/Views/ReminderPopupWindow.xaml.cs
Normal file
141
src/AxCopilot/Views/ReminderPopupWindow.xaml.cs
Normal file
@@ -0,0 +1,141 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Threading;
|
||||
using AxCopilot.Services;
|
||||
|
||||
namespace AxCopilot.Views;
|
||||
|
||||
/// <summary>
|
||||
/// 잠금 해제 시 화면 모서리에 표시되는 격려 팝업.
|
||||
/// 테마는 Application.Current.Resources의 DynamicResource로 자동 적용됩니다.
|
||||
/// 포커스를 빼앗지 않으며(WS_EX_NOACTIVATE), 지정된 초 후 자동으로 닫힙니다.
|
||||
/// </summary>
|
||||
public partial class ReminderPopupWindow : Window
|
||||
{
|
||||
// ─── 포커스 방지 P/Invoke (64비트 안전) ────────────────────────────────────
|
||||
[DllImport("user32.dll", EntryPoint = "GetWindowLongPtr")]
|
||||
private static extern IntPtr GetWindowLongPtr(IntPtr hwnd, int nIndex);
|
||||
[DllImport("user32.dll", EntryPoint = "SetWindowLongPtr")]
|
||||
private static extern IntPtr SetWindowLongPtr(IntPtr hwnd, int nIndex, IntPtr dwNewLong);
|
||||
|
||||
private const int GWL_EXSTYLE = -20;
|
||||
private const int WS_EX_NOACTIVATE = 0x08000000;
|
||||
private const int WS_EX_TOOLWINDOW = 0x00000080;
|
||||
|
||||
// ─── 타이머 ───────────────────────────────────────────────────────────────
|
||||
private readonly DispatcherTimer _timer;
|
||||
private readonly EventHandler _tickHandler;
|
||||
private int _remaining;
|
||||
|
||||
public ReminderPopupWindow(
|
||||
string quoteText,
|
||||
string? author,
|
||||
TimeSpan todayUsage,
|
||||
SettingsService settings)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
var cfg = settings.Settings.Reminder;
|
||||
|
||||
// ── 문구 / 출처 ──
|
||||
QuoteText.Text = quoteText;
|
||||
if (!string.IsNullOrWhiteSpace(author))
|
||||
{
|
||||
AuthorText.Text = $"— {author}";
|
||||
AuthorText.Visibility = Visibility.Visible;
|
||||
}
|
||||
|
||||
// ── 근무 시간 ──
|
||||
var h = (int)todayUsage.TotalHours;
|
||||
var m = todayUsage.Minutes;
|
||||
UsageText.Text = h > 0
|
||||
? $"오늘 총 {h}시간 {m}분 근무 중"
|
||||
: m >= 1
|
||||
? $"오늘 총 {m}분 근무 중"
|
||||
: "오늘 방금 시작했습니다";
|
||||
|
||||
// ── 카운트다운 ──
|
||||
_remaining = Math.Max(3, cfg.DisplaySeconds);
|
||||
CountdownBar.Maximum = _remaining;
|
||||
CountdownBar.Value = _remaining;
|
||||
|
||||
// ── 위치: 레이아웃 완료 후 설정 ──
|
||||
Loaded += (_, _) =>
|
||||
{
|
||||
SetNoActivate();
|
||||
PositionWindow(cfg.Corner);
|
||||
AnimateIn();
|
||||
};
|
||||
|
||||
// ── 타이머 ──
|
||||
_tickHandler = (_, _) =>
|
||||
{
|
||||
_remaining--;
|
||||
CountdownBar.Value = _remaining;
|
||||
if (_remaining <= 0) Close();
|
||||
};
|
||||
_timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) };
|
||||
_timer.Tick += _tickHandler;
|
||||
_timer.Start();
|
||||
|
||||
// ── Esc 키 닫기 ──
|
||||
KeyDown += (_, e) =>
|
||||
{
|
||||
if (e.Key == System.Windows.Input.Key.Escape) Close();
|
||||
};
|
||||
}
|
||||
|
||||
// ─── 포커스 방지 ─────────────────────────────────────────────────────────
|
||||
|
||||
protected override void OnSourceInitialized(EventArgs e)
|
||||
{
|
||||
base.OnSourceInitialized(e);
|
||||
SetNoActivate();
|
||||
}
|
||||
|
||||
private void SetNoActivate()
|
||||
{
|
||||
var hwnd = new WindowInteropHelper(this).Handle;
|
||||
if (hwnd == IntPtr.Zero) return;
|
||||
var style = (long)GetWindowLongPtr(hwnd, GWL_EXSTYLE);
|
||||
SetWindowLongPtr(hwnd, GWL_EXSTYLE, (IntPtr)(style | WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW));
|
||||
}
|
||||
|
||||
// ─── 위치 계산 ────────────────────────────────────────────────────────────
|
||||
|
||||
private void PositionWindow(string corner)
|
||||
{
|
||||
var area = SystemParameters.WorkArea;
|
||||
const double margin = 20;
|
||||
|
||||
Left = corner.Contains("left")
|
||||
? area.Left + margin
|
||||
: area.Right - ActualWidth - margin;
|
||||
|
||||
Top = corner.Contains("top")
|
||||
? area.Top + margin
|
||||
: area.Bottom - ActualHeight - margin;
|
||||
}
|
||||
|
||||
// ─── 등장 애니메이션 ──────────────────────────────────────────────────────
|
||||
|
||||
private void AnimateIn()
|
||||
{
|
||||
Opacity = 0;
|
||||
var anim = new System.Windows.Media.Animation.DoubleAnimation(0, 1,
|
||||
TimeSpan.FromMilliseconds(280));
|
||||
BeginAnimation(OpacityProperty, anim);
|
||||
}
|
||||
|
||||
// ─── 이벤트 ───────────────────────────────────────────────────────────────
|
||||
|
||||
private void CloseBtn_Click(object sender, RoutedEventArgs e) => Close();
|
||||
|
||||
protected override void OnClosed(EventArgs e)
|
||||
{
|
||||
_timer.Stop();
|
||||
_timer.Tick -= _tickHandler;
|
||||
base.OnClosed(e);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user