[200] 디자인 동일화 1/2 — 스크롤바·창 기본값·아이콘 색 인자
[200]SheetMe Studio Designer 와 6개 표면을 대조한 결과, 뼈대는 이미 같았다 — WindowChrome 파라미터, 5열 골격, 색 토큰 다크·라이트 64개 값 전부, Button/TextBox/Menu/ TabItem/ComboBox/CheckBox 등 스타일이 바이트 단위로 일치한다. 실제 격차는 "껍데기 안에 무엇이 들어가는가" 셋이었다: 스크롤바, 타이틀바 구성, 좌측 패널·인스펙터 정보 밀도. 이번 커밋은 그중 전 화면에 깔려 체감이 가장 큰 스크롤바와 테마 기반을 맞춘다. [스크롤바] [200] 통일 규격 이식 — 9px 폭(기존 11px), 상하 14px 화살표 버튼, 막대 CornerRadius 5. 막대 색은 토큰이 아니라 리터럴 중립 그레이(#8A8F96/#A6ACB3/#C4CAD0)다 — 다크·라이트 양쪽에서 같은 회색으로 보여야 배경에 따라 튀지 않는다([200] 과 동일한 의도적 선택). MinThumbTrack.cs 를 [200] src/Ui/SheetMe.Ui 에서 복사 이식(vendoring, 헤더에 동기화 의무 명시). 기본 WPF Track 은 Thumb.MinHeight 를 무시해 항목이 많은 목록에서 막대가 실처럼 가늘어지는데, 기존 ScrollThumb 의 MinHeight 48 이 바로 그래서 먹지 않고 있었다. MinThumbTrack 이 arrange 를 직접 해 최소 길이 44px 를 보장한다. [창 기본값] Window 암시 스타일에 FontSize 13 + Foreground B.Ink 추가, Background 를 B.Panel → B.AppBg 로. 이게 없어 대화상자 8종이 WPF 기본 12px + 시스템 검정으로 렌더돼 메인 셸(13px)과 결이 어긋나 있었다. 토큰 참조는 DynamicResource 유지 — [200] 의 StaticResource 를 그대로 베끼면 다크에서 값이 굳는다. [아이콘 색 인자] 컨버터 ConverterParameter 를 "크기" 단독에서 "크기|브러시키" 로 확장. 구분자가 없으면 종전 동작(크기만, B.Muted)을 그대로 유지해 기존 호출부 7곳이 안전하다. 지금까지 B.Muted 가 하드코딩돼 있어 활성 문서 탭 아이콘 강조나 레이어 타입 아이콘 색 구분이 구조적으로 불가능했다. 후속 배치의 선행 조건이다. 팔레트 아이콘 매핑도 정리 — MaskedTextBox 가 TextBox 와 같은 아이콘이라 구분이 안 됐다. [그 외] DocTabItem 비활성 탭에 하단 테두리를 넣고 활성만 끊어 크롬식 룩으로. BarToolToggle(플로팅 바 선택/손 — 켜짐을 B.Accent2 로 은은하게) 과 MiniToggle 키 스타일 신설. 암시 ToggleButton 스타일은 인스펙터·레이어 토글도 쓰므로 손대지 않고 키로 분리했다. B.FloatShadow 토큰 신설 — 플로팅 바 그림자가 고정값 하나라 라이트에서 흰 배경에 검은 그림자가 그대로 얹혀 있었다. 다크 20/3/0.5, 라이트 26/5/0.14 로 분리. 기존 34개 토큰 값은 손대지 않았다. 검증: 테스트 124/124, edit-smoke 실패 0, 왕복 1,271건 diff 0/예외 0. **종이 렌더 P062 픽셀 대조 차이 0** — 암시 스타일 변경이 용지에 새지 않음을 확인. x:Key 없는 암시 스타일 개수도 이식 전과 동일(25개)하게 유지했다. 남은 배치: C(인스펙터 접이식 섹션·세그 토글) · D(좌측 패널·타이틀바·빈 상태) · E(선택 오버레이) · F(캔버스 크롬 — 종이에 닿는 유일한 배치) · G(토스트·ThemedDialog). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
68f0fbd5a3
commit
7591fbed74
@@ -0,0 +1,106 @@
|
|||||||
|
// 출처: [200]SheetMe src/Ui/SheetMe.Ui/MinThumbTrack.cs 복사 이식(vendoring).
|
||||||
|
// 두 저장소가 분리돼 ProjectReference 가 불가능하고, 스크롤 썸 산술은 변할 이유가 없는 고정 자산이다.
|
||||||
|
// 원본이 바뀌면 여기도 함께 갱신할 것.
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Controls.Primitives;
|
||||||
|
|
||||||
|
namespace SheetMe.Designer.Controls;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 썸(thumb) 최소 길이를 '확실히' 보장하는 Track.
|
||||||
|
/// 기본 WPF Track 은 콘텐츠가 많을수록 썸을 비례로 줄이며 Thumb.MinHeight 를 반영하지 않아
|
||||||
|
/// (아이템 1000+ 리스트에서 실처럼 가늘어짐), 여기서 직접 arrange 하여 하한(<see cref="MinThumbLength"/>)을 둔다.
|
||||||
|
/// 드래그(Thumb.DragDelta → ScrollBar 이 Track.ValueFromDistance 호출)도 자체 density 로 정확히 매핑한다.
|
||||||
|
/// 스크롤바 표준 동작: 세로는 value=Min→썸 위, 가로는 value=Min→썸 왼쪽(IsDirectionReversed 는 직접 처리).
|
||||||
|
/// </summary>
|
||||||
|
public class MinThumbTrack : Track
|
||||||
|
{
|
||||||
|
public static readonly DependencyProperty MinThumbLengthProperty =
|
||||||
|
DependencyProperty.Register(nameof(MinThumbLength), typeof(double), typeof(MinThumbTrack),
|
||||||
|
new FrameworkPropertyMetadata(44.0, FrameworkPropertyMetadataOptions.AffectsArrange));
|
||||||
|
|
||||||
|
/// <summary>썸 최소 길이(px). 트랙이 이보다 짧으면 트랙 길이로 클램프.</summary>
|
||||||
|
public double MinThumbLength
|
||||||
|
{
|
||||||
|
get => (double)GetValue(MinThumbLengthProperty);
|
||||||
|
set => SetValue(MinThumbLengthProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private double _density; // 값/픽셀 (드래그 매핑용)
|
||||||
|
private double _thumbLength; // 현재 썸 길이(px)
|
||||||
|
|
||||||
|
protected override Size ArrangeOverride(Size finalSize)
|
||||||
|
{
|
||||||
|
double viewport = ViewportSize;
|
||||||
|
var thumb = Thumb;
|
||||||
|
if (double.IsNaN(viewport) || thumb is null)
|
||||||
|
return base.ArrangeOverride(finalSize); // 슬라이더 모드 등은 기본 동작
|
||||||
|
|
||||||
|
bool vertical = Orientation == Orientation.Vertical;
|
||||||
|
double trackLength = vertical ? finalSize.Height : finalSize.Width;
|
||||||
|
if (trackLength <= 0.0) return finalSize;
|
||||||
|
|
||||||
|
double min = Minimum, max = Maximum;
|
||||||
|
double range = Math.Max(0.0, max - min);
|
||||||
|
double value = Math.Max(min, Math.Min(max, Value));
|
||||||
|
viewport = Math.Max(0.0, viewport);
|
||||||
|
|
||||||
|
double thumbLength, decreaseLength;
|
||||||
|
if (range <= 0.0 || viewport <= 0.0)
|
||||||
|
{
|
||||||
|
thumbLength = trackLength; // 스크롤 불필요 → 썸이 트랙 전체
|
||||||
|
decreaseLength = 0.0;
|
||||||
|
_density = 0.0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
double extent = range + viewport;
|
||||||
|
thumbLength = trackLength * viewport / extent; // 비례 크기
|
||||||
|
double minThumb = Math.Min(MinThumbLength, trackLength); // 트랙보다 길 수 없음
|
||||||
|
if (thumbLength < minThumb) thumbLength = minThumb; // ← 하한 강제
|
||||||
|
if (thumbLength > trackLength) thumbLength = trackLength;
|
||||||
|
|
||||||
|
double remaining = Math.Max(0.0, trackLength - thumbLength);
|
||||||
|
_density = remaining > 0.0 ? range / remaining : 0.0;
|
||||||
|
decreaseLength = remaining * ((value - min) / range); // 시작(위/왼쪽)~썸 사이 여백
|
||||||
|
}
|
||||||
|
_thumbLength = thumbLength;
|
||||||
|
double increaseLength = Math.Max(0.0, trackLength - thumbLength - decreaseLength);
|
||||||
|
|
||||||
|
var dec = DecreaseRepeatButton;
|
||||||
|
var inc = IncreaseRepeatButton;
|
||||||
|
if (vertical)
|
||||||
|
{
|
||||||
|
double w = finalSize.Width;
|
||||||
|
dec?.Arrange(new Rect(0, 0, w, decreaseLength));
|
||||||
|
thumb.Arrange(new Rect(0, decreaseLength, w, thumbLength));
|
||||||
|
inc?.Arrange(new Rect(0, decreaseLength + thumbLength, w, increaseLength));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
double h = finalSize.Height;
|
||||||
|
dec?.Arrange(new Rect(0, 0, decreaseLength, h));
|
||||||
|
thumb.Arrange(new Rect(decreaseLength, 0, thumbLength, h));
|
||||||
|
inc?.Arrange(new Rect(decreaseLength + thumbLength, 0, increaseLength, h));
|
||||||
|
}
|
||||||
|
return finalSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>썸 드래그 거리 → 값 변화. 아래/오른쪽으로 끌면 value 증가(스크롤 내려감).</summary>
|
||||||
|
public override double ValueFromDistance(double horizontal, double vertical)
|
||||||
|
{
|
||||||
|
double dist = Orientation == Orientation.Vertical ? vertical : horizontal;
|
||||||
|
return dist * _density;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override double ValueFromPoint(Point pt)
|
||||||
|
{
|
||||||
|
bool vertical = Orientation == Orientation.Vertical;
|
||||||
|
double trackLength = vertical ? ActualHeight : ActualWidth;
|
||||||
|
double remaining = Math.Max(1.0, trackLength - _thumbLength);
|
||||||
|
double pos = (vertical ? pt.Y : pt.X) - _thumbLength / 2.0;
|
||||||
|
double f = Math.Max(0.0, Math.Min(1.0, pos / remaining));
|
||||||
|
return Minimum + f * (Maximum - Minimum);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,17 +16,18 @@ public static class PaletteIconCatalog
|
|||||||
["Label"] = ("type", "표시"),
|
["Label"] = ("type", "표시"),
|
||||||
["PictureBox"] = ("image", "표시"),
|
["PictureBox"] = ("image", "표시"),
|
||||||
["Line"] = ("minus", "표시"),
|
["Line"] = ("minus", "표시"),
|
||||||
|
// MaskedTextBox 는 TextBox 와 아이콘이 겹쳐 구분이 안 됐다 — [200] 어휘로 분리
|
||||||
["TextBox"] = ("text-cursor-input", "입력"),
|
["TextBox"] = ("text-cursor-input", "입력"),
|
||||||
["MaskedTextBox"] = ("text-cursor-input", "입력"),
|
["MaskedTextBox"] = ("type", "입력"),
|
||||||
["ComboBox"] = ("chevron-down", "입력"),
|
["ComboBox"] = ("list", "입력"),
|
||||||
["ListBox"] = ("list", "입력"),
|
["ListBox"] = ("list-checks", "입력"),
|
||||||
["CheckList"] = ("list-checks", "입력"),
|
["CheckList"] = ("list-checks", "입력"),
|
||||||
["DateTimePicker"] = ("calendar", "입력"),
|
["DateTimePicker"] = ("calendar", "입력"),
|
||||||
["CalcBox"] = ("sigma", "입력"),
|
["CalcBox"] = ("sigma", "입력"),
|
||||||
["CheckBox"] = ("square-check", "선택"),
|
["CheckBox"] = ("square-check", "선택"),
|
||||||
["RadioButton"] = ("circle-dot", "선택"),
|
["RadioButton"] = ("circle-dot", "선택"),
|
||||||
["Panel"] = ("box", "컨테이너"),
|
["Panel"] = ("package", "컨테이너"),
|
||||||
["GroupBox"] = ("package", "컨테이너"),
|
["GroupBox"] = ("box", "컨테이너"),
|
||||||
["Button"] = ("mouse-pointer-click", "동작/데이터"),
|
["Button"] = ("mouse-pointer-click", "동작/데이터"),
|
||||||
["DataTable"] = ("table", "동작/데이터"),
|
["DataTable"] = ("table", "동작/데이터"),
|
||||||
};
|
};
|
||||||
@@ -41,7 +42,39 @@ public static class PaletteIconCatalog
|
|||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>팔레트 타입명 → Lucide 아이콘 비주얼(FrameworkElement) 컨버터. parameter=크기(기본 18)</summary>
|
/// <summary>
|
||||||
|
/// 아이콘 컨버터 공용 파라미터 해석 — <c>"크기"</c> 또는 <c>"크기|브러시키"</c>.
|
||||||
|
///
|
||||||
|
/// 브러시 키를 생략하면 종전처럼 <c>B.Muted</c> 를 쓴다(하위호환). 색을 인자로 받을 수 있어야
|
||||||
|
/// 활성 문서 탭 아이콘을 강조색으로, 레이어 타입 아이콘을 본문색으로 굽는 것이 가능해진다.
|
||||||
|
/// </summary>
|
||||||
|
internal static class IconParam
|
||||||
|
{
|
||||||
|
public static (double Size, Brush Brush) Parse(object? parameter, double defaultSize)
|
||||||
|
{
|
||||||
|
var size = defaultSize;
|
||||||
|
var key = "B.Muted";
|
||||||
|
|
||||||
|
if (parameter is string text && text.Length > 0)
|
||||||
|
{
|
||||||
|
var bar = text.IndexOf('|');
|
||||||
|
var sizePart = bar >= 0 ? text[..bar] : text;
|
||||||
|
if (bar >= 0 && bar + 1 < text.Length)
|
||||||
|
{
|
||||||
|
key = text[(bar + 1)..].Trim();
|
||||||
|
}
|
||||||
|
if (double.TryParse(sizePart, NumberStyles.Float, CultureInfo.InvariantCulture, out var parsed))
|
||||||
|
{
|
||||||
|
size = parsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var brush = System.Windows.Application.Current?.TryFindResource(key) as Brush ?? Brushes.Gray;
|
||||||
|
return (size, brush);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>팔레트 타입명 → Lucide 아이콘 비주얼(FrameworkElement) 컨버터. parameter=<c>"크기"</c> 또는 <c>"크기|브러시키"</c>(기본 18, B.Muted)</summary>
|
||||||
public sealed class TypeToIconConverter : IValueConverter
|
public sealed class TypeToIconConverter : IValueConverter
|
||||||
{
|
{
|
||||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
@@ -50,8 +83,7 @@ public sealed class TypeToIconConverter : IValueConverter
|
|||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
var size = parameter is string s && double.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out var v) ? v : 18;
|
var (size, brush) = IconParam.Parse(parameter, 18);
|
||||||
var brush = System.Windows.Application.Current.TryFindResource("B.Muted") as Brush ?? Brushes.Gray;
|
|
||||||
return LucideIcons.Icon(PaletteIconCatalog.IconOf(type), size, brush);
|
return LucideIcons.Icon(PaletteIconCatalog.IconOf(type), size, brush);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,7 +114,7 @@ public sealed class InverseBoolConverter : IValueConverter
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Lucide 아이콘명 → 비주얼 컨버터(문서 탭 file-text, 플로팅 바 layout-grid 등 고정 아이콘용).
|
/// Lucide 아이콘명 → 비주얼 컨버터(문서 탭 file-text, 플로팅 바 layout-grid 등 고정 아이콘용).
|
||||||
/// Binding Source 에 아이콘명 문자열을 넣어 사용 — 항목마다 새 비주얼이 생성되어 공유 부모 충돌이 없다.
|
/// Binding Source 에 아이콘명 문자열을 넣어 사용 — 항목마다 새 비주얼이 생성되어 공유 부모 충돌이 없다.
|
||||||
/// parameter=크기(기본 14).
|
/// parameter=<c>"크기"</c> 또는 <c>"크기|브러시키"</c>(기본 14, B.Muted).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class IconNameToVisualConverter : IValueConverter
|
public sealed class IconNameToVisualConverter : IValueConverter
|
||||||
{
|
{
|
||||||
@@ -92,8 +124,7 @@ public sealed class IconNameToVisualConverter : IValueConverter
|
|||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
var size = parameter is string s && double.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out var v) ? v : 14;
|
var (size, brush) = IconParam.Parse(parameter, 14);
|
||||||
var brush = System.Windows.Application.Current.TryFindResource("B.Muted") as Brush ?? Brushes.Gray;
|
|
||||||
return LucideIcons.Icon(name, size, brush);
|
return LucideIcons.Icon(name, size, brush);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:shell="clr-namespace:System.Windows.Shell;assembly=PresentationFramework">
|
xmlns:shell="clr-namespace:System.Windows.Shell;assembly=PresentationFramework"
|
||||||
|
xmlns:ctl="clr-namespace:SheetMe.Designer.Controls">
|
||||||
|
|
||||||
<!-- ============================================================
|
<!-- ============================================================
|
||||||
디자이너 공유 테마 (암시 스타일 + 컨트롤 템플릿)
|
디자이너 공유 테마 (암시 스타일 + 컨트롤 템플릿)
|
||||||
@@ -9,10 +10,14 @@
|
|||||||
· 스타일 키(Subtle/CaptionBtn/B.ComboItem/ScrollThumb…)는 StaticResource 유지
|
· 스타일 키(Subtle/CaptionBtn/B.ComboItem/ScrollThumb…)는 StaticResource 유지
|
||||||
============================================================ -->
|
============================================================ -->
|
||||||
|
|
||||||
<!-- ===== 창 공통(다이얼로그 배경/폰트 — 로컬 지정이 있으면 로컬 우선) ===== -->
|
<!-- ===== 창 공통(다이얼로그 배경/폰트 — 로컬 지정이 있으면 로컬 우선) =====
|
||||||
|
FontSize/Foreground 가 없으면 대화상자들이 WPF 기본 12px + 시스템 검정으로 렌더돼
|
||||||
|
메인 셸(13px)과 결이 어긋난다. 토큰 참조는 DynamicResource 로 둬야 테마 전환에 따라간다. -->
|
||||||
<Style TargetType="Window">
|
<Style TargetType="Window">
|
||||||
<Setter Property="Background" Value="{DynamicResource B.Panel}" />
|
<Setter Property="Background" Value="{DynamicResource B.AppBg}" />
|
||||||
<Setter Property="FontFamily" Value="Malgun Gothic" />
|
<Setter Property="FontFamily" Value="Malgun Gothic" />
|
||||||
|
<Setter Property="FontSize" Value="13" />
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource B.Ink}" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<!-- ===== 타이포 ===== -->
|
<!-- ===== 타이포 ===== -->
|
||||||
@@ -386,8 +391,9 @@
|
|||||||
<Setter Property="Template">
|
<Setter Property="Template">
|
||||||
<Setter.Value>
|
<Setter.Value>
|
||||||
<ControlTemplate TargetType="TabItem">
|
<ControlTemplate TargetType="TabItem">
|
||||||
|
<!-- 비활성 탭은 사면 테두리, 활성 탭만 하단을 끊어 본문과 이어지게 한다(크롬식 룩) -->
|
||||||
<Border x:Name="bd" CornerRadius="6,6,0,0" Padding="11,5,6,6" Margin="0,0,3,0"
|
<Border x:Name="bd" CornerRadius="6,6,0,0" Padding="11,5,6,6" Margin="0,0,3,0"
|
||||||
Background="Transparent" BorderBrush="Transparent" BorderThickness="1,1,1,0"
|
Background="Transparent" BorderBrush="Transparent" BorderThickness="1"
|
||||||
SnapsToDevicePixels="True">
|
SnapsToDevicePixels="True">
|
||||||
<ContentPresenter ContentSource="Header" VerticalAlignment="Center"
|
<ContentPresenter ContentSource="Header" VerticalAlignment="Center"
|
||||||
TextElement.Foreground="{Binding Foreground, RelativeSource={RelativeSource TemplatedParent}}" />
|
TextElement.Foreground="{Binding Foreground, RelativeSource={RelativeSource TemplatedParent}}" />
|
||||||
@@ -400,6 +406,7 @@
|
|||||||
<Trigger Property="IsSelected" Value="True">
|
<Trigger Property="IsSelected" Value="True">
|
||||||
<Setter TargetName="bd" Property="Background" Value="{DynamicResource B.Surface}" />
|
<Setter TargetName="bd" Property="Background" Value="{DynamicResource B.Surface}" />
|
||||||
<Setter TargetName="bd" Property="BorderBrush" Value="{DynamicResource B.Line}" />
|
<Setter TargetName="bd" Property="BorderBrush" Value="{DynamicResource B.Line}" />
|
||||||
|
<Setter TargetName="bd" Property="BorderThickness" Value="1,1,1,0" />
|
||||||
<Setter Property="Foreground" Value="{DynamicResource B.Ink}" />
|
<Setter Property="Foreground" Value="{DynamicResource B.Ink}" />
|
||||||
<Setter Property="FontWeight" Value="SemiBold" />
|
<Setter Property="FontWeight" Value="SemiBold" />
|
||||||
</Trigger>
|
</Trigger>
|
||||||
@@ -461,6 +468,34 @@
|
|||||||
</Setter>
|
</Setter>
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
|
<!-- 플로팅 바 도구 토글(선택/손) — [200] 은 켜짐을 B.Accent2 로 은은하게 채운다.
|
||||||
|
암시 ToggleButton 스타일(위)은 인스펙터·레이어 토글도 함께 쓰므로 손대지 않고 키 스타일로 분리한다. -->
|
||||||
|
<Style x:Key="BarToolToggle" TargetType="ToggleButton" BasedOn="{StaticResource {x:Type ToggleButton}}">
|
||||||
|
<Style.Triggers>
|
||||||
|
<Trigger Property="IsChecked" Value="True">
|
||||||
|
<Setter Property="Background" Value="{DynamicResource B.Accent2}" />
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource B.Ink}" />
|
||||||
|
</Trigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<!-- 레이어 행의 눈·자물쇠 같은 소형 토글 — 꺼짐은 흐리게, 켜짐만 강조색 -->
|
||||||
|
<Style x:Key="MiniToggle" TargetType="ToggleButton" BasedOn="{StaticResource {x:Type ToggleButton}}">
|
||||||
|
<Setter Property="Width" Value="24" />
|
||||||
|
<Setter Property="Height" Value="22" />
|
||||||
|
<Setter Property="Padding" Value="0" />
|
||||||
|
<Setter Property="Opacity" Value="0.55" />
|
||||||
|
<Style.Triggers>
|
||||||
|
<Trigger Property="IsMouseOver" Value="True">
|
||||||
|
<Setter Property="Opacity" Value="1" />
|
||||||
|
</Trigger>
|
||||||
|
<Trigger Property="IsChecked" Value="True">
|
||||||
|
<Setter Property="Opacity" Value="1" />
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource B.Accent}" />
|
||||||
|
</Trigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
|
||||||
<!-- ===== 체크박스 ===== -->
|
<!-- ===== 체크박스 ===== -->
|
||||||
<Style TargetType="CheckBox">
|
<Style TargetType="CheckBox">
|
||||||
<Setter Property="Foreground" Value="{DynamicResource B.Ink}" />
|
<Setter Property="Foreground" Value="{DynamicResource B.Ink}" />
|
||||||
@@ -734,28 +769,58 @@
|
|||||||
</Setter>
|
</Setter>
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<!-- ===== 스크롤바 (슬림 다크) — 모든 ScrollViewer/ListBox/캔버스에 일괄 적용 ===== -->
|
<!-- ===== 스크롤바 ([200] 통일 규격 이식) =====
|
||||||
|
9px 폭 + 상하 14px 화살표 + 둥근 막대. 전 ScrollViewer/ListBox/캔버스/팝업에 암시 적용된다.
|
||||||
|
|
||||||
|
막대 색은 토큰이 아니라 리터럴 중립 그레이다 — 다크·라이트 양쪽에서 같은 회색으로 보여야
|
||||||
|
스크롤바가 배경에 따라 튀지 않는다([200] 과 동일한 의도적 선택).
|
||||||
|
|
||||||
|
막대 최소 길이는 MinThumbTrack 이 보장한다. 기본 WPF Track 은 Thumb.MinHeight 를 무시해
|
||||||
|
항목이 많은 목록에서 막대가 실처럼 가늘어진다. -->
|
||||||
|
<SolidColorBrush x:Key="ScrollThumbBrush" Color="#8A8F96" />
|
||||||
|
<SolidColorBrush x:Key="ScrollThumbHover" Color="#A6ACB3" />
|
||||||
|
<SolidColorBrush x:Key="ScrollThumbDrag" Color="#C4CAD0" />
|
||||||
|
<SolidColorBrush x:Key="ScrollArrow" Color="#9AA0A8" />
|
||||||
|
<SolidColorBrush x:Key="ScrollBtnHover" Color="#22808080" />
|
||||||
|
|
||||||
<Style x:Key="ScrollThumb" TargetType="Thumb">
|
<Style x:Key="ScrollThumb" TargetType="Thumb">
|
||||||
<Setter Property="OverridesDefaultStyle" Value="True" />
|
<Setter Property="OverridesDefaultStyle" Value="True" />
|
||||||
<Setter Property="IsTabStop" Value="False" />
|
<Setter Property="IsTabStop" Value="False" />
|
||||||
<!-- 막대 최소 크기 — 아이템이 많아도 실처럼 가늘어지지 않게(트랙보다 길면 WPF가 트랙 길이로 클램프) -->
|
<Setter Property="MinHeight" Value="24" />
|
||||||
<Setter Property="MinHeight" Value="48" />
|
<Setter Property="MinWidth" Value="24" />
|
||||||
<Setter Property="MinWidth" Value="48" />
|
|
||||||
<Setter Property="Template">
|
<Setter Property="Template">
|
||||||
<Setter.Value>
|
<Setter.Value>
|
||||||
<ControlTemplate TargetType="Thumb">
|
<ControlTemplate TargetType="Thumb">
|
||||||
<Border x:Name="t" CornerRadius="4" Background="{DynamicResource B.ScrollThumb}" Margin="2" SnapsToDevicePixels="True" />
|
<Border x:Name="t" CornerRadius="5" Background="{StaticResource ScrollThumbBrush}" Margin="2" SnapsToDevicePixels="True" />
|
||||||
<ControlTemplate.Triggers>
|
<ControlTemplate.Triggers>
|
||||||
<Trigger Property="IsMouseOver" Value="True"><Setter TargetName="t" Property="Background" Value="{DynamicResource B.ScrollThumbHover}" /></Trigger>
|
<Trigger Property="IsMouseOver" Value="True"><Setter TargetName="t" Property="Background" Value="{StaticResource ScrollThumbHover}" /></Trigger>
|
||||||
<Trigger Property="IsDragging" Value="True"><Setter TargetName="t" Property="Background" Value="{DynamicResource B.ScrollThumbDrag}" /></Trigger>
|
<Trigger Property="IsDragging" Value="True"><Setter TargetName="t" Property="Background" Value="{StaticResource ScrollThumbDrag}" /></Trigger>
|
||||||
</ControlTemplate.Triggers>
|
</ControlTemplate.Triggers>
|
||||||
</ControlTemplate>
|
</ControlTemplate>
|
||||||
</Setter.Value>
|
</Setter.Value>
|
||||||
</Setter>
|
</Setter>
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
|
<Style x:Key="ScrollLineBtn" TargetType="RepeatButton">
|
||||||
|
<Setter Property="OverridesDefaultStyle" Value="True" />
|
||||||
|
<Setter Property="Focusable" Value="False" />
|
||||||
|
<Setter Property="IsTabStop" Value="False" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="RepeatButton">
|
||||||
|
<Border x:Name="b" Background="Transparent" CornerRadius="3" SnapsToDevicePixels="True">
|
||||||
|
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||||
|
</Border>
|
||||||
|
<ControlTemplate.Triggers>
|
||||||
|
<Trigger Property="IsMouseOver" Value="True"><Setter TargetName="b" Property="Background" Value="{StaticResource ScrollBtnHover}" /></Trigger>
|
||||||
|
</ControlTemplate.Triggers>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
|
||||||
<Style x:Key="ScrollPageBtn" TargetType="RepeatButton">
|
<Style x:Key="ScrollPageBtn" TargetType="RepeatButton">
|
||||||
<Setter Property="OverridesDefaultStyle" Value="True" />
|
<Setter Property="OverridesDefaultStyle" Value="True" />
|
||||||
<Setter Property="Background" Value="Transparent" />
|
|
||||||
<Setter Property="Focusable" Value="False" />
|
<Setter Property="Focusable" Value="False" />
|
||||||
<Setter Property="IsTabStop" Value="False" />
|
<Setter Property="IsTabStop" Value="False" />
|
||||||
<Setter Property="Template">
|
<Setter Property="Template">
|
||||||
@@ -764,41 +829,70 @@
|
|||||||
</Setter.Value>
|
</Setter.Value>
|
||||||
</Setter>
|
</Setter>
|
||||||
</Style>
|
</Style>
|
||||||
<!-- 슬림 스크롤바 — 전 ScrollViewer/ListBox 공통(암시 적용) -->
|
|
||||||
|
<ControlTemplate x:Key="VScrollBar" TargetType="ScrollBar">
|
||||||
|
<Grid Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="14" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="14" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<RepeatButton Grid.Row="0" Style="{StaticResource ScrollLineBtn}" Command="ScrollBar.LineUpCommand">
|
||||||
|
<Path Stretch="None" Data="M1,5 L4.5,1.5 L8,5" Stroke="{StaticResource ScrollArrow}"
|
||||||
|
StrokeThickness="1.4" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" />
|
||||||
|
</RepeatButton>
|
||||||
|
<ctl:MinThumbTrack x:Name="PART_Track" MinThumbLength="44" Grid.Row="1" Orientation="Vertical"
|
||||||
|
Minimum="{TemplateBinding Minimum}" Maximum="{TemplateBinding Maximum}"
|
||||||
|
Value="{TemplateBinding Value}" ViewportSize="{TemplateBinding ViewportSize}">
|
||||||
|
<ctl:MinThumbTrack.DecreaseRepeatButton><RepeatButton Style="{StaticResource ScrollPageBtn}" Command="ScrollBar.PageUpCommand" /></ctl:MinThumbTrack.DecreaseRepeatButton>
|
||||||
|
<ctl:MinThumbTrack.Thumb><Thumb Style="{StaticResource ScrollThumb}" /></ctl:MinThumbTrack.Thumb>
|
||||||
|
<ctl:MinThumbTrack.IncreaseRepeatButton><RepeatButton Style="{StaticResource ScrollPageBtn}" Command="ScrollBar.PageDownCommand" /></ctl:MinThumbTrack.IncreaseRepeatButton>
|
||||||
|
</ctl:MinThumbTrack>
|
||||||
|
<RepeatButton Grid.Row="2" Style="{StaticResource ScrollLineBtn}" Command="ScrollBar.LineDownCommand">
|
||||||
|
<Path Stretch="None" Data="M1,1.5 L4.5,5 L8,1.5" Stroke="{StaticResource ScrollArrow}"
|
||||||
|
StrokeThickness="1.4" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" />
|
||||||
|
</RepeatButton>
|
||||||
|
</Grid>
|
||||||
|
</ControlTemplate>
|
||||||
|
|
||||||
|
<ControlTemplate x:Key="HScrollBar" TargetType="ScrollBar">
|
||||||
|
<Grid Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="14" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="14" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<RepeatButton Grid.Column="0" Style="{StaticResource ScrollLineBtn}" Command="ScrollBar.LineLeftCommand">
|
||||||
|
<Path Stretch="None" Data="M5,1 L1.5,4.5 L5,8" Stroke="{StaticResource ScrollArrow}"
|
||||||
|
StrokeThickness="1.4" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" />
|
||||||
|
</RepeatButton>
|
||||||
|
<ctl:MinThumbTrack x:Name="PART_Track" MinThumbLength="44" Grid.Column="1" Orientation="Horizontal"
|
||||||
|
Minimum="{TemplateBinding Minimum}" Maximum="{TemplateBinding Maximum}"
|
||||||
|
Value="{TemplateBinding Value}" ViewportSize="{TemplateBinding ViewportSize}">
|
||||||
|
<ctl:MinThumbTrack.DecreaseRepeatButton><RepeatButton Style="{StaticResource ScrollPageBtn}" Command="ScrollBar.PageLeftCommand" /></ctl:MinThumbTrack.DecreaseRepeatButton>
|
||||||
|
<ctl:MinThumbTrack.Thumb><Thumb Style="{StaticResource ScrollThumb}" /></ctl:MinThumbTrack.Thumb>
|
||||||
|
<ctl:MinThumbTrack.IncreaseRepeatButton><RepeatButton Style="{StaticResource ScrollPageBtn}" Command="ScrollBar.PageRightCommand" /></ctl:MinThumbTrack.IncreaseRepeatButton>
|
||||||
|
</ctl:MinThumbTrack>
|
||||||
|
<RepeatButton Grid.Column="2" Style="{StaticResource ScrollLineBtn}" Command="ScrollBar.LineRightCommand">
|
||||||
|
<Path Stretch="None" Data="M1,1 L4.5,4.5 L1,8" Stroke="{StaticResource ScrollArrow}"
|
||||||
|
StrokeThickness="1.4" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" />
|
||||||
|
</RepeatButton>
|
||||||
|
</Grid>
|
||||||
|
</ControlTemplate>
|
||||||
|
|
||||||
<Style TargetType="ScrollBar">
|
<Style TargetType="ScrollBar">
|
||||||
<Setter Property="Background" Value="Transparent" />
|
<Setter Property="Background" Value="Transparent" />
|
||||||
<Setter Property="Width" Value="11" />
|
<Setter Property="SnapsToDevicePixels" Value="True" />
|
||||||
<Setter Property="MinWidth" Value="11" />
|
<Setter Property="Width" Value="9" />
|
||||||
<Setter Property="Template">
|
<Setter Property="MinWidth" Value="9" />
|
||||||
<Setter.Value>
|
<Setter Property="Template" Value="{StaticResource VScrollBar}" />
|
||||||
<ControlTemplate TargetType="ScrollBar">
|
|
||||||
<Grid Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
|
|
||||||
<Track x:Name="PART_Track" Orientation="{TemplateBinding Orientation}" IsDirectionReversed="True">
|
|
||||||
<Track.DecreaseRepeatButton>
|
|
||||||
<RepeatButton Style="{StaticResource ScrollPageBtn}" Command="ScrollBar.PageUpCommand" />
|
|
||||||
</Track.DecreaseRepeatButton>
|
|
||||||
<Track.Thumb>
|
|
||||||
<Thumb Style="{StaticResource ScrollThumb}" />
|
|
||||||
</Track.Thumb>
|
|
||||||
<Track.IncreaseRepeatButton>
|
|
||||||
<RepeatButton Style="{StaticResource ScrollPageBtn}" Command="ScrollBar.PageDownCommand" />
|
|
||||||
</Track.IncreaseRepeatButton>
|
|
||||||
</Track>
|
|
||||||
</Grid>
|
|
||||||
<ControlTemplate.Triggers>
|
|
||||||
<Trigger Property="Orientation" Value="Horizontal">
|
|
||||||
<Setter TargetName="PART_Track" Property="IsDirectionReversed" Value="False" />
|
|
||||||
</Trigger>
|
|
||||||
</ControlTemplate.Triggers>
|
|
||||||
</ControlTemplate>
|
|
||||||
</Setter.Value>
|
|
||||||
</Setter>
|
|
||||||
<Style.Triggers>
|
<Style.Triggers>
|
||||||
<Trigger Property="Orientation" Value="Horizontal">
|
<Trigger Property="Orientation" Value="Horizontal">
|
||||||
<Setter Property="Width" Value="Auto" />
|
<Setter Property="Width" Value="Auto" />
|
||||||
<Setter Property="MinWidth" Value="0" />
|
<Setter Property="MinWidth" Value="0" />
|
||||||
<Setter Property="Height" Value="11" />
|
<Setter Property="Height" Value="9" />
|
||||||
<Setter Property="MinHeight" Value="11" />
|
<Setter Property="MinHeight" Value="9" />
|
||||||
|
<Setter Property="Template" Value="{StaticResource HScrollBar}" />
|
||||||
</Trigger>
|
</Trigger>
|
||||||
</Style.Triggers>
|
</Style.Triggers>
|
||||||
</Style>
|
</Style>
|
||||||
|
|||||||
@@ -43,4 +43,7 @@
|
|||||||
<SolidColorBrush x:Key="B.CloseHover" Color="#E04A2B" />
|
<SolidColorBrush x:Key="B.CloseHover" Color="#E04A2B" />
|
||||||
<SolidColorBrush x:Key="B.SliderRing" Color="Transparent" /><!-- 다크는 테두리 없음(흰 썸 그대로) -->
|
<SolidColorBrush x:Key="B.SliderRing" Color="Transparent" /><!-- 다크는 테두리 없음(흰 썸 그대로) -->
|
||||||
|
|
||||||
|
<!-- 플로팅 요소(팔레트 바·플라이아웃) 그림자 — 라이트는 흰 배경에 검은 그림자가 튀지 않게 더 넓고 옅게 -->
|
||||||
|
<DropShadowEffect x:Key="B.FloatShadow" BlurRadius="20" ShadowDepth="3" Direction="315" Opacity="0.5" Color="Black" RenderingBias="Quality" />
|
||||||
|
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
|
|||||||
@@ -43,4 +43,7 @@
|
|||||||
<SolidColorBrush x:Key="B.CloseHover" Color="#E04A2B" />
|
<SolidColorBrush x:Key="B.CloseHover" Color="#E04A2B" />
|
||||||
<SolidColorBrush x:Key="B.SliderRing" Color="#CFCFCF" /><!-- 라이트는 흰 썸 가장자리 링 -->
|
<SolidColorBrush x:Key="B.SliderRing" Color="#CFCFCF" /><!-- 라이트는 흰 썸 가장자리 링 -->
|
||||||
|
|
||||||
|
<!-- 플로팅 요소(팔레트 바·플라이아웃) 그림자 — 라이트는 흰 배경에 검은 그림자가 튀지 않게 더 넓고 옅게 -->
|
||||||
|
<DropShadowEffect x:Key="B.FloatShadow" BlurRadius="26" ShadowDepth="5" Direction="270" Opacity="0.14" Color="Black" RenderingBias="Quality" />
|
||||||
|
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
|
|||||||
@@ -442,20 +442,21 @@
|
|||||||
<Border Background="{DynamicResource B.Surface}" BorderBrush="{DynamicResource B.Line}" BorderThickness="1"
|
<Border Background="{DynamicResource B.Surface}" BorderBrush="{DynamicResource B.Line}" BorderThickness="1"
|
||||||
CornerRadius="12" Padding="7,5" HorizontalAlignment="Center" VerticalAlignment="Bottom"
|
CornerRadius="12" Padding="7,5" HorizontalAlignment="Center" VerticalAlignment="Bottom"
|
||||||
Margin="0,0,0,20">
|
Margin="0,0,0,20">
|
||||||
|
<!-- 그림자는 토큰으로 — 라이트에서 흰 배경에 검은 그림자가 그대로 얹히던 것을 테마별로 분리 -->
|
||||||
<Border.Effect>
|
<Border.Effect>
|
||||||
<DropShadowEffect BlurRadius="20" ShadowDepth="3" Opacity="0.35" Color="Black"/>
|
<StaticResource ResourceKey="B.FloatShadow"/>
|
||||||
</Border.Effect>
|
</Border.Effect>
|
||||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||||||
<ToggleButton Width="38" Height="34" Padding="0" Focusable="False"
|
<ToggleButton Style="{StaticResource BarToolToggle}" Width="38" Height="34" Padding="0" Focusable="False"
|
||||||
ToolTip="선택 도구 — 클릭으로 선택, 드래그로 이동"
|
ToolTip="선택 도구 — 클릭으로 선택, 드래그로 이동"
|
||||||
IsChecked="{Binding IsHandTool, Converter={StaticResource InverseBool}, Mode=OneWay}"
|
IsChecked="{Binding IsHandTool, Converter={StaticResource InverseBool}, Mode=OneWay}"
|
||||||
Click="OnSelectToolClick"
|
Click="OnSelectToolClick"
|
||||||
Content="{Binding Source=mouse-pointer-click, Converter={StaticResource IconName}, ConverterParameter=18}"/>
|
Content="{Binding Source=mouse-pointer-click, Converter={StaticResource IconName}, ConverterParameter=18|B.Ink}"/>
|
||||||
<ToggleButton Width="38" Height="34" Padding="0" Focusable="False"
|
<ToggleButton Style="{StaticResource BarToolToggle}" Width="38" Height="34" Padding="0" Focusable="False"
|
||||||
ToolTip="손 도구 — 드래그로 화면 이동 (Space 누르는 동안 임시)"
|
ToolTip="손 도구 — 드래그로 화면 이동 (Space 누르는 동안 임시)"
|
||||||
IsChecked="{Binding IsHandTool, Mode=OneWay}"
|
IsChecked="{Binding IsHandTool, Mode=OneWay}"
|
||||||
Click="OnHandToolClick"
|
Click="OnHandToolClick"
|
||||||
Content="{Binding Source=hand, Converter={StaticResource IconName}, ConverterParameter=18}"/>
|
Content="{Binding Source=hand, Converter={StaticResource IconName}, ConverterParameter=18|B.Ink}"/>
|
||||||
<Border Width="1" Height="22" Background="{DynamicResource B.Line}" Margin="5,0" VerticalAlignment="Center"/>
|
<Border Width="1" Height="22" Background="{DynamicResource B.Line}" Margin="5,0" VerticalAlignment="Center"/>
|
||||||
<Button x:Name="AddControlBtn" Style="{StaticResource Subtle}" Width="38" Height="34" Padding="0"
|
<Button x:Name="AddControlBtn" Style="{StaticResource Subtle}" Width="38" Height="34" Padding="0"
|
||||||
ToolTip="컨트롤 추가 — 활성 페이지 중앙에 배치" Click="OnAddControlFlyoutClick"
|
ToolTip="컨트롤 추가 — 활성 페이지 중앙에 배치" Click="OnAddControlFlyoutClick"
|
||||||
|
|||||||
Reference in New Issue
Block a user