Files
SheetMe/src/SheetMe.Designer/Views/InspectorView.xaml
T
MsystechandClaude Opus 5 a5083fd0bb 인스펙터 리디자인 — 목업대로
우측 속성 패널을 [200] 인스펙터 목업에 맞췄다. 바뀐 것 여섯.

· 탭이 알약에서 밑줄로. 알약은 '고르는 필터'로 읽히는데 탭은 화면을 가르는 것이지 값이 아니다.
  좌측 패널 탭 스타일을 재사용할 수 없었다 — 저쪽은 TabControl 의 키 없는 암시 스타일이고
  템플릿 안에 알약 컨테이너가 박혀 있어 고치면 레이어·팔레트 패널이 통째로 바뀐다.
  미선택도 밑줄 두께 2 를 Transparent 로 잡아 둔다. 안 그러면 고르는 순간 글자가 2px 튄다.
  밑줄과 글자는 같은 토큰(B.AccentText)이다 — B.Accent 는 라이트에서 3.0:1 이라 글자로 못 쓴다.

· 탭 배지를 뺐다. 잃는 게 없다: 개수가 0 인 탭은 RefreshTabCounts 가 탭 자체를 접으므로
  배지가 "0"을 보여 줄 기회가 애초에 없고, 누르기 전에 정확한 개수를 알아야 할 작업이 없다.
  DesignCount/DataCount/BehaviorCount 는 남긴다 — ShowDataTab/ShowBehaviorTab 이 그걸 물고 있다.

· 제목 줄이 생겼다. 타입 아이콘 + 이름 + ⋮. 무엇을 편집 중인지 아이콘으로 즉시 읽힌다.
  타입은 툴팁으로 보냈다(Summary 가 "TextBox — TextBox1" 형태로 이미 그 말을 한다).
  여럿을 골랐어도 타입이 같으면 그 타입 아이콘을 쓴다.

· 참/거짓이 체크박스에서 스위치로. 17px 사각형이 목록 오른쪽에 흩어져 정렬 축이 없었다.
  손잡이는 켬·끔 모두 흰색이다 — 색이 아니라 위치가 상태를 말하게 한다.
  불확정(다중선택 값 불일치)은 가운데다. 꺼짐으로 보이면 조용한 덮어쓰기 사고가 된다.
  미끄러지지 않고 튄다: 이 저장소에는 Storyboard 가 한 곳도 없어 여기만 움직이면 물성이 달라진다.
  ToggleButton 을 유지했으므로 스크린리더가 On/Off/Indeterminate 를 그대로 읽는다.

· 섹션 머리 — 위쪽에 구분선(아래에 그으면 마지막 섹션 밑에 뜬 선이 남는다), 제목을 B.Ink 로 승격,
  개수는 접혔을 때만. 펼쳤으면 개수는 바로 아래가 답이라 잡음이다.
  목업의 오른쪽 ⌃ 는 안 그린다 — 왼쪽 셰브론이 이미 같은 상태를 말하는데 한 행에 같은 정보를
  두 번 그리면 둘이 다른 뜻이라고 읽힌다.

· 라벨 폭 92 → 72, 세그먼트는 가로 꽉. 목업의 56 은 못 준다 — 목업에 없는 데이터·동작 탭에
  11자 라벨("입력 파라미터 컨트롤")이 있고 56 이면 3줄로 접힌다. 72 가 2줄을 지키는 최소값이다.
  줄어든 20px 은 그대로 입력 칸으로 갔다(121 → 141). 태그 제안 칩의 하드코딩 들여쓰기도 같이 옮겼다.

목업에 있지만 넣지 않은 것 둘. 회전 행과 그 옆 자물쇠 — Rotation/Angle 이 Core·레지스트리 전부
0건이다. 레거시 WinForms 컨트롤에 회전이 없다. 모델부터 새로 만들 신규 기능이지 리스킨이 아니다.
자물쇠도 종횡비 잠금이 없는 기능이고, 레이어 잠금과 같은 아이콘을 쓰면 오해가 확정된다.
'자동 줄바꿈'은 그 키가 레지스트리에 없어 실제 키인 '자동 높이'로 둔다.

'선택 정보' 탭은 만들지 않기로 했다(사용자 결정). 탭은 3개 그대로, 밑줄 스타일만 입혔다.
정렬 행은 디자인 탭 상단에 그대로 둔다 — 목업이 그려진 시점보다 나중에 생긴 기능이라
목업의 부재는 '빼라'는 뜻이 아니다.

새 토큰 B.SwitchOff 를 양 테마에 넣었다. 기존 토큰으로는 못 만든다 —
B.Input 은 라이트에서 패널과 같은 색이고, 다크는 입력 테두리가 Transparent 라
36x20 알약이 #262626#151515 = 1.23:1 로 사라진다. 비텍스트 3:1 을 넘긴 값으로 골랐다.
아이콘 ellipsis-vertical / file-text 2종 추가.

게이트: 테스트 267/267, --edit-smoke 237건 0실패, --query-popup 17/17, --maxrect 10/10,
--db-smoke 3000 diff 0·예외 0, --db-render P062 md5 8d683835f5d81e7bb41c79071d6bf954 동일,
--dialog-shots 57파일(인스펙터 스냅샷은 전부 바뀐다 — 이번 변경의 산출물이다).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 11:07:06 +09:00

924 lines
61 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<UserControl x:Class="SheetMe.Designer.Views.InspectorView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ins="clr-namespace:SheetMe.Designer.ViewModels.Inspector"
xmlns:ctl="clr-namespace:SheetMe.Designer.Controls"
xmlns:bh="clr-namespace:SheetMe.Designer.Behaviors"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance ins:InspectorViewModel}">
<UserControl.Resources>
<ctl:IconNameToVisualConverter x:Key="IconName"/>
<BooleanToVisibilityConverter x:Key="BoolToVisibility"/>
<!--
탭 — 밑줄형. 알약은 '고르는 필터'로 읽히는데 탭은 화면을 가르는 것이지 값이 아니다.
좌측 패널 탭 스타일을 재사용할 수 없다: 저쪽은 TabControl 의 키 없는 암시 스타일이고
템플릿 안에 알약 컨테이너가 박혀 있어, 고치면 레이어·팔레트 패널이 통째로 바뀐다.
미선택도 두께 2 를 Transparent 로 잡아 둔다 — 안 그러면 고르는 순간 글자가 2px 튄다.
밑줄과 글자는 같은 토큰이어야 한다. B.Accent 는 라이트에서 3.0:1 이라 글자로 못 쓴다.
-->
<Style x:Key="TabUnderline" TargetType="ToggleButton">
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Foreground" Value="{DynamicResource B.Muted}"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Border x:Name="bd" Background="Transparent" BorderBrush="Transparent"
BorderThickness="0,0,0,2" Padding="0,7,0,5" SnapsToDevicePixels="True">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center">
<ContentPresenter.Resources>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="{Binding Foreground, RelativeSource={RelativeSource AncestorType=ToggleButton}}"/>
<Setter Property="TextTrimming" Value="CharacterEllipsis"/>
</Style>
</ContentPresenter.Resources>
</ContentPresenter>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="{DynamicResource B.Ink}"/>
</Trigger>
<Trigger Property="IsKeyboardFocused" Value="True">
<Setter TargetName="bd" Property="Background" Value="{DynamicResource B.Hover}"/>
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="bd" Property="BorderBrush" Value="{DynamicResource B.AccentText}"/>
<Setter Property="Foreground" Value="{DynamicResource B.AccentText}"/>
<Setter Property="FontWeight" Value="SemiBold"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!--
스위치 — 참/거짓 속성. 체크박스는 17px 사각형이 목록 오른쪽에 흩어져 정렬 축이 없다.
손잡이는 켬·끔 모두 흰색이다. 색이 아니라 <b>위치</b>가 상태를 말하게 한다.
불확정(다중선택 값 불일치)은 가운데 — 꺼짐으로 보이면 조용한 덮어쓰기 사고가 된다.
미끄러지지 않고 튄다: 이 저장소에는 Storyboard 가 한 곳도 없다. 여기만 움직이면 물성이 달라진다.
-->
<Style x:Key="SwitchToggle" TargetType="ToggleButton">
<Setter Property="Width" Value="36"/>
<Setter Property="Height" Value="20"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Border x:Name="track" CornerRadius="10" SnapsToDevicePixels="True"
Background="{DynamicResource B.SwitchOff}"
BorderThickness="1" BorderBrush="Transparent">
<Ellipse x:Name="knob" Width="14" Height="14" Margin="3,0"
HorizontalAlignment="Left" VerticalAlignment="Center"
Fill="{DynamicResource B.OnAccent}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="track" Property="Background" Value="{DynamicResource B.AccentFill}"/>
<Setter TargetName="knob" Property="HorizontalAlignment" Value="Right"/>
</Trigger>
<Trigger Property="IsChecked" Value="{x:Null}">
<Setter TargetName="knob" Property="HorizontalAlignment" Value="Center"/>
<Setter TargetName="track" Property="Opacity" Value="0.55"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="track" Property="Opacity" Value="0.85"/>
</Trigger>
<Trigger Property="IsKeyboardFocused" Value="True">
<Setter TargetName="track" Property="BorderBrush" Value="{DynamicResource B.Accent}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="track" Property="Opacity" Value="0.45"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- 3탭 알약 — 좌측 패널 탭과 같은 결(선택만 채움) -->
<Style x:Key="TabPill" TargetType="ToggleButton">
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Foreground" Value="{DynamicResource B.Muted}"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Border x:Name="bd" Background="Transparent" CornerRadius="6" Padding="0,5" Margin="1">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center">
<ContentPresenter.Resources>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="{Binding Foreground, RelativeSource={RelativeSource AncestorType=ToggleButton}}"/>
</Style>
</ContentPresenter.Resources>
</ContentPresenter>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="bd" Property="Background" Value="{DynamicResource B.Hover}"/>
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="bd" Property="Background" Value="{DynamicResource B.Surface}"/>
<Setter Property="Foreground" Value="{DynamicResource B.Ink}"/>
<Setter Property="FontWeight" Value="Bold"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- 탭 배지 — 0 이면 그 탭에 볼 게 없다는 뜻이라 흐리게 -->
<Style x:Key="TabBadge" TargetType="TextBlock">
<Setter Property="FontSize" Value="10.5"/>
<Setter Property="FontWeight" Value="Normal"/>
<Setter Property="Margin" Value="4,1,0,0"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Foreground" Value="{DynamicResource B.Muted}"/>
<Style.Triggers>
<Trigger Property="Text" Value="0">
<Setter Property="Opacity" Value="0.45"/>
</Trigger>
</Style.Triggers>
</Style>
<!--
라벨 폭. 실측상 가장 긴 라벨이 11자("입력 파라미터 컨트롤", "선택지(줄바꿈 구분)")라
86px 에서는 "액션 대상 컨…" 처럼 잘렸다. 92px + 줄바꿈이면 두 줄로 접혀 전부 읽힌다
(자르기보다 낫다 — 잘린 라벨은 툴팁을 띄워봐야 아는 정보가 된다).
-->
<Style x:Key="RowLabel" TargetType="TextBlock">
<Setter Property="Width" Value="72"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Foreground" Value="{DynamicResource B.Muted}"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="TextWrapping" Value="Wrap"/>
<Setter Property="Margin" Value="0,0,6,0"/>
</Style>
<!--
인스펙터 전용 입력 치수 — 전역 TextBox 는 MinHeight 30 이라 한 화면에 몇 행 못 들어간다
(TextBox 하나만 21행이면 700px 를 넘겨 스크롤이 필수가 된다). 여기서만 26 으로 낮춘다.
전역 스타일을 낮추면 검색칩·대화상자 입력까지 같이 줄어드니 반드시 지역 스타일이어야 한다.
-->
<Style x:Key="RowInput" TargetType="TextBox" BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="MinHeight" Value="26"/>
<Setter Property="FontSize" Value="12"/>
</Style>
<!-- ComboBox 는 테마가 MinHeight 가 아니라 Height 를 '고정'한다 — MinHeight 만 낮추면
선택 행만 30px 로 남아 다른 행과 4px 어긋난다. 반드시 Height 를 내려야 한다. -->
<Style x:Key="RowCombo" TargetType="ComboBox" BasedOn="{StaticResource {x:Type ComboBox}}">
<Setter Property="Height" Value="26"/>
<Setter Property="FontSize" Value="12"/>
</Style>
<!-- 쌍 행의 짧은 라벨(X/Y/너비/높이) — 값 칸을 최대한 넓게 남긴다 -->
<Style x:Key="PairLabel" TargetType="TextBlock">
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Foreground" Value="{DynamicResource B.Muted}"/>
<Setter Property="FontSize" Value="11.5"/>
<Setter Property="Margin" Value="0,0,5,0"/>
<Setter Property="MinWidth" Value="22"/>
</Style>
<!--
정렬 버튼 — 액션이지 상태가 아니다. SegmentToggle 처럼 채우면 '켜져 있다'로 읽힌다.
채움 없이 호버 반응만 남긴다. 30x26 / radius 6 / 사이 2px 은 [200] 와이어프레임 .icn 실측값.
-->
<Style x:Key="AlignBtn" TargetType="Button">
<Setter Property="Width" Value="30"/>
<Setter Property="Height" Value="26"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Margin" Value="0,0,2,0"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="bd" Background="Transparent" CornerRadius="6"
BorderThickness="1" BorderBrush="Transparent" SnapsToDevicePixels="True">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="bd" Property="Background" Value="{DynamicResource B.Hover}"/>
</Trigger>
<Trigger Property="IsKeyboardFocused" Value="True">
<Setter TargetName="bd" Property="BorderBrush" Value="{DynamicResource B.Accent}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="bd" Property="Opacity" Value="0.45"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- 정렬 기준 알약 — 둘 중 하나만 켜지는 선택지라 RadioButton 이다(토글 두 개면 둘 다 꺼질 수 있다) -->
<Style x:Key="ScopePill" TargetType="RadioButton">
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="FontSize" Value="11.5"/>
<Setter Property="Margin" Value="0,0,4,0"/>
<Setter Property="Foreground" Value="{DynamicResource B.Muted}"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RadioButton">
<Border x:Name="bd" Background="{DynamicResource B.Input}" CornerRadius="6"
BorderThickness="1" BorderBrush="{DynamicResource B.InputBorder}"
Padding="9,3" SnapsToDevicePixels="True">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center">
<ContentPresenter.Resources>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="{Binding Foreground, RelativeSource={RelativeSource AncestorType=RadioButton}}"/>
</Style>
</ContentPresenter.Resources>
</ContentPresenter>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="bd" Property="Background" Value="{DynamicResource B.Hover}"/>
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="bd" Property="Background" Value="{DynamicResource B.Accent}"/>
<Setter TargetName="bd" Property="BorderBrush" Value="{DynamicResource B.Accent}"/>
<Setter Property="Foreground" Value="White"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- 세그먼트 한 칸 — 붙어 있는 아이콘 토글(굵게/기울임/밑줄/취소선).
폭을 고정하지 않는다 — UniformGrid 가 가로를 꽉 채워 나눈다(목업의 "가로 꽉"). -->
<Style x:Key="SegmentToggle" TargetType="ToggleButton">
<Setter Property="Height" Value="26"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Border x:Name="bd" Background="{DynamicResource B.Input}"
BorderBrush="{DynamicResource B.InputBorder}" BorderThickness="1"
Margin="0,0,2,0" CornerRadius="6" SnapsToDevicePixels="True">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="bd" Property="BorderBrush" Value="{DynamicResource B.Line2}"/>
</Trigger>
<!-- 켜짐은 채움 + 강조 테두리 둘 다 — 채움만으로는 라이트에서 1.1:1 이라 안 읽힌다 -->
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="bd" Property="Background" Value="{DynamicResource B.Accent2}"/>
<Setter TargetName="bd" Property="BorderBrush" Value="{DynamicResource B.Accent}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!--
"여러 값" 표시 — 다중선택에서 값이 갈리면 편집기가 빈 칸으로 보인다.
그 빈 칸은 "값 없음"과 구별되지 않아, 사용자가 여기에 무심코 입력하면 서로 다르던 값이
전부 한 값으로 덮인다. 비파괴 오버레이로만 알린다(입력을 가로막지 않는다).
-->
<Style x:Key="MixedHint" TargetType="TextBlock">
<Setter Property="Text" Value="여러 값"/>
<Setter Property="FontSize" Value="11"/>
<Setter Property="FontStyle" Value="Italic"/>
<Setter Property="Foreground" Value="{DynamicResource B.Muted}"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Margin" Value="7,0,0,0"/>
<Setter Property="IsHitTestVisible" Value="False"/>
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsMixed}" Value="True"/>
<Condition Binding="{Binding ValueText}" Value=""/>
</MultiDataTrigger.Conditions>
<Setter Property="Visibility" Value="Visible"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
<!--
편집기 위에 겹치는 변형 — 입력 중에는 비운다.
값은 LostFocus 에만 소스로 올라가므로 ValueText 는 타이핑 중 계속 "" 이고, 겹친 안내가
방금 친 글자 위에 그대로 남는다.
-->
<Style x:Key="MixedHintOverlay" TargetType="TextBlock" BasedOn="{StaticResource MixedHint}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsKeyboardFocusWithin, RelativeSource={RelativeSource AncestorType=Grid}}" Value="True">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
<!-- 접이식 섹션 헤더 — 클릭으로 접기/펴기. 인스펙터는 고급 속성까지 켜면 수십 행이 된다 -->
<DataTemplate DataType="{x:Type ins:SectionRowViewModel}">
<Button Command="{Binding ToggleCommand}" Cursor="Hand" HorizontalContentAlignment="Stretch"
Margin="0,0,0,2" Padding="0,3">
<Button.Template>
<ControlTemplate TargetType="Button">
<!-- 구분선은 섹션 '위'에 긋는다 — 아래에 그으면 마지막 섹션 밑에 뜬 선이 남는다 -->
<Border x:Name="bd" Background="Transparent" CornerRadius="5" Padding="3,9,3,5" Margin="0,4,0,0"
BorderThickness="0,1,0,0" BorderBrush="{DynamicResource B.Line}">
<DockPanel>
<ContentControl DockPanel.Dock="Left" Margin="0,0,5,0" VerticalAlignment="Center"
IsTabStop="False" Focusable="False"
Content="{Binding ChevronIcon, Converter={StaticResource IconName}, ConverterParameter=12}"/>
<!-- 개수는 접혔을 때만. 펼쳤으면 바로 아래가 답이라 잡음이다 -->
<TextBlock x:Name="count" DockPanel.Dock="Right" Text="{Binding CountText}" FontSize="11"
Foreground="{DynamicResource B.Muted}" VerticalAlignment="Center" Margin="6,0,2,0"
Visibility="Collapsed"/>
<TextBlock Text="{Binding Label}" FontWeight="Bold" FontSize="12"
Foreground="{DynamicResource B.Ink}" VerticalAlignment="Center"
TextTrimming="CharacterEllipsis"/>
</DockPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="bd" Property="Background" Value="{DynamicResource B.Hover}"/>
</Trigger>
<DataTrigger Binding="{Binding IsExpanded}" Value="False">
<Setter TargetName="count" Property="Visibility" Value="Visible"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
</DataTemplate>
<DataTemplate DataType="{x:Type ins:TextRowViewModel}">
<DockPanel Margin="0,2">
<TextBlock Text="{Binding Label}" Style="{StaticResource RowLabel}" ToolTip="{Binding Label}"/>
<Grid>
<TextBox Style="{StaticResource RowInput}" bh:InspectorFieldBehavior.CommitOnEnter="True" Text="{Binding ValueText, UpdateSourceTrigger=LostFocus}"/>
<TextBlock Style="{StaticResource MixedHintOverlay}"/>
</Grid>
</DockPanel>
</DataTemplate>
<!-- 두 값 한 줄 — X|Y, 너비|높이. 자식 행이 값·커밋을 그대로 들고 있다 -->
<DataTemplate DataType="{x:Type ins:PairRowViewModel}">
<Grid Margin="0,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="8"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<DockPanel Grid.Column="0">
<TextBlock Text="{Binding LeftLabel}" Style="{StaticResource PairLabel}"/>
<Grid DataContext="{Binding Left}">
<TextBox Style="{StaticResource RowInput}" bh:InspectorFieldBehavior.CommitOnEnter="True"
bh:InspectorFieldBehavior.Spin="True" ToolTip="↑/↓ 1씩 · Shift 10씩 · Enter 확정 · Esc 되돌리기"
Text="{Binding ValueText, UpdateSourceTrigger=LostFocus}"/>
<TextBlock Style="{StaticResource MixedHintOverlay}" Text="여러"/>
</Grid>
</DockPanel>
<DockPanel Grid.Column="2">
<TextBlock Text="{Binding RightLabel}" Style="{StaticResource PairLabel}"/>
<Grid DataContext="{Binding Right}">
<TextBox Style="{StaticResource RowInput}" bh:InspectorFieldBehavior.CommitOnEnter="True"
bh:InspectorFieldBehavior.Spin="True" ToolTip="↑/↓ 1씩 · Shift 10씩 · Enter 확정 · Esc 되돌리기"
Text="{Binding ValueText, UpdateSourceTrigger=LostFocus}"/>
<TextBlock Style="{StaticResource MixedHintOverlay}" Text="여러"/>
</Grid>
</DockPanel>
</Grid>
</DataTemplate>
<!--
정렬 격자 — 3열 UniformGrid 라 9값은 3×3, 3값은 1×3 으로 알아서 접힌다.
선택 표시는 클릭으로만 바뀌므로 ToggleButton 이 아니라 Button 을 쓰고(토글은 두 번 눌러
해제되어 값이 비는 사고가 난다) 현재 값과 같은 칸만 강조한다.
-->
<DataTemplate DataType="{x:Type ins:AlignRowViewModel}">
<DockPanel Margin="0,2">
<TextBlock Text="{Binding Label}" Style="{StaticResource RowLabel}" VerticalAlignment="Top" Margin="0,4,6,0"/>
<ItemsControl ItemsSource="{Binding Cells}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate><UniformGrid Columns="3"/></ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Height="26" Padding="0" Margin="0,0,2,2" Cursor="Hand"
ToolTip="{Binding Value}"
Command="{Binding DataContext.SelectCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding Value}">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border x:Name="bd" Background="{DynamicResource B.Input}"
BorderBrush="{DynamicResource B.InputBorder}" BorderThickness="1"
CornerRadius="6" SnapsToDevicePixels="True">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="bd" Property="BorderBrush" Value="{DynamicResource B.Line2}"/>
</Trigger>
<DataTrigger Binding="{Binding IsCurrent}" Value="True">
<Setter TargetName="bd" Property="Background" Value="{DynamicResource B.Accent2}"/>
<Setter TargetName="bd" Property="BorderBrush" Value="{DynamicResource B.Accent}"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
<!-- 아이콘이 있으면 아이콘, 없으면 방향 화살표 -->
<Grid>
<ContentControl IsTabStop="False" Focusable="False"
Content="{Binding Icon, Converter={StaticResource IconName}, ConverterParameter=14|B.Ink}"
Visibility="{Binding HasIcon, Converter={StaticResource BoolToVisibility}}"/>
<TextBlock Text="{Binding Glyph}" FontSize="13" HorizontalAlignment="Center"
VerticalAlignment="Center" Foreground="{DynamicResource B.Ink}"
Visibility="{Binding HasGlyph, Converter={StaticResource BoolToVisibility}}"/>
</Grid>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DockPanel>
</DataTemplate>
<!-- 아이콘 토글 묶음 — 굵게/기울임/밑줄/취소선 -->
<DataTemplate DataType="{x:Type ins:SegmentRowViewModel}">
<DockPanel Margin="0,2">
<TextBlock Text="{Binding Label}" Style="{StaticResource RowLabel}"/>
<!-- 가로를 꽉 채워 나눈다 — 라벨을 72 로 줄인 만큼이 그대로 칸으로 간다 -->
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate><UniformGrid Rows="1"/></ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ToggleButton Style="{StaticResource SegmentToggle}"
IsChecked="{Binding Toggle.IsOn}"
IsThreeState="{Binding Toggle.IsMixed, Mode=OneWay}"
ToolTip="{Binding ToolTip}"
Content="{Binding Icon, Converter={StaticResource IconName}, ConverterParameter=14|B.Ink}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DockPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type ins:MultilineTextRowViewModel}">
<DockPanel Margin="0,2">
<TextBlock Text="{Binding Label}" Style="{StaticResource RowLabel}" VerticalAlignment="Top"
Margin="0,4,0,0" ToolTip="{Binding Label}"/>
<Grid>
<TextBox Style="{StaticResource RowInput}" bh:InspectorFieldBehavior.CommitOnEnter="True" Text="{Binding ValueText, UpdateSourceTrigger=LostFocus}"
AcceptsReturn="True" TextWrapping="Wrap" MinHeight="48" MaxHeight="120"
VerticalScrollBarVisibility="Auto"/>
<TextBlock Style="{StaticResource MixedHintOverlay}" VerticalAlignment="Top" Margin="7,7,0,0"/>
</Grid>
</DockPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type ins:NumberRowViewModel}">
<DockPanel Margin="0,2">
<TextBlock Text="{Binding Label}" Style="{StaticResource RowLabel}" ToolTip="{Binding Label}"/>
<Grid>
<!-- 글꼴 크기처럼 소수를 실제로 받는 칸만 Ctrl 미세조정(0.1)을 켠다 -->
<TextBox Style="{StaticResource RowInput}" bh:InspectorFieldBehavior.CommitOnEnter="True"
bh:InspectorFieldBehavior.Spin="True" bh:InspectorFieldBehavior.FineStep="0.1"
ToolTip="↑/↓ 1씩 · Shift 10씩 · Ctrl 0.1씩 · Enter 확정 · Esc 되돌리기"
Text="{Binding ValueText, UpdateSourceTrigger=LostFocus}"/>
<TextBlock Style="{StaticResource MixedHintOverlay}"/>
</Grid>
</DockPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type ins:ToggleRowViewModel}">
<DockPanel Margin="0,2">
<TextBlock Text="{Binding Label}" Style="{StaticResource RowLabel}" ToolTip="{Binding Label}"/>
<!-- 3상태: 다중선택에서 값이 갈리면 불확정(IsOn=null). 꺼짐으로 보이면 안 된다 -->
<ToggleButton Style="{StaticResource SwitchToggle}"
IsChecked="{Binding IsOn}" IsThreeState="{Binding IsMixed, Mode=OneWay}"
AutomationProperties.Name="{Binding Label}"
HorizontalAlignment="Left" VerticalAlignment="Center"/>
<TextBlock Style="{StaticResource MixedHint}"/>
</DockPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type ins:ChoiceRowViewModel}">
<DockPanel Margin="0,2">
<TextBlock Text="{Binding Label}" Style="{StaticResource RowLabel}" ToolTip="{Binding Label}"/>
<Grid>
<ComboBox Style="{StaticResource RowCombo}" ItemsSource="{Binding Choices}" SelectedItem="{Binding ValueText}"/>
<TextBlock Style="{StaticResource MixedHintOverlay}" Margin="9,0,0,0"/>
</Grid>
</DockPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type ins:TagPickerRowViewModel}">
<StackPanel Margin="0,2">
<DockPanel>
<TextBlock Text="{Binding Label}" Style="{StaticResource RowLabel}"/>
<!-- 26px 칩 옆에서 "…" 는 점 하나로 보인다 — 아이콘 버튼으로 -->
<Button DockPanel.Dock="Right" Width="28" Height="26" Padding="0" Margin="4,0,0,0"
Command="{Binding BrowseCommand}" ToolTip="전체 목록에서 선택"
Content="{Binding Source=list, Converter={StaticResource IconName}, ConverterParameter=13}"/>
<Grid>
<TextBox Style="{StaticResource RowInput}" bh:InspectorFieldBehavior.CommitOnEnter="True" Text="{Binding ValueText, UpdateSourceTrigger=LostFocus}"
ToolTip="직접 입력하거나 아래 제안을 누르세요"/>
<TextBlock Style="{StaticResource MixedHintOverlay}"/>
</Grid>
</DockPanel>
<!-- 이 컨트롤 타입에서 실제로 쓰이는 태그(빈도순). 아직 안 골랐을 때만 뜬다 —
이미 정해진 행 아래에도 칩을 깔면 좁은 인스펙터가 금방 길어진다. -->
<ItemsControl ItemsSource="{Binding Suggestions}" Margin="72,5,0,2"
Visibility="{Binding ShowSuggestions, Converter={StaticResource BoolToVisibility}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Style="{StaticResource SuggestChip}" Content="{Binding}"
Command="{Binding DataContext.PickCommand,
RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type ins:ReadOnlyRowViewModel}">
<DockPanel Margin="0,2">
<TextBlock Text="{Binding Label}" Style="{StaticResource RowLabel}"/>
<TextBlock Text="{Binding ValueText}" FontSize="11" Foreground="{DynamicResource B.Muted}"
VerticalAlignment="Center" TextTrimming="CharacterEllipsis"/>
</DockPanel>
</DataTemplate>
<!-- 전체 속성 — 목록의 탈출구라 채움 없는 테두리 버튼으로 둔다(값이 아니라 이동이다) -->
<DataTemplate DataType="{x:Type ins:ToggleAdvancedRowViewModel}">
<Button Command="{Binding ToggleCommand}" Cursor="Hand"
Margin="0,10,0,2" HorizontalAlignment="Stretch" FontSize="11">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border x:Name="bd" Background="Transparent" CornerRadius="6" Padding="6,5"
BorderThickness="1" BorderBrush="{DynamicResource B.Line2}"
SnapsToDevicePixels="True">
<TextBlock Text="{Binding ButtonText}" HorizontalAlignment="Center"
Foreground="{DynamicResource B.Muted}" FontSize="11"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="bd" Property="Background" Value="{DynamicResource B.Hover}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
</DataTemplate>
<DataTemplate DataType="{x:Type ins:AddPropertyRowViewModel}">
<StackPanel Margin="0,6,0,2">
<DockPanel>
<Button DockPanel.Dock="Right" Content="추가" Padding="8,2" Margin="4,0,0,0"
Command="{Binding AddCommand}"
ToolTip="레거시 컨트롤의 속성 이름을 정확히 입력하세요 (예: EnterTabYon, AutoHeight)"/>
<TextBox Text="{Binding KeyText, UpdateSourceTrigger=PropertyChanged}" FontSize="12"
ToolTip="새 속성 키 입력 — 아래 제안을 눌러도 됩니다"/>
</DockPanel>
<!-- 이 타입에서 실제로 쓰이는 키 제안(운영 DB 집계). 누르면 바로 추가된다.
철자를 외워 맞히게 두면 라벨의 소문자 visible 같은 함정에서 조용히 틀린다. -->
<ItemsControl ItemsSource="{Binding Matches}" Margin="0,6,0,0"
Visibility="{Binding HasMatches, Converter={StaticResource BoolToVisibility}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Style="{StaticResource SuggestChip}" Content="{Binding}"
Command="{Binding DataContext.PickCommand,
RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding}"
ToolTip="{Binding StringFormat='{}{0} 속성을 추가합니다'}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<TextBlock Text="{Binding MoreText}" FontSize="10.5" Margin="0,0,0,2"
Foreground="{DynamicResource B.Muted}"
Visibility="{Binding HasMore, Converter={StaticResource BoolToVisibility}}"/>
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type ins:DataTableFieldRowViewModel}">
<DockPanel Margin="0,2">
<TextBlock Text="{Binding Label}" Style="{StaticResource RowLabel}"/>
<Button DockPanel.Dock="Right" Content="편집…" Padding="8,2" Height="26" Margin="4,0,0,0"
Command="{Binding EditCommand}"
ToolTip="데이터소스 배선 편집기 — 문법을 직접 치지 않아도 됩니다"/>
<TextBlock Text="{Binding Summary}" FontSize="11" Foreground="{DynamicResource B.Muted}"
VerticalAlignment="Center" TextTrimming="CharacterEllipsis"
ToolTip="{Binding ValueText}"/>
</DockPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type ins:QueryRowViewModel}">
<DockPanel Margin="0,2">
<TextBlock Text="{Binding Label}" Style="{StaticResource RowLabel}"/>
<Button DockPanel.Dock="Right" Content="편집…" Padding="8,2" Height="26" Margin="4,0,0,0"
Command="{Binding EditCommand}" ToolTip="쿼리 편집기 열기 (데이터소스 더블클릭과 동일)"/>
<TextBlock Text="{Binding Summary}" FontSize="11" Foreground="{DynamicResource B.Muted}"
VerticalAlignment="Center" TextTrimming="CharacterEllipsis"
ToolTip="{Binding ValueText}"/>
</DockPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type ins:MaskRowViewModel}">
<StackPanel Margin="0,2">
<DockPanel>
<TextBlock Text="{Binding Label}" Style="{StaticResource RowLabel}"/>
<Button DockPanel.Dock="Right" Width="28" Height="26" Padding="0" Margin="4,0,0,0"
Command="{Binding BrowseCommand}"
ToolTip="마스크 편집기 — 자주 쓰는 마스크 목록과 미리보기"
Content="{Binding Source=list, Converter={StaticResource IconName}, ConverterParameter=13}"/>
<Grid>
<TextBox Style="{StaticResource RowInput}" bh:InspectorFieldBehavior.CommitOnEnter="True" Text="{Binding ValueText, UpdateSourceTrigger=LostFocus}"
FontFamily="Consolas, D2Coding, 굴림체"
ToolTip="0=필수 숫자, 9=선택 숫자, #=숫자·부호 — 오른쪽 버튼에서 목록·미리보기"/>
<TextBlock Style="{StaticResource MixedHintOverlay}"/>
</Grid>
</DockPanel>
<!-- 화면에 실제로 어떻게 보이는지 — 마스크 문법을 몰라도 결과로 확인 -->
<TextBlock Text="{Binding Preview}" FontSize="11" Margin="0,2,0,0"
FontFamily="Consolas, D2Coding, 굴림체"
Foreground="{DynamicResource B.Muted}" TextTrimming="CharacterEllipsis"/>
</StackPanel>
</DataTemplate>
<!-- 팔레트 견본 하나 — 윤곽은 테마색이 아니라 견본 자신의 명도로 정한다
(테마 테두리를 쓰면 라이트에서 흰 견본이, 다크에서 검정 견본이 빈칸으로 보인다) -->
<DataTemplate x:Key="ColorSwatchTemplate" DataType="{x:Type ins:ColorSwatchViewModel}">
<Button Width="22" Height="22" Margin="0,0,4,4" Padding="0" Cursor="Hand"
ToolTip="{Binding ToolTip}"
Command="{Binding DataContext.ApplySwatchCommand,
RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding}">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border x:Name="sw" Background="{Binding Preview}" BorderBrush="{Binding Edge}"
BorderThickness="1" CornerRadius="4" SnapsToDevicePixels="True"/>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="sw" Property="BorderBrush" Value="{DynamicResource B.Accent}"/>
<Setter TargetName="sw" Property="BorderThickness" Value="2"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
</DataTemplate>
<DataTemplate DataType="{x:Type ins:ColorRowViewModel}">
<DockPanel Margin="0,2">
<TextBlock Text="{Binding Label}" Style="{StaticResource RowLabel}"
ToolTip="{Binding LabelToolTip}"/>
<!--
스와치 = 인라인 팔레트 토글. 실측상 배경색의 84%가 White 와 "224, 224, 224"
단 두 값이라, 그 자리에서 바로 고르는 편이 대화상자를 여는 것보다 빠르다.
임의 색은 팔레트 아래 '자세히…' 로 기존 피커(SV/Hue/HEX/RGB)를 연다.
-->
<Grid DockPanel.Dock="Right" Margin="4,0,0,0">
<ToggleButton x:Name="SwatchToggle" Width="30" Height="26" Padding="0" Cursor="Hand"
IsChecked="{Binding IsPaletteOpen, Mode=TwoWay}"
ToolTip="클릭하여 색 고르기">
<ToggleButton.Template>
<ControlTemplate TargetType="ToggleButton">
<Border x:Name="bd" Background="{Binding Preview}" BorderBrush="{DynamicResource B.Line2}"
BorderThickness="1" CornerRadius="4" SnapsToDevicePixels="True"/>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="bd" Property="BorderBrush" Value="{DynamicResource B.Accent}"/>
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="bd" Property="BorderBrush" Value="{DynamicResource B.Accent}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</ToggleButton.Template>
</ToggleButton>
<Popup IsOpen="{Binding IsPaletteOpen, Mode=TwoWay}" StaysOpen="False" AllowsTransparency="True"
PlacementTarget="{Binding ElementName=SwatchToggle}" Placement="Bottom"
HorizontalOffset="-176" VerticalOffset="4">
<Border Background="{DynamicResource B.Surface}" BorderBrush="{DynamicResource B.Line2}"
BorderThickness="1" CornerRadius="8" Padding="10" Effect="{DynamicResource B.FloatShadow}">
<StackPanel Width="212">
<TextBlock Text="배경" FontSize="11" Foreground="{DynamicResource B.Muted}" Margin="0,0,0,5"/>
<ItemsControl ItemsSource="{Binding BackgroundSwatches}"
ItemTemplate="{StaticResource ColorSwatchTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate><WrapPanel/></ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<TextBlock Text="글자·강조" FontSize="11" Foreground="{DynamicResource B.Muted}" Margin="0,8,0,5"/>
<ItemsControl ItemsSource="{Binding ForegroundSwatches}"
ItemTemplate="{StaticResource ColorSwatchTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate><WrapPanel/></ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<Border Height="1" Background="{DynamicResource B.Line}" Margin="0,9,0,8"/>
<DockPanel>
<Button DockPanel.Dock="Right" Content="자세히…" Height="28" Padding="10,0"
Command="{Binding PickCommand}"
ToolTip="색상 피커 — 채도·명도·색상환, HEX·RGB 직접 입력"/>
<Button HorizontalAlignment="Left" Content="비우기" Height="28" Padding="10,0"
Style="{StaticResource Subtle}" Command="{Binding ClearCommand}"
ToolTip="속성값을 지웁니다(기본값·상속으로 되돌림)"/>
</DockPanel>
</StackPanel>
</Border>
</Popup>
</Grid>
<Grid>
<TextBox Style="{StaticResource RowInput}" bh:InspectorFieldBehavior.CommitOnEnter="True" Text="{Binding ValueText, UpdateSourceTrigger=LostFocus}"
ToolTip="R, G, B 또는 색 이름 (예: 224, 224, 224 / White) — 오른쪽 스와치 클릭 시 피커"/>
<TextBlock Style="{StaticResource MixedHintOverlay}"/>
</Grid>
</DockPanel>
</DataTemplate>
</UserControl.Resources>
<Grid>
<DockPanel>
<!-- 제목 — 아이콘으로 무엇을 편집 중인지 즉시 읽히고, ⋮ 가 컨트롤 단위 명령의 자리다.
이름만 남기고 타입은 툴팁으로 보낸다(Summary 가 "TextBox — TextBox4" 형태). -->
<DockPanel DockPanel.Dock="Top" Margin="10,8,6,4"
Visibility="{Binding HasSelection, Converter={StaticResource BoolToVisibility}}">
<ContentControl DockPanel.Dock="Left" Margin="0,0,7,0" VerticalAlignment="Center"
IsTabStop="False" Focusable="False"
Content="{Binding HeaderIcon, Converter={StaticResource IconName}, ConverterParameter=16|B.Muted}"/>
<Button DockPanel.Dock="Right" Width="24" Height="24" Padding="0"
Style="{StaticResource Subtle}" ToolTip="이 컨트롤에 대한 명령"
AutomationProperties.Name="더보기"
Visibility="{Binding IsSingleSelection, Converter={StaticResource BoolToVisibility}}"
Content="{Binding Source=ellipsis-vertical, Converter={StaticResource IconName}, ConverterParameter=14|B.Muted}"/>
<TextBlock Text="{Binding HeaderName}" FontWeight="Bold" FontSize="12.5"
Foreground="{DynamicResource B.Ink}" VerticalAlignment="Center"
TextTrimming="CharacterEllipsis" ToolTip="{Binding Summary}"/>
</DockPanel>
<!--
디자인 / 데이터 / 동작 — 밑줄 탭.
배지를 뺐다. 개수가 0 인 탭은 RefreshTabCounts 가 탭 자체를 접으므로
배지가 "0"을 보여 줄 기회가 없고, 누르기 전에 정확한 개수를 알아야 할 일이 없다.
-->
<Border DockPanel.Dock="Top" Margin="10,2,10,8"
BorderBrush="{DynamicResource B.Line}" BorderThickness="0,0,0,1"
Visibility="{Binding ShowTabStrip, Converter={StaticResource BoolToVisibility}}">
<UniformGrid Rows="1">
<ToggleButton Style="{StaticResource TabUnderline}" IsChecked="{Binding IsDesignTab, Mode=TwoWay}"
Content="디자인" ToolTip="글자·색·정렬·크기 등 보이는 방식"/>
<ToggleButton Style="{StaticResource TabUnderline}" IsChecked="{Binding IsDataTab, Mode=TwoWay}"
Content="데이터"
Visibility="{Binding ShowDataTab, Converter={StaticResource BoolToVisibility}}"
ToolTip="자동 채움·조회·초기값·입력 형식 등 값에 관한 것"/>
<ToggleButton Style="{StaticResource TabUnderline}" IsChecked="{Binding IsBehaviorTab, Mode=TwoWay}"
Content="동작"
Visibility="{Binding ShowBehaviorTab, Converter={StaticResource BoolToVisibility}}"
ToolTip="액션 태그·대상 컨트롤 등 조작했을 때 벌어지는 일"/>
</UniformGrid>
</Border>
<!--
정렬 — 기준은 언제나 '담고 있는 것'이다. 선택 1개면 페이지(컨테이너 안이면 그 컨테이너),
2개 이상이면 선택 영역. 툴팁이 지금 무엇이 기준인지 말해 준다.
행 VM(Rows)이 아니라 여기 두는 이유 둘: AlignRowViewModel 은 이미 '텍스트 정렬'로
이름과 템플릿을 선점했고, 정렬은 스크롤과 무관하게 늘 보이는 편이 조작에 맞다.
-->
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="12,2,10,6"
ToolTipService.ShowOnDisabled="True"
Visibility="{Binding ShowAlignBar, Converter={StaticResource BoolToVisibility}}">
<Button Style="{StaticResource AlignBtn}" Command="{Binding AlignCommand}" CommandParameter="Left"
AutomationProperties.Name="왼쪽 맞춤"
ToolTip="{Binding AlignScopeText, StringFormat=왼쪽 맞춤 — {0}}"
Content="{Binding Source=align-obj-left, Converter={StaticResource IconName}, ConverterParameter=18|B.Muted}"/>
<Button Style="{StaticResource AlignBtn}" Command="{Binding AlignCommand}" CommandParameter="CenterH"
AutomationProperties.Name="가로 가운데 맞춤"
ToolTip="{Binding AlignScopeText, StringFormat=가로 가운데 — {0}}"
Content="{Binding Source=align-obj-center-h, Converter={StaticResource IconName}, ConverterParameter=18|B.Muted}"/>
<Button Style="{StaticResource AlignBtn}" Command="{Binding AlignCommand}" CommandParameter="Right"
AutomationProperties.Name="오른쪽 맞춤"
ToolTip="{Binding AlignScopeText, StringFormat=오른쪽 맞춤 — {0}}"
Content="{Binding Source=align-obj-right, Converter={StaticResource IconName}, ConverterParameter=18|B.Muted}"/>
<!-- 가로 축과 세로 축 사이 틈 8px — 참조 .align .gap -->
<Button Style="{StaticResource AlignBtn}" Margin="8,0,2,0" Command="{Binding AlignCommand}" CommandParameter="Top"
IsEnabled="{Binding CanAlignVertical}" AutomationProperties.Name="위 맞춤"
ToolTip="{Binding VerticalScopeText, StringFormat=위 맞춤 — {0}}"
Content="{Binding Source=align-obj-top, Converter={StaticResource IconName}, ConverterParameter=18|B.Muted}"/>
<Button Style="{StaticResource AlignBtn}" Command="{Binding AlignCommand}" CommandParameter="CenterV"
IsEnabled="{Binding CanAlignVertical}" AutomationProperties.Name="세로 가운데 맞춤"
ToolTip="{Binding VerticalScopeText, StringFormat=세로 가운데 — {0}}"
Content="{Binding Source=align-obj-middle, Converter={StaticResource IconName}, ConverterParameter=18|B.Muted}"/>
<Button Style="{StaticResource AlignBtn}" Command="{Binding AlignCommand}" CommandParameter="Bottom"
IsEnabled="{Binding CanAlignVertical}" AutomationProperties.Name="아래 맞춤"
ToolTip="{Binding VerticalScopeText, StringFormat=아래 맞춤 — {0}}"
Content="{Binding Source=align-obj-bottom, Converter={StaticResource IconName}, ConverterParameter=18|B.Muted}"/>
</StackPanel>
<!--
간격 균등 — 사이가 있어야 나눌 것이 있다(3개 이상). 없으면 회색으로 남기지 않고 접는다.
정렬 6개와 같은 줄에 두면 8칸 276px 가 되어 패널 최소 폭(250)에서 잘린다 — 줄을 나눈다.
-->
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="12,0,10,6"
Visibility="{Binding CanDistribute, Converter={StaticResource BoolToVisibility}}">
<TextBlock Text="간격" VerticalAlignment="Center" FontSize="11.5" Margin="0,0,8,0"
Foreground="{DynamicResource B.Muted}"/>
<Button Style="{StaticResource AlignBtn}" Command="{Binding DistributeCommand}" CommandParameter="HorizEqual"
AutomationProperties.Name="가로 간격 균등" ToolTip="가로 간격을 고르게"
Content="{Binding Source=distribute-h, Converter={StaticResource IconName}, ConverterParameter=18|B.Muted}"/>
<Button Style="{StaticResource AlignBtn}" Command="{Binding DistributeCommand}" CommandParameter="VertEqual"
AutomationProperties.Name="세로 간격 균등" ToolTip="세로 간격을 고르게"
Content="{Binding Source=distribute-v, Converter={StaticResource IconName}, ConverterParameter=18|B.Muted}"/>
</StackPanel>
<!--
정렬 기준 — 여럿을 골라 놓고도 '서로 맞추기'와 '종이에 맞추기'는 다른 작업이다.
왼쪽 문구는 선택에 따라 바뀐다: 여럿이면 '선택 영역', 하나면 '부모'(또는 '페이지').
-->
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="12,0,10,10"
Visibility="{Binding ShowAlignScope, Converter={StaticResource BoolToVisibility}}">
<TextBlock Text="기준" VerticalAlignment="Center" FontSize="11.5" Margin="0,0,8,0"
Foreground="{DynamicResource B.Muted}"/>
<RadioButton Style="{StaticResource ScopePill}" GroupName="AlignScope"
IsChecked="{Binding AlignToSelection, Mode=TwoWay}"
Content="{Binding AlignSelectionScopeLabel}"/>
<RadioButton Style="{StaticResource ScopePill}" GroupName="AlignScope"
IsChecked="{Binding AlignToPage, Mode=TwoWay}" Content="페이지"
ToolTip="{Binding PageScopeTip}"/>
</StackPanel>
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding Rows}" Margin="10,0,10,10">
<!-- 접힌 섹션의 행은 컨테이너만 숨긴다 — 행 VM 을 재생성하지 않아 편집 상태가 유지된다 -->
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Style.Triggers>
<DataTrigger Binding="{Binding IsRowVisible}" Value="False">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</ScrollViewer>
</DockPanel>
<!-- 선택은 있는데 이 탭이 빈 경우 — 빈 화면은 고장으로 읽힌다 -->
<TextBlock Text="{Binding EmptyTabText}" FontSize="12" TextAlignment="Center" TextWrapping="Wrap"
Foreground="{DynamicResource B.Muted}" HorizontalAlignment="Center"
VerticalAlignment="Center" Margin="24,0">
<TextBlock.Style>
<Style TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsTabEmpty}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<!-- 빈 상태 — 선택이 없으면 "선택 없음" 한 줄만 떠 있어 패널이 고장난 것처럼 보였다 -->
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Margin="24,0">
<StackPanel.Style>
<Style TargetType="StackPanel">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding HasSelection, FallbackValue=False}" Value="False">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<ContentControl HorizontalAlignment="Center" IsTabStop="False" Focusable="False" Opacity="0.45"
Content="{Binding Source=mouse-pointer-click, Converter={StaticResource IconName}, ConverterParameter=30}"/>
<TextBlock Text="컨트롤을 선택하면 속성이 표시됩니다" FontSize="12" TextAlignment="Center"
TextWrapping="Wrap" Foreground="{DynamicResource B.Muted}" Margin="0,12,0,0"/>
</StackPanel>
</Grid>
</UserControl>