태그 행에서 모달 없이 태그를 고른다 — 인라인 완성
4단계 계속. 주 경로에서 1080x700 모달이 사라진다.
칩 상위 8종만으로 운영 사용의 41.7%, 상위 16종으로 61.5% 를 덮는다.
나머지는 이름을 정확히 16자 외워 치거나 모달을 열어야 했고, 자유 입력은 열려 있는데
제안이 없어서 <b>오타가 검증 없이 저장되고 EMR 에서 조용히 빈 칸이 됐다</b>.
같은 인스펙터의 '속성 추가' 행에는 이미 부분일치 제안이 있었다 —
패턴은 있는데 운영 8,032건이 걸린 칸에만 없었다.
<b>새 패턴을 발명하지 않았다.</b> QueryEditorWindow 의 CompletionPopup 에서 배운 것을 그대로 쓴다:
StaysOpen=False 로 두면 팝업이 마우스를 캡처해 항목 클릭을 '바깥 클릭'으로 먹어 버린다
(눌러도 입력이 안 되고 사라진다 — 그 함정을 이미 한 번 겪었다).
AllowsTransparency 도 켜지 않는다 — 레이어드 창이 되면 ClearType 이 꺼져 13px 한글이 뭉개진다
(진단 cleartype 의 D 표본). 대신 안쪽에 ClearTypeHint 를 건다.
<b>칩이 사라지는 기준을 바꿨다.</b> 전에는 ValueText.Length == 0 이라 값이 정해지면 칩이 사라졌다.
그래서 잘못 고른 태그를 바꿀 때 도움이 끊겼다 — 태그를 고치는 일은 처음 고르는 일만큼 잦은데
고치는 쪽만 맨손이었다. 이제 기준은 '값이 있는지'가 아니라 <b>'타이핑 중인지'</b>다.
타이핑을 시작하면 아래 완성 목록이 같은 일을 더 정확하게 하므로 칩을 접는다.
그 옛 규칙을 고정하던 검사 하나를 새 규칙으로 고쳤다 — 의도한 변경이다.
완성 목록은 입력 중인 글자를 <b>인자로</b> 받는다. 태그 행의 Text 바인딩이
UpdateSourceTrigger=LostFocus 라 타이핑 중에는 ValueText 가 아직 옛 값이고,
그것으로 목록을 만들면 한 글자 뒤처진다.
순위는 이 타입에서 자주 쓰이는 순서 → 운영 사용 건수 → 이름이다.
알파벳 순으로 두면 상위 10종(전체 사용의 47.5%)이 목록 아래로 흩어진다.
검색은 TagSearch 를 그대로 쓴다 — 별칭·초성이 여기서도 듣는다('환자명'으로도 뜬다).
<b>Popup.IsOpen 은 기본이 TwoWay 다.</b> 읽기 전용 속성에 바인딩하면 XamlParseException 이고,
그 실패가 --dialog-shots 를 통째로 죽였다(종료코드 3). Mode=OneWay 를 명시한다.
게이트가 아니면 인스펙터를 띄우는 화면에서만 터졌을 것이다.
실측 14건: 값이 있어도 칩 유지 · 타이핑 전 미표시 · 타이핑 시 칩 접힘 · 별칭 적용 ·
자주 쓰는 것이 위 · 10줄 상한 · ↑↓ 순환 · Enter 확정과 커밋 · 확정 후 닫힘/칩 복귀 ·
정확히 하나면 안 가림 · Esc 가 값을 보존.
게이트: 테스트 291/291, --edit-smoke 0실패(신규 14), --dialog-shots 글자 2,465개 넘침 0 대조군 4/4,
--modal-check 0실패, --cleartype 11/11, --maxrect 0실패, --scale-budget 5/5, 빌드 경고 0,
--db-smoke 1271건 diff 0, --db-render P062 md5 8d683835f5d81e7bb41c79071d6bf954 동일.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
a869e5a5bc
commit
9b2ae0ea67
@@ -548,9 +548,41 @@
|
||||
Command="{Binding BrowseCommand}" ToolTip="전체 목록에서 선택"
|
||||
Content="{Binding Source=list, Converter={StaticResource IconName}, ConverterParameter=13}"/>
|
||||
<Grid>
|
||||
<TextBox Style="{StaticResource RowInput}" bh:InspectorFieldBehavior.CommitOnEnter="True" Text="{Binding ValueText, UpdateSourceTrigger=LostFocus}"
|
||||
ToolTip="직접 입력하거나 아래 제안을 누르세요"/>
|
||||
<TextBox x:Name="TagInput" Style="{StaticResource RowInput}"
|
||||
bh:InspectorFieldBehavior.CommitOnEnter="True"
|
||||
bh:TagCompletionBehavior.Enabled="True"
|
||||
Text="{Binding ValueText, UpdateSourceTrigger=LostFocus}"
|
||||
ToolTip="치면 목록이 뜹니다 — ↑↓ 이동, Enter 확정, Esc 취소"/>
|
||||
<TextBlock Style="{StaticResource MixedHintOverlay}"/>
|
||||
<!--
|
||||
인라인 완성 — 이 행에서 모달 없이 태그를 고르는 주 경로.
|
||||
StaysOpen=True 가 필수다: False 면 팝업이 마우스를 캡처해 항목 클릭을
|
||||
'바깥 클릭'으로 먹어 버린다(눌러도 입력이 안 되고 사라진다 —
|
||||
QueryEditorWindow 의 CompletionPopup 에서 같은 것을 이미 겪었다).
|
||||
AllowsTransparency 를 켜지 않는다 — 레이어드 창이 되면 ClearType 이 꺼져
|
||||
13px 한글이 뭉개진다(진단 cleartype 의 D 표본).
|
||||
-->
|
||||
<Popup IsOpen="{Binding ShowCompletions, Mode=OneWay}" PlacementTarget="{Binding ElementName=TagInput}"
|
||||
Placement="Bottom" StaysOpen="True" Focusable="False">
|
||||
<Border Background="{DynamicResource B.Surface}" BorderBrush="{DynamicResource B.Line}"
|
||||
BorderThickness="1" CornerRadius="6" Padding="2"
|
||||
MinWidth="{Binding ActualWidth, ElementName=TagInput}"
|
||||
RenderOptions.ClearTypeHint="Enabled">
|
||||
<ItemsControl ItemsSource="{Binding Completions}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border Height="24" Padding="7,0" CornerRadius="4"
|
||||
Background="Transparent"
|
||||
MouseLeftButtonDown="OnCompletionClicked">
|
||||
<TextBlock Text="{Binding}" FontFamily="Consolas" FontSize="11.5"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource B.Ink}"/>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</Border>
|
||||
</Popup>
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
<!-- 이 컨트롤 타입에서 실제로 쓰이는 태그(빈도순). 아직 안 골랐을 때만 뜬다 —
|
||||
|
||||
@@ -84,6 +84,44 @@ public partial class InspectorView : UserControl
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 완성 목록의 한 줄을 눌렀다.
|
||||
///
|
||||
/// 팝업은 StaysOpen=True 다 — False 로 두면 팝업이 마우스를 캡처해 이 클릭을
|
||||
/// '바깥 클릭'으로 먹어 버린다(눌러도 입력이 안 되고 사라진다).
|
||||
/// 그래서 클릭을 직접 받아 값으로 확정하고 코드로 닫는다.
|
||||
/// </summary>
|
||||
private void OnCompletionClicked(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (sender is not FrameworkElement { DataContext: string picked }
|
||||
|| picked.Length == 0
|
||||
|| FindRowOwner(sender) is not PropertyRowViewModel row)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (row is ViewModels.Inspector.TagPickerRowViewModel tagRow)
|
||||
{
|
||||
tagRow.EndTyping();
|
||||
}
|
||||
row.ValueText = picked;
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
/// <summary>이 요소가 속한 행 VM — 팝업 안이라 시각 트리가 본체와 끊겨 논리 부모로도 올라간다</summary>
|
||||
private static PropertyRowViewModel? FindRowOwner(object node)
|
||||
{
|
||||
var current = node as DependencyObject;
|
||||
for (var i = 0; i < 40 && current is not null; i++)
|
||||
{
|
||||
if (current is FrameworkElement { DataContext: PropertyRowViewModel row })
|
||||
{
|
||||
return row;
|
||||
}
|
||||
current = VisualParentOf(current);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>Ctrl+1/2/3 이나 F4 로 인스펙터에 들어올 때 첫 편집 칸을 잡아 준다</summary>
|
||||
public void FocusFirstRow()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user