using System.Runtime.InteropServices; using System.Windows; using System.Windows.Interop; using System.Windows.Threading; using AxCopilot.Services; namespace AxCopilot.Views; /// /// 잠금 해제 시 화면 모서리에 표시되는 격려 팝업. /// 테마는 Application.Current.Resources의 DynamicResource로 자동 적용됩니다. /// 포커스를 빼앗지 않으며(WS_EX_NOACTIVATE), 지정된 초 후 자동으로 닫힙니다. /// 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); } }