Files
SheetMe/src/SheetMe.Designer/Views/QueryEditorWindow.xaml
T
MsystechandClaude Opus 5 0e2e2fb0e6 검증 실행에 Ctrl+Enter 추가 — 단축키를 선언으로 옮겼다
Ctrl+Enter 로도 검증이 돌게 했다(F5 는 그대로 남는다).

■ PreviewKeyDown 대신 KeyBinding 으로

처음에는 PreviewKeyDown 에서 Keyboard.Modifiers 를 읽어 처리했는데, 그 경로는 검사할 수가 없다 —
Keyboard.Modifiers 는 <b>실제 키보드 상태</b>에서 나오므로 합성 키 이벤트로는 Ctrl 이 잡히지 않는다.
진단을 쓰다 이걸 발견했고(검사 2건이 계속 실패했다), 단축키를 Window.InputBindings 선언으로 옮겼다.
이제 진단이 등록된 KeyBinding 을 찾아 그 명령을 직접 실행해 배선을 확인한다.

■ 자동완성 Enter 와 뜻이 갈린다

목록이 떠 있을 때 Enter 는 후보 확정이다. Ctrl+Enter 가 그 처리에 먼저 걸리면 검증이 안 돈다.
PreviewKeyDown 의 목록 처리에서 Ctrl+Enter 만 비켜 가게 하고, 실행할 때 목록을 닫는다.

--query-popup 진단에 4건 추가(Ctrl+Enter 등록 · F5 유지 · 실행됨 · 실행하며 목록 닫힘). 현재 16/16.

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

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 16:19:41 +09:00

331 lines
22 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<Window x:Class="SheetMe.Designer.Views.QueryEditorWindow"
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="1180" Height="760" MinWidth="880" MinHeight="520"
WindowStartupLocation="CenterOwner" ShowInTaskbar="False"
Style="{StaticResource ThemedWindow}">
<Window.Resources>
<!-- 처음 펼쳐 둘 그룹 판단 — 가장 많이 쓰는 '환자'와 이 서식의 컨트롤만 펴 둔다 -->
<ctl:QueryGroupExpandedConverter x:Key="GroupExpanded"/>
<!-- 카드: 두 영역을 눈으로 갈라 준다 -->
<Style x:Key="Card" TargetType="Border">
<Setter Property="CornerRadius" Value="10"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="{DynamicResource B.InputBorder}"/>
<Setter Property="Background" Value="{DynamicResource B.Surface}"/>
</Style>
<Style x:Key="CardTitle" TargetType="TextBlock">
<Setter Property="FontSize" Value="13.5"/>
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="Foreground" Value="{DynamicResource B.Ink}"/>
</Style>
<!-- 편집기 툴바 버튼 -->
<Style x:Key="ToolBtn" TargetType="Button" BasedOn="{StaticResource Subtle}">
<Setter Property="Padding" Value="8,3"/>
<Setter Property="FontSize" Value="11.5"/>
<Setter Property="Foreground" Value="{DynamicResource B.Ink}"/>
</Style>
<!-- 갈래 칩 -->
<Style x:Key="BucketChip" TargetType="ToggleButton">
<Setter Property="FontSize" Value="11.5"/>
<Setter Property="Padding" Value="10,3"/>
<Setter Property="Margin" Value="0,0,6,0"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Foreground" Value="{DynamicResource B.Ink}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Border x:Name="chip" CornerRadius="12" Padding="{TemplateBinding Padding}"
Background="{DynamicResource B.Input}"
BorderBrush="{DynamicResource B.InputBorder}" BorderThickness="1">
<ContentPresenter VerticalAlignment="Center"/>
</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}"/>
<Setter Property="Foreground" Value="{DynamicResource B.OnAccent}"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="chip" Property="BorderBrush" Value="{DynamicResource B.Accent}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<!-- 검증 실행 단축키 — 선언으로 두면 실제 배선을 진단이 그대로 확인할 수 있다.
PreviewKeyDown 에서 손으로 Keyboard.Modifiers 를 읽으면 그 경로를 검사할 방법이 없다. -->
<Window.InputBindings>
<KeyBinding Modifiers="Control" Key="Return" Command="{Binding TrialCommand}"/>
<KeyBinding Key="F5" Command="{Binding TrialCommand}"/>
</Window.InputBindings>
<DockPanel Margin="16,14,16,14">
<!-- ── 머리말 ── -->
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="0,0,0,12">
<TextBlock x:Name="TitleTextBlock" Text="쿼리 편집" FontSize="17" FontWeight="Bold"
Foreground="{DynamicResource B.Ink}" VerticalAlignment="Center"/>
<Border x:Name="TargetChip" CornerRadius="6" Padding="8,2" Margin="10,0,0,0"
VerticalAlignment="Center"
Background="{DynamicResource B.Input}" BorderBrush="{DynamicResource B.InputBorder}"
BorderThickness="1">
<TextBlock x:Name="TargetChipText" FontSize="11.5" FontFamily="D2Coding, 굴림체, Consolas"
Foreground="{DynamicResource B.AccentText}"/>
</Border>
</StackPanel>
<!-- ── 아래 버튼 줄 ── -->
<DockPanel DockPanel.Dock="Bottom" Margin="0,12,0,0">
<TextBlock x:Name="ProblemText" VerticalAlignment="Center" Margin="0,0,12,0"
Foreground="{DynamicResource B.Danger}" FontSize="11.5"
TextTrimming="CharacterEllipsis"/>
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal">
<Button x:Name="TrialButton" Padding="14,6" Click="OnTrial"
ToolTip="DB 에서 실제로 돌려 봅니다 (Ctrl+Enter 또는 F5). 조회만 실행되며 표본 몇 행만 가져옵니다.">
<StackPanel Orientation="Horizontal">
<TextBlock Text="▶" FontSize="10" VerticalAlignment="Center" Margin="0,0,6,0"/>
<TextBlock Text="검증 실행" VerticalAlignment="Center"/>
</StackPanel>
</Button>
<Button Content="확인" Padding="24,6" Margin="8,0,0,0"
Style="{StaticResource Primary}" Click="OnConfirm" IsDefault="True"/>
<Button Content="취소" Padding="20,6" Margin="8,0,0,0" IsCancel="True"/>
</StackPanel>
</DockPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition x:Name="SplitterColumn" Width="12"/>
<ColumnDefinition x:Name="VariableColumn" Width="430"/>
</Grid.ColumnDefinitions>
<!-- ══ SQL 편집기 카드 ══ -->
<Border Grid.Column="0" Style="{StaticResource Card}">
<DockPanel Margin="14,12,14,12">
<TextBlock DockPanel.Dock="Top" Text="SQL 편집기" Style="{StaticResource CardTitle}"
Margin="0,0,0,10"/>
<!-- 툴바 -->
<DockPanel DockPanel.Dock="Top" Margin="0,0,0,8">
<Button DockPanel.Dock="Right" Style="{StaticResource ToolBtn}" Click="OnClearAll"
Foreground="{DynamicResource B.Danger}" Content="전체 지우기"
ToolTip="편집 내용을 모두 지웁니다"/>
<StackPanel Orientation="Horizontal">
<Button Style="{StaticResource ToolBtn}" Content="포맷" Click="OnFormat"
ToolTip="예약어를 대문자로 바꾸고 절마다 줄을 나눕니다 (Ctrl+Shift+F). 문자열·치환 변수는 건드리지 않습니다."/>
<Button Style="{StaticResource ToolBtn}" Content="주석" Click="OnToggleComment"
ToolTip="선택한 줄에 -- 를 붙이거나 뗍니다 (Ctrl+/)"/>
<Button Style="{StaticResource ToolBtn}" Content="자동완성" Click="OnInvokeCompletion"
ToolTip="커서 위치에서 후보를 띄웁니다 (Ctrl+Space)"/>
</StackPanel>
</DockPanel>
<!-- 상태 줄 -->
<Border DockPanel.Dock="Bottom" Margin="0,8,0,0" Padding="10,5" CornerRadius="6"
Background="{DynamicResource B.Input}">
<DockPanel>
<TextBlock DockPanel.Dock="Right" x:Name="CaretText" FontSize="11"
Foreground="{DynamicResource B.Muted}"/>
<StackPanel Orientation="Horizontal">
<Ellipse x:Name="ConnDot" Width="7" Height="7" VerticalAlignment="Center"
Margin="0,0,6,0" Fill="{DynamicResource B.Muted}"/>
<TextBlock x:Name="ConnText" FontSize="11" VerticalAlignment="Center"
Foreground="{DynamicResource B.Muted}"/>
<TextBlock Text="│" FontSize="11" Margin="10,0" VerticalAlignment="Center"
Foreground="{DynamicResource B.InputBorder}"/>
<TextBlock x:Name="LengthText" FontSize="11" VerticalAlignment="Center"
Foreground="{DynamicResource B.Muted}"/>
</StackPanel>
</DockPanel>
</Border>
<!-- 검증 결과 -->
<Border x:Name="TrialPane" DockPanel.Dock="Bottom" Visibility="Collapsed" Margin="0,8,0,0"
MaxHeight="260" CornerRadius="8" BorderThickness="1"
BorderBrush="{DynamicResource B.InputBorder}" Background="{DynamicResource B.Input}">
<DockPanel Margin="10,8">
<DockPanel DockPanel.Dock="Top" Margin="0,0,0,6">
<Button DockPanel.Dock="Right" Content="닫기" Padding="8,1"
Style="{StaticResource ToolBtn}" Click="OnCloseTrial"/>
<TextBlock x:Name="TrialSummary" FontSize="11.5" TextWrapping="Wrap"/>
</DockPanel>
<DataGrid x:Name="TrialGrid" AutoGenerateColumns="True" IsReadOnly="True"
HeadersVisibility="Column" FontSize="11.5" MaxHeight="200"
GridLinesVisibility="Horizontal" Background="Transparent"
AutomationProperties.Name="검증 결과 표본"/>
</DockPanel>
</Border>
<!-- 편집 영역: 색칠 레이어 + 투명 TextBox -->
<Border BorderThickness="1" CornerRadius="8" ClipToBounds="True"
BorderBrush="{DynamicResource B.InputBorder}" Background="{DynamicResource B.Input}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" Background="{DynamicResource B.Surface}" Padding="8,6">
<TextBlock x:Name="LineNumbers" FontFamily="D2Coding, 굴림체, Consolas" FontSize="13"
Foreground="{DynamicResource B.Muted}" TextAlignment="Right" MinWidth="24"/>
</Border>
<Grid Grid.Column="1">
<ctl:SqlHighlightLayer x:Name="Highlight" IsHitTestVisible="False"/>
<!-- StaysOpen=False 여야 바깥을 클릭했을 때 닫힌다 -->
<Popup x:Name="CompletionPopup" Placement="Relative" AllowsTransparency="True"
StaysOpen="False" PopupAnimation="None" Focusable="False">
<Border Background="{DynamicResource B.Surface}" BorderThickness="1"
BorderBrush="{DynamicResource B.InputBorder}" CornerRadius="8" Padding="3">
<ListBox x:Name="CompletionList" MaxHeight="260" MinWidth="280" FontSize="12"
BorderThickness="0" Background="Transparent"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
MouseDoubleClick="OnCompletionPicked">
<ListBox.ItemTemplate>
<DataTemplate>
<DockPanel MinWidth="260">
<TextBlock DockPanel.Dock="Right" Text="{Binding KindLabel}" FontSize="10"
Foreground="{DynamicResource B.Muted}" Margin="10,0,0,0"
VerticalAlignment="Center"/>
<TextBlock Text="{Binding Text}" FontFamily="D2Coding, 굴림체, Consolas"
TextTrimming="CharacterEllipsis"/>
</DockPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Border>
</Popup>
<TextBox x:Name="SqlBox"
FontFamily="D2Coding, 굴림체, Consolas" FontSize="13"
AcceptsReturn="True" AcceptsTab="False"
TextWrapping="NoWrap" Padding="8,6"
Background="Transparent" BorderThickness="0"
Foreground="Transparent"
CaretBrush="{DynamicResource B.Ink}"
SelectionBrush="{DynamicResource B.Accent}" SelectionOpacity="0.35"
HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
TextChanged="OnSqlChanged" PreviewKeyDown="OnSqlKeyDown"
SelectionChanged="OnSqlSelectionChanged" LostFocus="OnSqlLostFocus"
AutomationProperties.Name="SQL 편집"/>
</Grid>
</Grid>
</Border>
</DockPanel>
</Border>
<GridSplitter Grid.Column="1" Style="{StaticResource ColSplitter}"/>
<!-- ══ 치환 변수 카드 ══ -->
<Border x:Name="VariablePane" Grid.Column="2" Style="{StaticResource Card}">
<DockPanel Margin="14,12,14,12">
<TextBlock DockPanel.Dock="Top" Text="치환 변수" Style="{StaticResource CardTitle}"
Margin="0,0,0,10"/>
<!-- 검색: 돋보기를 겹쳐 둔다 — 빈 상자만 있으면 무엇을 하는 칸인지 안 읽힌다 -->
<Grid DockPanel.Dock="Top" Margin="0,0,0,8">
<TextBox x:Name="VariableSearch" Padding="28,4,8,4"
VerticalContentAlignment="Center" TextChanged="OnVariableSearchChanged"
AutomationProperties.Name="치환 변수 검색"
ToolTip="변수명·설명·초성으로 찾습니다 (예: ㅊㅌㅂㅎ → 차트번호)"/>
<TextBlock Text="🔍" FontSize="11" Margin="9,0,0,0" IsHitTestVisible="False"
HorizontalAlignment="Left" VerticalAlignment="Center"
Foreground="{DynamicResource B.Muted}"/>
<TextBlock x:Name="SearchPlaceholder" Text="변수명 또는 설명 검색" FontSize="11.5"
Margin="30,0,0,0" IsHitTestVisible="False"
HorizontalAlignment="Left" VerticalAlignment="Center"
Foreground="{DynamicResource B.Muted}"/>
</Grid>
<!-- 갈래 칩 -->
<ItemsControl x:Name="BucketChips" DockPanel.Dock="Top" Margin="0,0,0,8">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate><WrapPanel/></ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ToggleButton Style="{StaticResource BucketChip}"
IsChecked="{Binding IsActive, Mode=OneWay}"
Tag="{Binding Name}" Click="OnBucketClick">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding Count}" Margin="6,0,0,0" Opacity="0.75"/>
</StackPanel>
</ToggleButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!-- 안내 -->
<ListBox x:Name="VariableList" FontSize="11.5" MouseDoubleClick="OnInsertVariable"
KeyDown="OnVariableListKeyDown"
BorderThickness="0" Background="Transparent"
HorizontalContentAlignment="Stretch"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
AutomationProperties.Name="치환 변수 목록">
<ListBox.ItemTemplate>
<DataTemplate>
<DockPanel Margin="0,3" ToolTip="{Binding Token}">
<!-- ⊕ 로 바로 삽입 — 더블클릭을 모르는 사람도 넣을 수 있어야 한다 -->
<Button DockPanel.Dock="Right" Width="22" Height="22" Padding="0"
Margin="8,0,0,0" Style="{StaticResource ToolBtn}"
Content="" FontSize="13"
Foreground="{DynamicResource B.AccentText}"
Command="{Binding DataContext.InsertCommand,
RelativeSource={RelativeSource AncestorType=ListBox}}"
CommandParameter="{Binding}"
ToolTip="커서 위치에 삽입"/>
<TextBlock DockPanel.Dock="Right" Text="{Binding ShortForm}"
Foreground="{DynamicResource B.Muted}" FontSize="10.5"
FontFamily="D2Coding, 굴림체, Consolas"
VerticalAlignment="Center" Margin="8,0,0,0"/>
<TextBlock Text="{Binding Description}" FontFamily="Segoe UI, 맑은 고딕" FontSize="12"
TextTrimming="CharacterEllipsis" VerticalAlignment="Center"/>
</DockPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="GroupItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GroupItem">
<Expander IsExpanded="{Binding Name,
Converter={StaticResource GroupExpanded}, Mode=OneTime}"
Margin="0,4,0,0">
<Expander.Header>
<DockPanel MinWidth="330">
<Border DockPanel.Dock="Right" CornerRadius="9" Padding="6,1"
VerticalAlignment="Center" Margin="6,0,0,0"
Background="{DynamicResource B.Input}">
<TextBlock Text="{Binding ItemCount}" FontSize="10"
Foreground="{DynamicResource B.Muted}"/>
</Border>
<TextBlock Text="{Binding Name}" FontWeight="SemiBold"
FontFamily="Segoe UI, 맑은 고딕" FontSize="12"/>
</DockPanel>
</Expander.Header>
<ItemsPresenter Margin="8,2,0,6"/>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListBox.GroupStyle>
</ListBox>
</DockPanel>
</Border>
</Grid>
</DockPanel>
</Window>