사용자 지적이 맞았다. Effect 가 걸린 요소는 하위 트리가 중간 서피스로 렌더링되면서 ClearType 이 꺼지고, 글자가 회색 안티에일리어싱으로 그려진다. 13px 한글이 특히 뭉개진다. 저장소는 이 사실을 이미 알고 있었다 — MessageDialogView.xaml:18 과 TagPickerDialogView.xaml:11 이 "AllowsTransparency 를 켜면 ClearType 이 꺼져 13px 한글이 뭉개진다"고 적어 두었다. 그런데 그 판단이 <b>창에만</b> 적용되고 Effect 와 Popup 에는 적용되지 않았다. ClearTypeHint 는 저장소 전체에 0 곳이었다. 추론으로 끝내지 않았다. RenderTargetBitmap 으로는 이 질문에 답할 수 없다 — 그 자체가 알파를 가진 중간 서피스라서 Effect 가 있든 없든 양쪽 다 회색으로 나오고, 검사가 통과하면서 아무것도 증명하지 못한다. 그래서 진짜 화면을 BitBlt 으로 떠서 글리프의 채널 어긋난 픽셀(ClearType 은 R≠G≠B, 회색 AA 는 R=G=B)을 셌다. --cleartype 표본 11종 실측: A 아무것도 없음 색번짐 1115 ← 대조군. 이게 0 이면 판정 불가로 끝낸다 B Effect 색번짐 0 ← 지적한 그대로 C1 힌트를 Effect 요소 자신에게 색번짐 0 ← 듣지 않는다 C2 힌트를 안쪽 불투명 배경에 색번짐 1344 ← 여기가 맞는 자리 D 레이어드 팝업 색번짐 0 ← 두 번째 원인 E 팝업 + 힌트 색번짐 1115 ← AllowsTransparency 를 끌 필요가 없다 I 힌트 바깥에 ScrollViewer 색번짐 0 ← ScrollViewer 도 중간 서피스를 연다 J 힌트를 ScrollViewer 안쪽에 색번짐 1115 F 앱 콤보 드롭다운(실제 템플릿) 색번짐 0 → 수리 후 1115 두 번 헛짚었고 두 번 다 측정이 정정했다. CornerRadius 를 의심했으나 아니었고, ScrollViewer 가 범인이었다. 그리고 F 를 처음 통과시켰을 때는 선택된 콤보 항목의 파란 배경(B.Sel, 채널 차이 61)이 색번짐으로 잡힌 것이었다 — 색번짐 4359 인데 글자픽셀은 1075 였다. 그래서 배경이 유채색이면 판정을 거부하는 장치를 넣었다. 규칙: 힌트는 Effect·ScrollViewer·레이어드 팝업 <b>안쪽</b>의 불투명 배경 요소에 건다. 적용한 곳 — 글자가 많은 순: 콤보 드롭다운(DesignerTheme) · 메뉴 하위팝업(배치·정렬 명령이 다 여기 있다) · 색 팝오버 · 컨트롤 추가 플라이아웃 · 줌 플라이아웃 남긴 곳: 플로팅 바(글자는 줌 퍼센트뿐), QueryEditorWindow 의 Card 스타일 (Effect 가 DataGrid·편집기의 중첩 ScrollViewer 를 감싸서 힌트를 넣을 자리가 여럿이다 — 그림자를 빼는 쪽이 맞아 보이고, 그건 디자인 결정이라 따로 다룬다). 게이트: --cleartype 11/11, 테스트 277/277, --edit-smoke 0실패, --modal-check 0실패, --maxrect 0실패, --dialog-shots 57파일, --db-smoke 1271건 diff 0, --db-render P062 md5 8d683835f5d81e7bb41c79071d6bf954 동일(종이 렌더 불변). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
946 lines
63 KiB
XML
946 lines
63 KiB
XML
<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="Cursor" Value="Hand"/>
|
||
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
|
||
<Setter Property="HorizontalContentAlignment" Value="Left"/>
|
||
<Setter Property="Foreground" Value="{DynamicResource B.Muted}"/>
|
||
<Setter Property="FontSize" Value="12"/>
|
||
<Setter Property="Template">
|
||
<Setter.Value>
|
||
<ControlTemplate TargetType="ToggleButton">
|
||
<!--
|
||
스위치가 앞, 글씨가 뒤다. 라벨을 72px 칸에 밀어 넣으면
|
||
"저장본 재조회" 같은 이름이 두세 줄로 접혀 행이 뚱뚱해진다.
|
||
앞으로 빼면 글씨가 남은 폭을 다 쓰고 스위치끼리 세로 축도 또렷해진다.
|
||
라벨까지 한 ToggleButton 안이라 글씨를 눌러도 켜진다.
|
||
-->
|
||
<DockPanel Background="Transparent">
|
||
<Border x:Name="track" DockPanel.Dock="Left" Width="36" Height="20"
|
||
CornerRadius="10" SnapsToDevicePixels="True"
|
||
VerticalAlignment="Center"
|
||
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>
|
||
<ContentPresenter Margin="8,0,0,0" 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>
|
||
</DockPanel>
|
||
<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"/>
|
||
<Setter Property="Foreground" Value="{DynamicResource B.Ink}"/>
|
||
</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,3">
|
||
<TextBlock DockPanel.Dock="Right" Style="{StaticResource MixedHint}"/>
|
||
<!-- 3상태: 다중선택에서 값이 갈리면 불확정(IsOn=null). 꺼짐으로 보이면 안 된다 -->
|
||
<ToggleButton Style="{StaticResource SwitchToggle}"
|
||
IsChecked="{Binding IsOn}" IsThreeState="{Binding IsMixed, Mode=OneWay}"
|
||
Content="{Binding Label}" ToolTip="{Binding Label}"
|
||
AutomationProperties.Name="{Binding Label}"/>
|
||
</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}">
|
||
<!-- Effect 와 레이어드 팝업이 각각 중간 서피스를 만들어 ClearType 을 끈다.
|
||
힌트는 Effect 가 걸린 바깥 Border 에 걸면 듣지 않으므로, 안쪽 불투명 배경에 건다. -->
|
||
<StackPanel Width="212" Background="{DynamicResource B.Surface}"
|
||
RenderOptions.ClearTypeHint="Enabled">
|
||
<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>
|