태그 선택 — 목업 구조로. 다만 없는 정보는 만들지 않는다
1080x700 3단(분류 / 결과 / 태그 정보), 큰 제목 + 부제, 검색칩, 바닥에 선택 칩과 버튼. 목업의 뼈대를 그대로 가져왔다. 가져오지 않은 것과 그 이유: · 설명 한 줄 — 384건 전부 없다. 원천(bzDataInterface)에도 주석이 0줄이라 사람이 써야 한다. LLM 으로 지어내면 안 된다: 태그가 실제로 무엇을 꺼내는지는 VB 함수 본문의 SQL 을 읽어야 알고, 틀린 설명은 없는 설명보다 나쁘다. · 예시 값 — 값은 런타임에 HIS DB 에서 환자 문맥을 받아 계산된다. '강남성심병원' 같은 더미를 넣으면 사용자가 그것을 실제 반환값으로 믿는다. 실제 조회 가능 여부는 별도 조사 중이다. · 최근 사용 일시 — 저장소가 없다. 만들어도 첫 실행에 384칸이 전부 빈다. · 태그 키 절 — 태그는 이름이 곧 키다. 같은 문자열을 두 번 그리는 대신 우측 큰 이름을 고정폭으로 두고 복사 버튼을 붙였다. · 2단 소분류(기관·문서 > 기본 정보) — 데이터는 1단이다. 억지로 가르면 1개짜리 버킷 25개가 나오고, 손으로 배당하면 새 태그가 자동 분류되지 않는 부채가 남는다. · 상단 가로 칩 필터 줄 — 좌측 분류 트리와 같은 필터를 두 번 그린다. 두 곳이 갈라지면 어느 쪽이 진실인지 알 수 없다. 필터의 단일 출처는 좌측 열이다. · ⌘K 칩 — Windows 앱이고, 창이 열리면 생성자가 이미 검색칸에 포커스를 준다. 바꿔 넣은 것: · '관련 필드' → '변형'. 없는 개념을 흉내내는 대신 지금 데이터로 계산되는 진짜 관계를 쓴다 — 마지막 밑줄 앞이 같은 태그들(PAT_주민번호 → _Dash · _Blind). TagSearch.VariantsOf 신설. 밑줄이 하나뿐이면 그건 접두어 경계라 제외한다(안 그러면 PAT_ 전체가 변형으로 딸려 온다). · '자주 사용' 배지 → '추천'. 그런 임계값은 코드에 없다. 있는 것은 컨트롤 타입별 큐레이션 (LegacyTagUsageCatalog.SuggestFor)뿐이라 '자주 사용'이라 부르면 근거를 왜곡한다. · 사용 횟수에 스냅샷 표기를 붙였다. 숫자만 크게 띄우면 실시간 집계로 읽힌다. · 상단 칩 줄 자리에는 좌측과 겹치지 않는 것 하나만 — '미사용 숨기기'. 384종 중 227종이 미사용이라 값이 크다. 분류와 겹쳐서 걸린다. 창 껍데기는 OS 크롬을 유지했다. 목업의 '오른쪽 위 X + 둥근 카드'는 WindowStyle=None 이 필요한데 이 앱 대화상자 10종이 전부 OS 크롬이라 이 창만 바꾸면 규약이 두 벌이 되고, AllowsTransparency 는 ClearType 이 꺼져 13px 한글이 뭉개져 금지돼 있다. 둥근 모서리와 그림자는 DWM 이 이미 그린다. 게이트: 테스트 267/267, --edit-smoke 0실패, --modal-check 2/2, --maxrect 10/10, --query-popup 17/17, --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
6ae05b73cd
commit
b2bdc7fa41
@@ -1,70 +1,145 @@
|
||||
<Window x:Class="SheetMe.Designer.Views.TagPickerDialogView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="태그 선택" Width="720" Height="620" MinWidth="560" MinHeight="440"
|
||||
xmlns:ctl="clr-namespace:SheetMe.Designer.Controls"
|
||||
Title="태그 선택" Width="1080" Height="700" MinWidth="900" MinHeight="560"
|
||||
WindowStartupLocation="CenterOwner" ShowInTaskbar="False"
|
||||
Style="{StaticResource ThemedWindow}">
|
||||
<!--
|
||||
창 껍데기는 OS 크롬을 유지한다. 목업의 '오른쪽 위 X + 둥근 카드'는 WindowStyle=None 이 필요한데,
|
||||
이 앱의 대화상자 10종이 전부 OS 크롬이라 이 창만 바꾸면 크롬 규약이 두 벌이 된다.
|
||||
AllowsTransparency 는 ClearType 이 꺼져 13px 한글이 뭉개져서 쓰지 않는다(MessageDialogView 주석 참조).
|
||||
둥근 모서리와 그림자는 DWM 이 이미 그린다.
|
||||
-->
|
||||
<Window.Resources>
|
||||
<!-- 분류 목록 한 줄: 이름 + 건수 -->
|
||||
<ctl:IconNameToVisualConverter x:Key="IconName"/>
|
||||
|
||||
<!-- 분류 한 줄: 이름 + 건수 -->
|
||||
<DataTemplate x:Key="CategoryItem">
|
||||
<DockPanel MinWidth="120">
|
||||
<DockPanel MinWidth="120" Height="22">
|
||||
<TextBlock DockPanel.Dock="Right" Text="{Binding Count}" FontSize="10.5"
|
||||
Foreground="{DynamicResource B.Muted}" VerticalAlignment="Center" Margin="8,0,0,0"/>
|
||||
<TextBlock Text="{Binding Name}" TextTrimming="CharacterEllipsis"/>
|
||||
</DockPanel>
|
||||
</DataTemplate>
|
||||
|
||||
<!-- 태그 한 줄: 이름 + 소분류 + 사용 건수 배지 -->
|
||||
<DataTemplate x:Key="TagItem">
|
||||
<DockPanel MinWidth="240" Margin="0,1">
|
||||
<Border DockPanel.Dock="Right" VerticalAlignment="Center" Margin="8,0,0,0"
|
||||
CornerRadius="4" Padding="5,1" Background="{DynamicResource B.Input}"
|
||||
BorderBrush="{DynamicResource B.InputBorder}" BorderThickness="1"
|
||||
Visibility="{Binding HasUsage, Converter={StaticResource BoolToVisibility}}"
|
||||
ToolTip="운영 서식에서 쓰인 횟수">
|
||||
<TextBlock Text="{Binding UsageText}" FontSize="10" Foreground="{DynamicResource B.Muted}"/>
|
||||
</Border>
|
||||
<TextBlock DockPanel.Dock="Right" Text="미사용" FontSize="10" Margin="8,0,0,0"
|
||||
VerticalAlignment="Center" Foreground="{DynamicResource B.Muted}"
|
||||
Visibility="{Binding IsUnused, Converter={StaticResource BoolToVisibility}}"
|
||||
ToolTip="운영 서식에서 한 번도 쓰이지 않은 태그입니다"/>
|
||||
<TextBlock Text="{Binding Name}" TextTrimming="CharacterEllipsis" VerticalAlignment="Center"/>
|
||||
</DockPanel>
|
||||
</DataTemplate>
|
||||
|
||||
<!--
|
||||
태그 한 줄. 이름은 저장되는 값 그대로라 고정폭으로 둔다 — 비슷한 이름
|
||||
(PAT_주민번호 / PAT_주민번호_Dash)을 눈으로 가르려면 자릿수가 맞아야 한다.
|
||||
-->
|
||||
<DataTemplate x:Key="TagItem">
|
||||
<DockPanel MinWidth="240" Height="26">
|
||||
<Border DockPanel.Dock="Right" VerticalAlignment="Center" Margin="8,0,2,0"
|
||||
CornerRadius="4" Padding="6,1" Background="{DynamicResource B.Input}"
|
||||
BorderBrush="{DynamicResource B.InputBorder}" BorderThickness="1"
|
||||
Visibility="{Binding HasUsage, Converter={StaticResource BoolToVisibility}}"
|
||||
ToolTip="운영 서식에서 쓰인 횟수(집계 시점 기준)">
|
||||
<TextBlock Text="{Binding UsageText}" FontSize="10" Foreground="{DynamicResource B.Muted}"/>
|
||||
</Border>
|
||||
<TextBlock DockPanel.Dock="Right" Text="미사용" FontSize="10" Margin="8,0,2,0"
|
||||
VerticalAlignment="Center" Foreground="{DynamicResource B.Muted}"
|
||||
Visibility="{Binding IsUnused, Converter={StaticResource BoolToVisibility}}"
|
||||
ToolTip="운영 서식에서 한 번도 쓰이지 않은 태그입니다"/>
|
||||
<TextBlock Text="{Binding Name}" FontFamily="Consolas" FontSize="12.5"
|
||||
TextTrimming="CharacterEllipsis" VerticalAlignment="Center"/>
|
||||
</DockPanel>
|
||||
</DataTemplate>
|
||||
|
||||
<!-- 우측 정보 패널의 절 제목 -->
|
||||
<Style x:Key="InfoLabel" TargetType="TextBlock">
|
||||
<Setter Property="FontSize" Value="11"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource B.Muted}"/>
|
||||
<Setter Property="Margin" Value="0,12,0,3"/>
|
||||
</Style>
|
||||
</Window.Resources>
|
||||
|
||||
<DockPanel Margin="14">
|
||||
<TextBlock x:Name="TitleText" DockPanel.Dock="Top" Foreground="{DynamicResource B.Muted}"
|
||||
TextWrapping="Wrap" Margin="0,0,0,10"/>
|
||||
<!-- 머리 — Bold 를 쓰지 않는다. Malgun Gothic 에 SemiBold 가 없어 합성되면 획이 뭉갠다 -->
|
||||
<StackPanel DockPanel.Dock="Top" Margin="0,0,0,10">
|
||||
<TextBlock Text="태그 선택" FontSize="17" Foreground="{DynamicResource B.Ink}"/>
|
||||
<TextBlock x:Name="TitleText" FontSize="12.5" TextWrapping="Wrap" Margin="0,3,0,0"
|
||||
Foreground="{DynamicResource B.Muted}"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- 검색: 초성·다중 토큰 -->
|
||||
<!-- 검색줄 — 레이어 검색칩과 같은 규범. 내부 MinHeight=0 과 BorderBrush 는 회귀 사고 기록이라 지우지 말 것 -->
|
||||
<DockPanel DockPanel.Dock="Top" Margin="0,0,0,10">
|
||||
<TextBlock DockPanel.Dock="Right" x:Name="CountText" VerticalAlignment="Center" Margin="10,0,0,0"
|
||||
<TextBlock DockPanel.Dock="Right" x:Name="CountText" VerticalAlignment="Center" Margin="12,0,2,0"
|
||||
Foreground="{DynamicResource B.Muted}" FontSize="11"/>
|
||||
<TextBox x:Name="SearchBox" Padding="6,4" VerticalContentAlignment="Center"
|
||||
TextChanged="OnSearchChanged" PreviewKeyDown="OnSearchKeyDown"
|
||||
AutomationProperties.Name="태그 검색"
|
||||
ToolTip="이름 일부 또는 초성으로 찾습니다 (예: ㅊㅌㅂㅎ → 차트번호). 공백으로 나누면 모두 포함하는 것만 남습니다."/>
|
||||
<CheckBox DockPanel.Dock="Right" x:Name="HideUnused" Content="미사용 숨기기"
|
||||
VerticalAlignment="Center" Margin="12,0,0,0" FontSize="12"
|
||||
Foreground="{DynamicResource B.Muted}"
|
||||
Checked="OnFilterToggled" Unchecked="OnFilterToggled"
|
||||
ToolTip="운영에서 한 번도 안 쓰인 태그를 목록에서 뺍니다"/>
|
||||
<Border Height="32" CornerRadius="7" Background="{DynamicResource B.Input}" BorderThickness="1">
|
||||
<Border.Style>
|
||||
<Style TargetType="Border">
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource B.InputBorder}"/>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsKeyboardFocusWithin" Value="True">
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource B.Accent}"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Border.Style>
|
||||
<DockPanel>
|
||||
<ContentControl DockPanel.Dock="Left" Margin="9,0,7,0" VerticalAlignment="Center" IsTabStop="False"
|
||||
Content="{Binding Source=search, Converter={StaticResource IconName}, ConverterParameter=13|B.Muted}"/>
|
||||
<Grid>
|
||||
<TextBox x:Name="SearchBox" Style="{StaticResource ChipTextBox}"
|
||||
TextChanged="OnSearchChanged" PreviewKeyDown="OnSearchKeyDown"
|
||||
AutomationProperties.Name="태그 검색"
|
||||
ToolTip="이름 일부 또는 초성으로 찾습니다 (예: ㅊㅌㅂㅎ → 차트번호). 공백으로 나누면 모두 포함하는 것만 남습니다."/>
|
||||
<TextBlock Text="태그명 또는 초성으로 검색… (예: 병원명, ㅊㅌㅂㅎ)" FontSize="12"
|
||||
Foreground="{DynamicResource B.Muted}" VerticalAlignment="Center"
|
||||
IsHitTestVisible="False" Margin="1,0,0,0">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}">
|
||||
<Setter Property="Visibility" Value="Collapsed"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Text, ElementName=SearchBox}" Value="">
|
||||
<Setter Property="Visibility" Value="Visible"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
</DockPanel>
|
||||
|
||||
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,12,0,0">
|
||||
<TextBlock x:Name="SelectedText" VerticalAlignment="Center" Margin="0,0,12,0"
|
||||
FontFamily="Consolas" FontSize="11.5" Foreground="{DynamicResource B.Muted}"
|
||||
TextTrimming="CharacterEllipsis" MaxWidth="330"/>
|
||||
<Button Content="값 지우기" Padding="12,4" Click="OnClear"/>
|
||||
<Button x:Name="PickButton" Content="선택" Padding="18,4" Margin="8,0,0,0"
|
||||
Style="{StaticResource Primary}" Click="OnPick" IsDefault="True"/>
|
||||
<Button Content="취소" Padding="18,4" Margin="8,0,0,0" IsCancel="True"/>
|
||||
</StackPanel>
|
||||
<!-- 바닥 — 고른 것을 칩으로 보여 준다. 반환 계약이 단일값이라 칩은 항상 하나다 -->
|
||||
<DockPanel DockPanel.Dock="Bottom" Margin="0,12,0,0">
|
||||
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal">
|
||||
<Button Content="값 지우기" Padding="12,4" Click="OnClear"/>
|
||||
<Button Content="취소" Padding="18,4" Margin="8,0,0,0" IsCancel="True"/>
|
||||
<Button x:Name="PickButton" Content="선택" Padding="18,4" Margin="8,0,0,0"
|
||||
Style="{StaticResource Primary}" Click="OnPick" IsDefault="True"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||||
<TextBlock Text="선택된 태그" FontSize="11.5" VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource B.Muted}" Margin="2,0,8,0"/>
|
||||
<Border x:Name="SelectedChip" CornerRadius="6" Padding="9,3"
|
||||
Background="{DynamicResource B.LayerSubtree}"
|
||||
BorderBrush="{DynamicResource B.Accent}" BorderThickness="1"
|
||||
Visibility="Collapsed">
|
||||
<TextBlock x:Name="SelectedText" FontFamily="Consolas" FontSize="11.5"
|
||||
Foreground="{DynamicResource B.Ink}"
|
||||
TextTrimming="CharacterEllipsis" MaxWidth="330"/>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</DockPanel>
|
||||
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="190"/>
|
||||
<ColumnDefinition Width="200"/>
|
||||
<ColumnDefinition Width="12"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="12"/>
|
||||
<ColumnDefinition Width="300"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- 왼쪽: 분류. '무엇이 있는지' 는 여기서 먼저 보인다 -->
|
||||
<!-- 왼쪽: 분류. 필터의 단일 출처다 — 같은 필터를 가로 칩 줄로 한 번 더 그리지 않는다 -->
|
||||
<DockPanel Grid.Column="0">
|
||||
<TextBlock DockPanel.Dock="Top" Text="분류" FontSize="11"
|
||||
Foreground="{DynamicResource B.Muted}" Margin="2,0,0,4"/>
|
||||
@@ -73,7 +148,7 @@
|
||||
AutomationProperties.Name="태그 분류"/>
|
||||
</DockPanel>
|
||||
|
||||
<!-- 오른쪽: 태그 -->
|
||||
<!-- 가운데: 결과 -->
|
||||
<DockPanel Grid.Column="2">
|
||||
<TextBlock DockPanel.Dock="Top" x:Name="ListCaption" FontSize="11"
|
||||
Foreground="{DynamicResource B.Muted}" Margin="2,0,0,4"/>
|
||||
@@ -83,6 +158,73 @@
|
||||
HorizontalContentAlignment="Stretch"
|
||||
AutomationProperties.Name="태그 목록"/>
|
||||
</DockPanel>
|
||||
|
||||
<!--
|
||||
오른쪽: 고른 태그의 정보.
|
||||
설명·예시 값·최근 사용은 데이터에 없어서 넣지 않았다 — 라벨만 있고 비는 칸은
|
||||
"내가 뭘 잘못했나"를 묻게 만들고, 가짜 예시는 사용자가 실제 반환값으로 믿는다.
|
||||
-->
|
||||
<DockPanel Grid.Column="4">
|
||||
<TextBlock DockPanel.Dock="Top" Text="태그 정보" FontSize="11"
|
||||
Foreground="{DynamicResource B.Muted}" Margin="2,0,0,4"/>
|
||||
<Border BorderBrush="{DynamicResource B.Line2}" BorderThickness="1" CornerRadius="8"
|
||||
Background="{DynamicResource B.Surface}" Padding="12,10">
|
||||
<Grid>
|
||||
<TextBlock x:Name="InfoEmpty" Text="목록에서 태그를 고르면 여기에 표시됩니다"
|
||||
FontSize="12" TextWrapping="Wrap" VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center" TextAlignment="Center"
|
||||
Foreground="{DynamicResource B.Muted}"/>
|
||||
<ScrollViewer x:Name="InfoPane" VerticalScrollBarVisibility="Auto" Visibility="Collapsed">
|
||||
<StackPanel>
|
||||
<!-- 태그는 이름이 곧 키다. 그래서 '태그 키' 절을 따로 두지 않고 여기에 복사를 붙인다 -->
|
||||
<DockPanel>
|
||||
<Button DockPanel.Dock="Right" x:Name="CopyButton" Style="{StaticResource Subtle}"
|
||||
Width="26" Height="24" Padding="0" Margin="6,0,0,0"
|
||||
Click="OnCopyTag" ToolTip="태그 이름 복사"
|
||||
AutomationProperties.Name="태그 이름 복사"
|
||||
Content="{Binding Source=copy, Converter={StaticResource IconName}, ConverterParameter=13|B.Muted}"/>
|
||||
<TextBlock x:Name="InfoName" FontFamily="Consolas" FontSize="13.5"
|
||||
TextWrapping="Wrap" VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource B.Ink}"/>
|
||||
</DockPanel>
|
||||
|
||||
<Border x:Name="InfoCategoryChip" CornerRadius="5" Padding="7,2" Margin="0,8,0,0"
|
||||
HorizontalAlignment="Left" Background="{DynamicResource B.Chip}">
|
||||
<TextBlock x:Name="InfoCategory" FontSize="11"
|
||||
Foreground="{DynamicResource B.Muted}"/>
|
||||
</Border>
|
||||
|
||||
<TextBlock Text="사용 빈도" Style="{StaticResource InfoLabel}"/>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Border x:Name="InfoRecommendChip" CornerRadius="5" Padding="7,2" Margin="0,0,8,0"
|
||||
Background="{DynamicResource B.LayerSubtree}" Visibility="Collapsed"
|
||||
ToolTip="이 컨트롤 타입에서 자주 쓰이는 태그입니다">
|
||||
<TextBlock Text="추천" FontSize="11" Foreground="{DynamicResource B.AccentText}"/>
|
||||
</Border>
|
||||
<TextBlock x:Name="InfoUsage" FontSize="12.5" VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource B.Ink}"/>
|
||||
</StackPanel>
|
||||
<TextBlock x:Name="InfoUsageNote" FontSize="10.5" TextWrapping="Wrap" Margin="0,3,0,0"
|
||||
Foreground="{DynamicResource B.Muted}"/>
|
||||
|
||||
<StackPanel x:Name="InfoVariantBlock" Visibility="Collapsed">
|
||||
<TextBlock Text="변형" Style="{StaticResource InfoLabel}"
|
||||
ToolTip="같은 값을 다른 형식으로 내는 태그들"/>
|
||||
<ItemsControl x:Name="InfoVariants">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding}" FontFamily="Consolas" FontSize="11.5"
|
||||
Margin="0,1" TextTrimming="CharacterEllipsis"
|
||||
Foreground="{DynamicResource B.Muted}"/>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Border>
|
||||
</DockPanel>
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
</Window>
|
||||
|
||||
@@ -112,7 +112,17 @@ public partial class TagPickerDialogView : Window
|
||||
: allTags.Where(t => TagSearch.MatchesAll(t, tokens)).ToList();
|
||||
}
|
||||
|
||||
private bool PassesFilter(string tag) => activeFilter switch
|
||||
private bool PassesFilter(string tag)
|
||||
{
|
||||
// 미사용 숨기기는 분류와 <b>겹쳐서</b> 건다 — 분류를 고른 채로도 미사용만 걷어낼 수 있어야 한다
|
||||
if (HideUnused.IsChecked == true && !LegacyTagUsageCounts.IsUsed(tag))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return MatchesCategory(tag);
|
||||
}
|
||||
|
||||
private bool MatchesCategory(string tag) => activeFilter switch
|
||||
{
|
||||
FilterAll => true,
|
||||
FilterPreferred => preferred.Contains(tag, StringComparer.Ordinal),
|
||||
@@ -226,11 +236,63 @@ public partial class TagPickerDialogView : Window
|
||||
{
|
||||
var tag = (TagList.SelectedItem as TagListItem)?.Name;
|
||||
SelectedText.Text = tag ?? string.Empty;
|
||||
SelectedChip.Visibility = tag is { Length: > 0 } ? Visibility.Visible : Visibility.Collapsed;
|
||||
PickButton.IsEnabled = tag is { Length: > 0 };
|
||||
ShowTagInfo(tag);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 우측 정보 패널.
|
||||
///
|
||||
/// 설명·예시 값·최근 사용은 <b>데이터에 없다</b>. 라벨만 두고 비우면 사용자가 자기 잘못을 의심하고,
|
||||
/// 가짜 예시를 채우면 실제 반환값으로 믿는다. 그래서 그 절들은 아예 만들지 않았다.
|
||||
/// 대신 지금 데이터로 계산되는 것만 보여 준다 — 분류, 운영 사용 건수, 같은 갈래의 변형.
|
||||
/// </summary>
|
||||
private void ShowTagInfo(string? tag)
|
||||
{
|
||||
if (tag is not { Length: > 0 })
|
||||
{
|
||||
InfoPane.Visibility = Visibility.Collapsed;
|
||||
InfoEmpty.Visibility = Visibility.Visible;
|
||||
return;
|
||||
}
|
||||
InfoPane.Visibility = Visibility.Visible;
|
||||
InfoEmpty.Visibility = Visibility.Collapsed;
|
||||
|
||||
InfoName.Text = tag;
|
||||
InfoCategory.Text = TagSearch.CategoryOf(tag);
|
||||
|
||||
var usage = LegacyTagUsageCounts.Of(tag);
|
||||
InfoUsage.Text = usage > 0 ? $"{usage:N0}회" : "운영에서 쓰인 적 없음";
|
||||
// 숫자만 크게 띄우면 실시간 집계로 읽힌다 — 스냅샷임을 붙여 둔다
|
||||
InfoUsageNote.Text = usage > 0
|
||||
? "운영 서식 집계 스냅샷입니다. 지금 값과 다를 수 있습니다."
|
||||
: "이 태그를 처음 쓰는 것일 수 있습니다.";
|
||||
|
||||
// '추천' 은 사용 횟수가 아니라 컨트롤 타입별 큐레이션이다 — '자주 사용' 이라 부르면 근거가 왜곡된다
|
||||
InfoRecommendChip.Visibility = preferred.Contains(tag, StringComparer.Ordinal)
|
||||
? Visibility.Visible
|
||||
: Visibility.Collapsed;
|
||||
|
||||
// 변형 — 같은 값을 다른 형식으로 내는 태그(PAT_주민번호 / _Dash / _Blind)
|
||||
var variants = TagSearch.VariantsOf(tag, allTags);
|
||||
InfoVariants.ItemsSource = variants;
|
||||
InfoVariantBlock.Visibility = variants.Count > 0 ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
|
||||
private void OnCopyTag(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if ((TagList.SelectedItem as TagListItem)?.Name is { Length: > 0 } tag)
|
||||
{
|
||||
Clipboard.SetText(tag);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSearchChanged(object sender, TextChangedEventArgs e) => Rebuild();
|
||||
|
||||
/// <summary>미사용 숨기기 — 분류와 겹쳐서 걸리므로 목록만 다시 만들면 된다</summary>
|
||||
private void OnFilterToggled(object sender, RoutedEventArgs e) => Rebuild();
|
||||
|
||||
/// <summary>
|
||||
/// 검색칸에서 바로 목록을 움직인다 — 검색하고 Tab 으로 옮겨 가는 왕복을 없앤다.
|
||||
/// Enter 는 선택 확정(IsDefault 버튼이 받는다).
|
||||
|
||||
Reference in New Issue
Block a user