쿼리 편집기 — 렌더를 목업과 픽셀 대조해 찾은 11건 수정

목업 사양을 글로 옮긴 뒤, 라이트·다크 렌더 4장과 XAML 을 각각 다른 눈으로 대조하고
그 지적을 다시 반증하는 감사를 돌렸다. 확정된 것만 고쳤다. 대부분 '내가 지정한 색이
전역 스타일에 먹히고 있었다'는 한 가지 뿌리에서 나왔다.

■ 글자색이 지정한 대로 안 나오던 것 (가장 컸다)

'포맷'·'전체 지우기'·'닫기'·+ 가 전부 본문색이었다. 라이트 '전체 지우기' 영역
히스토그램에 파란 픽셀이 0개였다. 원인은 전역 TextBlock 스타일(Foreground=B.Ink)이
ContentPresenter 가 만든 글자에 <b>직접</b> 걸려 Button.Foreground 상속을 이기는 것.
공유 버튼 템플릿이 이미 쓰던 방어(ContentPresenter.Resources 로 TextBlock Foreground 를
버튼에 바인딩)를 툴바·칩 템플릿에도 넣었다.
같은 이유로 파란 채움 칩 '전체' 위 글자가 검정이었다 — 채움도 B.Accent(#0099FF, 흰 글자 3.0:1)에서
B.AccentFill(#0F6FBF, 5.19:1)로 바꿔 양 테마가 함께 해결된다.

■ 사각 편집 상자 안에 파란 둥근 상자가 겹쳐 있던 것

전역 TextBox 템플릿이 BorderThickness=1·CornerRadius=8 을 하드코딩하고 포커스 시 그 테두리를
강조색으로 칠한다. SqlBox 의 BorderThickness=0 은 무시된다.
y=300 가로 스캔에서 구분선(#DCDCDC) 바로 옆 x=59 가 #0099FF 였고, 세로로 훑으면 반경 8 의 호를 그렸다.
사용자가 지적한 '줄칸과 입력칸 사이 선의 라운드'가 이것이었다 — 무테 템플릿(ChipTextBox)으로 바꿨다.

■ 다크에서만 사라지던 것 3가지

· 줄번호 홈통: B.Input 과 B.Chip 이 둘 다 #262626 이라 면이 갈라지지 않았다(1px 선만 남음).
  편집 영역 배경을 B.Surface(#1C1C1C)로 바꿔 홈통이 드러나게 했다.
· 결과/상태 상자와 카드 경계: B.InputBorder 가 다크에서 Transparent 라 테두리 픽셀이 0개였다.
  B.Line2 로 바꿨다.
· 현재 줄 띠: B.Hover(#242424)가 편집 배경(#262626)보다 오히려 어두워 1.02:1 이었다.
  편집기 전용 토큰 B.SqlCurrentLine 을 양 테마에 신설했다(다른 화면의 호버 룩은 그대로).
· 선택된 변수 행: B.Sel 이 다크에서 #3C3C3C 중성 회색이라 '옅은 파랑'이 아니었다.
  공유 토큰은 두고 이 목록에만 B.LayerSubtree 를 준다.

■ 그 밖

· 카드에 그림자가 0px 이었다 — B.CardShadow 를 신설했다(기존 B.FloatShadow 는 팔레트용이라 세다).
· 카드 사이 GridSplitter 의 회색 띠가 목업에 없는 선으로 보였다 — 투명으로.
· 결과 표: 마지막 컬럼 뒤 채움 머리글이 회색이라 이름 없는 컬럼처럼 보였다(Column=null DataTrigger 로 처리).
· 결과 표: 본문 글자가 머리글보다 8px 왼쪽으로 밀려 있었다 — DataGridCell 기본 템플릿이 Padding 을
  쓰지 않아서다. 템플릿을 지정해 여백이 먹게 했다.

회귀: 테스트 230/230, 편집 스모크 실패 0, 팝업·정렬·결과표·단축키 점검 16/16,
종이 렌더 P062 바이트 동일.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Msystech
2026-08-13 17:14:55 +09:00
co-authored by Claude Opus 5
parent c98e2413b5
commit 6ce055b00c
4 changed files with 85 additions and 13 deletions
@@ -150,7 +150,7 @@ public sealed class SqlHighlightLayer : FrameworkElement
// 커서가 있는 줄 — 긴 쿼리에서 지금 어디를 고치고 있는지 잃지 않게 옅은 띠를 깐다.
// 위치는 TextBox 에게 묻는다(아래 이유와 같다).
if (source.SelectionLength == 0 && TryFindResource("B.Hover") is Brush band)
if (source.SelectionLength == 0 && TryFindResource("B.SqlCurrentLine") is Brush band)
{
var caretRect = RectAt(source.CaretIndex);
if (caretRect is { } cr && cr.Bottom >= 0 && cr.Top <= height)
@@ -56,6 +56,9 @@
<SolidColorBrush x:Key="B.SqlText" Color="#7FC98A" />
<SolidColorBrush x:Key="B.SqlComment" Color="#8A8F98" />
<SolidColorBrush x:Key="B.SqlVariable" Color="#F0A868" />
<!-- 편집기 현재 줄 띠 — 편집 면(B.Surface #1C1C1C)보다 밝아야 띠로 읽힌다 -->
<SolidColorBrush x:Key="B.SqlCurrentLine" Color="#2B2B2B" />
<DropShadowEffect x:Key="B.CardShadow" BlurRadius="10" ShadowDepth="1" Direction="270" Opacity="0.35" Color="Black" RenderingBias="Quality" />
<!-- 강조 채움 버튼의 호버·누름. 흰 글자를 얹으므로 밝히지 않고 어둡게 간다(양 테마 동일값).
이게 없으면 공유 버튼 템플릿의 호버 트리거가 채움을 회색으로 덮어 흰 글자가 사라진다. -->
<SolidColorBrush x:Key="B.AccentFillHover" Color="#0C5B9C" />
@@ -91,6 +91,10 @@
<SolidColorBrush x:Key="B.SqlText" Color="#1A7F37" />
<SolidColorBrush x:Key="B.SqlComment" Color="#6B7280" />
<SolidColorBrush x:Key="B.SqlVariable" Color="#9A3412" />
<!-- 편집기 현재 줄 띠 — B.Hover 는 버튼 호버용이라 편집 면 위에서 안 보인다(다크에선 더 어둡기까지 하다) -->
<SolidColorBrush x:Key="B.SqlCurrentLine" Color="#F0F4F8" />
<!-- 카드 그림자 — B.FloatShadow 는 팔레트·플라이아웃용이라 카드엔 세다 -->
<DropShadowEffect x:Key="B.CardShadow" BlurRadius="10" ShadowDepth="1" Direction="270" Opacity="0.07" Color="Black" RenderingBias="Quality" />
<!-- 강조 채움 버튼의 호버·누름 — 다크 대응본 주석 참조(양 테마 동일값) -->
<SolidColorBrush x:Key="B.AccentFillHover" Color="#0C5B9C" />
<SolidColorBrush x:Key="B.AccentFillPressed" Color="#0A4C83" />
@@ -13,8 +13,10 @@
<Style x:Key="Card" TargetType="Border">
<Setter Property="CornerRadius" Value="10"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="{DynamicResource B.InputBorder}"/>
<!-- 다크의 B.InputBorder 는 Transparent 라 카드 경계가 사라진다 -->
<Setter Property="BorderBrush" Value="{DynamicResource B.Line2}"/>
<Setter Property="Background" Value="{DynamicResource B.Surface}"/>
<Setter Property="Effect" Value="{DynamicResource B.CardShadow}"/>
</Style>
<Style x:Key="CardTitle" TargetType="TextBlock">
@@ -35,7 +37,16 @@
<ControlTemplate TargetType="Button">
<Grid Background="Transparent">
<ContentPresenter Margin="{TemplateBinding Padding}"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
HorizontalAlignment="Center" VerticalAlignment="Center">
<!-- 전역 TextBlock 스타일(Foreground=B.Ink)이 ContentPresenter 가 만든 글자에
직접 걸려 Button.Foreground 상속을 이긴다 — 공유 버튼 템플릿과 같은 방어 -->
<ContentPresenter.Resources>
<Style TargetType="TextBlock">
<Setter Property="Foreground"
Value="{Binding Foreground, RelativeSource={RelativeSource AncestorType=ButtonBase}}" />
</Style>
</ContentPresenter.Resources>
</ContentPresenter>
<Border x:Name="rule" Height="2" VerticalAlignment="Bottom"
Background="Transparent" CornerRadius="1"/>
</Grid>
@@ -61,7 +72,16 @@
<ControlTemplate TargetType="Button">
<Grid Background="Transparent">
<ContentPresenter Margin="{TemplateBinding Padding}"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
HorizontalAlignment="Center" VerticalAlignment="Center">
<!-- 전역 TextBlock 스타일(Foreground=B.Ink)이 ContentPresenter 가 만든 글자에
직접 걸려 Button.Foreground 상속을 이긴다 — 공유 버튼 템플릿과 같은 방어 -->
<ContentPresenter.Resources>
<Style TargetType="TextBlock">
<Setter Property="Foreground"
Value="{Binding Foreground, RelativeSource={RelativeSource AncestorType=ButtonBase}}" />
</Style>
</ContentPresenter.Resources>
</ContentPresenter>
<Border Height="2" VerticalAlignment="Bottom" CornerRadius="1"
Background="{DynamicResource B.Accent}"/>
</Grid>
@@ -103,22 +123,54 @@
<Setter Property="BorderThickness" Value="0,0,1,1"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Style.Triggers>
<!-- 마지막 컬럼 뒤의 채움 머리글 — Column 이 null 이다.
회색 면을 그대로 두면 이름 없는 컬럼이 하나 더 있는 것처럼 보인다. -->
<DataTrigger Binding="{Binding Column, RelativeSource={RelativeSource Self}}" Value="{x:Null}">
<Setter Property="Background" Value="{DynamicResource B.Surface}"/>
<Setter Property="BorderThickness" Value="0,0,0,1"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Setter.Value>
</Setter>
<Setter Property="CellStyle">
<Setter.Value>
<Style TargetType="DataGridCell">
<Setter Property="Padding" Value="12,0"/>
<Setter Property="Padding" Value="10,0"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Foreground" Value="{DynamicResource B.Ink}"/>
<Setter Property="Background" Value="Transparent"/>
<!-- 기본 셀 템플릿은 Padding 을 쓰지 않는다 — 그대로 두면 본문 글자가
머리글보다 왼쪽으로 밀려 컬럼이 어긋나 보인다 -->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="DataGridCell">
<Border Background="{TemplateBinding Background}"
Padding="{TemplateBinding Padding}">
<ContentPresenter VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
</Style>
<!-- 변수 목록 행 — 선택은 '옅은 파랑'이어야 한다.
공유 B.Sel 은 다크에서 #3C3C3C 중성 회색이라 선택이 호버처럼 읽힌다.
토큰은 다른 화면과 공유하므로 건드리지 않고 이 목록에만 파란 계열을 준다. -->
<Style x:Key="VarRowItem" TargetType="ListBoxItem" BasedOn="{StaticResource {x:Type ListBoxItem}}">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="{DynamicResource B.LayerSubtree}"/>
<Setter Property="Foreground" Value="{DynamicResource B.Ink}"/>
</Trigger>
</Style.Triggers>
</Style>
<!-- 갈래 칩 -->
<Style x:Key="BucketChip" TargetType="ToggleButton">
<Setter Property="FontSize" Value="11.5"/>
@@ -132,12 +184,20 @@
<Border x:Name="chip" CornerRadius="12" Padding="{TemplateBinding Padding}"
Background="{DynamicResource B.Input}"
BorderBrush="{DynamicResource B.InputBorder}" BorderThickness="1">
<ContentPresenter VerticalAlignment="Center"/>
<ContentPresenter VerticalAlignment="Center">
<ContentPresenter.Resources>
<Style TargetType="TextBlock">
<Setter Property="Foreground"
Value="{Binding Foreground, RelativeSource={RelativeSource AncestorType=ButtonBase}}" />
</Style>
</ContentPresenter.Resources>
</ContentPresenter>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="chip" Property="Background" Value="{DynamicResource B.Accent}"/>
<Setter TargetName="chip" Property="BorderBrush" Value="{DynamicResource B.Accent}"/>
<!-- 흰 글자를 얹으므로 브랜드색(#0099FF, 3.0:1)이 아니라 짙은 채움을 쓴다 -->
<Setter TargetName="chip" Property="Background" Value="{DynamicResource B.AccentFill}"/>
<Setter TargetName="chip" Property="BorderBrush" Value="{DynamicResource B.AccentFill}"/>
<Setter Property="Foreground" Value="{DynamicResource B.OnAccent}"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
@@ -224,7 +284,7 @@
결과가 닫혀 있어도 상태 줄은 남아야 하므로 상자 자체는 항상 보인다. -->
<Border DockPanel.Dock="Bottom" Margin="0,10,0,0" MaxHeight="300"
CornerRadius="8" BorderThickness="1"
BorderBrush="{DynamicResource B.InputBorder}" Background="{DynamicResource B.Surface}">
BorderBrush="{DynamicResource B.Line2}" Background="{DynamicResource B.Surface}">
<DockPanel>
<!-- 상태 줄 -->
<Border DockPanel.Dock="Bottom" Padding="12,7"
@@ -264,8 +324,10 @@
<!-- 편집 영역: 색칠 레이어 + 투명 TextBox -->
<!-- 모서리를 둥글리지 않는다. 둥글리면 클리핑 때문에 줄번호 칸의 회색 면과
본문 사이 구분선까지 함께 휘어 보인다. -->
<!-- 본문 면은 B.Surface 다. B.Input 을 쓰면 다크에서 홈통(B.Chip)과 같은 #262626 이라
홈통이 통째로 사라진다(양 토큰이 같은 값이다). -->
<Border BorderThickness="1" CornerRadius="0"
BorderBrush="{DynamicResource B.InputBorder}" Background="{DynamicResource B.Input}">
BorderBrush="{DynamicResource B.Line2}" Background="{DynamicResource B.Surface}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
@@ -302,7 +364,10 @@
</ListBox>
</Border>
</Popup>
<TextBox x:Name="SqlBox"
<!-- 무테 템플릿을 쓴다. 전역 TextBox 템플릿은 BorderThickness=1·CornerRadius=8 을
하드코딩하고 포커스 시 그 테두리를 강조색으로 칠해서,
사각 편집 상자 안에 파란 둥근 상자가 하나 더 겹쳐 보였다. -->
<TextBox x:Name="SqlBox" Style="{StaticResource ChipTextBox}"
FontFamily="D2Coding, 굴림체, Consolas" FontSize="13.5"
AcceptsReturn="True" AcceptsTab="False"
TextWrapping="NoWrap" Padding="12,10"
@@ -320,7 +385,7 @@
</DockPanel>
</Border>
<GridSplitter Grid.Column="1" Style="{StaticResource ColSplitter}"/>
<GridSplitter Grid.Column="1" Style="{StaticResource ColSplitter}" Background="Transparent"/>
<!-- ══ 치환 변수 카드 ══ -->
<Border x:Name="VariablePane" Grid.Column="2" Style="{StaticResource Card}">
@@ -364,7 +429,7 @@
<!-- 안내 -->
<ListBox x:Name="VariableList" FontSize="11.5" MouseDoubleClick="OnInsertVariable"
<ListBox x:Name="VariableList" ItemContainerStyle="{StaticResource VarRowItem}" FontSize="11.5" MouseDoubleClick="OnInsertVariable"
KeyDown="OnVariableListKeyDown"
BorderThickness="0" Background="Transparent"
HorizontalContentAlignment="Stretch"