1
1
using System . Data ;
2
2
using System . Diagnostics ;
3
+ using System . Globalization ;
3
4
using System . Text . Json ;
4
5
using System . Web ;
5
6
using J . Core ;
@@ -13,6 +14,9 @@ namespace J.App;
13
14
14
15
public sealed partial class MainForm : Form
15
16
{
17
+ public const int SEARCH_TEXT_SMALL_WIDTH = 125 ;
18
+ public const int SEARCH_TEXT_BIG_WIDTH = 200 ;
19
+
16
20
private readonly IServiceProvider _serviceProvider ;
17
21
private readonly LibraryProviderAdapter _libraryProvider ;
18
22
private readonly Client _client ;
@@ -59,7 +63,8 @@ public sealed partial class MainForm : Form
59
63
_browserTabButton ,
60
64
_importTabButton ,
61
65
_tagsTabButton ;
62
- private readonly ToolStripSeparator _rightmostSeparator ;
66
+ private readonly ToolStripSeparator _rightmostSeparator ,
67
+ _browserNavigationSeparator ;
63
68
private readonly MyToolStripTextBox _searchText ;
64
69
private readonly MyWebView2 _browser ;
65
70
private readonly System . Windows . Forms . Timer _searchDebounceTimer ;
@@ -153,7 +158,7 @@ ProcessTempDir processTempDir
153
158
_filterClearButton . Click += FilterClearButton_Click ;
154
159
}
155
160
156
- _toolStrip . Items . Add ( _searchText = ui . NewToolStripTextBox ( 200 ) ) ;
161
+ _toolStrip . Items . Add ( _searchText = ui . NewToolStripTextBox ( SEARCH_TEXT_BIG_WIDTH ) ) ;
157
162
{
158
163
_searchText . Margin += ui . RightSpacing ;
159
164
_searchText . Alignment = ToolStripItemAlignment . Right ;
@@ -339,7 +344,7 @@ ProcessTempDir processTempDir
339
344
} ;
340
345
}
341
346
342
- _toolStrip . Items . Add ( ui . NewToolStripSeparator ( ) ) ;
347
+ _toolStrip . Items . Add ( _browserNavigationSeparator = ui . NewToolStripSeparator ( ) ) ;
343
348
344
349
_toolStrip . Items . Add ( _browserTabButton = ui . NewToolStripTabButton ( "Loading..." ) ) ;
345
350
{
@@ -441,7 +446,7 @@ ProcessTempDir processTempDir
441
446
442
447
Text = "Jackpot Media Library" ;
443
448
Size = ui . GetSize ( 1600 , 900 ) ;
444
- MinimumSize = ui . GetSize ( 1200 , 400 ) ;
449
+ MinimumSize = ui . GetSize ( 900 , 400 ) ;
445
450
CenterToScreen ( ) ;
446
451
FormBorderStyle = FormBorderStyle . None ;
447
452
Icon = ui . GetIconResource ( "App.ico" ) ;
@@ -596,13 +601,76 @@ private void Browser_NavigationCompleted(object? sender, CoreWebView2NavigationC
596
601
if ( title . Length > 100 )
597
602
title = title [ ..100 ] + "..." ;
598
603
599
- _browserTabButton . Text = title ? . Replace ( "&" , "&&" ) ?? "" ;
604
+ _browserTabButton . Text = TruncateTitleText ( title ? . Replace ( "&" , "&&" ) ?? "" , _ui . GetLength ( 150 ) ) ;
600
605
_browseBackButton . Enabled = _browser . CanGoBack ;
601
606
_browseForwardButton . Enabled = _browser . CanGoForward ;
602
607
}
603
608
catch { }
604
609
}
605
610
611
+ private string TruncateTitleText ( string text , int maxPixelWidth )
612
+ {
613
+ if ( string . IsNullOrEmpty ( text ) )
614
+ return text ;
615
+
616
+ var font = _browserTabButton . Font ;
617
+
618
+ // Using the Control's context for DPI-aware measurements
619
+ using var graphics = CreateGraphics ( ) ;
620
+
621
+ // Check if the full text already fits
622
+ var fullSize = TextRenderer . MeasureText (
623
+ graphics ,
624
+ text ,
625
+ font ,
626
+ new Size ( maxPixelWidth , 0 ) ,
627
+ TextFormatFlags . SingleLine | TextFormatFlags . NoPrefix
628
+ ) ;
629
+
630
+ if ( fullSize . Width <= maxPixelWidth )
631
+ return text ;
632
+
633
+ // Create text enumerator for proper grapheme cluster handling
634
+ var enumerator = StringInfo . GetTextElementEnumerator ( text ) ;
635
+ List < string > elements = new ( text . Length ) ;
636
+ while ( enumerator . MoveNext ( ) )
637
+ {
638
+ elements . Add ( enumerator . GetTextElement ( ) ) ;
639
+ }
640
+
641
+ // Binary search for the longest fitting prefix
642
+ int left = 0 ;
643
+ int right = elements . Count ;
644
+
645
+ while ( left < right )
646
+ {
647
+ int mid = ( left + right + 1 ) / 2 ;
648
+ string candidate = string . Concat ( elements . Take ( mid ) ) + "..." ;
649
+ var size = TextRenderer . MeasureText (
650
+ graphics ,
651
+ candidate ,
652
+ font ,
653
+ new Size ( maxPixelWidth , 0 ) ,
654
+ TextFormatFlags . SingleLine | TextFormatFlags . NoPrefix
655
+ ) ;
656
+
657
+ if ( size . Width <= maxPixelWidth )
658
+ {
659
+ left = mid ;
660
+ }
661
+ else
662
+ {
663
+ right = mid - 1 ;
664
+ }
665
+ }
666
+
667
+ // If we can't fit even one grapheme cluster plus ellipsis, return just ellipsis
668
+ if ( left == 0 )
669
+ return "..." ;
670
+
671
+ return string . Concat ( elements . Take ( left ) ) + "..." ;
672
+ }
673
+
606
674
private async Task GoHomeAsync ( )
607
675
{
608
676
await _client . InhibitScrollRestoreAsync ( ) . ConfigureAwait ( true ) ;
@@ -1108,6 +1176,22 @@ protected override void OnResize(EventArgs e)
1108
1176
ApplyFullscreenPreference ( ) ;
1109
1177
_lastWindowState = WindowState ;
1110
1178
}
1179
+
1180
+ var size1 = Width >= _ui . GetLength ( 1100 ) ;
1181
+ var size2 = Width >= _ui . GetLength ( 1300 ) ;
1182
+
1183
+ _viewButton . DisplayStyle = size1 ? ToolStripItemDisplayStyle . ImageAndText : ToolStripItemDisplayStyle . Text ;
1184
+ _sortButton . DisplayStyle = size1 ? ToolStripItemDisplayStyle . ImageAndText : ToolStripItemDisplayStyle . Text ;
1185
+ _filterButton . DisplayStyle = size1
1186
+ ? ToolStripItemDisplayStyle . ImageAndText
1187
+ : ToolStripItemDisplayStyle . Text ;
1188
+ _browserNavigationSeparator . Visible = size1 ;
1189
+ _searchText . Width = _ui . GetLength ( size1 ? SEARCH_TEXT_BIG_WIDTH : SEARCH_TEXT_SMALL_WIDTH ) ;
1190
+
1191
+ _browseBackButton . Visible = size2 ;
1192
+ _browseForwardButton . Visible = size2 ;
1193
+ _refreshButton . Visible = size2 ;
1194
+ _homeButton . Visible = size2 ;
1111
1195
}
1112
1196
catch { }
1113
1197
}
0 commit comments