[200] 디자인 동일화 2/2 — 좌측 패널·타이틀바·인스펙터 정보 밀도
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>
This commit is contained in:
co-authored by
Claude Opus 5
parent
7591fbed74
commit
9d20cc2971
@@ -4,60 +4,141 @@
|
||||
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}">
|
||||
<TextBlock Text="{Binding Label}" FontWeight="Bold" Foreground="{DynamicResource B.Muted}"
|
||||
Margin="0,10,0,4" FontSize="12"/>
|
||||
<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}"/>
|
||||
<TextBox Text="{Binding ValueText, UpdateSourceTrigger=LostFocus}" FontSize="12"/>
|
||||
<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"/>
|
||||
<TextBox Text="{Binding ValueText, UpdateSourceTrigger=LostFocus}" FontSize="12"
|
||||
AcceptsReturn="True" TextWrapping="Wrap" MinHeight="48" MaxHeight="120"
|
||||
VerticalScrollBarVisibility="Auto"/>
|
||||
<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}"/>
|
||||
<TextBox Text="{Binding ValueText, UpdateSourceTrigger=LostFocus}" FontSize="12"/>
|
||||
<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}"/>
|
||||
<CheckBox IsChecked="{Binding IsOn}" VerticalAlignment="Center"/>
|
||||
<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}"/>
|
||||
<ComboBox ItemsSource="{Binding Choices}" SelectedItem="{Binding ValueText}" FontSize="12"/>
|
||||
<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>
|
||||
|
||||
@@ -66,8 +147,11 @@
|
||||
<TextBlock Text="{Binding Label}" Style="{StaticResource RowLabel}"/>
|
||||
<Button DockPanel.Dock="Right" Content="…" Width="24" Margin="4,0,0,0"
|
||||
Command="{Binding BrowseCommand}" ToolTip="목록에서 선택"/>
|
||||
<TextBox Text="{Binding ValueText, UpdateSourceTrigger=LostFocus}" FontSize="12"
|
||||
ToolTip="직접 입력 또는 … 버튼으로 선택"/>
|
||||
<Grid>
|
||||
<TextBox Text="{Binding ValueText, UpdateSourceTrigger=LostFocus}" FontSize="12"
|
||||
ToolTip="직접 입력 또는 … 버튼으로 선택"/>
|
||||
<TextBlock Style="{StaticResource MixedHintOverlay}"/>
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
</DataTemplate>
|
||||
|
||||
@@ -113,9 +197,12 @@
|
||||
<Button DockPanel.Dock="Right" Content="…" Padding="8,1" Margin="4,0,0,0"
|
||||
Command="{Binding BrowseCommand}"
|
||||
ToolTip="마스크 편집기 — 자주 쓰는 마스크 목록과 미리보기"/>
|
||||
<TextBox Text="{Binding ValueText, UpdateSourceTrigger=LostFocus}" FontSize="12"
|
||||
FontFamily="Consolas, D2Coding, 굴림체"
|
||||
ToolTip="0=필수 숫자, 9=선택 숫자, #=숫자·부호 — 오른쪽 … 버튼에서 목록·미리보기"/>
|
||||
<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"
|
||||
@@ -143,17 +230,51 @@
|
||||
</ControlTemplate>
|
||||
</Button.Template>
|
||||
</Button>
|
||||
<TextBox Text="{Binding ValueText, UpdateSourceTrigger=LostFocus}" FontSize="12"
|
||||
ToolTip="R, G, B 또는 색 이름 (예: 224, 224, 224 / White) — 오른쪽 스와치 클릭 시 피커"/>
|
||||
<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>
|
||||
|
||||
<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"/>
|
||||
</ScrollViewer>
|
||||
</DockPanel>
|
||||
<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>
|
||||
|
||||
Reference in New Issue
Block a user