using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Input; using System.Windows.Media; using AxCopilot.Models; using AxCopilot.Services; namespace AxCopilot.Views; public partial class MacroEditorWindow : Window { private readonly SettingsService _settings; private readonly MacroEntry? _editing; // 유형 목록 private static readonly string[] StepTypes = { "app", "url", "folder", "notification", "cmd" }; private static readonly string[] StepTypeLabels = { "앱", "URL", "폴더", "알림", "PowerShell" }; // 각 행의 컨트롤 참조 private readonly List _rows = new(); // 공유 타입 팝업 private readonly Popup _typePopup = new() { StaysOpen = false, AllowsTransparency = true, Placement = System.Windows.Controls.Primitives.PlacementMode.Bottom }; private StepRowUi? _typeTargetRow; public MacroEditorWindow(MacroEntry? entry, SettingsService settings) { InitializeComponent(); _settings = settings; _editing = entry; Loaded += OnLoaded; } private void OnLoaded(object sender, RoutedEventArgs e) { BuildTypePopup(); if (_editing != null) { NameBox.Text = _editing.Name; DescBox.Text = _editing.Description; foreach (var step in _editing.Steps) AddRow(step); } if (_rows.Count == 0) AddRow(null); // 기본 빈 행 } // ─── 팝업 빌드 ────────────────────────────────────────────────────────── private void BuildTypePopup() { var bg = TryFindResource("ItemBackground") as Brush ?? Brushes.DimGray; var border = TryFindResource("BorderColor") as Brush ?? Brushes.Gray; var fg = TryFindResource("PrimaryText") as Brush ?? Brushes.White; var hover = TryFindResource("ItemHoverBackground") as Brush ?? Brushes.Gray; var panel = new StackPanel { Background = bg, MinWidth = 100 }; for (int i = 0; i < StepTypes.Length; i++) { var idx = i; var label = StepTypeLabels[i]; var item = new Border { Padding = new Thickness(12, 6, 12, 6), Background = Brushes.Transparent, Cursor = Cursors.Hand, Tag = idx }; item.MouseEnter += (_, _) => item.Background = hover; item.MouseLeave += (_, _) => item.Background = Brushes.Transparent; item.MouseLeftButtonUp += (_, _) => { if (_typeTargetRow != null) SetRowType(_typeTargetRow, idx); _typePopup.IsOpen = false; }; item.Child = new TextBlock { Text = label, FontSize = 12, Foreground = fg }; panel.Children.Add(item); } var outerBorder = new Border { Background = bg, BorderBrush = border, BorderThickness = new Thickness(1), CornerRadius = new CornerRadius(6), Child = panel, Effect = new System.Windows.Media.Effects.DropShadowEffect { BlurRadius = 12, ShadowDepth = 3, Opacity = 0.3, Color = Colors.Black, Direction = 270 } }; _typePopup.Child = outerBorder; } // ─── 행 추가 ──────────────────────────────────────────────────────────── private void AddRow(MacroStep? step) { var bg = TryFindResource("ItemBackground") as Brush ?? Brushes.DimGray; var border = TryFindResource("BorderColor") as Brush ?? Brushes.Gray; var accent = TryFindResource("AccentColor") as Brush ?? Brushes.Blue; var secFg = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray; var primFg = TryFindResource("PrimaryText") as Brush ?? Brushes.White; var row = new StepRowUi(); var grid = new Grid { Margin = new Thickness(0, 0, 0, 4) }; grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(90) }); grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }); grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(80) }); grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(70) }); grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(28) }); // Col 0: 유형 버튼 var typeLbl = new TextBlock { FontSize = 11, VerticalAlignment = VerticalAlignment.Center, Foreground = primFg, Text = StepTypeLabels[0] }; var typeBtn = new Border { Background = bg, BorderBrush = border, BorderThickness = new Thickness(1), CornerRadius = new CornerRadius(4), Padding = new Thickness(6, 4, 6, 4), Cursor = Cursors.Hand, Child = typeLbl, Margin = new Thickness(0, 0, 4, 0) }; typeBtn.MouseLeftButtonUp += (_, _) => { _typeTargetRow = row; _typePopup.PlacementTarget = typeBtn; _typePopup.IsOpen = true; }; row.TypeButton = typeBtn; row.TypeLabel = typeLbl; Grid.SetColumn(typeBtn, 0); // Col 1: 대상 var targetBox = new TextBox { FontSize = 11, Foreground = primFg, Background = (Brush)TryFindResource("LauncherBackground")! ?? Brushes.Black, BorderBrush = border, BorderThickness = new Thickness(1), Padding = new Thickness(6, 4, 6, 4), Margin = new Thickness(0, 0, 4, 0), Text = step?.Target ?? "" }; row.TargetBox = targetBox; Grid.SetColumn(targetBox, 1); // Col 2: 표시 이름 var labelBox = new TextBox { FontSize = 11, Foreground = secFg, Background = (Brush)TryFindResource("LauncherBackground")! ?? Brushes.Black, BorderBrush = border, BorderThickness = new Thickness(1), Padding = new Thickness(6, 4, 6, 4), Margin = new Thickness(0, 0, 4, 0), Text = step?.Label ?? "" }; row.LabelBox = labelBox; Grid.SetColumn(labelBox, 2); // Col 3: 딜레이 var delayBox = new TextBox { FontSize = 11, Foreground = primFg, Background = (Brush)TryFindResource("LauncherBackground")! ?? Brushes.Black, BorderBrush = border, BorderThickness = new Thickness(1), Padding = new Thickness(6, 4, 6, 4), Margin = new Thickness(0, 0, 4, 0), Text = (step?.DelayMs ?? 500).ToString() }; row.DelayBox = delayBox; Grid.SetColumn(delayBox, 3); // Col 4: 삭제 var delBtn = new Border { Width = 24, Height = 24, CornerRadius = new CornerRadius(4), Background = Brushes.Transparent, Cursor = Cursors.Hand, VerticalAlignment = VerticalAlignment.Center, Child = new TextBlock { Text = "\uE711", FontFamily = new FontFamily("Segoe MDL2 Assets"), FontSize = 11, Foreground = secFg, HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center } }; delBtn.MouseEnter += (_, _) => delBtn.Background = new SolidColorBrush(Color.FromArgb(0x30, 0xC0, 0x50, 0x50)); delBtn.MouseLeave += (_, _) => delBtn.Background = Brushes.Transparent; delBtn.MouseLeftButtonUp += (_, _) => RemoveRow(row); Grid.SetColumn(delBtn, 4); grid.Children.Add(typeBtn); grid.Children.Add(targetBox); grid.Children.Add(labelBox); grid.Children.Add(delayBox); grid.Children.Add(delBtn); row.Grid = grid; // 유형 초기화 int typeIdx = step != null ? Array.IndexOf(StepTypes, step.Type.ToLowerInvariant()) : 0; if (typeIdx < 0) typeIdx = 0; SetRowType(row, typeIdx); _rows.Add(row); StepsPanel.Children.Add(grid); } private void SetRowType(StepRowUi row, int typeIdx) { row.TypeIndex = typeIdx; row.TypeLabel.Text = StepTypeLabels[typeIdx]; var accent = TryFindResource("AccentColor") as Brush ?? Brushes.Blue; var secFg = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray; row.TypeLabel.Foreground = accent; } private void RemoveRow(StepRowUi row) { StepsPanel.Children.Remove(row.Grid); _rows.Remove(row); } // ─── 버튼 이벤트 ───────────────────────────────────────────────────────── private void BtnAddStep_Click(object sender, MouseButtonEventArgs e) => AddRow(null); private void BtnSave_Click(object sender, MouseButtonEventArgs e) { var name = NameBox.Text.Trim(); if (string.IsNullOrWhiteSpace(name)) { MessageBox.Show("매크로 이름을 입력하세요.", "저장 오류", MessageBoxButton.OK, MessageBoxImage.Warning); return; } var steps = _rows .Where(r => !string.IsNullOrWhiteSpace(r.TargetBox.Text)) .Select(r => new MacroStep { Type = StepTypes[r.TypeIndex], Target = r.TargetBox.Text.Trim(), Label = r.LabelBox.Text.Trim(), DelayMs = int.TryParse(r.DelayBox.Text, out var d) ? Math.Max(0, d) : 500 }) .ToList(); var entry = _editing ?? new MacroEntry(); entry.Name = name; entry.Description = DescBox.Text.Trim(); entry.Steps = steps; if (_editing == null) _settings.Settings.Macros.Add(entry); _settings.Save(); NotificationService.Notify("AX Copilot", $"매크로 '{entry.Name}' 저장됨"); Close(); } private void BtnCancel_Click(object sender, MouseButtonEventArgs e) => Close(); private void BtnClose_Click(object sender, MouseButtonEventArgs e) => Close(); private void TitleBar_MouseDown(object sender, MouseButtonEventArgs e) { if (e.LeftButton == MouseButtonState.Pressed) DragMove(); } // ─── 내부 행 참조 클래스 ────────────────────────────────────────────────── private class StepRowUi { public Grid Grid = null!; public Border TypeButton = null!; public TextBlock TypeLabel = null!; public TextBox TargetBox = null!; public TextBox LabelBox = null!; public TextBox DelayBox = null!; public int TypeIndex; } }