1/2 가 껍데기(스크롤바·창 기본값)를 맞췄다면 이번은 그 안에 든 정보다. [200] 과의 실제 격차는
장식이 아니라 "한 화면에서 얼마나 읽히는가"였고, 그중 둘은 데이터 손상 경로이기도 했다.
[레이어] 행 라벨을 Model.Id → 신규 LayerName(Text 우선, 없으면 Id)으로. 운영 서식의 Id 는
대부분 Label12 꼴 자동 이름이라 291개짜리 목록이 아무것도 알려주지 않았다. Id 는 외부 참조
키이므로(DataTableField 대상명, EventHandlerMappingTag 의 ShtCod_컨트롤명 규약) 버리지 않고
툴팁 + 검색 대상으로 유지한다. 앞에 타입 아이콘(Accent), 뒤 숨김/잠금을 MiniToggle(eye/eye-off,
lock/unlock)로 — 사각 체크박스로는 상태가 안 읽혔다.
검색칩 신설(LayerFilter, 이름·Id 부분일치). 컨트롤 최대 1,067개 서식에서 스크롤만으로는 대상을
찾을 수 없다. 칩 안 TextBox 에 MinHeight=0 필수 — 전역 TextBox MinHeight 30 이 32px 칩에서
글자를 자른다.
[인스펙터·여러 값] IsMixed 가 설정만 되고 화면 어디에도 바인딩돼 있지 않았다. 다중선택에서 값이
갈리면 편집기가 빈 칸이 되는데 그 빈 칸은 "값 없음"과 구별되지 않는다. 무심코 입력하면 서로
달랐던 값이 전부 한 값으로 덮인다. 편집기 위에 비파괴 오버레이("여러 값")를 얹고, 포커스가
들어오면 감춘다 — 값은 LostFocus 에만 올라가므로 타이핑 중 ValueText 는 계속 "" 이고 안내가
방금 친 글자 위에 남는다.
토글 행은 IsOn 을 bool? 로 바꿔 3상태로. false 로 내리면 "전부 꺼짐"으로 보이고, 사용자가 한 번
켰다 끄면 전부 False 가 되어 조용히 뭉개진다.
[인스펙터·접이식] 섹션 헤더를 클릭 가능한 접기/펴기로(셰브런 + 소속 행 수). 고급 속성까지 켜면
수십 행이 된다. Rows 는 평면 컬렉션을 유지하고 섹션이 자기 행을 들고 있다가 컨테이너
Visibility 만 토글한다 — 행 VM 을 재생성하지 않아 편집 중 값·포커스가 보존되고, 다중선택
재구성이 잦은 이 화면에서 트리 재구축 비용도 피한다.
[타이틀바] 로고 메뉴 루트(셰브런) 아래로 5개 메뉴를 흡수하고, 편집 아이콘 스트립(실행취소·다시
실행 | 복사·삭제·그룹·해제)과 우측 액션(테마·저장·미리보기)을 배치. 중앙 제목은 뺐다 — 문서 탭이
같은 정보를 이미 들고 있다.
[빈 상태] 문서 0개 → 회색 허공 대신 다음 행동 3개(새 서식 / DB에서 열기 / 파일 열기).
선택 0개 → 인스펙터에 "선택 없음" 한 줄만 떠 패널이 고장 난 것처럼 보이던 것을 안내로 교체.
[그 외] 서식 목록에 하단 상태 한 줄(로딩·건수·DB 미접속) — 종전에는 목록 위에 "불러오는 중"
오버레이가 덮여 목록이 가려졌고 건수는 창 하단 전역 상태줄에만 있었다. 활성 문서 탭 아이콘을
Accent 로. 플로팅 바의 ±버튼 제거([200] 동일 — Ctrl+휠이 주 조작이고 배율 버튼이 플라이아웃을
겸한다), 대신 "화면 맞춤" 추가. ZoomFit 은 문서 전체가 아니라 활성 페이지 한 장 기준이다
(7페이지 서식을 통째로 맞추면 14% 가 되어 아무것도 안 보인다). 뷰포트 크기는 캔버스 뷰가
SizeChanged 에서 VM 에 통지 — VM 이 시각 트리를 몰라도 되게 한다.
열 기본 폭 232→248 / 280→300.
검증: 테스트 124/124, edit-smoke 실패 0, 왕복 1,271건 diff 0/예외 0.
**종이 렌더 P062 픽셀 대조 차이 0**(4,802바이트 전부 일치).
실 UI 확인: P062 레이어 행·인스펙터 접기, P093 291컨트롤에서 레이어 검색("알러지" → 10건),
116개 선택 시 X/Y/너비/높이 "여러 값" 표시.
남은 배치: E(선택 오버레이) · F(캔버스 크롬 — 종이에 닿는 유일한 배치) · G(토스트·ThemedDialog).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
281 lines
16 KiB
XML
281 lines
16 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"
|
|
mc:Ignorable="d"
|
|
d:DataContext="{d:DesignInstance ins:InspectorViewModel}">
|
|
|
|
<UserControl.Resources>
|
|
<ctl:IconNameToVisualConverter x:Key="IconName"/>
|
|
|
|
<!-- 행 VM 타입별 암시적 템플릿 -->
|
|
<Style x:Key="RowLabel" TargetType="TextBlock">
|
|
<Setter Property="Width" Value="86"/>
|
|
<Setter Property="VerticalAlignment" Value="Center"/>
|
|
<Setter Property="Foreground" Value="{DynamicResource B.Muted}"/>
|
|
<Setter Property="FontSize" Value="12"/>
|
|
<Setter Property="TextTrimming" Value="CharacterEllipsis"/>
|
|
</Style>
|
|
<Style x:Key="RowEditor" TargetType="Control">
|
|
<Setter Property="FontSize" Value="12"/>
|
|
</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,10,0,3" Padding="0,3">
|
|
<Button.Template>
|
|
<ControlTemplate TargetType="Button">
|
|
<Border x:Name="bd" Background="Transparent" CornerRadius="5" Padding="3,4">
|
|
<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 DockPanel.Dock="Right" Text="{Binding CountText}" FontSize="11"
|
|
Foreground="{DynamicResource B.Muted}" VerticalAlignment="Center" Margin="6,0,2,0"/>
|
|
<TextBlock Text="{Binding Label}" FontWeight="Bold" FontSize="12"
|
|
Foreground="{DynamicResource B.Muted}" VerticalAlignment="Center"
|
|
TextTrimming="CharacterEllipsis"/>
|
|
</DockPanel>
|
|
</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:TextRowViewModel}">
|
|
<DockPanel Margin="0,2">
|
|
<TextBlock Text="{Binding Label}" Style="{StaticResource RowLabel}" ToolTip="{Binding Label}"/>
|
|
<Grid>
|
|
<TextBox Text="{Binding ValueText, UpdateSourceTrigger=LostFocus}" FontSize="12"/>
|
|
<TextBlock Style="{StaticResource MixedHintOverlay}"/>
|
|
</Grid>
|
|
</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 Text="{Binding ValueText, UpdateSourceTrigger=LostFocus}" FontSize="12"
|
|
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>
|
|
<TextBox Text="{Binding ValueText, UpdateSourceTrigger=LostFocus}" FontSize="12"/>
|
|
<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). 꺼짐으로 보이면 안 된다 -->
|
|
<CheckBox IsChecked="{Binding IsOn}" IsThreeState="{Binding IsMixed, Mode=OneWay}"
|
|
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 ItemsSource="{Binding Choices}" SelectedItem="{Binding ValueText}" FontSize="12"/>
|
|
<TextBlock Style="{StaticResource MixedHintOverlay}" Margin="9,0,0,0"/>
|
|
</Grid>
|
|
</DockPanel>
|
|
</DataTemplate>
|
|
|
|
<DataTemplate DataType="{x:Type ins:TagPickerRowViewModel}">
|
|
<DockPanel Margin="0,2">
|
|
<TextBlock Text="{Binding Label}" Style="{StaticResource RowLabel}"/>
|
|
<Button DockPanel.Dock="Right" Content="…" Width="24" Margin="4,0,0,0"
|
|
Command="{Binding BrowseCommand}" ToolTip="목록에서 선택"/>
|
|
<Grid>
|
|
<TextBox Text="{Binding ValueText, UpdateSourceTrigger=LostFocus}" FontSize="12"
|
|
ToolTip="직접 입력 또는 … 버튼으로 선택"/>
|
|
<TextBlock Style="{StaticResource MixedHintOverlay}"/>
|
|
</Grid>
|
|
</DockPanel>
|
|
</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 Content="{Binding ButtonText}" Command="{Binding ToggleCommand}"
|
|
Margin="0,6,0,2" Padding="6,3" HorizontalAlignment="Stretch"
|
|
Background="{DynamicResource B.Chip}" BorderBrush="{DynamicResource B.Line2}" FontSize="11" Foreground="{DynamicResource B.Muted}"/>
|
|
</DataTemplate>
|
|
|
|
<DataTemplate DataType="{x:Type ins:AddPropertyRowViewModel}">
|
|
<DockPanel Margin="0,6,0,2">
|
|
<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>
|
|
</DataTemplate>
|
|
|
|
<DataTemplate DataType="{x:Type ins:QueryRowViewModel}">
|
|
<DockPanel Margin="0,2">
|
|
<TextBlock Text="{Binding Label}" Style="{StaticResource RowLabel}"/>
|
|
<Button DockPanel.Dock="Right" Content="편집…" Padding="6,1" 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" Content="…" Padding="8,1" Margin="4,0,0,0"
|
|
Command="{Binding BrowseCommand}"
|
|
ToolTip="마스크 편집기 — 자주 쓰는 마스크 목록과 미리보기"/>
|
|
<Grid>
|
|
<TextBox Text="{Binding ValueText, UpdateSourceTrigger=LostFocus}" FontSize="12"
|
|
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 DataType="{x:Type ins:ColorRowViewModel}">
|
|
<DockPanel Margin="0,2">
|
|
<TextBlock Text="{Binding Label}" Style="{StaticResource RowLabel}"/>
|
|
<!-- 스와치 = 피커 버튼(클릭 → 색상 선택 대화상자), 직접 입력도 병행 -->
|
|
<Button DockPanel.Dock="Right" Width="26" Height="22" Margin="4,0,0,0" Padding="0"
|
|
Command="{Binding PickCommand}" Cursor="Hand"
|
|
ToolTip="클릭하여 색상 선택">
|
|
<Button.Template>
|
|
<ControlTemplate TargetType="Button">
|
|
<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>
|
|
</ControlTemplate.Triggers>
|
|
</ControlTemplate>
|
|
</Button.Template>
|
|
</Button>
|
|
<Grid>
|
|
<TextBox Text="{Binding ValueText, UpdateSourceTrigger=LostFocus}" FontSize="12"
|
|
ToolTip="R, G, B 또는 색 이름 (예: 224, 224, 224 / White) — 오른쪽 스와치 클릭 시 피커"/>
|
|
<TextBlock Style="{StaticResource MixedHintOverlay}"/>
|
|
</Grid>
|
|
</DockPanel>
|
|
</DataTemplate>
|
|
</UserControl.Resources>
|
|
|
|
<Grid>
|
|
<DockPanel>
|
|
<TextBlock DockPanel.Dock="Top" Text="{Binding Summary}" FontWeight="Bold"
|
|
Margin="10,10,10,2" Foreground="{DynamicResource B.Muted}" TextTrimming="CharacterEllipsis"/>
|
|
<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>
|
|
|
|
<!-- 빈 상태 — 선택이 없으면 "선택 없음" 한 줄만 떠 있어 패널이 고장난 것처럼 보였다 -->
|
|
<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>
|