레이어 패널에 [200]SheetMe 그룹 기능 이식 — 폴더 행·계층·타입 필터·다중선택
[200] 의 그룹도 컨테이너가 아니라 GroupId 태그 + 이름 사전이었다. SheetMe 가 이미 같은 구조(ControlElement.GroupId, JsonIgnore)를 갖고 있어 저장 포맷은 그대로 두고 표시·조작 계층만 얹는다 — 왕복 XML 에는 아무 흔적도 남지 않는다. 레이어 목록을 평탄화 결과(LayerRows)로 바꿨다. 그룹 폴더 행과 컨트롤 행이 한 시퀀스로 섞이고, 계층은 들여쓰기 여백으로만 표현한다(TreeView 를 쓰면 가상화와 기존 행 템플릿을 다 버려야 한다 — 인스펙터 섹션이 쓰는 방식과 같게 맞췄다). 새로 되는 것 - 그룹 폴더 행: 접기/펼치기, 멤버 수, 이름 인라인 편집(더블클릭), 그룹 전체 눈·자물쇠 - 그룹 이름 — FormDocument.GroupNames(편집 전용). 비우면 기본 그룹 으로 복귀 - 컨테이너(패널/그룹박스) 자식이 레이어에 보인다. 지금까지는 아예 접근할 수 없었다 - 목록 다중선택(Shift/Ctrl) — 캔버스 선택과 양방향 동기 - 타입 필터: 그 페이지에 실제로 있는 타입만 후보로 띄운다 - 검색 0건·빈 페이지 안내 문구 함께 고친 결함 - 붙여넣기가 원본 그룹에 끼어들었다(Clone 이 GroupId 를 그대로 복사) → 복제본끼리 새 그룹 - 그룹 해제가 선택된 멤버만 풀어 1개짜리 반쪽 그룹이 남았다 → 그룹 전 멤버를 푼다 - 멤버가 1개 이하로 줄어든 그룹이 계속 남았다 → CleanupGroups 자가치유 - 셰브론이 늘 눌린 것처럼 보였다(IsChecked 채움) → MiniChevron 스타일 분리 --edit-smoke 에 레이어 검사 19건 추가(그룹 위생·평탄화·필터·XML 무영향). 회귀: 테스트 124/124, edit-smoke 실패 0, db-smoke 1,271건 diff 0/예외 0, 종이 렌더 P062 바이트 동일. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
d09bd90c8e
commit
a30421f9b5
@@ -39,19 +39,105 @@ public partial class MainView : Window
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 레이어 더블클릭 → 캔버스를 그 컨트롤로 이동.
|
||||
/// 레이어 더블클릭 → 컨트롤은 캔버스 이동, 그룹 폴더는 이름 편집 시작.
|
||||
/// 컨트롤이 수백 개인 서식에서는 목록에서 이름을 찾아도 종이 어디에 있는지 알 수 없었다.
|
||||
/// (ListBox 더블클릭은 InputBinding 을 지원하지 않아 코드비하인드로 위임한다 — 서식 목록과 같은 이유)
|
||||
/// </summary>
|
||||
private void OnLayerDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
|
||||
{
|
||||
if (DataContext is MainViewModel { CurrentDesigner: { } designer }
|
||||
&& LayerListBox.SelectedItem is ViewModels.Controls.ControlViewModel target)
|
||||
if (DataContext is not MainViewModel { CurrentDesigner: { } designer })
|
||||
{
|
||||
designer.RevealControl(target);
|
||||
return;
|
||||
}
|
||||
switch (LayerListBox.SelectedItem)
|
||||
{
|
||||
case ViewModels.Controls.ControlViewModel target:
|
||||
designer.RevealControl(target);
|
||||
break;
|
||||
case LayerGroupViewModel group:
|
||||
group.IsEditing = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 그룹 이름 편집 상자가 <b>보이게 된 순간</b> 포커스+전체선택 — 더블클릭 직후 바로 덮어쓸 수 있게.
|
||||
/// Loaded 로는 안 된다: 행이 만들어질 때 상자는 아직 접혀 있고, 나중에 펼쳐질 때 Loaded 는 다시 오지 않는다.
|
||||
/// 표시가 실제로 반영된 뒤에 포커스를 줘야 하므로 한 틱 미룬다.
|
||||
/// </summary>
|
||||
private void OnGroupNameBoxVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (sender is not System.Windows.Controls.TextBox box || !box.IsVisible)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
box.Focus();
|
||||
System.Windows.Input.Keyboard.Focus(box);
|
||||
box.SelectAll();
|
||||
}), System.Windows.Threading.DispatcherPriority.Input);
|
||||
}
|
||||
|
||||
/// <summary>Enter=확정 / Esc=취소 — 둘 다 편집 상태를 끈다</summary>
|
||||
private void OnGroupNameKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
|
||||
{
|
||||
if (sender is not System.Windows.Controls.TextBox box)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (e.Key == System.Windows.Input.Key.Enter)
|
||||
{
|
||||
box.GetBindingExpression(System.Windows.Controls.TextBox.TextProperty)?.UpdateSource();
|
||||
EndGroupNameEdit(box);
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (e.Key == System.Windows.Input.Key.Escape)
|
||||
{
|
||||
box.GetBindingExpression(System.Windows.Controls.TextBox.TextProperty)?.UpdateTarget();
|
||||
EndGroupNameEdit(box);
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>포커스가 빠지면 확정(LostFocus 바인딩이 이미 값을 밀어 넣는다)</summary>
|
||||
private void OnGroupNameLostFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is System.Windows.Controls.TextBox box)
|
||||
{
|
||||
EndGroupNameEdit(box);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EndGroupNameEdit(System.Windows.Controls.TextBox box)
|
||||
{
|
||||
if (box.DataContext is LayerGroupViewModel group)
|
||||
{
|
||||
group.IsEditing = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>레이어 타입 필터 — 활성 페이지에 실제로 있는 타입만 후보로 띄운다</summary>
|
||||
private void OnLayerFilterClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (DataContext is not MainViewModel { CurrentDesigner: { } designer })
|
||||
{
|
||||
return;
|
||||
}
|
||||
var dialog = new LayerTypeFilterDialog(designer.LayerTypeCandidates(), designer.LayerTypeFilter)
|
||||
{
|
||||
Owner = this,
|
||||
};
|
||||
if (dialog.ShowDialog() == true)
|
||||
{
|
||||
designer.SetLayerTypeFilter(dialog.SelectedTypes);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>타입 필터 해제</summary>
|
||||
private void OnLayerFilterClearClick(object sender, RoutedEventArgs e)
|
||||
=> (DataContext as MainViewModel)?.CurrentDesigner?.SetLayerTypeFilter(Array.Empty<string>());
|
||||
|
||||
private void OnThemeToggleClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ThemeManager.Toggle();
|
||||
|
||||
Reference in New Issue
Block a user