diff --git a/src/SheetMe.Designer/Diagnostics/ClearTypeCheck.cs b/src/SheetMe.Designer/Diagnostics/ClearTypeCheck.cs
index 994099e..a8039cf 100644
--- a/src/SheetMe.Designer/Diagnostics/ClearTypeCheck.cs
+++ b/src/SheetMe.Designer/Diagnostics/ClearTypeCheck.cs
@@ -47,6 +47,20 @@ internal static class ClearTypeCheck
var window = BuildProbe(out var samples, out var popups);
window.Show();
+ // ── 실제 창을 하나 잰다 ──
+ // 위 표본들은 기법이 듣는지를 잰다(합성 도형). 그것이 통과해도
+ // 실제 창에 기법을 안 걸었으면 글자는 계속 뭉개진다 — 실제로 그랬다.
+ // 쿼리 편집 창은 카드에 그림자 Effect 가 있어 그 안 글자가 전부 중간 서피스를 탄다.
+ var real = OpenQueryEditorForClearType();
+ if (real.Window is not null && real.Target is not null)
+ {
+ samples.Add(("G(실제 창) 쿼리 편집 카드 안 글자", real.Target, true));
+ }
+ else
+ {
+ Lines.Add("SKIP G(실제 창) — 창을 띄우지 못해 판정 불가");
+ }
+
// 창이 보이는 것과 합성기가 그려 낸 것은 다르다 — Loaded 직후 찍으면 빈 화면을 잰다.
// (--modal-check 에서 BeginInvoke 가 Loaded 보다 먼저 돌아 0 을 세던 것과 같은 함정)
var timer = new System.Windows.Threading.DispatcherTimer
@@ -84,6 +98,57 @@ internal static class ClearTypeCheck
return failures == 0 ? 0 : 1;
}
+ ///
+ /// 실제 쿼리 편집 창을 띄워 카드 제목 글자를 잴 대상으로 내준다.
+ ///
+ /// 합성 표본이 아니라 배포되는 창을 재야 한다 — 기법이 듣는 것과
+ /// 그 기법을 이 창에 걸어 둔 것은 다른 일이고, 후자가 빠져 있었다.
+ /// 창은 화면 안(0,0)에 띄운다. BitBlt 는 화면에 실제로 그려진 픽셀만 읽으므로
+ /// 화면 밖(-10000)에 두면 아무것도 못 잰다.
+ ///
+ private static (Window? Window, FrameworkElement? Target) OpenQueryEditorForClearType()
+ {
+ try
+ {
+ var editor = new Views.QueryEditorWindow("MDataTable1", "select 1 from dual")
+ {
+ Left = 0,
+ Top = 0,
+ WindowStartupLocation = WindowStartupLocation.Manual,
+ };
+ editor.Show();
+ editor.UpdateLayout();
+ // 카드 제목은 13.5px SemiBold — 뭉개짐이 가장 먼저 보이는 자리다
+ var target = FindTextBlock(editor, "SQL 편집기");
+ return (editor, target);
+ }
+ catch (Exception ex)
+ {
+ Lines.Add($"SKIP G(실제 창) — {ex.GetType().Name}: {ex.Message}");
+ return (null, null);
+ }
+ }
+
+ /// 이 글자를 담은 TextBlock 을 찾는다 — 없으면 null
+ private static FrameworkElement? FindTextBlock(DependencyObject root, string text)
+ {
+ if (root is System.Windows.Controls.TextBlock block
+ && block.Text.Contains(text, StringComparison.Ordinal))
+ {
+ return block;
+ }
+ var count = System.Windows.Media.VisualTreeHelper.GetChildrenCount(root);
+ for (var i = 0; i < count; i++)
+ {
+ if (FindTextBlock(System.Windows.Media.VisualTreeHelper.GetChild(root, i), text)
+ is { } found)
+ {
+ return found;
+ }
+ }
+ return null;
+ }
+
/// 대조군이 먼저 서야 나머지 숫자가 뜻을 갖는다
private static void Judge(List<(string Name, FrameworkElement Target, bool ExpectClearType)> samples)
{
diff --git a/src/SheetMe.Designer/Views/QueryEditorWindow.xaml b/src/SheetMe.Designer/Views/QueryEditorWindow.xaml
index 4bb0a24..710957a 100644
--- a/src/SheetMe.Designer/Views/QueryEditorWindow.xaml
+++ b/src/SheetMe.Designer/Views/QueryEditorWindow.xaml
@@ -241,6 +241,14 @@
+
+
@@ -259,7 +267,11 @@
-
+
+
@@ -393,7 +405,11 @@
-
+
+
diff --git a/src/SheetMe.Designer/Views/QueryEditorWindow.xaml.cs b/src/SheetMe.Designer/Views/QueryEditorWindow.xaml.cs
index 36574d7..a76b5f1 100644
--- a/src/SheetMe.Designer/Views/QueryEditorWindow.xaml.cs
+++ b/src/SheetMe.Designer/Views/QueryEditorWindow.xaml.cs
@@ -138,6 +138,7 @@ public partial class QueryEditorWindow : Window
TextCompositionManager.AddTextInputHandler(SqlBox, OnComposed);
RebuildBucketChips();
ApplyVariableFilter(string.Empty);
+ RefreshPatientButton();
var connection = Services.ConfigService.Current.ConnectionString;
workbench = new SheetMe.Data.Stores.OracleQueryWorkbench(connection);
if (!workbench.CanUseDb)
@@ -190,6 +191,41 @@ public partial class QueryEditorWindow : Window
/// 진단(--query-popup)에서 검증 실행을 부르는 통로 — 결과 표가 실제로 차는지 본다
internal void RunTrialForSmoke() => OnTrial(this, new RoutedEventArgs());
+ ///
+ /// 환자를 고른다 — 고르면 검증 실행이 실제 값으로 돈다.
+ ///
+ /// 권한 문·감사 기록·문맥 읽기는 전부 안에 있다.
+ /// 세션 하나를 공유하므로 미리보기와 같은 환자다 — 두 화면이 다른 환자를 보면 구분할 수 없다.
+ ///
+ private void OnPickPatient(object sender, RoutedEventArgs e)
+ {
+ if (Services.PatientSession.HasPatient)
+ {
+ // 이미 고른 환자가 있으면 이 버튼은 '해제'다. 바꾸려면 해제하고 다시 고른다 —
+ // 버튼 하나에 '고르기/바꾸기/해제'를 다 넣으면 무엇이 일어날지 예측할 수 없다.
+ Services.PatientSession.Set(null, string.Empty);
+ RefreshPatientButton();
+ return;
+ }
+ if (PatientPickerDialogView.Pick(this, out var label) is { } picked)
+ {
+ Services.PatientSession.Set(picked, label);
+ RefreshPatientButton();
+ }
+ }
+
+ /// 버튼이 지금 무엇을 하는지 글자로 말한다 — 상태를 색으로만 알리면 못 읽는다
+ private void RefreshPatientButton()
+ {
+ if (PatientButton is null)
+ {
+ return;
+ }
+ PatientButton.Content = Services.PatientSession.HasPatient
+ ? $"환자 해제 — {Services.PatientSession.Label}"
+ : "환자 선택...";
+ }
+
/// 커서 위치를 보고 목록을 띄우거나 닫는다
private void UpdateCompletion()
{