Repository navigation
Expand file tree
/
Copy pathMainWindow.xaml
More file actions
1451 lines (1393 loc) · 116 KB
/
Copy pathMainWindow.xaml
File metadata and controls
1451 lines (1393 loc) · 116 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<Window x:Class="KillerShell.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:KillerShell"
xmlns:models="clr-namespace:KillerShell.Models"
xmlns:shell="clr-namespace:System.Windows.Shell;assembly=PresentationFramework"
Title="KillerShell"
Icon="Resources/ksh-icon.ico"
Height="710" Width="1140" MinHeight="500" MinWidth="600"
WindowStyle="None"
AllowDrop="True" DragOver="Window_DragOver" Drop="Window_Drop"
Background="{DynamicResource BackgroundBrush}"
TextOptions.TextFormattingMode="Display"
TextOptions.TextRenderingMode="ClearType"
UseLayoutRounding="True"
SnapsToDevicePixels="True"
WindowStartupLocation="CenterScreen"
PreviewKeyDown="Window_PreviewKeyDown"
PreviewKeyUp="Window_PreviewKeyUp">
<shell:WindowChrome.WindowChrome>
<shell:WindowChrome CaptionHeight="36" ResizeBorderThickness="8" CornerRadius="0"
GlassFrameThickness="0" UseAeroCaptionButtons="False"/>
</shell:WindowChrome.WindowChrome>
<Grid x:Name="RootGrid" Opacity="0">
<Grid.RowDefinitions>
<RowDefinition Height="36"/> <!-- 0: title bar -->
<RowDefinition Height="*"/> <!-- 1: main content -->
<RowDefinition Height="24"/> <!-- 2: footer -->
</Grid.RowDefinitions>
<!-- ══════════ ROW 0 - TITLE BAR ══════════ -->
<Grid Grid.Row="0" Background="{DynamicResource BackgroundBrush}">
<Border IsHitTestVisible="False">
<Border.Background>
<ImageBrush x:Name="TitleGrainBrush" TileMode="Tile" ViewportUnits="Absolute" Viewport="0,0,256,256" Stretch="None"/>
</Border.Background>
<Border.OpacityMask><SolidColorBrush Color="White" Opacity="{DynamicResource GrainOpacity}"/></Border.OpacityMask>
</Border>
<!-- App icon + wordmark, exactly like KillerScan: icon in the corner, then the
typewriter wordmark with a blurred dark copy behind the crisp text
(faded by IconShadowOpacity; 0 in light mode = no shadow). -->
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="14,0">
<Image x:Name="TitleIcon" Width="27" Height="27" VerticalAlignment="Center" Margin="0,0,7,0"
RenderOptions.BitmapScalingMode="HighQuality" IsHitTestVisible="False"/>
<!-- The wordmark drives the app-wide accessibility size: roll the wheel over it
(AppScale.cs). Hit-test-visible in the chrome so the wheel reaches it, which
also opts it out of the native caption - so window drag and double-click
maximize come back via the LogoBar handlers. Family standard; the website
link lives on the About card's wordmark. -->
<Grid x:Name="LogoBar" VerticalAlignment="Center" shell:WindowChrome.IsHitTestVisibleInChrome="True"
Cursor="Hand" ToolTip="{DynamicResource Str_TT_AppSize}" Background="Transparent"
MouseWheel="LogoBar_MouseWheel" MouseLeftButtonDown="LogoBar_MouseLeftButtonDown">
<!-- Main-titlebar wordmark is 18/23.4 (KillerScan standard); dialogs use 15/19.5. -->
<TextBlock FontFamily="{DynamicResource WordmarkFont}" FontSize="18" Margin="1,2,0,0" Opacity="{DynamicResource IconShadowOpacity}">
<TextBlock.Effect><BlurEffect Radius="3"/></TextBlock.Effect>
<Run Text="Killer" Foreground="#D8000000"/><Run Text="Shell" FontSize="23.4" FontWeight="Bold" Foreground="#D8000000"/>
</TextBlock>
<TextBlock FontFamily="{DynamicResource WordmarkFont}" FontSize="18">
<Run Text="Killer" Foreground="{DynamicResource TextBrush}"/><Run Text="Shell" FontSize="23.4" FontWeight="Bold" Foreground="{DynamicResource PrimaryBrush}"/>
</TextBlock>
</Grid>
<!-- Elevated only (Elevation.cs). OUTSIDE LogoBar on purpose: the wordmark is
the scroll-to-zoom target, and widening its hit area with a caption would
mean rolling the wheel next to the logo silently resized the app. -->
<TextBlock x:Name="ElevatedTag" Visibility="Collapsed" VerticalAlignment="Center"
Margin="9,3,0,0" FontFamily="Consolas" FontSize="11.5"
Text="{DynamicResource Str_Title_Elevated}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" shell:WindowChrome.IsHitTestVisibleInChrome="True">
<!-- Language and theme moved to the icon rail (column 0), the way KillerNotes
has them. The title bar keeps only the window buttons. -->
<Button x:Name="MinimizeBtn" Click="MinimizeBtn_Click" Style="{StaticResource ChromeButton}" Content=""/>
<Button x:Name="MaximizeBtn" Click="MaximizeBtn_Click" Style="{StaticResource ChromeButton}" Content=""/>
<Button x:Name="CloseBtn" Click="CloseBtn_Click" Style="{StaticResource ChromeCloseButton}" Content=""/>
</StackPanel>
<!-- Theme + accent flyout -->
<!-- StaysOpen so the flyout doesn't take mouse capture: the results list keeps
scrolling under it. Outside clicks close it from code. -->
<!-- Placement follows the button: it used to hang below a title-bar glyph at the top
right, and now opens upward off a rail button pinned to the bottom left, so the
flyout grows over the pane instead of off the bottom edge. -->
<Popup x:Name="ThemePopup" PlacementTarget="{Binding ElementName=ThemeButton}" Placement="Top"
StaysOpen="True" AllowsTransparency="True" HorizontalOffset="-6" VerticalOffset="-2">
<Border Background="{DynamicResource MenuBackgroundBrush}" BorderBrush="{DynamicResource MenuBorderBrush}"
BorderThickness="1" CornerRadius="6" Padding="12,10" Margin="14">
<Border.Effect><DropShadowEffect Color="Black" BlurRadius="22" ShadowDepth="4" Direction="270" Opacity="0.55"/></Border.Effect>
<StackPanel>
<TextBlock Text="{DynamicResource Str_Lbl_Theme}" Foreground="{DynamicResource DimTextBrush}" FontFamily="Consolas" FontSize="10" Margin="0,0,0,4"/>
<StackPanel x:Name="ThemeSwatches" Orientation="Horizontal">
<Button Style="{StaticResource ThemeSwatch}" Tag="Dark" Background="#3a3a3a" Click="ThemeSwatch_Click" ToolTip="Dark"/>
<Button Style="{StaticResource ThemeSwatch}" Tag="Light" Background="#e8e8e8" Click="ThemeSwatch_Click" ToolTip="Light"/>
<Button Style="{StaticResource ThemeSwatch}" Tag="Black" Background="#000000" Click="ThemeSwatch_Click" ToolTip="Black"/>
<Button Style="{StaticResource ThemeSwatch}" Tag="Blood" Background="#4a1f20" Click="ThemeSwatch_Click" ToolTip="Blood"/>
<Button Style="{StaticResource ThemeSwatch}" Tag="Greed" Background="#0a5234" Click="ThemeSwatch_Click" ToolTip="Greed"/>
<Button Style="{StaticResource ThemeSwatch}" Tag="Cyanotic" Background="#0a4a6e" Click="ThemeSwatch_Click" ToolTip="Cyanotic"/>
</StackPanel>
<TextBlock x:Name="AccentLabel" Text="{DynamicResource Str_Lbl_Accent}" Foreground="{DynamicResource DimTextBrush}" FontFamily="Consolas" FontSize="10" Margin="0,8,0,4"/>
<StackPanel x:Name="AccentSwatches" Orientation="Horizontal">
<Button Style="{StaticResource ThemeSwatch}" Tag="Red" Background="#DD504B" Click="AccentSwatch_Click" ToolTip="Red"/>
<Button Style="{StaticResource ThemeSwatch}" Tag="Orange" Background="#E8962C" Click="AccentSwatch_Click" ToolTip="Orange"/>
<Button Style="{StaticResource ThemeSwatch}" Tag="Green" Background="#1ea54c" Click="AccentSwatch_Click" ToolTip="Green"/>
<Button Style="{StaticResource ThemeSwatch}" Tag="Teal" Background="#1FB8A8" Click="AccentSwatch_Click" ToolTip="Teal"/>
<Button Style="{StaticResource ThemeSwatch}" Tag="Blue" Background="#50AEE8" Click="AccentSwatch_Click" ToolTip="Blue"/>
<Button Style="{StaticResource ThemeSwatch}" Tag="Purple" Background="#B982E3" Click="AccentSwatch_Click" ToolTip="Purple"/>
</StackPanel>
<!-- Fonts sit behind a row rather than in the flyout itself: a font
list is hundreds of entries and belongs in a dialog, while this
flyout is swatches you pick at a glance. Room below for more of
these as other pickers break out. -->
<Border Height="1" Margin="0,12,0,6" Background="{DynamicResource MenuBorderBrush}"/>
<Button x:Name="FontsRowBtn" Click="FontsRow_Click"
Style="{StaticResource MenuRowButton}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="" FontFamily="Segoe MDL2 Assets" FontSize="12"
Margin="0,0,8,0" VerticalAlignment="Center"/>
<TextBlock Text="{DynamicResource Str_Fonts_Row}" FontFamily="Consolas"
FontSize="11.5" VerticalAlignment="Center"/>
</StackPanel>
</Button>
<Button x:Name="AssocRowBtn" Click="AssocRow_Click"
Style="{StaticResource MenuRowButton}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="" FontFamily="Segoe MDL2 Assets" FontSize="12"
Margin="0,0,8,0" VerticalAlignment="Center"/>
<TextBlock Text="{DynamicResource Str_Assoc_Row}" FontFamily="Consolas"
FontSize="11.5" VerticalAlignment="Center"/>
</StackPanel>
</Button>
</StackPanel>
</Border>
</Popup>
</Grid>
<!-- ══════════ ROW 1 - 2-PANEL GRID ══════════ -->
<!-- ScaleHost: the app-wide accessibility zoom (AppScale.cs) puts a LayoutTransform
here, not on the root grid, so the title bar and footer keep a fixed size and the
wordmark you scroll over never moves. LayoutTransform reflows and re-rasterizes
text; RenderTransform would bitmap-stretch it. -->
<Grid x:Name="ScaleHost" Grid.Row="1" Panel.ZIndex="1">
<Grid.ColumnDefinitions>
<!-- Folder tree, then the permanent icon rail on the tree's inner lip, then the
results, then the search panel out on the right edge. Both side panels
collapse to zero width and are driven from the one rail; the rail itself
never moves, which is the whole reason it sits between the tree and the
content rather than on the window's outer edge. -->
<ColumnDefinition x:Name="TreeCol" Width="240" MinWidth="0" MaxWidth="420"/>
<ColumnDefinition x:Name="RailCol" Width="24"/>
<ColumnDefinition x:Name="TreeGapCol" Width="6"/>
<ColumnDefinition Width="*" MinWidth="220"/>
<ColumnDefinition x:Name="SearchGapCol" Width="0"/>
<ColumnDefinition x:Name="SearchCol" Width="0" MinWidth="0" MaxWidth="380"/>
</Grid.ColumnDefinitions>
<!-- Continuous grain behind the whole row: both sidebars, the gaps, and the margins around the results pane -->
<Border Grid.ColumnSpan="6" IsHitTestVisible="False" Background="{DynamicResource GrainTileBrush}" Opacity="{DynamicResource GrainOpacity}"/>
<!-- COLUMN 0 - FOLDER TREE. Open by default: this is a file manager now, and the
tree is how you get around in one. Collapses from the rail. -->
<Grid Grid.Column="0" x:Name="TreePanel">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<!-- Auto, so the favorites slide-up's animated Height drives the split and
the tree above simply gives up the room (the Killculator arrangement). -->
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Grid.Row="0" Background="Transparent" Padding="4,6,2,6">
<TreeView x:Name="FolderTree" Style="{StaticResource FolderTreeView}"
TreeViewItem.Expanded="FolderTree_Expanded"
TreeViewItem.Collapsed="FolderTree_Collapsed"
ContextMenuOpening="FolderTree_ContextMenuOpening"
SelectedItemChanged="FolderTree_SelectedItemChanged">
<!-- Folder-only menu, so it reuses the results menu's strings where they
already say the right thing (FolderTree.cs). On the TreeView itself,
not the item template - see the results list for why. -->
<TreeView.ContextMenu>
<ContextMenu>
<MenuItem Header="{DynamicResource Str_Tab_New}" Click="TreeOpenNewTab_Click">
<MenuItem.Icon><TextBlock Style="{StaticResource MenuGlyph}" Text=""/></MenuItem.Icon>
</MenuItem>
<MenuItem Header="{DynamicResource Str_Menu_ShowExplorer}" Click="TreeShowInExplorer_Click">
<MenuItem.Icon><TextBlock Style="{StaticResource MenuGlyph}" Text=""/></MenuItem.Icon>
</MenuItem>
<MenuItem Header="{DynamicResource Str_Menu_SearchHere}" Click="TreeSearchHere_Click">
<MenuItem.Icon><TextBlock Style="{StaticResource MenuGlyph}" Text=""/></MenuItem.Icon>
</MenuItem>
<Separator/>
<MenuItem Tag="fav" Header="{DynamicResource Str_Menu_AddFavorite}" Click="TreeFavorite_Click">
<MenuItem.Icon><TextBlock Style="{StaticResource MenuGlyph}" Text=""/></MenuItem.Icon>
</MenuItem>
<MenuItem Header="{DynamicResource Str_Menu_CopyPath}" Click="TreeCopyPath_Click">
<MenuItem.Icon><TextBlock Style="{StaticResource MenuGlyph}" Text=""/></MenuItem.Icon>
</MenuItem>
<Separator/>
<MenuItem Header="{DynamicResource Str_Menu_Properties}" Click="TreeProperties_Click">
<MenuItem.Icon><TextBlock Style="{StaticResource MenuGlyph}" Text=""/></MenuItem.Icon>
</MenuItem>
<MenuItem Header="{DynamicResource Str_Menu_ShellMenu}" Click="TreeShellMenu_Click">
<MenuItem.Icon><TextBlock Style="{StaticResource MenuGlyph}" Text=""/></MenuItem.Icon>
</MenuItem>
</ContextMenu>
</TreeView.ContextMenu>
<TreeView.ItemContainerStyle>
<!-- Inherits the themed template from Controls.xaml and adds the
two-way state bindings, so RevealInTree can drive the tree from
code when navigation came from somewhere else. -->
<Style TargetType="TreeViewItem" BasedOn="{StaticResource FolderTreeItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:FolderNode}"
ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal" Margin="0,1">
<Image Source="{Binding Icon}" Width="16" Height="16"
VerticalAlignment="Center" Margin="0,0,6,0"
RenderOptions.BitmapScalingMode="HighQuality"/>
<TextBlock Text="{Binding Name}" VerticalAlignment="Center"
FontFamily="{DynamicResource MonoFont}" FontSize="11"
Foreground="{DynamicResource TextBrush}"
TextTrimming="CharacterEllipsis"
ToolTip="{Binding Path}"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Border>
<!-- Edge fades, KillerNotes' NotesFade pattern on both ends: nodes dissolve into
the chrome as they scroll off rather than being clipped mid-row. The overlay
IS the chrome - BackgroundBrush plus the same grain - under an opacity mask,
so it fades to an exact match for the sidebar (never darker, grain intact)
instead of a flat black band. Inset 12px on the right to clear the
scrollbar, and hit-test-transparent so the tree still takes clicks under it.
Each one is only there while there is something PAST it - none at the very
top, none at the very bottom, the way the sites do it. A fade over an edge
that is already flush just looks like that row has been smudged. Both ramp
in over their own height as a row slides under them
(TreePanel.SyncTreeEdgeFades), so they arrive exactly as fast as they become
true and neither pops. -->
<Border x:Name="TreeFadeTop" Grid.Row="0" Height="18" Opacity="0"
VerticalAlignment="Top" Margin="0,0,12,0" IsHitTestVisible="False">
<Border.OpacityMask>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#FF000000" Offset="0"/>
<GradientStop Color="#00000000" Offset="1"/>
</LinearGradientBrush>
</Border.OpacityMask>
<Grid>
<Border Background="{DynamicResource BackgroundBrush}"/>
<Border Background="{DynamicResource GrainTileBrush}" Opacity="{DynamicResource GrainOpacity}"/>
</Grid>
</Border>
<!-- Bottom margin is driven from code (TreePanel.cs): when the tree grows a
horizontal scrollbar the content's bottom edge moves UP by its height, and a
fade pinned to the row would sit over the scrollbar instead of over the last
row it is meant to be fading out. -->
<Border x:Name="TreeFadeBottom" Grid.Row="0" Height="22" Opacity="0"
VerticalAlignment="Bottom" Margin="0,0,12,0" IsHitTestVisible="False">
<Border.OpacityMask>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#00000000" Offset="0"/>
<GradientStop Color="#FF000000" Offset="1"/>
</LinearGradientBrush>
</Border.OpacityMask>
<Grid>
<Border Background="{DynamicResource BackgroundBrush}"/>
<Border Background="{DynamicResource GrainTileBrush}" Opacity="{DynamicResource GrainOpacity}"/>
</Grid>
</Border>
<!-- FAVORITES SLIDE-UP (Bookmarks.cs). Height animates 0 -> open so it rises
out of the sidebar's bottom edge, the way KillerNotes' Killculator does.
No header and no close button by design: the rail star is the handle and
the rows are the whole content. A hairline on top is the only separator,
since a full card would read as a second panel rather than a drawer. -->
<Border x:Name="BookmarksPanel" Grid.Row="1" Height="0" Visibility="Collapsed"
ClipToBounds="True" AllowDrop="True"
BorderBrush="{DynamicResource TreeLineBrush}" BorderThickness="0,1,0,0"
DragOver="BookmarksPanel_DragOver" Drop="BookmarksPanel_Drop">
<Grid>
<Border Background="{DynamicResource BackgroundBrush}"/>
<Border Background="{DynamicResource GrainTileBrush}" Opacity="{DynamicResource GrainOpacity}"/>
<ListBox x:Name="BookmarksList" Background="Transparent" BorderThickness="0"
Padding="4,4,2,4" Focusable="False" FocusVisualStyle="{x:Null}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Padding" Value="0"/>
<Setter Property="Margin" Value="0"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="Template">
<Setter.Value><ControlTemplate TargetType="ListBoxItem"><ContentPresenter/></ControlTemplate></Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type local:Bookmark}">
<Border Padding="4,3" CornerRadius="3" Cursor="Hand"
ToolTip="{Binding Path}"
MouseLeftButtonUp="Bookmark_Click">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="Transparent"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{DynamicResource RowHoverBrush}"/>
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
<Border.ContextMenu>
<ContextMenu>
<MenuItem Header="{DynamicResource Str_Menu_RemoveFavorite}"
Click="BookmarkRemove_Click">
<MenuItem.Icon><TextBlock Style="{StaticResource MenuGlyph}" Text=""/></MenuItem.Icon>
</MenuItem>
</ContextMenu>
</Border.ContextMenu>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Icon}" Width="16" Height="16"
VerticalAlignment="Center" Margin="0,0,6,0"
RenderOptions.BitmapScalingMode="HighQuality"/>
<TextBlock Text="{Binding Name}" VerticalAlignment="Center"
FontFamily="{DynamicResource MonoFont}" FontSize="11"
Foreground="{DynamicResource TextBrush}"
TextTrimming="CharacterEllipsis"/>
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<!-- Divider grip. Same Thumb approach as the tree's width handle, turned
through 90 degrees: the drawer's Height is animated by the open and
close, so a GridSplitter would be fighting that same property.
The Border's own 1px top edge is the visible divider; this is the
5px band you can actually grab. -->
<Thumb x:Name="BookmarksGrip" Height="5" VerticalAlignment="Top"
Panel.ZIndex="60" Cursor="SizeNS"
DragDelta="BookmarksGrip_DragDelta"
DragCompleted="BookmarksGrip_DragCompleted">
<Thumb.Template>
<ControlTemplate TargetType="Thumb">
<!-- Transparent, not empty: a Border with no Background is
not hit-testable and the grip would be unusable. -->
<Border Background="Transparent">
<Rectangle x:Name="hint" Height="1" VerticalAlignment="Top"
Fill="{DynamicResource TreeLineBrush}" Opacity="0"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="hint" Property="Opacity" Value="1"/>
</Trigger>
<Trigger Property="IsDragging" Value="True">
<Setter TargetName="hint" Property="Opacity" Value="1"/>
<Setter TargetName="hint" Property="Fill" Value="{DynamicResource PrimaryBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Thumb.Template>
</Thumb>
</Grid>
</Border>
<!-- RESIZE GRIP. A Thumb rather than a GridSplitter: a splitter resizes the two
columns either side of it, and this column's neighbor is the 24px rail,
which must never move. Driving TreeCol's width by hand lets the star column
absorb the change instead, and keeps the drag from fighting the collapse
animation, which sets that same Width property (TreePanel.cs).
Spans both rows so the whole edge is grabbable, favorites included, and
sits last so it is above the edge fades. -->
<Thumb x:Name="TreeResizeGrip" Grid.RowSpan="2" Panel.ZIndex="50"
HorizontalAlignment="Right" Width="5" Cursor="SizeWE"
DragDelta="TreeResizeGrip_DragDelta" DragCompleted="TreeResizeGrip_DragCompleted">
<Thumb.Template>
<ControlTemplate TargetType="Thumb">
<!-- Transparent, not empty: a Border with no Background is not
hit-testable, so the grip would be invisible AND unusable. -->
<Border x:Name="grip" Background="Transparent">
<Rectangle x:Name="hint" Width="1" HorizontalAlignment="Right"
Fill="{DynamicResource TreeLineBrush}" Opacity="0"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="hint" Property="Opacity" Value="1"/>
</Trigger>
<Trigger Property="IsDragging" Value="True">
<Setter TargetName="hint" Property="Opacity" Value="1"/>
<Setter TargetName="hint" Property="Fill" Value="{DynamicResource PrimaryBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Thumb.Template>
</Thumb>
</Grid>
<!-- COLUMN 1 - ICON RAIL, on the tree's inner lip. That placement is the
KillerNotes / KillerPDF pattern and it is the point of it: the icons sit right
next to the results whether the tree is open or shut and never move when it
collapses. Putting the rail on the window's outer edge instead makes them jump.
Layout is the family standard: ONE chevron pinned at the top that collapses the
sidebars, and a bottom stack of feature toggles. The 2px left margin on
centered elements is a 1px shift toward the pane, so the icons read as
centered on the strip. -->
<Grid Grid.Column="1">
<!-- Collapses THIS sidebar - the tree - and nothing else, exactly like
KillerNotes. The tree is the sidebar's default content, so it needs no
separate rail icon; the search panel on the far right is not this
chevron's business and has its own toggle below. -->
<Button x:Name="SidebarToggleBtn" Style="{StaticResource RailButton}"
Content="" FontSize="12"
VerticalAlignment="Top" HorizontalAlignment="Center" Margin="2,10,0,0"
ToolTip="{DynamicResource Str_TT_CollapseSidebar}" Click="TreePanel_Click"/>
<StackPanel x:Name="RailBottom" VerticalAlignment="Bottom" HorizontalAlignment="Center" Margin="2,0,0,6">
<!-- E721 (magnifier): the search panel, out on the right edge. Glyph never
changes - RailButton draws it in TextBrush and switches to the accent
on Tag="on", which is the open/shut tell. -->
<Button x:Name="SearchPanelBtn" Style="{StaticResource RailButton}" Content=""
FontSize="12" Margin="0,0,0,10" Click="SearchPanel_Click"
ToolTip="{DynamicResource Str_TT_SearchPanel}"/>
<!-- E734 (star): the saved-locations slide-up under the tree
(Bookmarks.cs). Accents while open, like the search toggle. -->
<Button x:Name="BookmarksBtn" Style="{StaticResource RailButton}" Content=""
FontSize="12" Margin="0,0,0,10" Click="BookmarksBtn_Click"
ToolTip="{DynamicResource Str_TT_Bookmarks}"/>
<!-- E8A1 (list): results density. Click cycles Comfortable -> Compact ->
Minimal, the wheel steps through them - the same gesture and the same
glyph KillerNotes uses for its sidebar density (Density.cs). -->
<Button x:Name="DensityBtn" Style="{StaticResource RailButton}" Content=""
FontSize="12" Margin="0,0,0,10" Click="Density_Click"
MouseWheel="Density_MouseWheel"
ToolTip="{DynamicResource Str_TT_Density}"/>
<!-- E9A6 (two panes): open or close the second pane, right-click flips
side-by-side / stacked. On the rail rather than in a pane toolbar
because it acts on the WINDOW - in the toolbar there were two of them,
one per pane, doing the identical thing, and either could be shed into
the overflow chevron on a narrow split. Accents while open, like the
search and bookmarks toggles above. -->
<Button x:Name="DualPaneRailBtn" Style="{StaticResource RailButton}" Content=""
FontSize="12" Margin="0,0,0,10" Click="DualPane_Click"
MouseRightButtonUp="DualPaneOrient_Click"
ToolTip="{DynamicResource Str_TT_DualPane}"/>
<!-- E70F (pencil): a blank document in a new tab in the FOCUSED pane, the
same glyph an editor tab carries (EditorTabs.GlyphEdit). No shortcut of
its own - F7 is the editor key and it belongs to the row you have
selected; this is the one way in that starts from nothing, and nothing
is written to disk until the first save asks where it goes. -->
<Button x:Name="NewDocRailBtn" Style="{StaticResource RailButton}" Content=""
FontSize="12" Margin="0,0,0,10" Click="NewDoc_Click"
ToolTip="{DynamicResource Str_TT_NewDoc}"/>
<!-- E756 (command prompt): opens a PowerShell here on a left click, and
right-click picks the shell. Four rows rather than four rail icons -
the rail is family furniture and a column of near-identical prompts
would read as clutter, while the pair you actually reach for most is
one click and one chord away regardless (F8 / Ctrl+F8). -->
<Button x:Name="TerminalRailBtn" Style="{StaticResource RailButton}" Content=""
FontSize="12" Margin="0,0,0,10" Click="TerminalRail_Click"
ToolTip="{DynamicResource Str_TT_Terminal}">
<Button.ContextMenu>
<ContextMenu Placement="Top">
<MenuItem Header="{DynamicResource Str_Term_PowerShell}" Click="RailShellPs_Click" InputGestureText="F8">
<MenuItem.Icon><TextBlock Style="{StaticResource MenuGlyph}" Text=""/></MenuItem.Icon>
</MenuItem>
<MenuItem Header="{DynamicResource Str_Term_PowerShellAdmin}" Click="RailShellPsAdmin_Click" InputGestureText="Ctrl+F8">
<MenuItem.Icon><TextBlock Style="{StaticResource MenuGlyph}" Text=""/></MenuItem.Icon>
</MenuItem>
<Separator/>
<MenuItem Header="{DynamicResource Str_Term_Cmd}" Click="RailShellCmd_Click" InputGestureText="Shift+F8">
<MenuItem.Icon><TextBlock Style="{StaticResource MenuGlyph}" Text=""/></MenuItem.Icon>
</MenuItem>
<MenuItem Header="{DynamicResource Str_Term_CmdAdmin}" Click="RailShellCmdAdmin_Click" InputGestureText="Ctrl+Shift+F8">
<MenuItem.Icon><TextBlock Style="{StaticResource MenuGlyph}" Text=""/></MenuItem.Icon>
</MenuItem>
<Separator/>
<!-- Rows are built as the submenu OPENS (ProfileMenu.cs), from
the PowerShell hosts actually installed - PowerShell 7 gets
installed on a machine mid-session often enough that a fixed
pair would go on lying for the rest of the session. The
placeholder child is what makes WPF draw the arrow and fire
SubmenuOpened at all; it is replaced before it is seen. -->
<MenuItem Header="{DynamicResource Str_Prof_Edit}" InputGestureText="Ctrl+,"
SubmenuOpened="RailProfileMenu_Opened">
<MenuItem.Icon><TextBlock Style="{StaticResource MenuGlyph}" Text=""/></MenuItem.Icon>
<MenuItem/>
</MenuItem>
</ContextMenu>
</Button.ContextMenu>
</Button>
<!-- "?" (a literal, in Consolas): the shortcuts card, F1. Sits directly above
the globe, which is where KillerNotes and KillerScan put it - the rail
order is family furniture, so it has to match. -->
<Button Style="{StaticResource RailButton}" Content="?"
FontFamily="Consolas" FontSize="18" Margin="0,0,0,10"
Click="Shortcuts_Click" ToolTip="{DynamicResource Str_Help_Shortcuts}"/>
<!-- E774 (globe): the locale menu, filled on click by BuildLanguageMenu
(Language.cs). Placement="Top" so it opens upward off the rail's
bottom rather than off the bottom edge of the window. -->
<Button x:Name="LangButton" Style="{StaticResource RailButton}" Content=""
Margin="0,0,0,10" Click="LangButton_Click"
ToolTip="{DynamicResource Str_TT_Language}">
<Button.ContextMenu>
<ContextMenu x:Name="LangMenu" Placement="Top"/>
</Button.ContextMenu>
</Button>
<!-- E790 (paint roller): the theme + accent flyout (ThemePopup, below). -->
<Button x:Name="ThemeButton" Style="{StaticResource RailButton}" Content=""
Click="ThemeButton_Click" ToolTip="{DynamicResource Str_TT_Theme}"/>
</StackPanel>
</Grid>
<!-- COLUMN 5 - SEARCH CONFIG, out on the right edge (textured, flows into the bars
above / footer below). Collapsed by default and toggled from the rail. -->
<Grid Grid.Column="5" x:Name="SearchPanel" Visibility="Collapsed">
<DockPanel Background="Transparent" LastChildFill="True">
<Border DockPanel.Dock="Bottom" Padding="12,8">
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" x:Name="SearchButton" Content="{DynamicResource Str_Btn_Search}" Style="{StaticResource OutlineButton}" Height="32" FontSize="13" Click="Search_Click"/>
<!-- New search: opens a fresh tab -->
<Button Grid.Column="1" Content="+" Width="32" Height="32" Margin="6,0,0,0" Padding="0"
FontFamily="Consolas" FontSize="15" Style="{StaticResource SurfaceButton}"
ToolTip="{DynamicResource Str_TT_NewTab}" Click="NewTab_Click"/>
</Grid>
<Button Content="{DynamicResource Str_Btn_Clear}" Style="{StaticResource SurfaceButton}" Height="28" Margin="0,6,0,0" Click="Clear_Click"/>
</StackPanel>
</Border>
<!-- Terms over filters with a draggable divider between them, clamped so
neither section can be crushed against an edge. -->
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" MinHeight="140"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto" MinHeight="80" MaxHeight="430"/>
</Grid.RowDefinitions>
<!-- Bottom-anchored so any slack in the row sits ABOVE the section: the
collapsed "advanced" line seats flat against the buttons below. -->
<Border Grid.Row="2" Padding="12,8,12,2" VerticalAlignment="Bottom">
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<StackPanel>
<TextBlock Text="{DynamicResource Str_Lbl_Filters}" Style="{StaticResource SectionHeader}"/>
<!-- Filter rows: [field v] on top, [condition toggle][value] below. AND-ed with
the terms. The condition is a toggle button with SHORT localized words
(is/not, before/after, over/under) so nothing ever truncates. -->
<ItemsControl x:Name="FiltersList">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type models:SearchFilter}">
<Border Background="{DynamicResource SurfaceBrush}" BorderBrush="{DynamicResource CardBorderBrush}"
BorderThickness="1" CornerRadius="5" Margin="0,0,0,6" Padding="6" Effect="{StaticResource ShadowBar}">
<Grid>
<Border IsHitTestVisible="False" CornerRadius="4" Margin="-6"
Background="{DynamicResource GrainTileBrush}" Opacity="{DynamicResource GrainOpacity}"/>
<StackPanel>
<Grid Margin="0,0,0,4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="4"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<ComboBox Grid.Column="0" SelectedIndex="{Binding FieldIndex, Mode=TwoWay}">
<ComboBoxItem Content="{DynamicResource Str_Filter_Ext}"/>
<ComboBoxItem Content="{DynamicResource Str_Filter_Date}"/>
<ComboBoxItem Content="{DynamicResource Str_Filter_Size}"/>
</ComboBox>
<Button Grid.Column="2" Content="x" Style="{StaticResource DangerButton}" Width="22" Height="22" Padding="0"
Tag="{Binding}" Click="RemoveFilter_Click"/>
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="74"/>
<ColumnDefinition Width="6"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Style="{StaticResource ModeButton}" Width="74" Height="26"
Tag="{Binding}" Click="ToggleFilterCondition_Click">
<TextBlock>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding FieldIndex}" Value="0"/>
<Condition Binding="{Binding ConditionIndex}" Value="0"/>
</MultiDataTrigger.Conditions>
<Setter Property="Text" Value="{DynamicResource Str_Cond_Is}"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding FieldIndex}" Value="0"/>
<Condition Binding="{Binding ConditionIndex}" Value="1"/>
</MultiDataTrigger.Conditions>
<Setter Property="Text" Value="{DynamicResource Str_Cond_IsNot}"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding FieldIndex}" Value="1"/>
<Condition Binding="{Binding ConditionIndex}" Value="0"/>
</MultiDataTrigger.Conditions>
<Setter Property="Text" Value="{DynamicResource Str_Cond_Before}"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding FieldIndex}" Value="1"/>
<Condition Binding="{Binding ConditionIndex}" Value="1"/>
</MultiDataTrigger.Conditions>
<Setter Property="Text" Value="{DynamicResource Str_Cond_After}"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding FieldIndex}" Value="2"/>
<Condition Binding="{Binding ConditionIndex}" Value="0"/>
</MultiDataTrigger.Conditions>
<Setter Property="Text" Value="{DynamicResource Str_Cond_Larger}"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding FieldIndex}" Value="2"/>
<Condition Binding="{Binding ConditionIndex}" Value="1"/>
</MultiDataTrigger.Conditions>
<Setter Property="Text" Value="{DynamicResource Str_Cond_Smaller}"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Button>
<Grid Grid.Column="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox Grid.ColumnSpan="2" Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}"
Visibility="{Binding IsExt, Converter={StaticResource BoolToVis}}"/>
<DatePicker Grid.ColumnSpan="2" SelectedDate="{Binding Date, Mode=TwoWay}"
Visibility="{Binding IsDate, Converter={StaticResource BoolToVis}}"/>
<TextBox Grid.Column="0" Text="{Binding SizeText, UpdateSourceTrigger=PropertyChanged}"
Visibility="{Binding IsSize, Converter={StaticResource BoolToVis}}"/>
<ComboBox Grid.Column="1" Width="56" Margin="4,0,0,0" SelectedIndex="{Binding UnitIndex, Mode=TwoWay}"
Visibility="{Binding IsSize, Converter={StaticResource BoolToVis}}">
<ComboBoxItem Content="KB"/>
<ComboBoxItem Content="MB"/>
</ComboBox>
</Grid>
</Grid>
</StackPanel>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button HorizontalAlignment="Left" Content="{DynamicResource Str_Btn_AddFilter}" Style="{StaticResource SurfaceButton}"
Padding="8,3" FontSize="10" Margin="0,2,0,0" Click="AddFilter_Click"/>
<!-- "advanced" accordion: include/exclude/case collapse out of the way -->
<Border Margin="0,10,0,0" BorderBrush="{DynamicResource CardBorderBrush}" BorderThickness="0,1,0,0" Padding="0,7,0,2">
<StackPanel>
<Border Background="Transparent" Cursor="Hand" MouseLeftButtonUp="AdvancedToggle_Click">
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="AdvancedChevron" Text="" FontFamily="Segoe MDL2 Assets" FontSize="9"
Foreground="{DynamicResource DimTextBrush}" VerticalAlignment="Center" Margin="0,1,7,0"/>
<TextBlock Text="{DynamicResource Str_Lbl_Advanced}" Foreground="{DynamicResource DimTextBrush}"
FontFamily="{DynamicResource MonoFont}" FontSize="10" VerticalAlignment="Center"/>
</StackPanel>
</Border>
<StackPanel x:Name="AdvancedPanel" Visibility="Collapsed">
<Grid Margin="0,9,0,3">
<TextBlock Text="{DynamicResource Str_Lbl_Include}" Foreground="{DynamicResource DimTextBrush}" FontFamily="{DynamicResource MonoFont}" FontSize="10" VerticalAlignment="Center"/>
<!-- Pattern cheat-sheet card, like KillerPDF's keyboard shortcuts overlay -->
<Button HorizontalAlignment="Right" Width="16" Height="16" Padding="0" Cursor="Hand"
ToolTip="{DynamicResource Str_TT_PatternHelp}" Click="PatternHelp_Click">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border x:Name="hb" Background="Transparent" BorderBrush="{DynamicResource InputBorderBrush}" BorderThickness="1" CornerRadius="8">
<TextBlock x:Name="ht" Text="?" Foreground="{DynamicResource DimTextBrush}" HorizontalAlignment="Center" VerticalAlignment="Center" FontFamily="Consolas" FontSize="10"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="hb" Property="BorderBrush" Value="{DynamicResource PrimaryBrush}"/>
<Setter TargetName="ht" Property="Foreground" Value="{DynamicResource PrimaryBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
<TextBox x:Name="IncludePatternsBox" Text="*.*" ToolTip="{DynamicResource Str_TT_Include}"/>
<TextBlock Text="{DynamicResource Str_Lbl_Exclude}" Foreground="{DynamicResource DimTextBrush}" FontFamily="{DynamicResource MonoFont}" FontSize="10" Margin="0,7,0,3"/>
<TextBox x:Name="ExcludePatternsBox" Text="" ToolTip="{DynamicResource Str_TT_Exclude}"/>
<CheckBox x:Name="CaseSensitiveCheck" Content="{DynamicResource Str_Chk_CaseSensitive}" Foreground="{DynamicResource MutedTextBrush}" Margin="0,9,0,0"/>
</StackPanel>
</StackPanel>
</Border>
</StackPanel>
</ScrollViewer>
</Border>
<!-- Draggable divider between terms and filters -->
<GridSplitter Grid.Row="1" Height="7" HorizontalAlignment="Stretch" Background="Transparent"
ResizeBehavior="PreviousAndNext" ResizeDirection="Rows"
Cursor="SizeNS" ShowsPreview="False" Focusable="False">
<GridSplitter.Template>
<ControlTemplate TargetType="GridSplitter">
<Border Background="Transparent">
<Border x:Name="line" Height="1" VerticalAlignment="Center"
Background="{DynamicResource CardBorderBrush}" Margin="12,0"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True"><Setter TargetName="line" Property="Background" Value="{DynamicResource PrimaryBrush}"/></Trigger>
<Trigger Property="IsDragging" Value="True"><Setter TargetName="line" Property="Background" Value="{DynamicResource PrimaryBrush}"/></Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</GridSplitter.Template>
</GridSplitter>
<Border Grid.Row="0" Padding="12,8,12,10">
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Top" Text="{DynamicResource Str_Lbl_SearchTerms}" Style="{StaticResource SectionHeader}"/>
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<ItemsControl x:Name="TermsList">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type models:TermGroup}">
<Border Background="{DynamicResource SurfaceBrush}" BorderBrush="{DynamicResource CardBorderBrush}"
BorderThickness="1" CornerRadius="5" Margin="0,0,0,8" Padding="6" Effect="{StaticResource ShadowBar}">
<Grid>
<Border IsHitTestVisible="False" CornerRadius="4" Margin="-6"
Background="{DynamicResource GrainTileBrush}" Opacity="{DynamicResource GrainOpacity}"/>
<StackPanel>
<!-- Group header: ANY/ALL combiner toggle + remove-group -->
<Grid Margin="0,0,0,5">
<Button HorizontalAlignment="Left" Content="{Binding ModeLabel, Mode=OneWay}" Style="{StaticResource ModeButton}"
Width="44" Height="20" FontSize="10" ToolTip="{Binding ModeTooltip}" Tag="{Binding}" Click="ToggleGroupMode_Click"/>
</Grid>
<ItemsControl ItemsSource="{Binding Terms}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type models:SearchTerm}">
<Grid Margin="0,0,0,4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="62"/>
<ColumnDefinition Width="6"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="4"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Content="{Binding ModeName, Mode=OneWay}" Style="{StaticResource ModeButton}"
ToolTip="{Binding ModeTooltip}" Tag="{Binding}" Click="ToggleMode_Click"/>
<TextBox Grid.Column="2" Text="{Binding Pattern, UpdateSourceTrigger=PropertyChanged}" ToolTip="{Binding ModeTooltip}"/>
<TextBlock Grid.Column="4" Text="{Binding MatchBadge}" Foreground="{DynamicResource PrimaryBrush}"
FontFamily="{DynamicResource MonoFont}" FontSize="10" VerticalAlignment="Center"/>
<Button Grid.Column="6" Content="x" Style="{StaticResource DangerButton}" Width="22" Height="22" Padding="0"
Tag="{Binding}" Click="RemoveTerm_Click"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button HorizontalAlignment="Left" Content="{DynamicResource Str_Btn_AddTerm}" Style="{StaticResource SurfaceButton}"
Padding="8,3" FontSize="10" Margin="0,2,0,0" Tag="{Binding}" Click="AddTerm_Click"/>
</StackPanel>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</DockPanel>
</Border>
</Grid>
</DockPanel>
</Grid>
<!-- end COLUMN 5 search panel -->
<!-- COLUMN 3 - THE PANES. One tab = one whole search or one folder. Tab visuals
mirror KillerPDF: active tab takes the pane background with a 3px accent
stripe on top and a soft shadow.
PaneHost carries BOTH panes on one 3x3 grid rather than one grid per
orientation. Side-by-side uses the columns and collapses the rows; stacked
uses the rows and collapses the columns; DualPane.cs moves RightPane and the
splitter between the two slots. One host means the panes are never rebuilt
when the orientation flips, so neither one loses its tabs or its scroll
position. Second pane is Collapsed until asked for, and its column/row is 0
wide, so a single-pane window lays out exactly as it did before. -->
<Grid Grid.Column="3" x:Name="PaneHost">
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="PaneColA" Width="*" MinWidth="180"/>
<ColumnDefinition x:Name="PaneColSplit" Width="0"/>
<ColumnDefinition x:Name="PaneColB" Width="0" MinWidth="0"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition x:Name="PaneRowA" Height="*" MinHeight="120"/>
<RowDefinition x:Name="PaneRowSplit" Height="0"/>
<RowDefinition x:Name="PaneRowB" Height="0" MinHeight="0"/>
</Grid.RowDefinitions>
<local:FilePane x:Name="LeftPane" Grid.Column="0" Grid.Row="0"/>
<!-- Both splitters exist; DualPane.cs shows whichever the orientation needs.
ResizeBehavior is PreviousAndNext so the drag moves the boundary between
the two panes and nothing else. -->
<GridSplitter x:Name="PaneSplitV" Grid.Column="1" Grid.Row="0" Width="6"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Background="Transparent" Cursor="SizeWE" Visibility="Collapsed"
ResizeBehavior="PreviousAndNext" ResizeDirection="Columns"/>
<GridSplitter x:Name="PaneSplitH" Grid.Row="1" Grid.Column="0" Height="6"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Background="Transparent" Cursor="SizeNS" Visibility="Collapsed"
ResizeBehavior="PreviousAndNext" ResizeDirection="Rows"/>
<local:FilePane x:Name="RightPane" Grid.Column="2" Grid.Row="0"
Visibility="Collapsed"/>
</Grid>
</Grid>
<!-- ══════════ ROW 2 - FOOTER (blends with the sidebar/panes, no divider line) ══════════ -->
<Border Grid.Row="2" Background="{DynamicResource BackgroundBrush}">
<!-- Two columns: the status area takes what is left, the version block sizes to its
content on the right. The portable badge is deliberately NOT a column - it stays
a CENTERED overlay spanning both, which is where it has always sat. Giving it its
own column pinned it against the version text instead.
Because it overlays, column 0 runs underneath it and cannot bound the status line
on its own - ElideFooterStatus (MainWindow.xaml.cs) clamps the line at the badge's
left edge while the badge is showing, and drops the clamp once the app is
installed and the badge is gone, so the line then runs on toward the version text.
The short per-TAB facts (scanned / matches / query) are NOT here - they live in
each pane's own status bar (FilePane.xaml). This bar carries the one item that
needs real width. -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Border Grid.ColumnSpan="2" IsHitTestVisible="False" Background="{DynamicResource GrainTileBrush}" Opacity="{DynamicResource GrainOpacity}"/>
<!-- 10.5 left, not the 16 the rest of the family uses: the status dot is the first
thing in this row and it should sit on the same vertical line as the icon rail
above it (RailCol, 24 wide, so its icons center at 12). The dot is 3 wide, so
the row starts at 12 - 1.5. Everything after it moves with it, which is the
"tiny bit left" this is for. -->
<Grid Grid.Column="0" VerticalAlignment="Center" Margin="10.5,0,12,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- App-wide zoom level (AppScale.cs). Leftmost in the status area at the same
16px inset, ChromeTextBrush, mono 11.5 - the exact slot and treatment
KillerScan gives its footer StatusText (MainWindow.xaml row 3). Hidden at
100%; a collapsed element takes no width or margin. -->
<TextBlock x:Name="AppScaleLabel" Grid.Column="0" Visibility="Collapsed" Foreground="{DynamicResource ChromeTextBrush}"
FontFamily="{DynamicResource MonoFont}" FontSize="11.5" VerticalAlignment="Center"
Margin="0,0,14,0" ToolTip="{DynamicResource Str_TT_AppSize}"/>
<!-- Traffic light: OkBrush / WarnBrush / DangerRed, set by ApplyStatusTone. The
resting state is green, not the accent - see MainWindow.xaml.cs. -->
<Border x:Name="StatusDot" Grid.Column="1" Width="3" Background="{DynamicResource OkBrush}" Margin="0,3,7,3" CornerRadius="2"/>
<!-- The running commentary: "Ready", "Opening C:\...", "164 item(s)", "Cannot
open X". No MaxWidth - the * column IS the bound, so the line uses whatever
the window gives it: it stops at the portable badge while that is showing,
and once the app is installed the badge collapses to zero width and the
line simply runs on toward the version text.
NO TextTrimming: that ellipsizes the TAIL, which on a path throws away the
file name - the only part worth reading. ElideFooterStatus
(MainWindow.xaml.cs) trims from the FRONT instead, and SizeChanged
re-runs it so a window resize re-cuts the path.
11.5 status / 10 corner type is the family footer standard. -->
<TextBlock x:Name="StatusText" Grid.Column="2" Text="{DynamicResource Str_Status_Ready}" Foreground="{DynamicResource TextFooter}"
FontFamily="{DynamicResource MonoFont}" FontSize="11.5" VerticalAlignment="Center"
SizeChanged="StatusText_SizeChanged"/>
</Grid>
<!-- Portable mode indicator (hidden when installed), same as KillerScan. Centered
across the whole footer, NOT parked in a column - a column pins it next to the
version text. IsVisibleChanged re-runs the status elision, because the line's
usable width changes the moment this appears or goes away. -->
<StackPanel x:Name="PortableBadge" Grid.ColumnSpan="2" Orientation="Horizontal"
HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="Collapsed"
IsVisibleChanged="PortableBadge_IsVisibleChanged">
<TextBlock Text="{DynamicResource Str_Portable}" Foreground="{DynamicResource TextFooter}"
FontFamily="Consolas" FontSize="9" VerticalAlignment="Center" Margin="0,0,8,0"/>
<Button x:Name="InstallBtn" Content="{DynamicResource Str_Btn_Install}" Click="Install_Click"
Style="{StaticResource OutlineButton}" FontFamily="Segoe UI" FontSize="10" Padding="8,1"
VerticalAlignment="Center"/>
</StackPanel>
<StackPanel Grid.Column="1" Orientation="Horizontal" VerticalAlignment="Center" Margin="0,0,16,0">
<TextBlock x:Name="VersionLabel" Text="v0.99" Foreground="{DynamicResource TextFooter}" FontFamily="{DynamicResource MonoFont}" FontSize="10" VerticalAlignment="Center" Margin="0,0,12,0"
Cursor="Hand" ToolTip="About" MouseLeftButtonUp="VersionLabel_Click"/>
<!-- 14px trailing margin keeps the copyright clear of the corner grip (family standard) -->
<TextBlock FontSize="10" VerticalAlignment="Center" FontFamily="{DynamicResource MonoFont}" Margin="0,0,14,0">
<Run Text="© 2026 " Foreground="{DynamicResource TextFooter}"/>
<Hyperlink NavigateUri="https://thekiller.net" RequestNavigate="Hyperlink_RequestNavigate" Foreground="{DynamicResource TextFooter}" TextDecorations="None">
<Run Text="Steve the Killer"/>
</Hyperlink>
</TextBlock>
</StackPanel>
<!-- Bottom-right resize grip: dotted triangle at the content corner, forwards a
HTBOTTOMRIGHT resize to Windows on drag (same as KillerPDF/KillerScan). -->
<Canvas x:Name="ResizeGripDots" Grid.ColumnSpan="2" Width="13" Height="13"
HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,4,4"
Background="Transparent" Cursor="SizeNWSE"
MouseLeftButtonDown="ResizeGrip_MouseDown">
<Rectangle Canvas.Left="10" Canvas.Top="2" Width="2" Height="2" Fill="{DynamicResource DimTextBrush}"/>
<Rectangle Canvas.Left="6" Canvas.Top="6" Width="2" Height="2" Fill="{DynamicResource DimTextBrush}"/>
<Rectangle Canvas.Left="10" Canvas.Top="6" Width="2" Height="2" Fill="{DynamicResource DimTextBrush}"/>
<Rectangle Canvas.Left="2" Canvas.Top="10" Width="2" Height="2" Fill="{DynamicResource DimTextBrush}"/>
<Rectangle Canvas.Left="6" Canvas.Top="10" Width="2" Height="2" Fill="{DynamicResource DimTextBrush}"/>
<Rectangle Canvas.Left="10" Canvas.Top="10" Width="2" Height="2" Fill="{DynamicResource DimTextBrush}"/>
</Canvas>
</Grid>
</Border>
<!-- ══════════ ABOUT OVERLAY (dim backdrop + centered card) ══════════ -->
<Grid x:Name="AboutOverlay" Grid.RowSpan="3" Panel.ZIndex="9000" Visibility="Collapsed"
Background="#88000000" MouseLeftButtonDown="AboutOverlay_Click">
<!-- Card structure copied from KillerScan (the rules for every About card):
the SHA-256 line is the ONLY thing that sets the width - it never wraps, so the
info panel sizes to it and the card sizes to the info panel. The header grid
below pins its width to that panel so a long tagline wraps instead of widening
the card. The drop shadow lives on a SEPARATE border so the card text never
renders through a bitmap effect (stays ClearType-crisp), and the whole surface
is grain-wrapped. -->
<Grid VerticalAlignment="Center" HorizontalAlignment="Center" MouseLeftButtonDown="AboutCard_Click">
<Border Background="{DynamicResource SurfaceBrush}" CornerRadius="6" IsHitTestVisible="False">
<Border.Effect><DropShadowEffect Color="Black" BlurRadius="26" ShadowDepth="5" Direction="270" Opacity="0.6"/></Border.Effect>
</Border>
<Border Background="{DynamicResource SurfaceBrush}" BorderBrush="{DynamicResource CardBorderBrush}"
BorderThickness="1" CornerRadius="6">
<Grid>
<Border IsHitTestVisible="False" CornerRadius="6"
Opacity="{DynamicResource GrainOpacity}" Background="{DynamicResource GrainTileBrush}"/>
<!-- Close X: glyph reddens on hover (foreground via style setter so the trigger wins) -->
<Button Content="✕" Click="AboutClose_Click" HorizontalAlignment="Right" VerticalAlignment="Top"
Width="28" Height="26" Margin="0,6,6,0" Panel.ZIndex="2" FontSize="11"
Background="Transparent" BorderThickness="0" Cursor="Hand">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Foreground" Value="{DynamicResource MutedTextBrush}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="Transparent">
<ContentPresenter x:Name="cp" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="#E04444"/>
<Setter TargetName="cp" Property="Effect">
<Setter.Value><DropShadowEffect Color="#000000" BlurRadius="4" ShadowDepth="1" Direction="270" Opacity="0.5"/></Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
<StackPanel Margin="18,10,18,18">
<!-- Header: icon left, wordmark (links to the site) + tagline right.
Width is pinned to the info panel below so the tagline wraps
instead of widening the card - the SHA-256 line stays the driver. -->
<Grid Margin="0,4,0,14" Width="{Binding ActualWidth, ElementName=AboutInfoGrid}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image x:Name="AboutIcon" Grid.Column="0" Width="58" Height="58" Margin="0,0,15,0" VerticalAlignment="Center"
RenderOptions.BitmapScalingMode="HighQuality">
<Image.Effect><DropShadowEffect Color="Black" BlurRadius="12" ShadowDepth="3" Direction="270" Opacity="0.55"/></Image.Effect>
</Image>
<StackPanel Grid.Column="1" VerticalAlignment="Center">
<Grid Margin="0,0,0,4" HorizontalAlignment="Left" Cursor="Hand" ToolTip="killershell.net"
Background="Transparent" MouseLeftButtonUp="Wordmark_Click">
<TextBlock FontFamily="{DynamicResource WordmarkFont}" Margin="1,2,0,0" IsHitTestVisible="False"
Opacity="{DynamicResource IconShadowOpacity}"><TextBlock.Effect><BlurEffect Radius="2"/></TextBlock.Effect><Run Text="Killer" FontSize="22" Foreground="#C0000000"/><Run Text="Shell" FontSize="28.6" FontWeight="Bold" Foreground="#C0000000"/></TextBlock>
<TextBlock FontFamily="{DynamicResource WordmarkFont}"><Run Text="Killer" FontSize="22" Foreground="{DynamicResource TextBrush}"/><Run Text="Shell" FontSize="28.6" FontWeight="Bold" Foreground="{DynamicResource PrimaryBrush}"/></TextBlock>
</Grid>
<!-- Tagline is composed here, not in the .xaml string files: GPLv3 and
"Killer Tools" are proper nouns and stay out of the translations
(same split KillerScan and KillerNotes use). -->
<TextBlock FontSize="12" Foreground="{DynamicResource MutedTextBrush}" TextWrapping="Wrap"><Run Text="{DynamicResource Str_About_DescA}"/><Run Text="GPLv3 "/><Hyperlink NavigateUri="https://killertools.net" RequestNavigate="AboutLink_Navigate" Foreground="{DynamicResource PrimaryBrush}" TextDecorations="None">Killer Tools</Hyperlink><Run Text="{DynamicResource Str_About_DescB}"/></TextBlock>
</StackPanel>
</Grid>
<!-- Info panel - matte, lifted above the grainy card. The shadow lives on
its own border so the field text never renders through a bitmap effect
(stays ClearType-crisp). Field spacing and sizes match KillerScan. -->
<Grid x:Name="AboutInfoGrid">