284 lines
12 KiB
C#
284 lines
12 KiB
C#
using System.Text.RegularExpressions;
|
|
using System.Windows;
|
|
using System.Windows.Controls;
|
|
using System.Windows.Input;
|
|
using System.Windows.Media;
|
|
using System.Windows.Shapes;
|
|
|
|
namespace AxCopilot.Views;
|
|
|
|
/// <summary>커스텀 디자인 무드 추가/편집 다이얼로그.</summary>
|
|
internal sealed partial class CustomMoodDialog : Window
|
|
{
|
|
private readonly TextBox _keyBox;
|
|
private readonly TextBox _labelBox;
|
|
private readonly TextBox _iconBox;
|
|
private readonly TextBox _descBox;
|
|
private readonly TextBox _cssBox;
|
|
private readonly bool _isEdit;
|
|
|
|
public string MoodKey => _keyBox.Text.Trim().ToLowerInvariant();
|
|
public string MoodLabel => _labelBox.Text.Trim();
|
|
public string MoodIcon => _iconBox.Text.Trim();
|
|
public string MoodDescription => _descBox.Text.Trim();
|
|
public string MoodCss => _cssBox.Text;
|
|
|
|
public CustomMoodDialog(
|
|
string existingKey = "",
|
|
string existingLabel = "",
|
|
string existingIcon = "🎯",
|
|
string existingDesc = "",
|
|
string existingCss = "")
|
|
{
|
|
_isEdit = !string.IsNullOrEmpty(existingKey);
|
|
Title = _isEdit ? "무드 편집" : "무드 추가";
|
|
Width = 560;
|
|
SizeToContent = SizeToContent.Height;
|
|
WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
|
ResizeMode = ResizeMode.NoResize;
|
|
WindowStyle = WindowStyle.None;
|
|
AllowsTransparency = true;
|
|
Background = Brushes.Transparent;
|
|
|
|
var bgBrush = Application.Current.TryFindResource("LauncherBackground") as Brush
|
|
?? new SolidColorBrush(Color.FromRgb(0x1A, 0x1B, 0x2E));
|
|
var primaryText = Application.Current.TryFindResource("PrimaryText") as Brush ?? Brushes.White;
|
|
var secondaryText = Application.Current.TryFindResource("SecondaryText") as Brush ?? Brushes.Gray;
|
|
var accentBrush = Application.Current.TryFindResource("AccentColor") as Brush ?? Brushes.Blue;
|
|
var itemBg = Application.Current.TryFindResource("ItemBackground") as Brush
|
|
?? new SolidColorBrush(Color.FromRgb(0x2A, 0x2B, 0x40));
|
|
var borderBrush = Application.Current.TryFindResource("BorderColor") as Brush ?? Brushes.Gray;
|
|
|
|
var root = new Border
|
|
{
|
|
Background = bgBrush,
|
|
CornerRadius = new CornerRadius(16),
|
|
BorderBrush = borderBrush,
|
|
BorderThickness = new Thickness(1),
|
|
Padding = new Thickness(28, 24, 28, 24),
|
|
Effect = new System.Windows.Media.Effects.DropShadowEffect
|
|
{
|
|
BlurRadius = 24, ShadowDepth = 4, Opacity = 0.3, Color = Colors.Black,
|
|
},
|
|
};
|
|
|
|
var stack = new StackPanel();
|
|
|
|
// 헤더
|
|
var header = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(0, 0, 0, 20) };
|
|
header.Children.Add(new TextBlock
|
|
{
|
|
Text = "\uE771",
|
|
FontFamily = new FontFamily("Segoe MDL2 Assets"),
|
|
FontSize = 18, Foreground = accentBrush,
|
|
VerticalAlignment = VerticalAlignment.Center,
|
|
Margin = new Thickness(0, 0, 10, 0),
|
|
});
|
|
header.Children.Add(new TextBlock
|
|
{
|
|
Text = _isEdit ? "디자인 무드 편집" : "새 커스텀 디자인 무드",
|
|
FontSize = 17, FontWeight = FontWeights.Bold,
|
|
Foreground = primaryText, VerticalAlignment = VerticalAlignment.Center,
|
|
});
|
|
stack.Children.Add(header);
|
|
|
|
// ── 키 + 라벨 + 아이콘 (한 줄) ──
|
|
var row1 = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(0, 0, 0, 8) };
|
|
|
|
// 키
|
|
var keyStack = new StackPanel { Margin = new Thickness(0, 0, 12, 0) };
|
|
AddLabel(keyStack, "키 (영문)", primaryText);
|
|
_keyBox = CreateTextBox(existingKey, primaryText, itemBg, accentBrush, borderBrush);
|
|
_keyBox.Width = 140;
|
|
_keyBox.IsEnabled = !_isEdit; // 편집 시 키 변경 불가
|
|
keyStack.Children.Add(new Border { CornerRadius = new CornerRadius(8), ClipToBounds = true, Child = _keyBox });
|
|
row1.Children.Add(keyStack);
|
|
|
|
// 라벨
|
|
var labelStack = new StackPanel { Margin = new Thickness(0, 0, 12, 0) };
|
|
AddLabel(labelStack, "이름", primaryText);
|
|
_labelBox = CreateTextBox(existingLabel, primaryText, itemBg, accentBrush, borderBrush);
|
|
_labelBox.Width = 180;
|
|
labelStack.Children.Add(new Border { CornerRadius = new CornerRadius(8), ClipToBounds = true, Child = _labelBox });
|
|
row1.Children.Add(labelStack);
|
|
|
|
// 아이콘 (이모지)
|
|
var iconStack = new StackPanel();
|
|
AddLabel(iconStack, "아이콘", primaryText);
|
|
_iconBox = CreateTextBox(existingIcon, primaryText, itemBg, accentBrush, borderBrush);
|
|
_iconBox.Width = 60;
|
|
_iconBox.TextAlignment = TextAlignment.Center;
|
|
iconStack.Children.Add(new Border { CornerRadius = new CornerRadius(8), ClipToBounds = true, Child = _iconBox });
|
|
row1.Children.Add(iconStack);
|
|
|
|
stack.Children.Add(row1);
|
|
|
|
// ── 설명 ──
|
|
AddLabel(stack, "설명", primaryText);
|
|
_descBox = CreateTextBox(existingDesc, primaryText, itemBg, accentBrush, borderBrush);
|
|
stack.Children.Add(new Border { CornerRadius = new CornerRadius(8), ClipToBounds = true, Child = _descBox, Margin = new Thickness(0, 0, 0, 8) });
|
|
|
|
// ── CSS ──
|
|
AddSeparator(stack, borderBrush);
|
|
AddLabel(stack, "CSS 스타일", primaryText);
|
|
AddHint(stack, "문서에 적용될 CSS입니다. body, h1~h6, table, .callout 등의 스타일을 정의하세요.", secondaryText);
|
|
_cssBox = CreateTextBox(existingCss, primaryText, itemBg, accentBrush, borderBrush, multiline: true, height: 200);
|
|
_cssBox.FontFamily = new FontFamily("Consolas, Courier New, monospace");
|
|
_cssBox.FontSize = 12;
|
|
stack.Children.Add(new Border { CornerRadius = new CornerRadius(8), ClipToBounds = true, Child = _cssBox });
|
|
|
|
// CSS 힌트 버튼
|
|
var hintBtn = new TextBlock
|
|
{
|
|
Text = "CSS 예시 보기",
|
|
FontSize = 11,
|
|
Foreground = accentBrush,
|
|
Cursor = Cursors.Hand,
|
|
Margin = new Thickness(0, 6, 0, 0),
|
|
TextDecorations = TextDecorations.Underline,
|
|
};
|
|
hintBtn.MouseLeftButtonDown += (_, _) =>
|
|
{
|
|
if (string.IsNullOrWhiteSpace(_cssBox.Text))
|
|
{
|
|
_cssBox.Text = CssExample;
|
|
}
|
|
};
|
|
stack.Children.Add(hintBtn);
|
|
|
|
// ── 버튼 바 ──
|
|
var btnBar = new StackPanel
|
|
{
|
|
Orientation = Orientation.Horizontal,
|
|
HorizontalAlignment = HorizontalAlignment.Right,
|
|
Margin = new Thickness(0, 20, 0, 0),
|
|
};
|
|
|
|
var cancelBtn = new Button
|
|
{
|
|
Content = "취소", Width = 80,
|
|
Padding = new Thickness(0, 8, 0, 8),
|
|
Margin = new Thickness(0, 0, 10, 0),
|
|
Background = Brushes.Transparent, Foreground = secondaryText,
|
|
BorderBrush = borderBrush, BorderThickness = new Thickness(1),
|
|
Cursor = Cursors.Hand, FontSize = 13,
|
|
};
|
|
cancelBtn.Click += (_, _) => { DialogResult = false; Close(); };
|
|
btnBar.Children.Add(cancelBtn);
|
|
|
|
var okBtn = new Button
|
|
{
|
|
Content = _isEdit ? "저장" : "추가", Width = 80,
|
|
Padding = new Thickness(0, 8, 0, 8),
|
|
Background = accentBrush, Foreground = Brushes.White,
|
|
BorderThickness = new Thickness(0),
|
|
Cursor = Cursors.Hand, FontSize = 13, FontWeight = FontWeights.SemiBold,
|
|
};
|
|
okBtn.Click += (_, _) => Validate();
|
|
btnBar.Children.Add(okBtn);
|
|
stack.Children.Add(btnBar);
|
|
|
|
root.Child = stack;
|
|
Content = root;
|
|
|
|
KeyDown += (_, ke) => { if (ke.Key == Key.Escape) { DialogResult = false; Close(); } };
|
|
Loaded += (_, _) => { (_isEdit ? _labelBox : _keyBox).Focus(); };
|
|
root.MouseLeftButtonDown += (_, me) =>
|
|
{
|
|
if (me.LeftButton == MouseButtonState.Pressed)
|
|
try { DragMove(); } catch { }
|
|
};
|
|
}
|
|
|
|
private void Validate()
|
|
{
|
|
if (string.IsNullOrWhiteSpace(_keyBox.Text))
|
|
{
|
|
CustomMessageBox.Show("키를 입력하세요 (영문 소문자).", "입력 오류", MessageBoxButton.OK, MessageBoxImage.Warning);
|
|
_keyBox.Focus(); return;
|
|
}
|
|
if (!Regex.IsMatch(_keyBox.Text.Trim(), @"^[a-z][a-z0-9_]{1,20}$"))
|
|
{
|
|
CustomMessageBox.Show("키는 영문 소문자로 시작하며, 소문자/숫자/밑줄만 허용됩니다 (2~21자).", "입력 오류", MessageBoxButton.OK, MessageBoxImage.Warning);
|
|
_keyBox.Focus(); return;
|
|
}
|
|
if (string.IsNullOrWhiteSpace(_labelBox.Text))
|
|
{
|
|
CustomMessageBox.Show("이름을 입력하세요.", "입력 오류", MessageBoxButton.OK, MessageBoxImage.Warning);
|
|
_labelBox.Focus(); return;
|
|
}
|
|
// 내장 무드 키와 충돌 확인
|
|
if (!_isEdit)
|
|
{
|
|
var builtinKeys = new[] { "modern", "professional", "creative", "minimal", "elegant", "dark", "colorful", "corporate", "magazine", "dashboard" };
|
|
if (builtinKeys.Contains(_keyBox.Text.Trim().ToLowerInvariant()))
|
|
{
|
|
CustomMessageBox.Show("내장 무드와 동일한 키는 사용할 수 없습니다.", "입력 오류", MessageBoxButton.OK, MessageBoxImage.Warning);
|
|
_keyBox.Focus(); return;
|
|
}
|
|
}
|
|
DialogResult = true;
|
|
Close();
|
|
}
|
|
|
|
private static void AddLabel(StackPanel parent, string text, Brush fg) =>
|
|
parent.Children.Add(new TextBlock
|
|
{
|
|
Text = text, FontSize = 12, FontWeight = FontWeights.SemiBold,
|
|
Foreground = fg, Margin = new Thickness(0, 0, 0, 6),
|
|
});
|
|
|
|
private static void AddHint(StackPanel parent, string text, Brush fg) =>
|
|
parent.Children.Add(new TextBlock
|
|
{
|
|
Text = text, FontSize = 11, Foreground = fg,
|
|
Margin = new Thickness(0, 0, 0, 8),
|
|
});
|
|
|
|
private static void AddSeparator(StackPanel parent, Brush color) =>
|
|
parent.Children.Add(new Rectangle
|
|
{
|
|
Height = 1, Fill = color,
|
|
Margin = new Thickness(0, 8, 0, 12), Opacity = 0.5,
|
|
});
|
|
|
|
private static TextBox CreateTextBox(string text, Brush fg, Brush bg, Brush caret, Brush border, bool multiline = false, int height = 100)
|
|
{
|
|
var tb = new TextBox
|
|
{
|
|
Text = text, FontSize = 13,
|
|
Padding = new Thickness(12, 8, 12, 8),
|
|
Foreground = fg, Background = bg,
|
|
CaretBrush = caret, BorderBrush = border,
|
|
BorderThickness = new Thickness(1),
|
|
};
|
|
if (multiline)
|
|
{
|
|
tb.AcceptsReturn = true;
|
|
tb.AcceptsTab = true;
|
|
tb.TextWrapping = TextWrapping.Wrap;
|
|
tb.MinHeight = height;
|
|
tb.MaxHeight = height + 100;
|
|
tb.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
|
|
}
|
|
return tb;
|
|
}
|
|
|
|
private const string CssExample = @"body {
|
|
font-family: 'Pretendard', 'Noto Sans KR', sans-serif;
|
|
max-width: 900px;
|
|
margin: 0 auto;
|
|
padding: 40px;
|
|
color: #1a1a2e;
|
|
background: #f8f9fc;
|
|
line-height: 1.8;
|
|
}
|
|
h1 { color: #2d3748; border-bottom: 2px solid #4a90d9; padding-bottom: 8px; }
|
|
h2 { color: #4a5568; margin-top: 2em; }
|
|
table { width: 100%; border-collapse: collapse; margin: 1.5em 0; }
|
|
th { background: #4a90d9; color: white; padding: 10px; text-align: left; }
|
|
td { padding: 8px 10px; border-bottom: 1px solid #e2e8f0; }
|
|
tr:nth-child(even) { background: #f0f4f8; }
|
|
";
|
|
}
|