레이어 패널에 [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
@@ -0,0 +1,36 @@
|
||||
<Window x:Class="SheetMe.Designer.Views.LayerTypeFilterDialog"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:ctl="clr-namespace:SheetMe.Designer.Controls"
|
||||
Title="타입으로 거르기" Width="380" Height="520"
|
||||
WindowStartupLocation="CenterOwner" ShowInTaskbar="False"
|
||||
Style="{StaticResource ThemedWindow}">
|
||||
<Window.Resources>
|
||||
<ctl:IconNameToVisualConverter x:Key="IconName"/>
|
||||
<ctl:TypeToIconConverter x:Key="TypeToIcon"/>
|
||||
</Window.Resources>
|
||||
<DockPanel Margin="12">
|
||||
<TextBlock DockPanel.Dock="Top" Foreground="{DynamicResource B.Muted}" TextWrapping="Wrap" Margin="0,0,0,8"
|
||||
Text="이 페이지에 실제로 있는 타입만 보여 줍니다. 아무것도 고르지 않으면 전체가 표시됩니다."/>
|
||||
|
||||
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,10,0,0">
|
||||
<Button Content="전체 해제" Padding="12,4" Click="OnClearAll"/>
|
||||
<Button Content="확인" Padding="20,4" Margin="8,0,0,0" Click="OnConfirm" IsDefault="True"/>
|
||||
<Button Content="취소" Padding="20,4" Margin="8,0,0,0" IsCancel="True"/>
|
||||
</StackPanel>
|
||||
|
||||
<ListBox x:Name="TypeList" SelectionMode="Multiple">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<DockPanel Margin="0,2">
|
||||
<TextBlock DockPanel.Dock="Right" Text="{Binding CountText}" FontSize="11"
|
||||
VerticalAlignment="Center" Foreground="{DynamicResource B.Muted}"/>
|
||||
<ContentControl DockPanel.Dock="Left" Margin="2,0,8,0" VerticalAlignment="Center" IsTabStop="False"
|
||||
Content="{Binding Type, Converter={StaticResource TypeToIcon}, ConverterParameter=15|B.Accent}"/>
|
||||
<TextBlock Text="{Binding Type}" FontSize="12.5" VerticalAlignment="Center"/>
|
||||
</DockPanel>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</DockPanel>
|
||||
</Window>
|
||||
@@ -0,0 +1,51 @@
|
||||
using System.Windows;
|
||||
|
||||
namespace SheetMe.Designer.Views;
|
||||
|
||||
/// <summary>
|
||||
/// 레이어 목록 타입 필터 — "체크박스만" 처럼 컨트롤 종류로 목록을 좁힌다.
|
||||
///
|
||||
/// 후보를 팔레트 전체가 아니라 <b>활성 페이지에 실제로 있는 타입</b>으로 한정한다:
|
||||
/// 서식 하나에 쓰이는 타입은 보통 5~8종이라 팔레트 16종을 다 늘어놓으면 없는 타입만 고르게 된다.
|
||||
/// </summary>
|
||||
public partial class LayerTypeFilterDialog : Window
|
||||
{
|
||||
#region Types
|
||||
/// <summary>후보 1행 — 타입명과 그 페이지에서의 개수</summary>
|
||||
private sealed record TypeRow(string Type, int Count)
|
||||
{
|
||||
public string CountText => $"{Count}개";
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>확정된 타입 집합 — 비어 있으면 전체 표시</summary>
|
||||
public IReadOnlyList<string> SelectedTypes { get; private set; } = Array.Empty<string>();
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
public LayerTypeFilterDialog(IEnumerable<(string Type, int Count)> candidates, IEnumerable<string> current)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
var rows = candidates.Select(c => new TypeRow(c.Type, c.Count)).ToList();
|
||||
TypeList.ItemsSource = rows;
|
||||
|
||||
var selected = new HashSet<string>(current, StringComparer.Ordinal);
|
||||
foreach (var row in rows.Where(r => selected.Contains(r.Type)))
|
||||
{
|
||||
TypeList.SelectedItems.Add(row);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
private void OnClearAll(object sender, RoutedEventArgs e) => TypeList.SelectedItems.Clear();
|
||||
|
||||
private void OnConfirm(object sender, RoutedEventArgs e)
|
||||
{
|
||||
SelectedTypes = TypeList.SelectedItems.Cast<TypeRow>().Select(r => r.Type).ToList();
|
||||
DialogResult = true;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
@@ -507,6 +507,15 @@
|
||||
<DockPanel>
|
||||
<ContentControl DockPanel.Dock="Left" Margin="9,0,7,0" VerticalAlignment="Center" IsTabStop="False"
|
||||
Content="{Binding Source=search, Converter={StaticResource IconName}, ConverterParameter=13}"/>
|
||||
<!-- 타입 필터 — 이름을 몰라도 "체크박스만" 처럼 종류로 좁힐 수 있어야 한다 -->
|
||||
<Button DockPanel.Dock="Right" Style="{StaticResource Subtle}" Width="26" Height="22" Padding="0"
|
||||
Margin="0,0,3,0" Click="OnLayerFilterClick"
|
||||
ToolTip="{Binding CurrentDesigner.LayerTypeFilterTip}"
|
||||
Content="{Binding Source=filter, Converter={StaticResource IconName}, ConverterParameter=13}"/>
|
||||
<Button DockPanel.Dock="Right" Style="{StaticResource Subtle}" Width="26" Height="22" Padding="0"
|
||||
Margin="0,0,1,0" Click="OnLayerFilterClearClick" ToolTip="타입 필터 해제"
|
||||
Visibility="{Binding CurrentDesigner.HasLayerTypeFilter, Converter={StaticResource BoolToVisibility}}"
|
||||
Content="{Binding Source=filter-x, Converter={StaticResource IconName}, ConverterParameter=13}"/>
|
||||
<Grid>
|
||||
<TextBox x:Name="LayerSearchBox" MinHeight="0" Padding="0" BorderThickness="0"
|
||||
Background="Transparent" VerticalContentAlignment="Center"
|
||||
@@ -528,54 +537,121 @@
|
||||
</DockPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 레이어 목록 (활성 페이지, 그리기 순서) -->
|
||||
<ListBox x:Name="LayerListBox" ItemsSource="{Binding CurrentDesigner.VisibleLayers}"
|
||||
SelectedItem="{Binding CurrentDesigner.SelectedLayerItem}"
|
||||
MouseDoubleClick="OnLayerDoubleClick"
|
||||
ToolTip="더블클릭하면 캔버스가 그 컨트롤로 이동합니다"
|
||||
Margin="4,0,4,6"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<DockPanel Margin="0,1">
|
||||
<!-- 눈·자물쇠는 아이콘이 상태에 따라 바뀐다 — 사각 체크로는 잠김/숨김이 안 읽힌다 -->
|
||||
<ToggleButton DockPanel.Dock="Right" Style="{StaticResource MiniToggle}"
|
||||
IsChecked="{Binding IsLockedFlag}" ToolTip="잠금"
|
||||
Content="{Binding LockIcon, Converter={StaticResource IconName}, ConverterParameter=13}"/>
|
||||
<ToggleButton DockPanel.Dock="Right" Style="{StaticResource MiniToggle}"
|
||||
IsChecked="{Binding IsHiddenFlag}" ToolTip="숨김"
|
||||
Content="{Binding HiddenIcon, Converter={StaticResource IconName}, ConverterParameter=13}"/>
|
||||
<ContentControl DockPanel.Dock="Left" Margin="2,0,6,0" VerticalAlignment="Center" IsTabStop="False"
|
||||
Content="{Binding Type, Converter={StaticResource TypeToIcon}, ConverterParameter=14|B.Accent}"/>
|
||||
<!--
|
||||
문구와 이름을 두 줄로. 컨트롤 이름(Id)은 외부 참조 키라
|
||||
(액션 대상·입력 파라미터가 이름으로 서로를 가리키고 CalcBox 수식도 이름으로 합산한다)
|
||||
배선하려면 목록에서 보여야 하는데, 248px 폭에 나란히 두면 양쪽 다 잘려
|
||||
'txtKinPhone…' 처럼 구분이 안 되는 꼬리가 남는다. 한 줄 더 쓰는 편이 낫다.
|
||||
표시 문구가 없어 이름이 곧 문구인 행에서는 둘째 줄이 뜨지 않는다.
|
||||
-->
|
||||
<StackPanel VerticalAlignment="Center">
|
||||
<StackPanel.Style>
|
||||
<Style TargetType="StackPanel">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsHiddenFlag}" Value="True">
|
||||
<Setter Property="Opacity" Value="0.55"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</StackPanel.Style>
|
||||
<TextBlock Text="{Binding LayerName}" FontSize="12"
|
||||
TextTrimming="CharacterEllipsis" ToolTip="{Binding LayerName}"/>
|
||||
<TextBlock Text="{Binding LayerId}" FontSize="10.5" Margin="0,1,0,0"
|
||||
Foreground="{DynamicResource B.Muted}"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
ToolTip="컨트롤 이름 — 다른 속성이 이 이름으로 참조합니다"
|
||||
Visibility="{Binding HasLayerId, Converter={StaticResource BoolToVisibility}}"/>
|
||||
</StackPanel>
|
||||
</DockPanel>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
<!--
|
||||
레이어 목록 (활성 페이지, 그리기 순서).
|
||||
그룹 폴더 행과 컨트롤 행이 한 시퀀스로 섞이는 평탄화 목록이라
|
||||
ItemTemplate 하나로는 안 되고(암시 DataType 매칭은 LabelViewModel 같은
|
||||
구체 형에만 걸린다) 선택기로 가른다. 계층은 LayerIndent 여백으로만 표현.
|
||||
-->
|
||||
<Grid Margin="4,0,4,6">
|
||||
<ListBox x:Name="LayerListBox" ItemsSource="{Binding CurrentDesigner.LayerRows}"
|
||||
bh:LayerListBehavior.Designer="{Binding CurrentDesigner}"
|
||||
SelectionMode="Extended"
|
||||
MouseDoubleClick="OnLayerDoubleClick"
|
||||
ToolTip="더블클릭 — 컨트롤은 캔버스 이동, 그룹은 이름 바꾸기"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
|
||||
<ListBox.ItemTemplateSelector>
|
||||
<ctl:LayerRowTemplateSelector>
|
||||
<!-- 그룹 폴더 행 — 셰브론 / 폴더 / 이름(더블클릭 편집) / (n) / 일괄 눈·자물쇠 -->
|
||||
<ctl:LayerRowTemplateSelector.GroupTemplate>
|
||||
<DataTemplate>
|
||||
<DockPanel Margin="0,1">
|
||||
<ToggleButton DockPanel.Dock="Right" Style="{StaticResource MiniToggle}"
|
||||
IsChecked="{Binding IsLockedFlag}" ToolTip="그룹 전체 잠금"
|
||||
Content="{Binding LockIcon, Converter={StaticResource IconName}, ConverterParameter=13}"/>
|
||||
<ToggleButton DockPanel.Dock="Right" Style="{StaticResource MiniToggle}"
|
||||
IsChecked="{Binding IsHiddenFlag}" ToolTip="그룹 전체 숨김"
|
||||
Content="{Binding HiddenIcon, Converter={StaticResource IconName}, ConverterParameter=13}"/>
|
||||
<ToggleButton DockPanel.Dock="Left" Style="{StaticResource MiniChevron}" Width="18"
|
||||
IsChecked="{Binding IsExpanded}" ToolTip="접기/펼치기"
|
||||
Content="{Binding ChevronIcon, Converter={StaticResource IconName}, ConverterParameter=12}"/>
|
||||
<ContentControl DockPanel.Dock="Left" Margin="0,0,6,0" VerticalAlignment="Center" IsTabStop="False"
|
||||
Content="{Binding Source=folder, Converter={StaticResource IconName}, ConverterParameter=14|B.Accent}"/>
|
||||
<TextBlock DockPanel.Dock="Right" Text="{Binding MemberCountText}" FontSize="11"
|
||||
VerticalAlignment="Center" Margin="4,0,4,0"
|
||||
Foreground="{DynamicResource B.Muted}"/>
|
||||
<Grid VerticalAlignment="Center">
|
||||
<TextBlock Text="{Binding Name}" FontSize="12" FontWeight="SemiBold"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
ToolTip="더블클릭하면 그룹 이름을 바꿉니다">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsEditing}" Value="True">
|
||||
<Setter Property="Visibility" Value="Collapsed"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
<TextBox MinHeight="0" Padding="2,0" FontSize="12"
|
||||
IsVisibleChanged="OnGroupNameBoxVisibleChanged"
|
||||
Text="{Binding Name, UpdateSourceTrigger=LostFocus}"
|
||||
KeyDown="OnGroupNameKeyDown" LostFocus="OnGroupNameLostFocus"
|
||||
Visibility="{Binding IsEditing, Converter={StaticResource BoolToVisibility}}"/>
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
</DataTemplate>
|
||||
</ctl:LayerRowTemplateSelector.GroupTemplate>
|
||||
|
||||
<ctl:LayerRowTemplateSelector.ControlTemplate>
|
||||
<DataTemplate>
|
||||
<DockPanel Margin="0,1">
|
||||
<!-- 눈·자물쇠는 아이콘이 상태에 따라 바뀐다 — 사각 체크로는 잠김/숨김이 안 읽힌다 -->
|
||||
<ToggleButton DockPanel.Dock="Right" Style="{StaticResource MiniToggle}"
|
||||
IsChecked="{Binding IsLockedFlag}" ToolTip="잠금"
|
||||
Content="{Binding LockIcon, Converter={StaticResource IconName}, ConverterParameter=13}"/>
|
||||
<ToggleButton DockPanel.Dock="Right" Style="{StaticResource MiniToggle}"
|
||||
IsChecked="{Binding IsHiddenFlag}" ToolTip="숨김"
|
||||
Content="{Binding HiddenIcon, Converter={StaticResource IconName}, ConverterParameter=13}"/>
|
||||
<!-- 계층 들여쓰기: 그룹 멤버 20, 컨테이너 자식 +16 -->
|
||||
<Border DockPanel.Dock="Left" Width="{Binding LayerIndent}"/>
|
||||
<!-- 컨테이너(패널/그룹박스)만 자식 접기 셰브론을 갖는다 -->
|
||||
<ToggleButton DockPanel.Dock="Left" Style="{StaticResource MiniChevron}" Width="16"
|
||||
IsChecked="{Binding IsLayerExpanded}" ToolTip="자식 접기/펼치기"
|
||||
Visibility="{Binding HasLayerChildren, Converter={StaticResource BoolToVisibility}}"
|
||||
Content="{Binding LayerChevronIcon, Converter={StaticResource IconName}, ConverterParameter=12}"/>
|
||||
<ContentControl DockPanel.Dock="Left" Margin="2,0,6,0" VerticalAlignment="Center" IsTabStop="False"
|
||||
Content="{Binding Type, Converter={StaticResource TypeToIcon}, ConverterParameter=14|B.Accent}"/>
|
||||
<!--
|
||||
문구와 이름을 두 줄로. 컨트롤 이름(Id)은 외부 참조 키라
|
||||
(액션 대상·입력 파라미터가 이름으로 서로를 가리키고 CalcBox 수식도 이름으로 합산한다)
|
||||
배선하려면 목록에서 보여야 하는데, 248px 폭에 나란히 두면 양쪽 다 잘려
|
||||
'txtKinPhone…' 처럼 구분이 안 되는 꼬리가 남는다. 한 줄 더 쓰는 편이 낫다.
|
||||
표시 문구가 없어 이름이 곧 문구인 행에서는 둘째 줄이 뜨지 않는다.
|
||||
-->
|
||||
<StackPanel VerticalAlignment="Center">
|
||||
<StackPanel.Style>
|
||||
<Style TargetType="StackPanel">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsHiddenFlag}" Value="True">
|
||||
<Setter Property="Opacity" Value="0.55"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</StackPanel.Style>
|
||||
<TextBlock Text="{Binding LayerName}" FontSize="12"
|
||||
TextTrimming="CharacterEllipsis" ToolTip="{Binding LayerName}"/>
|
||||
<TextBlock Text="{Binding LayerId}" FontSize="10.5" Margin="0,1,0,0"
|
||||
Foreground="{DynamicResource B.Muted}"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
ToolTip="컨트롤 이름 — 다른 속성이 이 이름으로 참조합니다"
|
||||
Visibility="{Binding HasLayerId, Converter={StaticResource BoolToVisibility}}"/>
|
||||
</StackPanel>
|
||||
</DockPanel>
|
||||
</DataTemplate>
|
||||
</ctl:LayerRowTemplateSelector.ControlTemplate>
|
||||
</ctl:LayerRowTemplateSelector>
|
||||
</ListBox.ItemTemplateSelector>
|
||||
</ListBox>
|
||||
|
||||
<!-- 빈 목록 안내 — 검색·필터가 다 걸러냈는지, 애초에 컨트롤이 없는지 구분해 준다 -->
|
||||
<TextBlock Text="{Binding CurrentDesigner.LayerEmptyText}" FontSize="12"
|
||||
Foreground="{DynamicResource B.Muted}" TextWrapping="Wrap" TextAlignment="Center"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Top" Margin="12,18,12,0"
|
||||
IsHitTestVisible="False"
|
||||
Visibility="{Binding CurrentDesigner.IsLayerListEmpty, Converter={StaticResource BoolToVisibility}}"/>
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
</TabItem>
|
||||
|
||||
|
||||
@@ -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