mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-26 15:57:56 +01:00
Remove TextField.Caption property; use Title with hotkey navigation support (#4352)
* Initial plan * Initial exploration - understanding TextField Caption and Title Co-authored-by: tig <585482+tig@users.noreply.github.com> * Remove TextField.Caption and use Title instead with hotkey support Co-authored-by: tig <585482+tig@users.noreply.github.com> * Add defensive check to ensure TitleTextFormatter.Text is set Co-authored-by: tig <585482+tig@users.noreply.github.com> * Final changes - all tests passing Co-authored-by: tig <585482+tig@users.noreply.github.com> * Fixed bugs. * Add comprehensive tests for caption rendering with attributes validation Co-authored-by: tig <585482+tig@users.noreply.github.com> * Fix: Disable TextField hotkey functionality to prevent input interception TextField's Title is used as a caption/placeholder, not for hotkey navigation. Hotkey visual formatting (underline) is still rendered in the caption, but hotkey functionality is disabled to prevent keys like 'E' and 'F' from being intercepted when typing in the field. Updated test to expect "_Find" instead of "Find" to match resource change. Co-authored-by: tig <585482+tig@users.noreply.github.com> * Fix: Support Alt+key hotkey navigation while allowing normal typing Override AddKeyBindingsForHotKey to only bind Alt+key combinations (e.g., Alt+F for "_Find"), not the bare keys. This allows: - Alt+F to navigate to the TextField with Title="_Find" - Normal typing of 'F', 'E', etc. without interception Previously, both bare key and Alt+key were bound, causing typing issues. Now TextField properly supports hotkey navigation without interfering with text input. Co-authored-by: tig <585482+tig@users.noreply.github.com> * Changes before error encountered Co-authored-by: tig <585482+tig@users.noreply.github.com> * Refactor hotkey handling to support command context Refactored `RaiseHandlingHotKey` to accept an `ICommandContext? ctx` parameter, enabling context-aware hotkey handling. Updated `Command.HotKey` definitions across multiple classes (`View`, `CheckBox`, `Label`, `MenuBarv2`, `RadioGroup`, `TextField`) to utilize the new context parameter. Enhanced XML documentation for `RaiseHandlingHotKey` to clarify its usage and return values. Added a context-aware hotkey handler to `TextField` with additional logic for focus handling. Refactored attribute initialization and improved code readability in `TextField` by aligning parameters and removing unused `HotKeySpecifier` initialization. These changes improve flexibility, maintainability, and consistency across the codebase. * Remove TextField.Caption property; use Title with hotkey navigation support Co-authored-by: tig <585482+tig@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: tig <585482+tig@users.noreply.github.com> Co-authored-by: Tig <tig@users.noreply.github.com>
This commit is contained in:
@@ -85,7 +85,7 @@ public class CharacterMap : Scenario
|
|||||||
X = Pos.Right (jumpLabel) + 1,
|
X = Pos.Right (jumpLabel) + 1,
|
||||||
Y = Pos.Y (_charMap),
|
Y = Pos.Y (_charMap),
|
||||||
Width = 17,
|
Width = 17,
|
||||||
Caption = "e.g. 01BE3 or ✈"
|
Title = "e.g. 01BE3 or ✈"
|
||||||
|
|
||||||
//SchemeName = "Dialog"
|
//SchemeName = "Dialog"
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ public class TextInputControls : Scenario
|
|||||||
X = Pos.Right (label) + 1,
|
X = Pos.Right (label) + 1,
|
||||||
Y = Pos.Bottom (textField),
|
Y = Pos.Bottom (textField),
|
||||||
Width = Dim.Percent (50) - 1,
|
Width = Dim.Percent (50) - 1,
|
||||||
Caption = "TextField with caption"
|
Title = "TextField with caption"
|
||||||
};
|
};
|
||||||
|
|
||||||
win.Add (textField);
|
win.Add (textField);
|
||||||
|
|||||||
4
Terminal.Gui/Resources/Strings.Designer.cs
generated
4
Terminal.Gui/Resources/Strings.Designer.cs
generated
@@ -628,7 +628,7 @@ namespace Terminal.Gui.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Enter Path.
|
/// Looks up a localized string similar to _Enter Path.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string fdPathCaption {
|
internal static string fdPathCaption {
|
||||||
get {
|
get {
|
||||||
@@ -682,7 +682,7 @@ namespace Terminal.Gui.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Find.
|
/// Looks up a localized string similar to _Find.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string fdSearchCaption {
|
internal static string fdSearchCaption {
|
||||||
get {
|
get {
|
||||||
|
|||||||
@@ -192,10 +192,7 @@
|
|||||||
<value>Modified</value>
|
<value>Modified</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="fdPathCaption" xml:space="preserve">
|
<data name="fdPathCaption" xml:space="preserve">
|
||||||
<value>Enter Path</value>
|
<value>_Enter Path</value>
|
||||||
</data>
|
|
||||||
<data name="fdSearchCaption" xml:space="preserve">
|
|
||||||
<value>Find</value>
|
|
||||||
</data>
|
</data>
|
||||||
<data name="fdSize" xml:space="preserve">
|
<data name="fdSize" xml:space="preserve">
|
||||||
<value>Size</value>
|
<value>Size</value>
|
||||||
@@ -359,4 +356,7 @@
|
|||||||
<value>_Tree</value>
|
<value>_Tree</value>
|
||||||
<comment>Show/Hide Tree View</comment>
|
<comment>Show/Hide Tree View</comment>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="fdSearchCaption" xml:space="preserve">
|
||||||
|
<value>_Find</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -22,9 +22,9 @@ public partial class View // Command APIs
|
|||||||
// HotKey - SetFocus and raise HandlingHotKey
|
// HotKey - SetFocus and raise HandlingHotKey
|
||||||
AddCommand (
|
AddCommand (
|
||||||
Command.HotKey,
|
Command.HotKey,
|
||||||
() =>
|
(ctx) =>
|
||||||
{
|
{
|
||||||
if (RaiseHandlingHotKey () is true)
|
if (RaiseHandlingHotKey (ctx) is true)
|
||||||
{
|
{
|
||||||
return 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"/>.
|
/// <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.
|
/// event. The default <see cref="Command.HotKey"/> handler calls this method.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="ctx">The context to pass with the command.</param>
|
||||||
/// <returns>
|
/// <returns>
|
||||||
/// <see langword="null"/> if no event was raised; input processing should continue.
|
/// <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
|
/// <see langword="false"/> if the event was raised and was not handled (or cancelled); input processing should
|
||||||
/// continue.
|
/// continue.
|
||||||
/// <see langword="true"/> if the event was raised and handled (or cancelled); input processing should stop.
|
/// <see langword="true"/> if the event was raised and handled (or cancelled); input processing should stop.
|
||||||
/// </returns>
|
/// </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})");
|
//Logging.Debug ($"{Title} ({args.Context?.Source?.Title})");
|
||||||
|
|
||||||
// Best practice is to invoke the virtual method first.
|
// Best practice is to invoke the virtual method first.
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ public class CheckBox : View
|
|||||||
// Hotkey - Advance state and raise Select event - DO NOT raise Accept
|
// Hotkey - Advance state and raise Select event - DO NOT raise Accept
|
||||||
AddCommand (Command.HotKey, ctx =>
|
AddCommand (Command.HotKey, ctx =>
|
||||||
{
|
{
|
||||||
if (RaiseHandlingHotKey () is true)
|
if (RaiseHandlingHotKey (ctx) is true)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ public class FileDialog : Dialog, IDesignable
|
|||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
_tbPath = new () { Width = Dim.Fill (),/* CaptionColor = new (Color.Black)*/ };
|
_tbPath = new () { Width = Dim.Fill () };
|
||||||
|
|
||||||
_tbPath.KeyDown += (s, k) =>
|
_tbPath.KeyDown += (s, k) =>
|
||||||
{
|
{
|
||||||
@@ -248,7 +248,6 @@ public class FileDialog : Dialog, IDesignable
|
|||||||
X = 0,
|
X = 0,
|
||||||
Width = Dim.Fill (),
|
Width = Dim.Fill (),
|
||||||
Y = Pos.AnchorEnd (),
|
Y = Pos.AnchorEnd (),
|
||||||
HotKey = Key.F.WithAlt,
|
|
||||||
Id = "_tbFind",
|
Id = "_tbFind",
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -456,8 +455,8 @@ public class FileDialog : Dialog, IDesignable
|
|||||||
_btnBack.Text = GetBackButtonText ();
|
_btnBack.Text = GetBackButtonText ();
|
||||||
_btnForward.Text = GetForwardButtonText ();
|
_btnForward.Text = GetForwardButtonText ();
|
||||||
|
|
||||||
_tbPath.Caption = Style.PathCaption;
|
_tbPath.Title = Style.PathCaption;
|
||||||
_tbFind.Caption = Style.SearchCaption;
|
_tbFind.Title = Style.SearchCaption;
|
||||||
|
|
||||||
_tbPath.Autocomplete.Scheme = new (_tbPath.GetScheme ())
|
_tbPath.Autocomplete.Scheme = new (_tbPath.GetScheme ())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ public class Label : View, IDesignable
|
|||||||
|
|
||||||
private bool? InvokeHotKeyOnNextPeer (ICommandContext commandContext)
|
private bool? InvokeHotKeyOnNextPeer (ICommandContext commandContext)
|
||||||
{
|
{
|
||||||
if (RaiseHandlingHotKey () == true)
|
if (RaiseHandlingHotKey (commandContext) == true)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,11 +31,11 @@ public class MenuBarv2 : Menuv2, IDesignable
|
|||||||
|
|
||||||
AddCommand (
|
AddCommand (
|
||||||
Command.HotKey,
|
Command.HotKey,
|
||||||
() =>
|
(ctx) =>
|
||||||
{
|
{
|
||||||
// Logging.Debug ($"{Title} - Command.HotKey");
|
// Logging.Debug ($"{Title} - Command.HotKey");
|
||||||
|
|
||||||
if (RaiseHandlingHotKey () is true)
|
if (RaiseHandlingHotKey (ctx) is true)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ public class RadioGroup : View, IDesignable, IOrientation
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (RaiseHandlingHotKey () == true)
|
if (RaiseHandlingHotKey (ctx) == true)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,9 +28,6 @@ public class TextField : View, IDesignable
|
|||||||
_selectedStart = -1;
|
_selectedStart = -1;
|
||||||
_text = new ();
|
_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;
|
ReadOnly = false;
|
||||||
Autocomplete = new TextFieldAutocomplete ();
|
Autocomplete = new TextFieldAutocomplete ();
|
||||||
Height = Dim.Auto (DimAutoStyle.Text, 1);
|
Height = Dim.Auto (DimAutoStyle.Text, 1);
|
||||||
@@ -40,9 +37,6 @@ public class TextField : View, IDesignable
|
|||||||
Used = true;
|
Used = true;
|
||||||
WantMousePositionReports = true;
|
WantMousePositionReports = true;
|
||||||
|
|
||||||
// By default, disable hotkeys (in case someome sets Title)
|
|
||||||
HotKeySpecifier = new ('\xffff');
|
|
||||||
|
|
||||||
_historyText.ChangeText += HistoryText_ChangeText;
|
_historyText.ChangeText += HistoryText_ChangeText;
|
||||||
|
|
||||||
Initialized += TextField_Initialized;
|
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
|
// Default keybindings for this view
|
||||||
// We follow this as closely as possible: https://en.wikipedia.org/wiki/Table_of_keyboard_shortcuts
|
// We follow this as closely as possible: https://en.wikipedia.org/wiki/Table_of_keyboard_shortcuts
|
||||||
KeyBindings.Add (Key.Delete, Command.DeleteCharRight);
|
KeyBindings.Add (Key.Delete, Command.DeleteCharRight);
|
||||||
@@ -411,15 +429,6 @@ public class TextField : View, IDesignable
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public IAutocomplete Autocomplete { get; set; }
|
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>
|
/// <summary>Get the Context Menu for this view.</summary>
|
||||||
[CanBeNull]
|
[CanBeNull]
|
||||||
public PopoverMenu ContextMenu { get; private set; }
|
public PopoverMenu ContextMenu { get; private set; }
|
||||||
@@ -920,7 +929,7 @@ public class TextField : View, IDesignable
|
|||||||
_isDrawing = true;
|
_isDrawing = true;
|
||||||
|
|
||||||
// Cache attributes as GetAttributeForRole might raise events
|
// 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 readonlyAttribute = GetAttributeForRole (VisualRole.ReadOnly);
|
||||||
Attribute normalAttribute = GetAttributeForRole (VisualRole.Editable);
|
Attribute normalAttribute = GetAttributeForRole (VisualRole.Editable);
|
||||||
|
|
||||||
@@ -943,7 +952,7 @@ public class TextField : View, IDesignable
|
|||||||
{
|
{
|
||||||
// Disabled
|
// Disabled
|
||||||
SetAttributeForRole (VisualRole.Disabled);
|
SetAttributeForRole (VisualRole.Disabled);
|
||||||
}
|
}
|
||||||
else if (idx == _cursorPosition && HasFocus && !Used && SelectedLength == 0 && !ReadOnly)
|
else if (idx == _cursorPosition && HasFocus && !Used && SelectedLength == 0 && !ReadOnly)
|
||||||
{
|
{
|
||||||
// Selected text
|
// Selected text
|
||||||
@@ -1157,7 +1166,6 @@ public class TextField : View, IDesignable
|
|||||||
///// </summary>
|
///// </summary>
|
||||||
//public event EventHandler<StateEventArgs<string>> TextChanged;
|
//public event EventHandler<StateEventArgs<string>> TextChanged;
|
||||||
|
|
||||||
|
|
||||||
/// <summary>Undoes the latest changes.</summary>
|
/// <summary>Undoes the latest changes.</summary>
|
||||||
public void Undo ()
|
public void Undo ()
|
||||||
{
|
{
|
||||||
@@ -1699,25 +1707,33 @@ public class TextField : View, IDesignable
|
|||||||
private void RenderCaption ()
|
private void RenderCaption ()
|
||||||
{
|
{
|
||||||
if (HasFocus
|
if (HasFocus
|
||||||
|| Caption == null
|
|| string.IsNullOrEmpty (Title)
|
||||||
|| Caption.Length == 0
|
|
||||||
|| Text?.Length > 0)
|
|| Text?.Length > 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var color = new Attribute (CaptionColor, GetAttributeForRole (VisualRole.Editable).Background, GetAttributeForRole (VisualRole.Editable).Style);
|
// Ensure TitleTextFormatter has the current Title text
|
||||||
SetAttribute (color);
|
// (should already be set by the Title property setter, but being defensive)
|
||||||
|
if (TitleTextFormatter.Text != Title)
|
||||||
Move (0, 0);
|
|
||||||
string render = Caption;
|
|
||||||
|
|
||||||
if (render.GetColumns () > Viewport.Width)
|
|
||||||
{
|
{
|
||||||
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)
|
private void SetClipboard (IEnumerable<Rune> text)
|
||||||
@@ -1814,11 +1830,11 @@ public class TextField : View, IDesignable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc/>
|
||||||
public bool EnableForDesign ()
|
public bool EnableForDesign ()
|
||||||
{
|
{
|
||||||
Text = "This is a test.";
|
Text = "This is a test.";
|
||||||
Caption = "Caption";
|
Title = "Caption";
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ public class FileDialogTests ()
|
|||||||
Assert.IsType<TextField> (dlg.MostFocused);
|
Assert.IsType<TextField> (dlg.MostFocused);
|
||||||
Assert.Same (tf, 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
|
// Dialog has not yet been confirmed with a choice
|
||||||
Assert.True (dlg.Canceled);
|
Assert.True (dlg.Canceled);
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ public class TextFieldTests (ITestOutputHelper output)
|
|||||||
TextField tf = GetTextFieldsInView ();
|
TextField tf = GetTextFieldsInView ();
|
||||||
|
|
||||||
// Caption has no effect when focused
|
// Caption has no effect when focused
|
||||||
tf.Caption = caption;
|
tf.Title = caption;
|
||||||
Application.RaiseKeyDownEvent ('\t');
|
Application.RaiseKeyDownEvent ('\t');
|
||||||
Assert.False (tf.HasFocus);
|
Assert.False (tf.HasFocus);
|
||||||
|
|
||||||
@@ -165,7 +165,7 @@ public class TextFieldTests (ITestOutputHelper output)
|
|||||||
|
|
||||||
TextField tf = GetTextFieldsInView ();
|
TextField tf = GetTextFieldsInView ();
|
||||||
|
|
||||||
tf.Caption = caption;
|
tf.Title = caption;
|
||||||
Application.RaiseKeyDownEvent ('\t');
|
Application.RaiseKeyDownEvent ('\t');
|
||||||
Assert.False (tf.HasFocus);
|
Assert.False (tf.HasFocus);
|
||||||
|
|
||||||
@@ -185,7 +185,7 @@ public class TextFieldTests (ITestOutputHelper output)
|
|||||||
tf.Draw ();
|
tf.Draw ();
|
||||||
DriverAssert.AssertDriverContentsAre ("", output);
|
DriverAssert.AssertDriverContentsAre ("", output);
|
||||||
|
|
||||||
tf.Caption = "Enter txt";
|
tf.Title = "Enter txt";
|
||||||
Application.RaiseKeyDownEvent ('\t');
|
Application.RaiseKeyDownEvent ('\t');
|
||||||
|
|
||||||
// Caption should appear when not focused and no text
|
// Caption should appear when not focused and no text
|
||||||
@@ -212,7 +212,7 @@ public class TextFieldTests (ITestOutputHelper output)
|
|||||||
DriverAssert.AssertDriverContentsAre ("", output);
|
DriverAssert.AssertDriverContentsAre ("", output);
|
||||||
|
|
||||||
// Caption has no effect when focused
|
// Caption has no effect when focused
|
||||||
tf.Caption = "Enter txt";
|
tf.Title = "Enter txt";
|
||||||
Assert.True (tf.HasFocus);
|
Assert.True (tf.HasFocus);
|
||||||
View.SetClipToScreen ();
|
View.SetClipToScreen ();
|
||||||
tf.Draw ();
|
tf.Draw ();
|
||||||
@@ -227,6 +227,104 @@ public class TextFieldTests (ITestOutputHelper output)
|
|||||||
Application.Top.Dispose ();
|
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]
|
[Fact]
|
||||||
[TextFieldTestsAutoInitShutdown]
|
[TextFieldTestsAutoInitShutdown]
|
||||||
public void Changing_SelectedStart_Or_CursorPosition_Update_SelectedLength_And_SelectedText ()
|
public void Changing_SelectedStart_Or_CursorPosition_Update_SelectedLength_And_SelectedText ()
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user