Răsfoiți Sursa

Merge branch 'gui-cs:v2_develop' into v2_develop

Tig 1 lună în urmă
părinte
comite
e058038fac

+ 1 - 1
Examples/UICatalog/Scenarios/CharacterMap/CharacterMap.cs

@@ -85,7 +85,7 @@ public class CharacterMap : Scenario
             X = Pos.Right (jumpLabel) + 1,
             Y = Pos.Y (_charMap),
             Width = 17,
-            Caption = "e.g. 01BE3 or ✈"
+            Title = "e.g. 01BE3 or ✈"
 
             //SchemeName = "Dialog"
         };

+ 1 - 1
Examples/UICatalog/Scenarios/TextInputControls.cs

@@ -68,7 +68,7 @@ public class TextInputControls : Scenario
             X = Pos.Right (label) + 1,
             Y = Pos.Bottom (textField),
             Width = Dim.Percent (50) - 1,
-            Caption = "TextField with caption"
+            Title = "TextField with caption"
         };
 
         win.Add (textField);

+ 2 - 2
Terminal.Gui/Resources/Strings.Designer.cs

@@ -628,7 +628,7 @@ namespace Terminal.Gui.Resources {
         }
         
         /// <summary>
-        ///   Looks up a localized string similar to Enter Path.
+        ///   Looks up a localized string similar to _Enter Path.
         /// </summary>
         internal static string fdPathCaption {
             get {
@@ -682,7 +682,7 @@ namespace Terminal.Gui.Resources {
         }
         
         /// <summary>
-        ///   Looks up a localized string similar to Find.
+        ///   Looks up a localized string similar to _Find.
         /// </summary>
         internal static string fdSearchCaption {
             get {

+ 4 - 4
Terminal.Gui/Resources/Strings.resx

@@ -192,10 +192,7 @@
     <value>Modified</value>
   </data>
   <data name="fdPathCaption" xml:space="preserve">
-    <value>Enter Path</value>
-  </data>
-  <data name="fdSearchCaption" xml:space="preserve">
-    <value>Find</value>
+    <value>_Enter Path</value>
   </data>
   <data name="fdSize" xml:space="preserve">
     <value>Size</value>
@@ -359,4 +356,7 @@
     <value>_Tree</value>
     <comment>Show/Hide Tree View</comment>
   </data>
+  <data name="fdSearchCaption" xml:space="preserve">
+    <value>_Find</value>
+  </data>
 </root>

+ 5 - 4
Terminal.Gui/ViewBase/View.Command.cs

@@ -22,9 +22,9 @@ public partial class View // Command APIs
         // HotKey - SetFocus and raise HandlingHotKey
         AddCommand (
                     Command.HotKey,
-                    () =>
+                    (ctx) =>
                     {
-                        if (RaiseHandlingHotKey () is true)
+                        if (RaiseHandlingHotKey (ctx) is true)
                         {
                             return true;
                         }
@@ -257,15 +257,16 @@ public partial class View // Command APIs
     ///     <see cref="OnHandlingHotKey"/> which can be cancelled; if not cancelled raises <see cref="Accepting"/>.
     ///     event. The default <see cref="Command.HotKey"/> handler calls this method.
     /// </summary>
+    /// <param name="ctx">The context to pass with the command.</param>
     /// <returns>
     ///     <see langword="null"/> if no event was raised; input processing should continue.
     ///     <see langword="false"/> if the event was raised and was not handled (or cancelled); input processing should
     ///     continue.
     ///     <see langword="true"/> if the event was raised and handled (or cancelled); input processing should stop.
     /// </returns>
-    protected bool? RaiseHandlingHotKey ()
+    protected bool? RaiseHandlingHotKey (ICommandContext? ctx)
     {
-        CommandEventArgs args = new () { Context = new CommandContext<KeyBinding> { Command = Command.HotKey } };
+        CommandEventArgs args = new () { Context = ctx };
         //Logging.Debug ($"{Title} ({args.Context?.Source?.Title})");
 
         // Best practice is to invoke the virtual method first.

+ 1 - 1
Terminal.Gui/Views/CheckBox.cs

@@ -32,7 +32,7 @@ public class CheckBox : View
         // Hotkey - Advance state and raise Select event - DO NOT raise Accept
         AddCommand (Command.HotKey, ctx =>
                                     {
-                                        if (RaiseHandlingHotKey () is true)
+                                        if (RaiseHandlingHotKey (ctx) is true)
                                         {
                                             return true;
                                         }

+ 3 - 4
Terminal.Gui/Views/FileDialogs/FileDialog.cs

@@ -148,7 +148,7 @@ public class FileDialog : Dialog, IDesignable
                                      e.Handled = true;
                                  };
 
-        _tbPath = new () { Width = Dim.Fill (),/* CaptionColor = new (Color.Black)*/ };
+        _tbPath = new () { Width = Dim.Fill () };
 
         _tbPath.KeyDown += (s, k) =>
                            {
@@ -248,7 +248,6 @@ public class FileDialog : Dialog, IDesignable
             X = 0,
             Width = Dim.Fill (),
             Y = Pos.AnchorEnd (),
-            HotKey = Key.F.WithAlt,
             Id = "_tbFind",
         };
 
@@ -456,8 +455,8 @@ public class FileDialog : Dialog, IDesignable
         _btnBack.Text = GetBackButtonText ();
         _btnForward.Text = GetForwardButtonText ();
 
-        _tbPath.Caption = Style.PathCaption;
-        _tbFind.Caption = Style.SearchCaption;
+        _tbPath.Title = Style.PathCaption;
+        _tbFind.Title = Style.SearchCaption;
 
         _tbPath.Autocomplete.Scheme = new (_tbPath.GetScheme ())
         {

+ 1 - 1
Terminal.Gui/Views/Label.cs

@@ -60,7 +60,7 @@ public class Label : View, IDesignable
 
     private bool? InvokeHotKeyOnNextPeer (ICommandContext commandContext)
     {
-        if (RaiseHandlingHotKey () == true)
+        if (RaiseHandlingHotKey (commandContext) == true)
         {
             return true;
         }

+ 2 - 2
Terminal.Gui/Views/Menu/MenuBarv2.cs

@@ -31,11 +31,11 @@ public class MenuBarv2 : Menuv2, IDesignable
 
         AddCommand (
                     Command.HotKey,
-                    () =>
+                    (ctx) =>
                     {
                         // Logging.Debug ($"{Title} - Command.HotKey");
 
-                        if (RaiseHandlingHotKey () is true)
+                        if (RaiseHandlingHotKey (ctx) is true)
                         {
                             return true;
                         }

+ 1 - 1
Terminal.Gui/Views/RadioGroup.cs

@@ -94,7 +94,7 @@ public class RadioGroup : View, IDesignable, IOrientation
             return false;
         }
 
-        if (RaiseHandlingHotKey () == true)
+        if (RaiseHandlingHotKey (ctx) == true)
         {
             return true;
         }

+ 47 - 31
Terminal.Gui/Views/TextInput/TextField.cs

@@ -28,9 +28,6 @@ public class TextField : View, IDesignable
         _selectedStart = -1;
         _text = new ();
 
-        // TODO: Determine if this is a good choice. Previously this was hard coded to 
-        // TODO: DarkGray which was NOT a good choice.
-        CaptionColor = GetAttributeForRole (VisualRole.Normal).Foreground.GetBrighterColor();
         ReadOnly = false;
         Autocomplete = new TextFieldAutocomplete ();
         Height = Dim.Auto (DimAutoStyle.Text, 1);
@@ -40,9 +37,6 @@ public class TextField : View, IDesignable
         Used = true;
         WantMousePositionReports = true;
 
-        // By default, disable hotkeys (in case someome sets Title)
-        HotKeySpecifier = new ('\xffff');
-
         _historyText.ChangeText += HistoryText_ChangeText;
 
         Initialized += TextField_Initialized;
@@ -324,6 +318,30 @@ public class TextField : View, IDesignable
                     }
                    );
 
+        AddCommand (
+                    Command.HotKey,
+                    ctx =>
+                    {
+                        if (RaiseHandlingHotKey (ctx) is true)
+                        {
+                            return true;
+                        }
+
+                        // If we have focus, then ignore the hotkey because the user
+                        // means to enter it
+                        if (HasFocus)
+                        {
+                            return false;
+                        }
+
+                        // This is what the default HotKey handler does:
+                        SetFocus ();
+
+                        // Always return true on hotkey, even if SetFocus fails because 
+                        // hotkeys are always handled by the View (unless RaiseHandlingHotKey cancels).
+                        return true;
+                    });
+
         // Default keybindings for this view
         // We follow this as closely as possible: https://en.wikipedia.org/wiki/Table_of_keyboard_shortcuts
         KeyBindings.Add (Key.Delete, Command.DeleteCharRight);
@@ -411,15 +429,6 @@ public class TextField : View, IDesignable
     /// </summary>
     public IAutocomplete Autocomplete { get; set; }
 
-    /// <summary>
-    ///     Gets or sets the text to render in control when no value has been entered yet and the <see cref="View"/> does
-    ///     not yet have input focus.
-    /// </summary>
-    public string Caption { get; set; }
-
-    /// <summary>Gets or sets the foreground <see cref="Color"/> to use when rendering <see cref="Caption"/>.</summary>
-    public Color CaptionColor { get; set; }
-
     /// <summary>Get the Context Menu for this view.</summary>
     [CanBeNull]
     public PopoverMenu ContextMenu { get; private set; }
@@ -920,7 +929,7 @@ public class TextField : View, IDesignable
         _isDrawing = true;
 
         // Cache attributes as GetAttributeForRole might raise events
-        Attribute selectedAttribute = new Attribute (GetAttributeForRole (VisualRole.Active));
+        var selectedAttribute = new Attribute (GetAttributeForRole (VisualRole.Active));
         Attribute readonlyAttribute = GetAttributeForRole (VisualRole.ReadOnly);
         Attribute normalAttribute = GetAttributeForRole (VisualRole.Editable);
 
@@ -943,7 +952,7 @@ public class TextField : View, IDesignable
             {
                 // Disabled
                 SetAttributeForRole (VisualRole.Disabled);
-            } 
+            }
             else if (idx == _cursorPosition && HasFocus && !Used && SelectedLength == 0 && !ReadOnly)
             {
                 // Selected text
@@ -1157,7 +1166,6 @@ public class TextField : View, IDesignable
     ///// </summary>
     //public event EventHandler<StateEventArgs<string>> TextChanged;
 
-
     /// <summary>Undoes the latest changes.</summary>
     public void Undo ()
     {
@@ -1699,25 +1707,33 @@ public class TextField : View, IDesignable
     private void RenderCaption ()
     {
         if (HasFocus
-            || Caption == null
-            || Caption.Length == 0
+            || string.IsNullOrEmpty (Title)
             || Text?.Length > 0)
         {
             return;
         }
 
-        var color = new Attribute (CaptionColor, GetAttributeForRole (VisualRole.Editable).Background, GetAttributeForRole (VisualRole.Editable).Style);
-        SetAttribute (color);
-
-        Move (0, 0);
-        string render = Caption;
-
-        if (render.GetColumns () > Viewport.Width)
+        // Ensure TitleTextFormatter has the current Title text
+        // (should already be set by the Title property setter, but being defensive)
+        if (TitleTextFormatter.Text != Title)
         {
-            render = render [..Viewport.Width];
+            TitleTextFormatter.Text = Title;
         }
 
-        AddStr (render);
+        var captionAttribute = new Attribute (
+                                              GetAttributeForRole (VisualRole.Editable).Foreground.GetDimColor (),
+                                              GetAttributeForRole (VisualRole.Editable).Background);
+
+        var hotKeyAttribute = new Attribute (
+                                             GetAttributeForRole (VisualRole.Editable).Foreground.GetDimColor (),
+                                             GetAttributeForRole (VisualRole.Editable).Background,
+                                             GetAttributeForRole (VisualRole.Editable).Style | TextStyle.Underline);
+
+        // Use TitleTextFormatter to render the caption with hotkey support
+        TitleTextFormatter.Draw (
+                                 ViewportToScreen (new Rectangle (0, 0, Viewport.Width, 1)),
+                                 captionAttribute,
+                                 hotKeyAttribute);
     }
 
     private void SetClipboard (IEnumerable<Rune> text)
@@ -1814,11 +1830,11 @@ public class TextField : View, IDesignable
         }
     }
 
-    /// <inheritdoc />
+    /// <inheritdoc/>
     public bool EnableForDesign ()
     {
         Text = "This is a test.";
-        Caption = "Caption";
+        Title = "Caption";
 
         return true;
     }

+ 1 - 1
Tests/UnitTests/FileServices/FileDialogTests.cs

@@ -107,7 +107,7 @@ public class FileDialogTests ()
         Assert.IsType<TextField> (dlg.MostFocused);
         Assert.Same (tf, dlg.MostFocused);
 
-        Assert.Equal ("Find", tf.Caption);
+        Assert.Equal ("_Find", tf.Title);
 
         // Dialog has not yet been confirmed with a choice
         Assert.True (dlg.Canceled);

+ 102 - 4
Tests/UnitTests/Views/TextFieldTests.cs

@@ -145,7 +145,7 @@ public class TextFieldTests (ITestOutputHelper output)
         TextField tf = GetTextFieldsInView ();
 
         // Caption has no effect when focused
-        tf.Caption = caption;
+        tf.Title = caption;
         Application.RaiseKeyDownEvent ('\t');
         Assert.False (tf.HasFocus);
 
@@ -165,7 +165,7 @@ public class TextFieldTests (ITestOutputHelper output)
 
         TextField tf = GetTextFieldsInView ();
 
-        tf.Caption = caption;
+        tf.Title = caption;
         Application.RaiseKeyDownEvent ('\t');
         Assert.False (tf.HasFocus);
 
@@ -185,7 +185,7 @@ public class TextFieldTests (ITestOutputHelper output)
         tf.Draw ();
         DriverAssert.AssertDriverContentsAre ("", output);
 
-        tf.Caption = "Enter txt";
+        tf.Title = "Enter txt";
         Application.RaiseKeyDownEvent ('\t');
 
         // Caption should appear when not focused and no text
@@ -212,7 +212,7 @@ public class TextFieldTests (ITestOutputHelper output)
         DriverAssert.AssertDriverContentsAre ("", output);
 
         // Caption has no effect when focused
-        tf.Caption = "Enter txt";
+        tf.Title = "Enter txt";
         Assert.True (tf.HasFocus);
         View.SetClipToScreen ();
         tf.Draw ();
@@ -227,6 +227,104 @@ public class TextFieldTests (ITestOutputHelper output)
         Application.Top.Dispose ();
     }
 
+    [Fact]
+    [AutoInitShutdown]
+    public void Title_RendersAsCaption_WithCorrectAttributes ()
+    {
+        TextField tf = GetTextFieldsInView ();
+
+        // Set a title (caption)
+        tf.Title = "Enter text";
+        
+        // Remove focus so caption appears
+        Application.RaiseKeyDownEvent ('\t');
+        Assert.False (tf.HasFocus);
+
+        View.SetClipToScreen ();
+        tf.Draw ();
+        
+        // Verify the caption text is rendered
+        DriverAssert.AssertDriverContentsAre ("Enter text", output);
+
+        // Verify the caption uses dimmed color attribute
+        Attribute captionAttr = new Attribute (
+            tf.GetAttributeForRole (VisualRole.Editable).Foreground.GetDimColor (),
+            tf.GetAttributeForRole (VisualRole.Editable).Background);
+
+        // All characters in "Enter text" should have the caption attribute
+        DriverAssert.AssertDriverAttributesAre ("0000000000", output, Application.Driver, captionAttr);
+
+        Application.Top.Dispose ();
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void Title_WithHotkey_RendersUnderlined ()
+    {
+        TextField tf = GetTextFieldsInView ();
+
+        // Title with hotkey should be rendered with the hotkey underlined when not focused
+        tf.Title = "_Find";
+        
+        // Remove focus so caption appears
+        Application.RaiseKeyDownEvent ('\t');
+        Assert.False (tf.HasFocus);
+
+        View.SetClipToScreen ();
+        tf.Draw ();
+        
+        // The hotkey character 'F' should be rendered (without the underscore in the actual text)
+        DriverAssert.AssertDriverContentsAre ("Find", output);
+
+        // Verify the hotkey character 'F' has underline style
+        Attribute captionAttr = new Attribute (
+            tf.GetAttributeForRole (VisualRole.Editable).Foreground.GetDimColor (),
+            tf.GetAttributeForRole (VisualRole.Editable).Background);
+        Attribute hotkeyAttr = new Attribute (
+            tf.GetAttributeForRole (VisualRole.Editable).Foreground.GetDimColor (),
+            tf.GetAttributeForRole (VisualRole.Editable).Background,
+            tf.GetAttributeForRole (VisualRole.Editable).Style | TextStyle.Underline);
+
+        // F is underlined (index 1), remaining characters use normal caption attribute (index 0)
+        DriverAssert.AssertDriverAttributesAre ("1000", output, Application.Driver, captionAttr, hotkeyAttr);
+
+        Application.Top.Dispose ();
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void Title_WithHotkey_MiddleCharacter_RendersUnderlined ()
+    {
+        TextField tf = GetTextFieldsInView ();
+
+        // Title with hotkey in middle of text
+        tf.Title = "Enter _Text";
+        
+        // Remove focus so caption appears
+        Application.RaiseKeyDownEvent ('\t');
+        Assert.False (tf.HasFocus);
+
+        View.SetClipToScreen ();
+        tf.Draw ();
+        
+        // The underscore should not be rendered, 'T' should be underlined
+        DriverAssert.AssertDriverContentsAre ("Enter Text", output);
+
+        // Verify the hotkey character 'T' has underline style
+        Attribute captionAttr = new Attribute (
+            tf.GetAttributeForRole (VisualRole.Editable).Foreground.GetDimColor (),
+            tf.GetAttributeForRole (VisualRole.Editable).Background);
+        Attribute hotkeyAttr = new Attribute (
+            tf.GetAttributeForRole (VisualRole.Editable).Foreground.GetDimColor (),
+            tf.GetAttributeForRole (VisualRole.Editable).Background,
+            tf.GetAttributeForRole (VisualRole.Editable).Style | TextStyle.Underline);
+
+        // "Enter " (6 chars) + "T" (underlined) + "ext" (3 chars)
+        DriverAssert.AssertDriverAttributesAre ("0000001000", output, Application.Driver, captionAttr, hotkeyAttr);
+
+        Application.Top.Dispose ();
+    }
+
     [Fact]
     [TextFieldTestsAutoInitShutdown]
     public void Changing_SelectedStart_Or_CursorPosition_Update_SelectedLength_And_SelectedText ()

BIN
local_packages/Terminal.Gui.2.0.0.nupkg


BIN
local_packages/Terminal.Gui.2.0.0.snupkg