정렬 — 기준은 언제나 '담고 있는 것'. 그리고 속성 패널에 정렬 행
"폼 기준으로 정렬"이 없었다. 정확히는 정렬 자체가 2개 이상에서만 동작했고 기준은 늘 선택 영역이라, 컨트롤 하나를 페이지 가운데 놓을 방법이 메뉴 어디에도 없었다. 규칙을 한 문장으로 바꿨다 — 기준은 그것을 담고 있는 것이다. 선택이 2개 이상이면 선택 영역이 그 자리를 대신하고, 1개면 담고 있는 것 — 컨테이너 안이면 컨테이너, 최상위면 페이지 — 이 기준이 된다. 피그마와 같다. 컨테이너 자식을 페이지 기준으로 맞추지 않는 이유. X/Y 는 부모 상대좌표라 '페이지 왼쪽'에 맞추면 음수가 되어 부모 밖으로 나간다. 디자이너 캔버스는 클리핑을 안 해서 멀쩡히 보이지만 레거시 런타임에서는 부모가 잘라먹는다 — 화면과 인쇄가 갈라지고 눈으로는 못 잡는 종류의 버그다. 같이 고친 것들: · 페이지를 넘는 선택의 세로 정렬을 거부한다. 다른 종이의 '위 맞춤'에는 뜻이 없고, 예전에는 실제로 깨졌다 — bbox 는 첫 페이지 월드 Y 인데 offset 은 자기 페이지 OffsetY 라 vm.Y 가 큰 음수가 되어 컨트롤이 제 종이 위 허공으로 날아갔다. 가로는 모든 페이지의 X 원점이 0 이라 그대로 허용한다. · 부모와 자식을 함께 선택한 경우 자식을 대상에서 뺀다. 부모를 옮기면 자식 월드 좌표가 따라 움직이는데 자식에게도 절대좌표를 대입하면 두 번 옮겨진다(레이어 목록에서 만들 수 있는 선택이다). · 반올림을 AwayFromZero 로 고정했다. 기본 ToEven 은 322.5→322, 323.5→324 로 방향이 값에 따라 뒤집힌다 — 폭이 제각각인 라벨 다섯 개를 가운데 맞춤하면 어떤 건 322, 어떤 건 323 에 앉는다. (스냅에서 반올림을 뺀 것과 반대로 보이지만 이유가 다르다. 거긴 가이드선이 모서리에서 뜨는 게 보이기 때문이고, 정렬은 선을 긋지 않는다. 저장 포맷은 어차피 정수다.) · 변경이 실제로 생길 때만 실행취소를 찍는다. 버튼으로 노출되면 연타되는데, 무조건 찍으면 빈 되돌리기 단계가 쌓이고 '저장 안 한 변경'으로 표시된다. 산술은 Core/Layout/AlignSolver 로 뺐다. 짧지만 경계가 잔소리 같은 규칙 덩어리라(반올림 방향, 기준보다 넓은 항목, 원점이 큰 둘째 장) 디자이너 쪽에서 검사하면 케이스마다 실행취소 스냅샷을 하나씩 먹는다. 단위 테스트 10건이 그걸 공짜로 고정한다. 속성 패널 정렬 행. [200]SheetMe 의 inspector-wireframe.html 을 따랐다 — 30x26 버튼 6개, radius 6, 사이 2px, 가로 축과 세로 축 사이 8px 틈, 아이콘은 stroke 1.5. LucideIcons 의 align-obj-* 6개는 등록만 되고 아무 데서도 안 쓰이던 죽은 항목이라 참조 도형으로 갈아 끼웠다(사각형 둘 + 기준선 → 사각형 하나 + 기준선). 좌표는 24격자다 — Build 가 Canvas 를 24x24 로 하드코딩해서, 참조의 16격자 좌표를 그대로 넣으면 아이콘이 좌상단 2/3 크기로 쏠린다. 크기 18 에서 stroke 2 × 18/24 = 1.5px 로 참조와 같은 굵기가 나온다. 버튼 스타일은 새로 만들었다. SegmentToggle 은 치수가 같지만 채워진 상태 토글 룩이고, 정렬은 상태가 아니라 액션이라 눌려 있는 것처럼 보이면 안 된다 — 채움 없이 호버만 남겼다. 표시 조건에 HasSelection 을 쓰지 않았다. 그건 페이지 속성 모드에서도 true 라 정렬할 컨트롤이 없는데 바가 뜬다. 선택 0 에서는 접어야 하는데, 안 접으면 '컨트롤을 선택하면…' 안내문 뒤에 버튼 줄이 그대로 남는다(같은 Grid 에 겹쳐 그려진다). 툴팁이 지금 무엇을 기준으로 정렬하는지 말한다 — "왼쪽 맞춤 — 페이지 기준" / "— Panel1 기준" / "— 선택 영역 기준". 세로 3버튼은 페이지를 넘는 선택에서 비활성되고 그 이유를 툴팁에 적는다. 아이콘만 있는 버튼이라 AutomationProperties.Name 을 붙였다 — 이 저장소에 버튼 자동화 속성 전례가 없어 새로 여는 관례다. 메뉴는 그대로 두되 툴팁으로 전제를 적었고, '페이지 가로/세로 가운데'는 헤더에 '묶음째'를 붙였다. 선택 1개에서는 CenterH 와 결과가 같아지고, 다중에서만 갈리기 때문이다(그건 상대 배치를 보존한 채 묶음을 옮기고, CenterH 는 각자 가운데로 모은다). 분배(간격 균등) 버튼은 넣지 않았다. 참조 와이어프레임에 없고, 3개 이상에서만 살아서 실사용 대부분(1~2개 선택)에 두 칸이 죽은 채 남아 줄이 고장 난 것처럼 읽힌다. --dialog-shots 에 선택 상태별 인스펙터 3장(12c/12d/12e)을 더했다. 정렬 바가 선택 0 과 페이지 속성 모드에서 사라지는지는 수치로 못 잡는다. 산출물 51 → 57. 기존 인스펙터 5장은 정렬 행만큼 아래로 밀린다 — 정상 diff 다. 알려진 별건(이번에 안 고침): 정렬 후 Ctrl+Z 하면 선택이 풀린다. Undo 가 문서를 통째로 갈아끼우며 Selection.Clear 하기 때문이고, 정렬은 '왼쪽 → 아니네 → 가운데'로 여러 번 시도하는 조작이라 버튼으로 노출되면서 드러난다. Id 기반 선택 복원이 필요해 별도로 다룬다. 게이트: 테스트 267/267(신규 10), --edit-smoke 234건 0실패(정렬 검사 11건 신규), --query-popup 17/17, --maxrect 10/10, --snap-shots 7종 0실패, --db-smoke 3000 diff 0·예외 0, --db-render P062 md5 8d683835f5d81e7bb41c79071d6bf954 동일, --dialog-shots 57파일. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
ba838ae54a
commit
26f696ab00
@@ -98,6 +98,40 @@
|
||||
<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>
|
||||
|
||||
<!-- 세그먼트 한 칸 — 붙어 있는 아이콘 토글(굵게/기울임/밑줄/취소선) -->
|
||||
<!-- 폭 30 + 간격 2 = 4칸 128px. 라벨 92 와 합쳐 220px 라 패널 최소폭(250 → 행 가용 221)에 들어간다 -->
|
||||
<Style x:Key="SegmentToggle" TargetType="ToggleButton">
|
||||
@@ -614,6 +648,46 @@
|
||||
</UniformGrid>
|
||||
</Border>
|
||||
|
||||
<!--
|
||||
정렬 — 기준은 언제나 '담고 있는 것'이다. 선택 1개면 페이지(컨테이너 안이면 그 컨테이너),
|
||||
2개 이상이면 선택 영역. 툴팁이 지금 무엇이 기준인지 말해 준다.
|
||||
|
||||
행 VM(Rows)이 아니라 여기 두는 이유 둘: AlignRowViewModel 은 이미 '텍스트 정렬'로
|
||||
이름과 템플릿을 선점했고, 정렬은 스크롤과 무관하게 늘 보이는 편이 조작에 맞다.
|
||||
-->
|
||||
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="12,2,10,8"
|
||||
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}"/>
|
||||
|
||||
<!-- 가로 축과 세로 축을 눈으로 가르는 틈 — 참조 .align .gap{width:8px} -->
|
||||
<Border Width="8" Margin="0,0,2,0"/>
|
||||
|
||||
<Button Style="{StaticResource AlignBtn}" 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>
|
||||
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<ItemsControl ItemsSource="{Binding Rows}" Margin="10,0,10,10">
|
||||
<!-- 접힌 섹션의 행은 컨테이너만 숨긴다 — 행 VM 을 재생성하지 않아 편집 상태가 유지된다 -->
|
||||
|
||||
@@ -188,12 +188,19 @@
|
||||
<MenuItem Header="그룹(_G)" Command="{Binding CurrentDesigner.GroupCommand}" InputGestureText="Ctrl+G"/>
|
||||
<MenuItem Header="그룹 해제(_U)" Command="{Binding CurrentDesigner.UngroupCommand}" InputGestureText="Ctrl+Shift+G"/>
|
||||
<Separator/>
|
||||
<MenuItem Header="왼쪽 맞춤" Command="{Binding CurrentDesigner.AlignCommand}" CommandParameter="Left"/>
|
||||
<MenuItem Header="오른쪽 맞춤" Command="{Binding CurrentDesigner.AlignCommand}" CommandParameter="Right"/>
|
||||
<MenuItem Header="위 맞춤" Command="{Binding CurrentDesigner.AlignCommand}" CommandParameter="Top"/>
|
||||
<MenuItem Header="아래 맞춤" Command="{Binding CurrentDesigner.AlignCommand}" CommandParameter="Bottom"/>
|
||||
<MenuItem Header="가로 가운데" Command="{Binding CurrentDesigner.AlignCommand}" CommandParameter="CenterH"/>
|
||||
<MenuItem Header="세로 가운데" Command="{Binding CurrentDesigner.AlignCommand}" CommandParameter="CenterV"/>
|
||||
<!-- 기준: 선택 1개면 페이지(컨테이너 안이면 그 컨테이너), 2개 이상이면 선택 영역 -->
|
||||
<MenuItem Header="왼쪽 맞춤" Command="{Binding CurrentDesigner.AlignCommand}" CommandParameter="Left"
|
||||
ToolTip="선택 1개면 페이지(또는 부모 컨테이너) 기준, 2개 이상이면 선택 영역 기준"/>
|
||||
<MenuItem Header="오른쪽 맞춤" Command="{Binding CurrentDesigner.AlignCommand}" CommandParameter="Right"
|
||||
ToolTip="선택 1개면 페이지(또는 부모 컨테이너) 기준, 2개 이상이면 선택 영역 기준"/>
|
||||
<MenuItem Header="위 맞춤" Command="{Binding CurrentDesigner.AlignCommand}" CommandParameter="Top"
|
||||
ToolTip="여러 페이지에 걸친 선택에서는 동작하지 않습니다"/>
|
||||
<MenuItem Header="아래 맞춤" Command="{Binding CurrentDesigner.AlignCommand}" CommandParameter="Bottom"
|
||||
ToolTip="여러 페이지에 걸친 선택에서는 동작하지 않습니다"/>
|
||||
<MenuItem Header="가로 가운데" Command="{Binding CurrentDesigner.AlignCommand}" CommandParameter="CenterH"
|
||||
ToolTip="선택 1개면 페이지(또는 부모 컨테이너) 기준, 2개 이상이면 선택 영역 기준"/>
|
||||
<MenuItem Header="세로 가운데" Command="{Binding CurrentDesigner.AlignCommand}" CommandParameter="CenterV"
|
||||
ToolTip="여러 페이지에 걸친 선택에서는 동작하지 않습니다"/>
|
||||
<Separator/>
|
||||
<MenuItem Header="같은 크기로">
|
||||
<MenuItem Header="너비/높이 모두(_B)" Command="{Binding CurrentDesigner.SizeToControlCommand}" CommandParameter="Both"/>
|
||||
@@ -213,8 +220,10 @@
|
||||
<MenuItem Header="붙이기(_C)" Command="{Binding CurrentDesigner.SpacingCommand}" CommandParameter="VertConcat"/>
|
||||
</MenuItem>
|
||||
<Separator/>
|
||||
<MenuItem Header="페이지 가로 가운데(_H)" Command="{Binding CurrentDesigner.CenterInPageCommand}" CommandParameter="H"/>
|
||||
<MenuItem Header="페이지 세로 가운데(_V)" Command="{Binding CurrentDesigner.CenterInPageCommand}" CommandParameter="V"/>
|
||||
<MenuItem Header="페이지 가로 가운데 — 묶음째(_H)"
|
||||
ToolTip="상대 배치를 유지한 채 선택 전체를 페이지 가운데로 옮깁니다" Command="{Binding CurrentDesigner.CenterInPageCommand}" CommandParameter="H"/>
|
||||
<MenuItem Header="페이지 세로 가운데 — 묶음째(_V)"
|
||||
ToolTip="상대 배치를 유지한 채 선택 전체를 페이지 가운데로 옮깁니다" Command="{Binding CurrentDesigner.CenterInPageCommand}" CommandParameter="V"/>
|
||||
<Separator/>
|
||||
<MenuItem Header="{Binding CurrentDesigner.TabOrderMenuHeader, FallbackValue=탭순서 편집 시작}"
|
||||
Command="{Binding CurrentDesigner.TabOrderCommand}"
|
||||
|
||||
Reference in New Issue
Block a user