피그마식 속성 조작 — Enter 확정·화살표 증감·정렬 격자
사용자 지적: "피그마같은 도구처럼 보기 편하게, 특히 속성 사용하기 불편해". 불편의 핵심은 밀도가 아니라 **조작**이었다. 모든 칸이 UpdateSourceTrigger=LostFocus 하나라 값을 고치고 나서 **다른 곳을 눌러야** 반영됐다 — 캔버스를 보며 숫자를 맞춰 가는 작업이 성립하지 않는다. [Enter 확정] 즉시 커밋하고 결과를 전체 선택한다(확정 신호이자 곧바로 덮어 칠 수 있는 상태). 여러 줄 입력은 Enter 가 줄바꿈이어야 하므로 AcceptsReturn 으로 코드에서 가른다 — 스타일 예외로 빼면 새 여러 줄 칸이 생길 때마다 잊는다. [Esc 되돌리기] **편집 전 값을 따로 보관하지 않는다.** LostFocus 트리거라 소스는 아직 손대지 않은 상태이고 소스가 곧 백업이다 — UpdateTarget() 으로 화면만 다시 읽는다. (초안은 포커스 시점 문자열을 기억했는데, 그러면 칸에 포커스를 둔 채 캔버스에서 컨트롤을 끌었을 때 Esc 가 낡은 값을 되살리고 LostFocus 가 그걸 커밋해 **컨트롤이 원위치로 돌아간다**. 검증에서 잡았다.) [↑/↓ 증감] 1씩, Shift 10씩. 증분은 칸마다 지정한다 — **Ctrl 미세조정은 기본 끔**이다. X/Y/너비/높이는 읽기·쓰기 양쪽에서 반올림하므로 0.1 을 더해도 모델은 그대로인데 문자열만 바뀌어, 화면은 그대로면서 Undo 스냅샷과 '수정됨' 표시만 남는다. 소수를 실제로 받는 글꼴 크기에서만 켠다. 그리고 **글꼴 크기는 NumberRow 가 아니라 TextRow 였다** — 정작 가장 미세조정하고 싶은 칸이 스핀 대상에서 빠져 있었다. NumberRow 로 승격했다. [Undo 폭주 차단] 커밋 1회 = 문서 전체 딥클론 1회다. ↑ 를 스무 번 누르면 스냅샷 스무 개가 쌓여 용량(100)을 갉아먹고, 저장 지점이 밀려나면 영구 '수정됨'이 된다. 행에 CoalesceUndo 플래그를 두고 경계·크기 행만 기존 SnapshotForNudge(400ms 코얼레스)로 보낸다. 값이 아니라 **커밋 방식만** 바꾸므로 Initialize/Commit 규약은 그대로다. [정렬 격자] 콤보에 "MiddleCenter" 같은 레거시 원문 열거값이 그대로 떴다. 무슨 뜻인지 알려면 아홉 개를 다 열어 봐야 하고 고르고 나서도 글자로만 확인된다. 3열 격자로 바꿔 9값은 3×3 방향 화살표, 3값은 1×3 정렬 아이콘으로 접힌다(같은 템플릿이 값 개수에 따라 알아서). **ToggleButton 이 아니라 Button 을 쓴다** — 토글이면 켜진 칸을 다시 눌러 해제되고, 그때 값이 빈 문자열이 되어 Set 이 속성을 **삭제**한다. ControlRegistry 주석이 경고하는 '선택만으로 속성이 사라지는' 사고(운영 249건)와 같은 계열이다. [바꾸지 않은 것] UpdateSourceTrigger 는 그대로 둔다. PropertyChanged 로 바꾸면 한 글자마다 딥클론이 쌓이고, 이름 행은 글자마다 RenameControl 이 돌며, 경계 행은 커밋 결과가 정규화 값으로 되쓰여 캐럿이 튄다. 피그마식 사용감은 트리거가 아니라 명시적 확정 제스처로 얻는다. [다음 후보 — 이번엔 넣지 않았다] 라벨을 끌어서 값을 바꾸는 스크럽은 UndoService 에 BeginBatch/EndBatch 를 넣어야 안전하다(지금 구조면 드래그 프레임마다 스냅샷 1개). 캡처 해제 처리도 이 저장소에 사고 선례가 있어 별도 작업으로 남긴다. 검증: 테스트 124/124, edit-smoke 실패 0, 왕복 1,271건 diff 0/예외 0, 종이 렌더 P062 픽셀 차이 0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
394807f8df
commit
e18ad87100
@@ -5,6 +5,7 @@
|
||||
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}">
|
||||
|
||||
@@ -196,7 +197,7 @@
|
||||
<DockPanel Margin="0,2">
|
||||
<TextBlock Text="{Binding Label}" Style="{StaticResource RowLabel}" ToolTip="{Binding Label}"/>
|
||||
<Grid>
|
||||
<TextBox Style="{StaticResource RowInput}" Text="{Binding ValueText, UpdateSourceTrigger=LostFocus}"/>
|
||||
<TextBox Style="{StaticResource RowInput}" bh:InspectorFieldBehavior.CommitOnEnter="True" Text="{Binding ValueText, UpdateSourceTrigger=LostFocus}"/>
|
||||
<TextBlock Style="{StaticResource MixedHintOverlay}"/>
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
@@ -213,20 +214,76 @@
|
||||
<DockPanel Grid.Column="0">
|
||||
<TextBlock Text="{Binding LeftLabel}" Style="{StaticResource PairLabel}"/>
|
||||
<Grid DataContext="{Binding Left}">
|
||||
<TextBox Style="{StaticResource RowInput}" Text="{Binding ValueText, UpdateSourceTrigger=LostFocus}"/>
|
||||
<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}" Text="{Binding ValueText, UpdateSourceTrigger=LostFocus}"/>
|
||||
<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}" HorizontalAlignment="Left">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate><UniformGrid Columns="3"/></ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Button Width="30" 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">
|
||||
@@ -253,7 +310,7 @@
|
||||
<TextBlock Text="{Binding Label}" Style="{StaticResource RowLabel}" VerticalAlignment="Top"
|
||||
Margin="0,4,0,0" ToolTip="{Binding Label}"/>
|
||||
<Grid>
|
||||
<TextBox Style="{StaticResource RowInput}" Text="{Binding ValueText, UpdateSourceTrigger=LostFocus}"
|
||||
<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"/>
|
||||
@@ -265,7 +322,11 @@
|
||||
<DockPanel Margin="0,2">
|
||||
<TextBlock Text="{Binding Label}" Style="{StaticResource RowLabel}" ToolTip="{Binding Label}"/>
|
||||
<Grid>
|
||||
<TextBox Style="{StaticResource RowInput}" Text="{Binding ValueText, UpdateSourceTrigger=LostFocus}"/>
|
||||
<!-- 글꼴 크기처럼 소수를 실제로 받는 칸만 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>
|
||||
@@ -299,7 +360,7 @@
|
||||
Command="{Binding BrowseCommand}" ToolTip="목록에서 선택"
|
||||
Content="{Binding Source=list, Converter={StaticResource IconName}, ConverterParameter=13}"/>
|
||||
<Grid>
|
||||
<TextBox Style="{StaticResource RowInput}" Text="{Binding ValueText, UpdateSourceTrigger=LostFocus}"
|
||||
<TextBox Style="{StaticResource RowInput}" bh:InspectorFieldBehavior.CommitOnEnter="True" Text="{Binding ValueText, UpdateSourceTrigger=LostFocus}"
|
||||
ToolTip="직접 입력하거나 오른쪽 버튼으로 목록에서 선택"/>
|
||||
<TextBlock Style="{StaticResource MixedHintOverlay}"/>
|
||||
</Grid>
|
||||
@@ -350,7 +411,7 @@
|
||||
ToolTip="마스크 편집기 — 자주 쓰는 마스크 목록과 미리보기"
|
||||
Content="{Binding Source=list, Converter={StaticResource IconName}, ConverterParameter=13}"/>
|
||||
<Grid>
|
||||
<TextBox Style="{StaticResource RowInput}" Text="{Binding ValueText, UpdateSourceTrigger=LostFocus}"
|
||||
<TextBox Style="{StaticResource RowInput}" bh:InspectorFieldBehavior.CommitOnEnter="True" Text="{Binding ValueText, UpdateSourceTrigger=LostFocus}"
|
||||
FontFamily="Consolas, D2Coding, 굴림체"
|
||||
ToolTip="0=필수 숫자, 9=선택 숫자, #=숫자·부호 — 오른쪽 버튼에서 목록·미리보기"/>
|
||||
<TextBlock Style="{StaticResource MixedHintOverlay}"/>
|
||||
@@ -383,7 +444,7 @@
|
||||
</Button.Template>
|
||||
</Button>
|
||||
<Grid>
|
||||
<TextBox Style="{StaticResource RowInput}" Text="{Binding ValueText, UpdateSourceTrigger=LostFocus}"
|
||||
<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>
|
||||
|
||||
Reference in New Issue
Block a user