mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-26 15:57:56 +01:00
Initial commit
This commit is contained in:
@@ -159,7 +159,7 @@ public static partial class Application // Mouse handling
|
||||
return;
|
||||
}
|
||||
|
||||
if (GrabMouse (deepestViewUnderMouse, mouseEvent))
|
||||
if (HandleMouseGrab (deepestViewUnderMouse, mouseEvent))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -245,7 +245,7 @@ public static partial class Application // Mouse handling
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool GrabMouse (View? deepestViewUnderMouse, MouseEvent mouseEvent)
|
||||
internal static bool HandleMouseGrab (View? deepestViewUnderMouse, MouseEvent mouseEvent)
|
||||
{
|
||||
if (MouseGrabView is { })
|
||||
{
|
||||
|
||||
@@ -14,6 +14,8 @@ public partial class View // Keyboard APIs
|
||||
HotKeySpecifier = (Rune)'_';
|
||||
TitleTextFormatter.HotKeyChanged += TitleTextFormatter_HotKeyChanged;
|
||||
|
||||
// TODO: It's incorrect to think of Commands as being Keyboard things. The code below should be moved to View.cs
|
||||
|
||||
// By default, the HotKey command sets the focus
|
||||
AddCommand (Command.HotKey, OnHotKey);
|
||||
|
||||
@@ -614,7 +616,7 @@ public partial class View // Keyboard APIs
|
||||
// Now, process any key bindings in the subviews that are tagged to KeyBindingScope.HotKey.
|
||||
foreach (View subview in Subviews)
|
||||
{
|
||||
if (subview == Focused)
|
||||
if (subview.HasFocus)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -406,7 +406,11 @@ public partial class View // Mouse APIs
|
||||
// If mouse is still in bounds, generate a click
|
||||
if (!WantContinuousButtonPressed && Viewport.Contains (mouseEvent.Position))
|
||||
{
|
||||
return OnMouseClick (new (mouseEvent));
|
||||
var meea = new MouseEventEventArgs (mouseEvent);
|
||||
|
||||
// We can ignore the return value of OnMouseClick; if the click is handled
|
||||
// meea.Handled and meea.MouseEvent.Handled will be true
|
||||
OnMouseClick (meea);
|
||||
}
|
||||
|
||||
return mouseEvent.Handled = true;
|
||||
|
||||
@@ -292,7 +292,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
|
||||
/// </returns>
|
||||
internal bool RestoreFocus ()
|
||||
{
|
||||
View [] indicies = GetFocusChain (NavigationDirection.Forward, TabStop);
|
||||
View [] indicies = GetFocusChain (NavigationDirection.Forward, null);
|
||||
|
||||
if (Focused is null && _previouslyFocused is { } && indicies.Contains (_previouslyFocused))
|
||||
{
|
||||
|
||||
@@ -21,7 +21,8 @@ namespace Terminal.Gui;
|
||||
/// be fired.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Set <see cref="View.WantContinuousButtonPressed"/> to <see langword="true"/> to have the <see cref="View.Accept"/> event
|
||||
/// Set <see cref="View.WantContinuousButtonPressed"/> to <see langword="true"/> to have the
|
||||
/// <see cref="View.Accept"/> event
|
||||
/// invoked repeatedly while the button is pressed.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
@@ -34,13 +35,13 @@ public class Button : View, IDesignable
|
||||
private bool _isDefault;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether <see cref="Button"/>s are shown with a shadow effect by default.
|
||||
/// Gets or sets whether <see cref="Button"/>s are shown with a shadow effect by default.
|
||||
/// </summary>
|
||||
[SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
|
||||
public static ShadowStyle DefaultShadow { get; set; } = ShadowStyle.None;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default Highlight Style.
|
||||
/// Gets or sets the default Highlight Style.
|
||||
/// </summary>
|
||||
[SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
|
||||
public static HighlightStyle DefaultHighlightStyle { get; set; } = HighlightStyle.Pressed | HighlightStyle.Hover;
|
||||
@@ -62,11 +63,31 @@ public class Button : View, IDesignable
|
||||
CanFocus = true;
|
||||
|
||||
// Override default behavior of View
|
||||
AddCommand (Command.HotKey, () =>
|
||||
{
|
||||
SetFocus ();
|
||||
return !OnAccept ();
|
||||
});
|
||||
AddCommand (
|
||||
Command.HotKey,
|
||||
() =>
|
||||
{
|
||||
bool cachedIsDefault = IsDefault; // Supports "Swap Default" in Buttons scenario
|
||||
|
||||
bool? handled = OnAccept ();
|
||||
|
||||
if (handled == true)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
SetFocus ();
|
||||
|
||||
// TODO: If `IsDefault` were a property on `View` *any* View could work this way. That's theoretical as
|
||||
// TODO: no use-case has been identified for any View other than Button to act like this.
|
||||
// If Accept was not handled...
|
||||
if (cachedIsDefault && SuperView is { })
|
||||
{
|
||||
return SuperView.InvokeCommand (Command.Accept);
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
KeyBindings.Add (Key.Space, Command.HotKey);
|
||||
KeyBindings.Add (Key.Enter, Command.HotKey);
|
||||
@@ -80,7 +101,7 @@ public class Button : View, IDesignable
|
||||
|
||||
private bool _wantContinuousButtonPressed;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <inheritdoc/>
|
||||
public override bool WantContinuousButtonPressed
|
||||
{
|
||||
get => _wantContinuousButtonPressed;
|
||||
@@ -104,10 +125,7 @@ public class Button : View, IDesignable
|
||||
}
|
||||
}
|
||||
|
||||
private void Button_MouseClick (object sender, MouseEventEventArgs e)
|
||||
{
|
||||
e.Handled = InvokeCommand (Command.HotKey) == true;
|
||||
}
|
||||
private void Button_MouseClick (object sender, MouseEventEventArgs e) { e.Handled = InvokeCommand (Command.HotKey) == true; }
|
||||
|
||||
private void Button_TitleChanged (object sender, EventArgs<string> e)
|
||||
{
|
||||
@@ -115,22 +133,24 @@ public class Button : View, IDesignable
|
||||
TextFormatter.HotKeySpecifier = HotKeySpecifier;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <inheritdoc/>
|
||||
public override string Text
|
||||
{
|
||||
get => base.Title;
|
||||
set => base.Text = base.Title = value;
|
||||
get => Title;
|
||||
set => base.Text = Title = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <inheritdoc/>
|
||||
public override Rune HotKeySpecifier
|
||||
{
|
||||
get => base.HotKeySpecifier;
|
||||
set => TextFormatter.HotKeySpecifier = base.HotKeySpecifier = value;
|
||||
}
|
||||
|
||||
/// <summary>Gets or sets whether the <see cref="Button"/> is the default action to activate in a dialog.</summary>
|
||||
/// <value><c>true</c> if is default; otherwise, <c>false</c>.</value>
|
||||
/// <summary>
|
||||
/// Gets or sets whether the <see cref="Button"/> will invoke the <see cref="Command.Accept"/>
|
||||
/// command on the <see cref="View.SuperView"/> if <see cref="View.Accept"/> is not handled by a subscriber.
|
||||
/// </summary>
|
||||
public bool IsDefault
|
||||
{
|
||||
get => _isDefault;
|
||||
@@ -158,6 +178,7 @@ public class Button : View, IDesignable
|
||||
if (TextFormatter.Text [i] == Text [0])
|
||||
{
|
||||
Move (i, 0);
|
||||
|
||||
return null; // Don't show the cursor
|
||||
}
|
||||
}
|
||||
@@ -170,6 +191,7 @@ public class Button : View, IDesignable
|
||||
protected override void UpdateTextFormatterText ()
|
||||
{
|
||||
base.UpdateTextFormatterText ();
|
||||
|
||||
if (NoDecorations)
|
||||
{
|
||||
TextFormatter.Text = Text;
|
||||
@@ -191,11 +213,11 @@ public class Button : View, IDesignable
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <inheritdoc/>
|
||||
public bool EnableForDesign ()
|
||||
{
|
||||
Title = "_Button";
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
namespace Terminal.Gui;
|
||||
|
||||
// TODO: FrameView is mis-named, really. It's far more about it being a TabGroup than a frame.
|
||||
/// <summary>
|
||||
/// The FrameView is a container View with a border around it.
|
||||
/// </summary>
|
||||
@@ -23,6 +24,7 @@ public class FrameView : View
|
||||
|
||||
private void FrameView_MouseClick (object sender, MouseEventEventArgs e)
|
||||
{
|
||||
// base sets focus on HotKey
|
||||
e.Handled = InvokeCommand (Command.HotKey) == true;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,25 +32,6 @@ public class Window : Toplevel
|
||||
BorderStyle = DefaultBorderStyle;
|
||||
ShadowStyle = DefaultShadow;
|
||||
|
||||
// This enables the default button to be activated by the Enter key.
|
||||
AddCommand (
|
||||
Command.Accept,
|
||||
() =>
|
||||
{
|
||||
// TODO: Perhaps all views should support the concept of being default?
|
||||
// ReSharper disable once InvertIf
|
||||
if (Subviews.FirstOrDefault (v => v is Button { IsDefault: true, Enabled: true }) is Button
|
||||
defaultBtn)
|
||||
{
|
||||
defaultBtn.InvokeCommand (Command.Accept);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return OnAccept ();
|
||||
}
|
||||
);
|
||||
|
||||
KeyBindings.Add (Key.Enter, Command.Accept);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ public class Buttons : Scenario
|
||||
// This is the default button (IsDefault = true); if user presses ENTER in the TextField
|
||||
// the scenario will quit
|
||||
var defaultButton = new Button { X = Pos.Center (), Y = Pos.AnchorEnd (), IsDefault = true, Text = "_Quit" };
|
||||
defaultButton.Accept += (s, e) => Application.RequestStop ();
|
||||
main.Accept += (s, e) => Application.RequestStop ();
|
||||
main.Add (defaultButton);
|
||||
|
||||
var swapButton = new Button
|
||||
@@ -46,6 +46,7 @@ public class Buttons : Scenario
|
||||
|
||||
swapButton.Accept += (s, e) =>
|
||||
{
|
||||
e.Handled = !swapButton.IsDefault;
|
||||
defaultButton.IsDefault = !defaultButton.IsDefault;
|
||||
swapButton.IsDefault = !swapButton.IsDefault;
|
||||
};
|
||||
@@ -57,6 +58,7 @@ public class Buttons : Scenario
|
||||
{
|
||||
string btnText = button.Text;
|
||||
MessageBox.Query ("Message", $"Did you click {txt}?", "Yes", "No");
|
||||
e.Handled = true;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -96,11 +98,19 @@ public class Buttons : Scenario
|
||||
main.Add (
|
||||
button = new () { X = 2, Y = Pos.Bottom (button) + 1, Height = 2, Text = "a Newline\nin the button" }
|
||||
);
|
||||
button.Accept += (s, e) => MessageBox.Query ("Message", "Question?", "Yes", "No");
|
||||
button.Accept += (s, e) =>
|
||||
{
|
||||
MessageBox.Query ("Message", "Question?", "Yes", "No");
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
var textChanger = new Button { X = 2, Y = Pos.Bottom (button) + 1, Text = "Te_xt Changer" };
|
||||
main.Add (textChanger);
|
||||
textChanger.Accept += (s, e) => textChanger.Text += "!";
|
||||
textChanger.Accept += (s, e) =>
|
||||
{
|
||||
textChanger.Text += "!";
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
main.Add (
|
||||
button = new ()
|
||||
|
||||
@@ -22,6 +22,7 @@ public class Dialogs : Scenario
|
||||
|
||||
var frame = new FrameView
|
||||
{
|
||||
TabStop = TabBehavior.TabStop, // FrameView normally sets to TabGroup
|
||||
X = Pos.Center (),
|
||||
Y = 1,
|
||||
Width = Dim.Percent (75),
|
||||
@@ -181,7 +182,7 @@ public class Dialogs : Scenario
|
||||
X = Pos.Center (), Y = Pos.Bottom (frame) + 2, IsDefault = true, Text = "_Show Dialog"
|
||||
};
|
||||
|
||||
showDialogButton.Accept += (s, e) =>
|
||||
app.Accept += (s, e) =>
|
||||
{
|
||||
Dialog dlg = CreateDemoDialog (
|
||||
widthEdit,
|
||||
@@ -194,6 +195,7 @@ public class Dialogs : Scenario
|
||||
);
|
||||
Application.Run (dlg);
|
||||
dlg.Dispose ();
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
app.Add (showDialogButton);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
- What are the visual cues that help the user know what keystrokes will change the focus?
|
||||
- What are the visual cues that help the user know what keystrokes will cause action in elements of the application that don't currently have focus?
|
||||
- What is the order in which UI elements are traversed when using keyboard navigation?
|
||||
- What are the default actions for standard key/mouse input (e.g. Hotkey, `Space`, `Enter`, `MouseClick`)?
|
||||
|
||||
## Lexicon & Taxonomy
|
||||
|
||||
@@ -208,7 +209,18 @@ These could also be named `Gain/Lose`. They could also be combined into a single
|
||||
|
||||
QUESTION: Should we retain the same names as in v1 to simplify porting? Or, given the semantics of `Handled` v. `Cancel` are reversed would it be better to rename and/or combine?
|
||||
|
||||
## `TabIndex` and `TabIndexes`
|
||||
## Built-In Views Interactivity
|
||||
|
||||
| | | | | **Keyboard** | | | | **Mouse** | | | | |
|
||||
|----------------|-------------------------|------------|---------------|--------------|-----------------------|------------------------------|---------------------------|------------------------------|------------------------------|------------------------------|----------------|---------------|
|
||||
| | **Number<br>of States** | **Static** | **IsDefault** | **Hotkeys** | **Select<br>Command** | **Accept<br>Command** | **Hotkey<br>Command** | **CanFocus<br>Click** | **CanFocus<br>DblCLick** | **!CanFocus<br>Click** | **RightClick** | **GrabMouse** |
|
||||
| **View** | 1 | Yes | No | 1 | OnSelect | OnAccept | Focus | Focus | | | | No |
|
||||
| **Label** | 1 | Yes | No | 1 | OnSelect | OnAccept | FocusNext | Focus | | FocusNext | | No |
|
||||
| **Button** | 1 | No | Yes | 1 | OnSelect | Focus<br>OnAccept | Focus<br>OnAccept | HotKey | | Select | | No |
|
||||
| **Checkbox** | 3 | No | No | 1 | OnSelect<br>Advance | OnAccept | OnAccept | Select | | Select | | No |
|
||||
| **RadioGroup** | > 1 | No | No | 2+ | Advance | Set SelectedItem<br>OnAccept | Focus<br>Set SelectedItem | SetFocus<br>Set _cursor | | SetFocus<br>Set _cursor | | No |
|
||||
| **Slider** | > 1 | No | No | 1 | SetFocusedOption | SetFocusedOption<br>OnAccept | Focus | SetFocus<br>SetFocusedOption | | SetFocus<br>SetFocusedOption | | Yes |
|
||||
| **ListView** | > 1 | No | No | 1 | MarkUnMarkRow | OpenSelectedItem<br>OnAccept | OnAccept | SetMark<br>OnSelectedChanged | OpenSelectedItem<br>OnAccept | | | No |
|
||||
|
||||
### v1 Behavior
|
||||
|
||||
|
||||
Reference in New Issue
Block a user