Improved a better clipped screen. Fixes some bugs with ScrollView, Menu. Added some virtual methods. (#410)

* Improved a better clipped screen. Fixes some bugs with ScrollView, Menu. Added some virtual methods.

* Added some more key features, like shift. Cleaning and updating some stuffs .Added more features to TextField.

* Closes the menu even in a button pressed in another view than menu.

* Added a OnKeyPress action. Now all the keys events are properly mapped to the keys modifiers. Fixed a issue which keys like (ã, á, â) aren't correctly written.

* Fixed an issue with the shift flag for keys ControlA_Z.

* Ensures quiting the terminal if no other option is provided by pressing Ctrl-Q

* Ensures the exception is thrown before ordered.Reverse.

* Changed Button4 To Button3 for CursesDriver compatibility.

* Added support for word selection through keyboard and the mouse. With triple click all text is selected.

* Changed AllowNewLine to AllowWrap.

* Fix topological sort in view class (#413)

* AllowWrap removed and keys issues fixed.

* Removing ordered.Reverse (); Something went wrong.

* Fixes FrameView title.

* Reverted some MapKeyModifiers that prevented the display of some characters.

* Avoiding open the menu-bar every time we typing (€@£§). Alt key now only highlight the menu-bar without open it.

* Fixes hot-key issue preventing menu closing after opened.

* Curses now supports hot-keys and simulates AltMask with Alt+Space.  Also supports shift and ctrl combinations to use with text selection.

* Maintains the menu highlighted while focused.

* Removed the IsOutBounds method. This feature is for a future presentation.

Co-authored-by: En3Tho <37334640+En3Tho@users.noreply.github.com>
This commit is contained in:
BDisp
2020-05-13 17:28:35 +01:00
committed by GitHub
parent 1e2d6c5d87
commit e6c5b2599a
16 changed files with 786 additions and 187 deletions

View File

@@ -15,19 +15,45 @@ namespace Designer {
MessageBox.ErrorQuery (50, 7, "Error", "There is nothing to close", "Ok");
}
static void Copy ()
{
TextField textField = menu.LastFocused as TextField;
if (textField != null && textField.SelectedLength != 0) {
textField.Copy ();
}
}
static void Cut ()
{
TextField textField = menu.LastFocused as TextField;
if (textField != null && textField.SelectedLength != 0) {
textField.Cut ();
}
}
static void Paste ()
{
TextField textField = menu.LastFocused as TextField;
if (textField != null) {
textField.Paste ();
}
}
public static MenuBar menu;
public static void Main (string [] args)
{
Application.Init ();
var menu = new MenuBar (new MenuBarItem [] {
menu = new MenuBar (new MenuBarItem [] {
new MenuBarItem ("_File", new MenuItem [] {
new MenuItem ("_Close", "", () => Close ()),
new MenuItem ("_Quit", "", () => { Application.RequestStop (); })
}),
new MenuBarItem ("_Edit", new MenuItem [] {
new MenuItem ("_Copy", "", null),
new MenuItem ("C_ut", "", null),
new MenuItem ("_Paste", "", null)
new MenuItem ("_Copy", "", Copy),
new MenuItem ("C_ut", "", Cut),
new MenuItem ("_Paste", "", Paste)
}),
});
@@ -36,18 +62,22 @@ namespace Designer {
X = Pos.Left (login),
Y = Pos.Bottom (login) + 1
};
var test = new Label ("Test: ") {
X = Pos.Left (login),
Y = Pos.Bottom (password) + 1
};
var surface = new Surface () {
X = 0,
Y = 1,
Width = Dim.Percent (80),
Height = Dim.Fill ()
Width = Dim.Percent (50),
Height = Dim.Percent (50)
};
var loginText = new TextField("") {
X = Pos.Right(password),
Y = Pos.Top(login),
Width = 40,
Width = Dim.Percent(90),
ColorScheme = new ColorScheme() {
Focus = Attribute.Make(Color.BrightYellow, Color.DarkGray),
Normal = Attribute.Make(Color.Green, Color.BrightYellow),
@@ -55,6 +85,10 @@ namespace Designer {
HotNormal = Attribute.Make(Color.Red, Color.BrightRed),
},
};
loginText.MouseEnter += LoginText_MouseEnter;
loginText.MouseLeave += LoginText_MouseLeave;
loginText.Enter += LoginText_Enter;
loginText.Leave += LoginText_Leave;
var passText = new TextField ("") {
Secret = true,
@@ -63,9 +97,35 @@ namespace Designer {
Width = Dim.Width (loginText)
};
surface.Add (login, password, loginText, passText);
var testText = new TextField ("") {
X = Pos.Left (loginText),
Y = Pos.Top (test),
Width = Dim.Width (loginText)
};
surface.Add (login, password, test, loginText, passText, testText);
Application.Top.Add (menu, surface);
Application.Run ();
}
private static void LoginText_Leave (object sender, EventArgs e)
{
((TextField)sender).Text = $"Leaving from: {sender}";
}
private static void LoginText_Enter (object sender, EventArgs e)
{
((TextField)sender).Text = $"Entering in: {sender}";
}
private static void LoginText_MouseLeave (object sender, MouseEvent e)
{
((TextField)sender).Text = $"Mouse leave at X: {e.X}; Y: {e.Y} HasFocus: {e.View.HasFocus}";
}
private static void LoginText_MouseEnter (object sender, MouseEvent e)
{
((TextField)sender).Text = $"Mouse enter at X: {e.X}; Y: {e.Y} HasFocus: {e.View.HasFocus}";
}
}
}

View File

@@ -413,11 +413,11 @@ static class Demo {
#endregion
#region OnKeyDown / OnKeyUp Demo
private static void OnKeyDownUpDemo ()
#region OnKeyDown / OnKeyPress / OnKeyUp Demo
private static void OnKeyDownPressUpDemo ()
{
var container = new Dialog (
"OnKeyDown & OnKeyUp demo", 80, 20,
"OnKeyDown & OnKeyPress & OnKeyUp demo", 80, 20,
new Button ("Close") { Clicked = () => { Application.RequestStop (); } }) {
Width = Dim.Fill (),
Height = Dim.Fill (),
@@ -433,20 +433,45 @@ static class Demo {
listView.ColorScheme = Colors.TopLevel;
container.Add (listView);
void KeyUpDown (KeyEvent keyEvent, string updown)
void KeyDownPressUp (KeyEvent keyEvent, string updown)
{
if ((keyEvent.Key & Key.CtrlMask) != 0) {
list.Add ($"Key{updown,-4}: Ctrl ");
} else if ((keyEvent.Key & Key.AltMask) != 0) {
list.Add ($"Key{updown,-4}: Alt ");
} else {
list.Add ($"Key{updown,-4}: {(((uint)keyEvent.KeyValue & (uint)Key.CharMask) > 26 ? $"{(char)keyEvent.KeyValue}" : $"{keyEvent.Key}")}");
const int ident = -5;
switch (updown) {
case "Down":
case "Up":
case "Press":
var msg = $"Key{updown,ident}: ";
if ((keyEvent.Key & Key.ShiftMask) != 0)
msg += "Shift ";
if ((keyEvent.Key & Key.CtrlMask) != 0)
msg += "Ctrl ";
if ((keyEvent.Key & Key.AltMask) != 0)
msg += "Alt ";
msg += $"{(((uint)keyEvent.KeyValue & (uint)Key.CharMask) > 26 ? $"{(char)keyEvent.KeyValue}" : $"{keyEvent.Key}")}";
list.Add (msg);
break;
default:
if ((keyEvent.Key & Key.ShiftMask) != 0) {
list.Add ($"Key{updown,ident}: Shift ");
} else if ((keyEvent.Key & Key.CtrlMask) != 0) {
list.Add ($"Key{updown,ident}: Ctrl ");
} else if ((keyEvent.Key & Key.AltMask) != 0) {
list.Add ($"Key{updown,ident}: Alt ");
} else {
list.Add ($"Key{updown,ident}: {(((uint)keyEvent.KeyValue & (uint)Key.CharMask) > 26 ? $"{(char)keyEvent.KeyValue}" : $"{keyEvent.Key}")}");
}
break;
}
listView.MoveDown ();
}
container.OnKeyDown += (KeyEvent keyEvent) => KeyUpDown (keyEvent, "Down");
container.OnKeyUp += (KeyEvent keyEvent) => KeyUpDown (keyEvent, "Up");
container.OnKeyDown += (KeyEvent keyEvent) => KeyDownPressUp (keyEvent, "Down");
container.OnKeyPress += (KeyEvent keyEvent) => KeyDownPressUp (keyEvent, "Press");
container.OnKeyUp += (KeyEvent keyEvent) => KeyDownPressUp (keyEvent, "Up");
Application.Run (container);
}
#endregion
@@ -518,7 +543,7 @@ static class Demo {
}),
new MenuBarItem ("A_ssorted", new MenuItem [] {
new MenuItem ("_Show text alignments", "", () => ShowTextAlignments ()),
new MenuItem ("_OnKeyDown/Up", "", () => OnKeyDownUpDemo ())
new MenuItem ("_OnKeyDown/Press/Up", "", () => OnKeyDownPressUpDemo ())
}),
new MenuBarItem ("_Test Menu and SubMenus", new MenuItem [] {
new MenuItem ("SubMenu1Item_1",
@@ -565,7 +590,7 @@ static class Demo {
new StatusItem(Key.F1, "~F1~ Help", () => Help()),
new StatusItem(Key.F2, "~F2~ Load", null),
new StatusItem(Key.F3, "~F3~ Save", null),
new StatusItem(Key.ControlX, "~^X~ Quit", () => { if (Quit ()) top.Running = false; }),
new StatusItem(Key.ControlQ, "~^Q~ Quit", () => { if (Quit ()) top.Running = false; }),
}) {
Parent = null,
};

View File

@@ -111,7 +111,7 @@ class Demo {
Y = Pos.Top (password),
Width = Dim.Width (loginText)
};
// Add some content
container.Add (
login,
@@ -162,7 +162,7 @@ class Demo {
static void Close ()
{
MessageBox.ErrorQuery (50, 5, "Error", "There is nothing to close", "Ok");
MessageBox.ErrorQuery (50, 7, "Error", "There is nothing to close", "Ok");
}
public static Label ml;

View File

@@ -7,6 +7,6 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Terminal.Gui" Version="0.17.0" />
<PackageReference Include="Terminal.Gui" Version="0.81.0" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30011.22
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StandaloneExample", "StandaloneExample.csproj", "{8DC768EF-530D-4261-BD35-FC41E13B041E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8DC768EF-530D-4261-BD35-FC41E13B041E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8DC768EF-530D-4261-BD35-FC41E13B041E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8DC768EF-530D-4261-BD35-FC41E13B041E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8DC768EF-530D-4261-BD35-FC41E13B041E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C9C434AC-B7E4-43AB-834A-F9489766FDFF}
EndGlobalSection
EndGlobal

View File

@@ -151,6 +151,44 @@ namespace Terminal.Gui {
{
return false;
}
/// <summary>
/// Method invoked when a mouse event is generated for the first time.
/// </summary>
/// <param name="mouseEvent"></param>
/// <returns><c>true</c>, if the event was handled, <c>false</c> otherwise.</returns>
public virtual bool OnMouseEnter (MouseEvent mouseEvent)
{
return false;
}
/// <summary>
/// Method invoked when a mouse event is generated for the last time.
/// </summary>
/// <param name="mouseEvent"></param>
/// <returns><c>true</c>, if the event was handled, <c>false</c> otherwise.</returns>
public virtual bool OnMouseLeave (MouseEvent mouseEvent)
{
return false;
}
/// <summary>
/// Method invoked when a view gets focus.
/// </summary>
/// <returns><c>true</c>, if the event was handled, <c>false</c> otherwise.</returns>
public virtual bool OnEnter ()
{
return false;
}
/// <summary>
/// Method invoked when a view loses focus.
/// </summary>
/// <returns><c>true</c>, if the event was handled, <c>false</c> otherwise.</returns>
public virtual bool OnLeave ()
{
return false;
}
}
/// <summary>
@@ -253,12 +291,22 @@ namespace Terminal.Gui {
/// <summary>
/// Event fired when the view get focus.
/// </summary>
public event EventHandler OnEnter;
public event EventHandler Enter;
/// <summary>
/// Event fired when the view lost focus.
/// </summary>
public event EventHandler OnLeave;
public event EventHandler Leave;
/// <summary>
/// Event fired when the view receives the mouse event for the first time.
/// </summary>
public event EventHandler<MouseEvent> MouseEnter;
/// <summary>
/// Event fired when the view loses mouse event for the last time.
/// </summary>
public event EventHandler<MouseEvent> MouseLeave;
internal Direction FocusDirection {
get => SuperView?.FocusDirection ?? focusDirection;
@@ -308,6 +356,10 @@ namespace Terminal.Gui {
/// <value><c>true</c> if want mouse position reports; otherwise, <c>false</c>.</value>
public virtual bool WantMousePositionReports { get; set; } = false;
/// <summary>
/// Gets or sets a value indicating whether this <see cref="T:Terminal.Gui.View"/> want continuous button pressed event.
/// </summary>
public virtual bool WantContinuousButtonPressed { get; set; } = false;
/// <summary>
/// Gets or sets the frame for the view.
/// </summary>
@@ -857,22 +909,34 @@ namespace Terminal.Gui {
}
internal set {
if (base.HasFocus != value)
if (value == true)
OnEnter?.Invoke (this, new EventArgs ());
if (value)
OnEnter ();
else
OnLeave?.Invoke (this, new EventArgs ());
OnLeave ();
SetNeedsDisplay ();
base.HasFocus = value;
// Remove focus down the chain of subviews if focus is removed
if (value == false && focused != null) {
OnLeave?.Invoke (focused, new EventArgs ());
if (!value && focused != null) {
focused.OnLeave ();
focused.HasFocus = false;
focused = null;
}
}
}
public override bool OnEnter ()
{
Enter?.Invoke (this, new EventArgs ());
return base.OnEnter ();
}
public override bool OnLeave ()
{
Leave?.Invoke (this, new EventArgs ());
return base.OnLeave ();
}
/// <summary>
/// Returns the currently focused view inside this view, or null if nothing is focused.
/// </summary>
@@ -958,6 +1022,7 @@ namespace Terminal.Gui {
// FIXED: optimize this by computing the intersection of region and view.Bounds
if (view.layoutNeeded)
view.LayoutSubviews ();
Application.CurrentView = view;
view.Redraw (view.Bounds);
}
view.NeedDisplay = Rect.Empty;
@@ -1001,9 +1066,15 @@ namespace Terminal.Gui {
SuperView?.SetFocus(this);
}
/// <summary>
/// Invoked when a character key is pressed and occurs after the key down event.
/// </summary>
public Action<KeyEvent> OnKeyPress;
/// <param name="keyEvent">Contains the details about the key that produced the event.</param>
public override bool ProcessKey (KeyEvent keyEvent)
{
OnKeyPress?.Invoke (keyEvent);
if (Focused?.ProcessKey (keyEvent) == true)
return true;
@@ -1013,6 +1084,7 @@ namespace Terminal.Gui {
/// <param name="keyEvent">Contains the details about the key that produced the event.</param>
public override bool ProcessHotKey (KeyEvent keyEvent)
{
OnKeyPress?.Invoke (keyEvent);
if (subviews == null || subviews.Count == 0)
return false;
foreach (var view in subviews)
@@ -1024,6 +1096,7 @@ namespace Terminal.Gui {
/// <param name="keyEvent">Contains the details about the key that produced the event.</param>
public override bool ProcessColdKey (KeyEvent keyEvent)
{
OnKeyPress?.Invoke (keyEvent);
if (subviews == null || subviews.Count == 0)
return false;
foreach (var view in subviews)
@@ -1345,6 +1418,24 @@ namespace Terminal.Gui {
{
return $"{GetType ().Name}({Id})({Frame})";
}
public override bool OnMouseEnter (MouseEvent mouseEvent)
{
if (!base.OnMouseEnter (mouseEvent)) {
MouseEnter?.Invoke (this, mouseEvent);
return false;
}
return true;
}
public override bool OnMouseLeave (MouseEvent mouseEvent)
{
if (!base.OnMouseLeave (mouseEvent)) {
MouseLeave?.Invoke (this, mouseEvent);
return false;
}
return true;
}
}
/// <summary>
@@ -1444,8 +1535,9 @@ namespace Terminal.Gui {
return true;
switch (keyEvent.Key) {
case Key.ControlC:
// TODO: stop current execution of this container
case Key.ControlQ:
// FIXED: stop current execution of this container
Application.RequestStop ();
break;
case Key.ControlZ:
Driver.Suspend ();
@@ -1460,6 +1552,7 @@ namespace Terminal.Gui {
case Key.Tab:
case Key.CursorRight:
case Key.CursorDown:
case Key.ControlI: // Unix
var old = Focused;
if (!FocusNext ())
FocusNext ();
@@ -1565,6 +1658,8 @@ namespace Terminal.Gui {
public override void Redraw (Rect region)
{
Application.CurrentView = this;
if (this == Application.Top) {
if (!NeedDisplay.IsEmpty) {
Driver.SetAttribute (Colors.TopLevel.Normal);
@@ -1698,9 +1793,9 @@ namespace Terminal.Gui {
return contentView.GetEnumerator ();
}
void DrawFrame ()
void DrawFrame (bool fill = true)
{
DrawFrame (new Rect (0, 0, Frame.Width, Frame.Height), padding, fill: true);
DrawFrame (new Rect (0, 0, Frame.Width, Frame.Height), padding, fill: fill);
}
/// <summary>
@@ -1745,23 +1840,31 @@ namespace Terminal.Gui {
public override void Redraw (Rect bounds)
{
Application.CurrentView = this;
if (!NeedDisplay.IsEmpty) {
DrawFrameWindow ();
}
contentView.Redraw (contentView.Bounds);
ClearNeedsDisplay ();
DrawFrameWindow (false);
void DrawFrameWindow (bool fill = true)
{
Driver.SetAttribute (ColorScheme.Normal);
DrawFrame ();
DrawFrame (fill);
if (HasFocus)
Driver.SetAttribute (ColorScheme.Normal);
var width = Frame.Width;
Driver.SetAttribute (ColorScheme.HotNormal);
var width = Frame.Width - (padding + 2) * 2;
if (Title != null && width > 4) {
Move (1+padding, padding);
Move (1 + padding, padding);
Driver.AddRune (' ');
var str = Title.Length > width ? Title [0, width-4] : Title;
var str = Title.Length >= width ? Title [0, width - 2] : Title;
Driver.AddStr (str);
Driver.AddRune (' ');
}
Driver.SetAttribute (ColorScheme.Normal);
}
contentView.Redraw (contentView.Bounds);
ClearNeedsDisplay ();
}
//
@@ -1778,7 +1881,7 @@ namespace Terminal.Gui {
int nx, ny;
if ((mouseEvent.Flags == (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition) ||
mouseEvent.Flags == MouseFlags.Button4Pressed)) {
mouseEvent.Flags == MouseFlags.Button3Pressed)) {
if (dragPosition.HasValue) {
if (SuperView == null) {
Application.Top.SetNeedsDisplay (Frame);
@@ -1856,11 +1959,17 @@ namespace Terminal.Gui {
public static Toplevel Top { get; private set; }
/// <summary>
/// The current toplevel object. This is updated when Application.Run enters and leaves and points to the current toplevel.
/// The current toplevel object. This is updated when Application.Run enters and leaves and points to the current toplevel.
/// </summary>
/// <value>The current.</value>
public static Toplevel Current { get; private set; }
/// <summary>
/// TThe current view object being redrawn.
/// </summary>
/// /// <value>The current.</value>
public static View CurrentView { get; set; }
/// <summary>
/// The mainloop driver for the applicaiton
/// </summary>
@@ -1960,6 +2069,7 @@ namespace Terminal.Gui {
SynchronizationContext.SetSynchronizationContext (new MainLoopSyncContext (MainLoop));
Top = topLevelFactory ();
Current = Top;
CurrentView = Top;
_initialized = true;
}
@@ -2003,7 +2113,7 @@ namespace Terminal.Gui {
static void ProcessKeyEvent (KeyEvent ke)
{
var chain = toplevels.ToList();
foreach (var topLevel in chain) {
if (topLevel.ProcessHotKey (ke))
@@ -2039,7 +2149,7 @@ namespace Terminal.Gui {
}
}
static void ProcessKeyUpEvent (KeyEvent ke)
{
var chain = toplevels.ToList ();
@@ -2111,9 +2221,18 @@ namespace Terminal.Gui {
/// </summary>
static public Action<MouseEvent> RootMouseEvent;
internal static View wantContinuousButtonPressedView;
static View lastMouseOwnerView;
static void ProcessMouseEvent (MouseEvent me)
{
var view = FindDeepestView (Current, me.X, me.Y, out int rx, out int ry);
if (view != null && view.WantContinuousButtonPressed)
wantContinuousButtonPressedView = view;
else
wantContinuousButtonPressedView = null;
RootMouseEvent?.Invoke (me);
if (mouseGrabView != null) {
var newxy = mouseGrabView.ScreenToView (me.X, me.Y);
@@ -2130,9 +2249,6 @@ namespace Terminal.Gui {
}
if (view != null) {
if (!view.WantMousePositionReports && me.Flags == MouseFlags.ReportMousePosition)
return;
var nme = new MouseEvent () {
X = rx,
Y = ry,
@@ -2141,6 +2257,24 @@ namespace Terminal.Gui {
OfY = ry,
View = view
};
if (lastMouseOwnerView == null) {
lastMouseOwnerView = view;
view.OnMouseEnter (nme);
} else if (lastMouseOwnerView != view) {
lastMouseOwnerView.OnMouseLeave (nme);
view.OnMouseEnter (nme);
lastMouseOwnerView = view;
}
if (!view.WantMousePositionReports && me.Flags == MouseFlags.ReportMousePosition)
return;
if (view.WantContinuousButtonPressed)
wantContinuousButtonPressedView = view;
else
wantContinuousButtonPressedView = null;
// Should we bubbled up the event, if it is not handled?
view.MouseEvent (nme);
}
@@ -2217,6 +2351,8 @@ namespace Terminal.Gui {
static void Redraw (View view)
{
Application.CurrentView = view;
view.Redraw (view.Bounds);
Driver.Refresh ();
}

View File

@@ -540,47 +540,73 @@ namespace Terminal.Gui {
Move (region.X, region.Y);
if (padding > 0) {
for (int l = 0; l < padding; l++)
for (b = 0; b < width; b++)
for (b = region.X; b < region.X + width; b++) {
AddRune (' ');
Move (b + 1, region.Y);
}
}
Move (region.X, region.Y + padding);
for (int c = 0; c < padding; c++)
for (int c = 0; c < padding; c++) {
AddRune (' ');
Move (region.X + 1, region.Y + padding);
}
AddRune (ULCorner);
for (b = 0; b < fwidth - 2; b++)
for (b = region.X; b < region.X + fwidth - 2; b++) {
AddRune (HLine);
Move (b + (padding > 0 ? padding + 2 : 2), region.Y + padding);
}
AddRune (URCorner);
for (int c = 0; c < padding; c++)
for (int c = 0; c < padding; c++) {
AddRune (' ');
Move (region.X + 1, region.Y + padding);
}
for (b = 1 + padding; b < fheight; b++) {
Move (region.X, region.Y + b);
for (int c = 0; c < padding; c++)
for (int c = 0; c < padding; c++) {
AddRune (' ');
Move (region.X + 1, region.Y + b);
}
AddRune (VLine);
if (fill) {
for (int x = 1; x < fwidth - 1; x++)
for (int x = region.X + 1; x < region.X + fwidth - 1; x++) {
AddRune (' ');
} else
Move (region.X + fwidth - 1, region.Y + b);
Move (x + (padding > 0 ? padding + 1 : 1), region.Y + b);
}
} else {
if (padding > 0)
Move (region.X + fwidth, region.Y + b);
else
Move (region.X + fwidth - 1, region.Y + b);
}
AddRune (VLine);
for (int c = 0; c < padding; c++)
for (int c = 0; c < padding; c++) {
AddRune (' ');
Move (region.X + 1, region.Y + b);
}
}
Move (region.X, region.Y + fheight);
for (int c = 0; c < padding; c++)
for (int c = 0; c < padding; c++) {
AddRune (' ');
Move (region.X + 1, region.Y + b);
}
AddRune (LLCorner);
for (b = 0; b < fwidth - 2; b++)
for (b = region.X; b < region.X + fwidth - 2; b++) {
AddRune (HLine);
Move (b + (padding > 0 ? padding + 2 : 2), region.Y + fheight);
}
AddRune (LRCorner);
for (int c = 0; c < padding; c++)
for (int c = 0; c < padding; c++) {
AddRune (' ');
Move (region.X + 1, region.Y);
}
if (padding > 0) {
Move (region.X, region.Y + height - padding);
for (int l = 0; l < padding; l++)
for (b = 0; b < width; b++)
for (int l = 0; l < padding; l++) {
for (b = region.X; b < region.X + width; b++) {
AddRune (' ');
Move (b + 1, region.Y + height - padding);
}
}
}
}

View File

@@ -141,6 +141,38 @@ namespace Terminal.Gui {
case Curses.KeyInsertChar: return Key.InsertChar;
case Curses.KeyBackTab: return Key.BackTab;
case Curses.KeyBackspace: return Key.Backspace;
case Curses.ShiftKeyUp: return Key.CursorUp | Key.ShiftMask;
case Curses.ShiftKeyDown: return Key.CursorDown | Key.ShiftMask;
case Curses.ShiftKeyLeft: return Key.CursorLeft | Key.ShiftMask;
case Curses.ShiftKeyRight: return Key.CursorRight | Key.ShiftMask;
case Curses.ShiftKeyHome: return Key.Home | Key.ShiftMask;
case Curses.ShiftKeyEnd: return Key.End | Key.ShiftMask;
case Curses.ShiftKeyNPage: return Key.PageDown | Key.ShiftMask;
case Curses.ShiftKeyPPage: return Key.PageUp | Key.ShiftMask;
case Curses.AltKeyUp: return Key.CursorUp | Key.AltMask;
case Curses.AltKeyDown: return Key.CursorDown | Key.AltMask;
case Curses.AltKeyLeft: return Key.CursorLeft | Key.AltMask;
case Curses.AltKeyRight: return Key.CursorRight | Key.AltMask;
case Curses.AltKeyHome: return Key.Home | Key.AltMask;
case Curses.AltKeyEnd: return Key.End | Key.AltMask;
case Curses.AltKeyNPage: return Key.PageDown | Key.AltMask;
case Curses.AltKeyPPage: return Key.PageUp | Key.AltMask;
case Curses.CtrlKeyUp: return Key.CursorUp | Key.CtrlMask;
case Curses.CtrlKeyDown: return Key.CursorDown | Key.CtrlMask;
case Curses.CtrlKeyLeft: return Key.CursorLeft | Key.CtrlMask;
case Curses.CtrlKeyRight: return Key.CursorRight | Key.CtrlMask;
case Curses.CtrlKeyHome: return Key.Home | Key.CtrlMask;
case Curses.CtrlKeyEnd: return Key.End | Key.CtrlMask;
case Curses.CtrlKeyNPage: return Key.PageDown | Key.CtrlMask;
case Curses.CtrlKeyPPage: return Key.PageUp | Key.CtrlMask;
case Curses.ShiftCtrlKeyUp: return Key.CursorUp | Key.ShiftMask | Key.CtrlMask;
case Curses.ShiftCtrlKeyDown: return Key.CursorDown | Key.ShiftMask | Key.CtrlMask;
case Curses.ShiftCtrlKeyLeft: return Key.CursorLeft | Key.ShiftMask | Key.CtrlMask;
case Curses.ShiftCtrlKeyRight: return Key.CursorRight | Key.ShiftMask | Key.CtrlMask;
case Curses.ShiftCtrlKeyHome: return Key.Home | Key.ShiftMask | Key.CtrlMask;
case Curses.ShiftCtrlKeyEnd: return Key.End | Key.ShiftMask | Key.CtrlMask;
case Curses.ShiftCtrlKeyNPage: return Key.PageDown | Key.ShiftMask | Key.CtrlMask;
case Curses.ShiftCtrlKeyPPage: return Key.PageUp | Key.ShiftMask | Key.CtrlMask;
default: return Key.Unknown;
}
}
@@ -188,7 +220,12 @@ namespace Terminal.Gui {
KeyEvent key;
// The ESC-number handling, debatable.
if (wch >= '1' && wch <= '9')
// Simulates the AltMask itself by pressing Alt + Space.
if (wch == (int)Key.Space)
key = new KeyEvent (Key.AltMask);
else if (wch - (int)Key.Space >= 'A' && wch - (int)Key.Space <= 'Z')
key = new KeyEvent ((Key)((uint)Key.AltMask + (wch - (int)Key.Space)));
else if (wch >= '1' && wch <= '9')
key = new KeyEvent ((Key)((int)Key.F1 + (wch - '0' - 1)));
else if (wch == '0')
key = new KeyEvent (Key.F10);

View File

@@ -81,11 +81,11 @@ namespace Terminal.Gui {
} else
needMove = true;
ccol++;
if (ccol == Cols) {
ccol = 0;
if (crow + 1 < Rows)
crow++;
}
//if (ccol == Cols) {
// ccol = 0;
// if (crow + 1 < Rows)
// crow++;
//}
if (sync)
UpdateScreen ();
}

View File

@@ -53,6 +53,7 @@ namespace Terminal.Gui {
var newConsoleMode = originalConsoleMode;
newConsoleMode |= (uint)(ConsoleModes.EnableMouseInput | ConsoleModes.EnableExtendedFlags);
newConsoleMode &= ~(uint)ConsoleModes.EnableQuickEditMode;
newConsoleMode &= ~(uint)ConsoleModes.EnableProcessedInput;
ConsoleMode = newConsoleMode;
}
@@ -124,6 +125,7 @@ namespace Terminal.Gui {
[Flags]
public enum ConsoleModes : uint {
EnableProcessedInput = 1,
EnableMouseInput = 16,
EnableQuickEditMode = 64,
EnableExtendedFlags = 128,
@@ -552,7 +554,6 @@ namespace Terminal.Gui {
this.mouseHandler = mouseHandler;
}
void IMainLoopDriver.MainIteration ()
{
if (result == null)
@@ -563,19 +564,50 @@ namespace Terminal.Gui {
case WindowsConsole.EventType.Key:
var map = MapKey (ToConsoleKeyInfoEx (inputEvent.KeyEvent));
if (map == (Key)0xffffffff) {
KeyEvent key;
KeyEvent key = default;
// Shift = VK_SHIFT = 0x10
// Ctrl = VK_CONTROL = 0x11
// Alt = VK_MENU = 0x12
switch (inputEvent.KeyEvent.wVirtualKeyCode) {
case 0x11:
key = new KeyEvent (Key.CtrlMask);
switch (inputEvent.KeyEvent.dwControlKeyState) {
case WindowsConsole.ControlKeyState.RightAltPressed:
case WindowsConsole.ControlKeyState.RightAltPressed |
WindowsConsole.ControlKeyState.LeftControlPressed |
WindowsConsole.ControlKeyState.EnhancedKey:
case WindowsConsole.ControlKeyState.EnhancedKey:
key = new KeyEvent (Key.CtrlMask | Key.AltMask);
break;
case 0x12:
case WindowsConsole.ControlKeyState.LeftAltPressed:
key = new KeyEvent (Key.AltMask);
break;
case WindowsConsole.ControlKeyState.RightControlPressed:
case WindowsConsole.ControlKeyState.LeftControlPressed:
key = new KeyEvent (Key.CtrlMask);
break;
case WindowsConsole.ControlKeyState.ShiftPressed:
key = new KeyEvent (Key.ShiftMask);
break;
case WindowsConsole.ControlKeyState.NumlockOn:
break;
case WindowsConsole.ControlKeyState.ScrolllockOn:
break;
case WindowsConsole.ControlKeyState.CapslockOn:
break;
default:
key = new KeyEvent (Key.Unknown);
switch (inputEvent.KeyEvent.wVirtualKeyCode) {
case 0x10:
key = new KeyEvent (Key.ShiftMask);
break;
case 0x11:
key = new KeyEvent (Key.CtrlMask);
break;
case 0x12:
key = new KeyEvent (Key.AltMask);
break;
default:
key = new KeyEvent (Key.Unknown);
break;
}
break;
}
@@ -652,7 +684,7 @@ namespace Terminal.Gui {
break;
case WindowsConsole.ButtonState.RightmostButtonPressed:
mouseFlag = MouseFlags.Button4Pressed;
mouseFlag = MouseFlags.Button3Pressed;
break;
}
@@ -679,6 +711,9 @@ namespace Terminal.Gui {
Flags = mouseFlag
};
var view = Application.wantContinuousButtonPressedView;
if (view == null)
break;
if (IsButtonPressed && (mouseFlag & MouseFlags.ReportMousePosition) == 0) {
mouseHandler (me);
mainLoop.Driver.Wakeup ();
@@ -699,7 +734,7 @@ namespace Terminal.Gui {
break;
case WindowsConsole.ButtonState.RightmostButtonPressed:
mouseFlag = MouseFlags.Button4Released;
mouseFlag = MouseFlags.Button3Released;
break;
}
IsButtonPressed = false;
@@ -721,7 +756,7 @@ namespace Terminal.Gui {
break;
case WindowsConsole.ButtonState.RightmostButtonPressed:
mouseFlag = MouseFlags.Button4Clicked;
mouseFlag = MouseFlags.Button3Clicked;
break;
}
} else {
@@ -740,7 +775,7 @@ namespace Terminal.Gui {
break;
case WindowsConsole.ButtonState.RightmostButtonPressed:
mouseFlag = MouseFlags.Button4DoubleClicked;
mouseFlag = MouseFlags.Button3DoubleClicked;
break;
}
IsButtonDoubleClicked = true;
@@ -755,7 +790,7 @@ namespace Terminal.Gui {
break;
case WindowsConsole.ButtonState.RightmostButtonPressed:
mouseFlag = MouseFlags.Button4TripleClicked;
mouseFlag = MouseFlags.Button3TripleClicked;
break;
}
IsButtonDoubleClicked = false;
@@ -817,33 +852,35 @@ namespace Terminal.Gui {
var keyInfo = keyInfoEx.consoleKeyInfo;
switch (keyInfo.Key) {
case ConsoleKey.Escape:
return Key.Esc;
return MapKeyModifiers (keyInfo, Key.Esc);
case ConsoleKey.Tab:
return keyInfo.Modifiers == ConsoleModifiers.Shift ? Key.BackTab : Key.Tab;
case ConsoleKey.Home:
return Key.Home;
return MapKeyModifiers (keyInfo, Key.Home);
case ConsoleKey.End:
return Key.End;
return MapKeyModifiers (keyInfo, Key.End);
case ConsoleKey.LeftArrow:
return Key.CursorLeft;
return MapKeyModifiers (keyInfo, Key.CursorLeft);
case ConsoleKey.RightArrow:
return Key.CursorRight;
return MapKeyModifiers (keyInfo, Key.CursorRight);
case ConsoleKey.UpArrow:
return Key.CursorUp;
return MapKeyModifiers (keyInfo, Key.CursorUp);
case ConsoleKey.DownArrow:
return Key.CursorDown;
return MapKeyModifiers (keyInfo, Key.CursorDown);
case ConsoleKey.PageUp:
return Key.PageUp;
return MapKeyModifiers (keyInfo, Key.PageUp);
case ConsoleKey.PageDown:
return Key.PageDown;
return MapKeyModifiers (keyInfo, Key.PageDown);
case ConsoleKey.Enter:
return Key.Enter;
return MapKeyModifiers (keyInfo, Key.Enter);
case ConsoleKey.Spacebar:
return Key.Space;
return MapKeyModifiers (keyInfo, Key.Space);
case ConsoleKey.Backspace:
return Key.Backspace;
return MapKeyModifiers (keyInfo, Key.Backspace);
case ConsoleKey.Delete:
return Key.DeleteChar;
return MapKeyModifiers (keyInfo, Key.DeleteChar);
case ConsoleKey.Insert:
return MapKeyModifiers (keyInfo, Key.InsertChar);
case ConsoleKey.NumPad0:
return keyInfoEx.NumLock ? (Key)(uint)'0' : Key.InsertChar;
@@ -883,7 +920,7 @@ namespace Terminal.Gui {
}
var key = keyInfo.Key;
var alphaBase = ((keyInfo.Modifiers == ConsoleModifiers.Shift) ^ (keyInfoEx.CapsLock)) ? 'A' : 'a';
//var alphaBase = ((keyInfo.Modifiers == ConsoleModifiers.Shift) ^ (keyInfoEx.CapsLock)) ? 'A' : 'a';
if (key >= ConsoleKey.A && key <= ConsoleKey.Z) {
var delta = key - ConsoleKey.A;
@@ -897,7 +934,8 @@ namespace Terminal.Gui {
else
return (Key)((uint)keyInfo.KeyChar);
}
return (Key)((uint)alphaBase + delta);
//return (Key)((uint)alphaBase + delta);
return (Key)((uint)keyInfo.KeyChar);
}
if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) {
var delta = key - ConsoleKey.D0;
@@ -915,6 +953,19 @@ namespace Terminal.Gui {
return (Key)(0xffffffff);
}
private static Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key)
{
Key keyMod = new Key ();
if (keyInfo.Modifiers.HasFlag (ConsoleModifiers.Shift))
keyMod = Key.ShiftMask;
if (keyInfo.Modifiers.HasFlag (ConsoleModifiers.Control))
keyMod |= Key.CtrlMask;
if (keyInfo.Modifiers.HasFlag (ConsoleModifiers.Alt))
keyMod |= Key.AltMask;
return keyMod != Key.ControlSpace ? keyMod | key : key;
}
public override void Init (Action terminalResized)
{
TerminalResized = terminalResized;
@@ -1006,11 +1057,11 @@ namespace Terminal.Gui {
AddStr (" ");
}
}
if (ccol == Cols) {
ccol = 0;
if (crow + 1 < Rows)
crow++;
}
//if (ccol == Cols) {
// ccol = 0;
// if (crow + 1 < Rows)
// crow++;
//}
if (sync)
UpdateScreen ();
}

View File

@@ -24,9 +24,10 @@ namespace Terminal.Gui {
/// Unicode runes are also stored here, the letter 'A" for example is encoded as a value 65 (not surfaced in the enum).
/// </para>
/// </remarks>
[Flags]
public enum Key : uint {
/// <summary>
/// Mask that indictes that this is a character value, values outside this range
/// Mask that indicates that this is a character value, values outside this range
/// indicate special characters like Alt-key combinations or special keys on the
/// keyboard like function keys, arrows keys and so on.
/// </summary>
@@ -42,7 +43,7 @@ namespace Terminal.Gui {
/// The key code for the user pressing Control-spacebar
/// </summary>
ControlSpace = 0,
/// <summary>
/// The key code for the user pressing Control-A
/// </summary>
@@ -80,10 +81,6 @@ namespace Terminal.Gui {
/// </summary>
ControlI,
/// <summary>
/// The key code for the user pressing the tab key (same as pressing Control-I).
/// </summary>
Tab = ControlI,
/// <summary>
/// The key code for the user pressing Control-J
/// </summary>
ControlJ,
@@ -151,7 +148,7 @@ namespace Terminal.Gui {
/// The key code for the user pressing Control-Z
/// </summary>
ControlZ,
/// <summary>
/// The key code for the user pressing the escape key
/// </summary>
@@ -172,6 +169,11 @@ namespace Terminal.Gui {
/// </summary>
Delete = 127,
/// <summary>
/// When this value is set, the Key encodes the sequence Shift-KeyValue.
/// </summary>
ShiftMask = 0x10000000,
/// <summary>
/// When this value is set, the Key encodes the sequence Alt-KeyValue.
/// And the actual value must be extracted by removing the AltMask.
@@ -270,6 +272,10 @@ namespace Terminal.Gui {
/// </summary>
F10,
/// <summary>
/// The key code for the user pressing the tab key (forwards tab key).
/// </summary>
Tab,
/// <summary>
/// Shift-tab key (backwards tab key).
/// </summary>
BackTab,
@@ -290,12 +296,18 @@ namespace Terminal.Gui {
public Key Key;
/// <summary>
/// The key value cast to an integer, you will typicall use this for
/// The key value cast to an integer, you will typical use this for
/// extracting the Unicode rune value out of a key, when none of the
/// symbolic options are in use.
/// </summary>
public int KeyValue => (int)Key;
/// <summary>
/// Gets a value indicating whether the Shift key was pressed.
/// </summary>
/// <value><c>true</c> if is shift; otherwise, <c>false</c>.</value>
public bool IsShift => (Key & Key.ShiftMask) != 0;
/// <summary>
/// Gets a value indicating whether the Alt key was pressed (real or synthesized)
/// </summary>
@@ -306,7 +318,8 @@ namespace Terminal.Gui {
/// Determines whether the value is a control key (and NOT just the ctrl key)
/// </summary>
/// <value><c>true</c> if is ctrl; otherwise, <c>false</c>.</value>
public bool IsCtrl => ((uint)Key >= 1) && ((uint)Key <= 26);
//public bool IsCtrl => ((uint)Key >= 1) && ((uint)Key <= 26);
public bool IsCtrl => (Key & Key.CtrlMask) != 0;
/// <summary>
/// Constructs a new KeyEvent from the provided Key value - can be a rune cast into a Key value

View File

@@ -1,5 +1,5 @@
/*
* This file is autogenerated by the attrib.c program, do not edit
* This file is autogenerated by the attrib.c program, do not edit
*/
using System;
@@ -101,6 +101,38 @@ namespace Unix.Terminal {
public const int KeyF9 = unchecked((int)0x111);
public const int KeyF10 = unchecked((int)0x112);
public const int KeyResize = unchecked((int)0x19a);
public const int ShiftKeyUp = unchecked((int)0x151);
public const int ShiftKeyDown = unchecked((int)0x150);
public const int ShiftKeyLeft = unchecked((int)0x189);
public const int ShiftKeyRight = unchecked((int)0x192);
public const int ShiftKeyNPage = unchecked((int)0x18c);
public const int ShiftKeyPPage = unchecked((int)0x18e);
public const int ShiftKeyHome = unchecked((int)0x187);
public const int ShiftKeyEnd = unchecked((int)0x182);
public const int AltKeyUp = unchecked((int)0x234);
public const int AltKeyDown = unchecked((int)0x20b);
public const int AltKeyLeft = unchecked((int)0x21f);
public const int AltKeyRight = unchecked((int)0x22e);
public const int AltKeyNPage = unchecked((int)0x224);
public const int AltKeyPPage = unchecked((int)0x229);
public const int AltKeyHome = unchecked((int)0x215);
public const int AltKeyEnd = unchecked((int)0x210);
public const int CtrlKeyUp = unchecked((int)0x236);
public const int CtrlKeyDown = unchecked((int)0x20d);
public const int CtrlKeyLeft = unchecked((int)0x221);
public const int CtrlKeyRight = unchecked((int)0x230);
public const int CtrlKeyNPage = unchecked((int)0x226);
public const int CtrlKeyPPage = unchecked((int)0x22b);
public const int CtrlKeyHome = unchecked((int)0x217);
public const int CtrlKeyEnd = unchecked((int)0x212);
public const int ShiftCtrlKeyUp = unchecked((int)0x237);
public const int ShiftCtrlKeyDown = unchecked((int)0x20e);
public const int ShiftCtrlKeyLeft = unchecked((int)0x222);
public const int ShiftCtrlKeyRight = unchecked((int)0x231);
public const int ShiftCtrlKeyNPage = unchecked((int)0x227);
public const int ShiftCtrlKeyPPage = unchecked((int)0x22c);
public const int ShiftCtrlKeyHome = unchecked((int)0x218);
public const int ShiftCtrlKeyEnd = unchecked((int)0x213);
public const int LC_ALL = 6;
static public int ColorPair(int n){

View File

@@ -43,6 +43,7 @@ namespace Terminal.Gui {
public FrameView (Rect frame, ustring title) : base (frame)
{
var cFrame = new Rect (1, 1 , frame.Width - 2, frame.Height - 2);
this.title = title;
contentView = new ContentView (cFrame);
Initialize ();
}
@@ -69,6 +70,7 @@ namespace Terminal.Gui {
/// <param name="title">Title.</param>
public FrameView (ustring title)
{
this.title = title;
contentView = new ContentView () {
X = 1,
Y = 1,
@@ -81,7 +83,6 @@ namespace Terminal.Gui {
void Initialize ()
{
base.Add (contentView);
Title = title;
}
void DrawFrame ()

View File

@@ -343,6 +343,29 @@ namespace Terminal.Gui {
return false;
});
}
public override bool KeyDown (KeyEvent keyEvent)
{
if (keyEvent.IsAlt) {
host.CloseAllMenus ();
return true;
}
return false;
}
public override bool ProcessHotKey (KeyEvent keyEvent)
{
// To ncurses simulate a AltMask key pressing Alt+Space because
// it can<61>t detect an alone special key down was pressed.
if (keyEvent.IsAlt && keyEvent.Key == Key.AltMask) {
KeyDown (keyEvent);
return true;
}
return false;
}
public override bool ProcessKey (KeyEvent kb)
{
bool disabled;
@@ -445,7 +468,8 @@ namespace Terminal.Gui {
Run (barItems.Children [meY].Action);
return true;
} else if (me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1DoubleClicked ||
me.Flags == MouseFlags.ReportMousePosition) {
me.Flags == MouseFlags.ReportMousePosition ||
me.Flags.HasFlag (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition)) {
disabled = false;
if (me.Y < 1)
return true;
@@ -534,9 +558,12 @@ namespace Terminal.Gui {
isMenuClosed = true;
}
bool openedByAltKey;
public override bool KeyDown (KeyEvent keyEvent)
{
if (keyEvent.IsAlt) {
openedByAltKey = true;
SetNeedsDisplay ();
openedByHotKey = false;
}
return false;
@@ -544,29 +571,45 @@ namespace Terminal.Gui {
/// <summary>
/// Track Alt key-up events. On Windows, when a user releases Alt (without another key), the menu gets focus but doesn't open.
/// We mimic that behavior here.
/// We mimic that behavior here.
/// </summary>
/// <param name="keyEvent"></param>
/// <returns></returns>
public override bool KeyUp (KeyEvent keyEvent)
{
if (keyEvent.IsAlt) {
// User pressed Alt - this may be a precursor to a menu accellerator (e.g. Alt-F)
if (openMenu == null) {
// There's no open menu, the first menu item should be highlight.
// The right way to do this is to SetFocus(MenuBar), but for some reason
// User pressed Alt - this may be a precursor to a menu accelerator (e.g. Alt-F)
if (!keyEvent.IsCtrl && openedByAltKey && isMenuClosed && openMenu == null && ((uint)keyEvent.Key & (uint)Key.CharMask) == 0) {
// There's no open menu, the first menu item should be highlight.
// The right way to do this is to SetFocus(MenuBar), but for some reason
// that faults.
Activate (0);
//Activate (0);
//StartMenu ();
isMenuClosed = false;
selected = 0;
CanFocus = true;
lastFocused = SuperView.MostFocused;
SuperView.SetFocus (this);
SetNeedsDisplay ();
} else {
// There's an open menu. If this Alt key-up is a pre-cursor to an acellerator
Application.GrabMouse (this);
} else if (!openedByHotKey) {
// There's an open menu. If this Alt key-up is a pre-cursor to an accelerator
// we don't want to close the menu because it'll flash.
// How to deal with that?
if (!openedByHotKey) {
if (openMenu != null)
CloseAllMenus ();
}
openedByAltKey = false;
isMenuClosed = true;
selected = -1;
CanFocus = false;
if (lastFocused != null)
SuperView?.SetFocus (lastFocused);
SetNeedsDisplay ();
Application.UngrabMouse ();
}
return true;
}
return false;
@@ -589,9 +632,12 @@ namespace Terminal.Gui {
if (i == selected) {
hotColor = i == selected ? ColorScheme.HotFocus : ColorScheme.HotNormal;
normalColor = i == selected ? ColorScheme.Focus : ColorScheme.Normal;
} else {
} else if (openedByAltKey) {
hotColor = ColorScheme.HotNormal;
normalColor = ColorScheme.Normal;
} else {
hotColor = ColorScheme.Normal;
normalColor = ColorScheme.Normal;
}
DrawHotString ($" {menu.Title} ", hotColor, normalColor);
pos += 1 + menu.TitleLength + 2;
@@ -815,6 +861,7 @@ namespace Terminal.Gui {
}
isMenuClosed = true;
openedByHotKey = false;
openedByAltKey = false;
}
View FindDeepestMenu (View view, ref int count)
@@ -890,7 +937,7 @@ namespace Terminal.Gui {
}
}
bool openedByHotKey = false;
bool openedByHotKey;
internal bool FindAndOpenMenuByHotkey (KeyEvent kb)
{
int pos = 0;
@@ -901,15 +948,7 @@ namespace Terminal.Gui {
int p = mi.Title.IndexOf ('_');
if (p != -1 && p + 1 < mi.Title.Length) {
if (Char.ToUpperInvariant ((char)mi.Title [p + 1]) == c) {
if (mi.IsTopLevel) {
var menu = new Menu (this, i, 0, mi);
menu.Run (mi.Action);
} else {
openedByHotKey = true;
Application.GrabMouse (this);
selected = i;
OpenMenu (i);
}
ProcessMenu (i, mi);
return true;
}
}
@@ -917,6 +956,19 @@ namespace Terminal.Gui {
return false;
}
private void ProcessMenu (int i, MenuBarItem mi)
{
if (mi.IsTopLevel) {
var menu = new Menu (this, i, 0, mi);
menu.Run (mi.Action);
} else {
openedByHotKey = true;
Application.GrabMouse (this);
selected = i;
OpenMenu (i);
}
}
public override bool ProcessHotKey (KeyEvent kb)
{
if (kb.Key == Key.F9) {
@@ -927,10 +979,16 @@ namespace Terminal.Gui {
return true;
}
if (kb.IsAlt) {
// To ncurses simulate a AltMask key pressing Alt+Space because
// it can<61>t detect an alone special key down was pressed.
if (kb.IsAlt && kb.Key == Key.AltMask && openMenu == null) {
KeyDown (kb);
KeyUp (kb);
return true;
} else if (kb.IsAlt) {
if (FindAndOpenMenuByHotkey (kb)) return true;
}
var kc = kb.KeyValue;
//var kc = kb.KeyValue;
return base.ProcessHotKey (kb);
}
@@ -951,6 +1009,15 @@ namespace Terminal.Gui {
case Key.ControlC:
//TODO: Running = false;
CloseMenu ();
if (openedByAltKey) {
openedByAltKey = false;
LastFocused.SuperView?.SetFocus (LastFocused);
}
break;
case Key.CursorDown:
case Key.Enter:
ProcessMenu (selected, Menus [selected]);
break;
default:
@@ -958,10 +1025,12 @@ namespace Terminal.Gui {
if ((key >= 'a' && key <= 'z') || (key >= 'A' && key <= 'Z') || (key >= '0' && key <= '9')) {
char c = Char.ToUpper ((char)key);
if (Menus [selected].IsTopLevel)
if (selected == -1 || Menus [selected].IsTopLevel)
return false;
foreach (var mi in Menus [selected].Children) {
if (mi == null)
continue;
int p = mi.Title.IndexOf ('_');
if (p != -1 && p + 1 < mi.Title.Length) {
if (mi.Title [p + 1] == c) {
@@ -985,14 +1054,15 @@ namespace Terminal.Gui {
}
handled = false;
if (me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1DoubleClicked ||
(me.Flags == MouseFlags.ReportMousePosition && selected > -1)) {
if (me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1Clicked || me.Flags == MouseFlags.Button1DoubleClicked ||
(me.Flags == MouseFlags.ReportMousePosition && selected > -1) ||
(me.Flags.HasFlag (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition) && selected > -1)) {
int pos = 1;
int cx = me.X;
for (int i = 0; i < Menus.Length; i++) {
if (cx > pos && me.X < pos + 1 + Menus [i].TitleLength) {
if (selected == i && (me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1DoubleClicked) &&
!isMenuClosed) {
!isMenuClosed) {
Application.UngrabMouse ();
if (Menus [i].IsTopLevel) {
var menu = new Menu (this, i, 0, Menus [i]);
@@ -1036,8 +1106,8 @@ namespace Terminal.Gui {
Application.GrabMouse (me.View);
me.View.MouseEvent (me);
}
} else if (!(me.View is MenuBar || me.View is Menu) && (me.Flags.HasFlag (MouseFlags.Button1Pressed) ||
me.Flags == MouseFlags.Button1DoubleClicked)) {
} else if (!(me.View is MenuBar || me.View is Menu) && (me.Flags.HasFlag (MouseFlags.Button1Clicked) ||
me.Flags == MouseFlags.Button1DoubleClicked || me.Flags == MouseFlags.Button1Pressed)) {
Application.UngrabMouse ();
CloseAllMenus ();
handled = false;
@@ -1046,7 +1116,8 @@ namespace Terminal.Gui {
handled = false;
return false;
}
} else if (isMenuClosed && (me.Flags.HasFlag (MouseFlags.Button1Pressed) || me.Flags == MouseFlags.Button1DoubleClicked)) {
} else if (isMenuClosed && (me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1DoubleClicked ||
me.Flags.HasFlag (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition))) {
Application.GrabMouse (current);
} else {
handled = false;

View File

@@ -79,6 +79,7 @@ namespace Terminal.Gui {
vertical = isVertical;
this.position = position;
this.size = size;
WantContinuousButtonPressed = true;
}
/// <summary>

View File

@@ -78,15 +78,16 @@ namespace Terminal.Gui {
CanFocus = true;
Used = true;
WantMousePositionReports = true;
OnLeave += TextField_OnLeave;
}
void TextField_OnLeave (object sender, EventArgs e)
public override bool OnLeave ()
{
if (Application.mouseGrabView != null && Application.mouseGrabView == this)
Application.UngrabMouse ();
if (SelectedLength != 0 && !(Application.mouseGrabView is MenuBar))
ClearAllSelection ();
return base.OnLeave ();
}
public override Rect Frame {
@@ -94,10 +95,15 @@ namespace Terminal.Gui {
set {
base.Frame = value;
var w = base.Frame.Width;
first = point > w ? point - w : 0;
//first = point > w ? point - w : 0;
Adjust ();
}
}
List<ustring> historyText;
int idxhistoryText;
bool isFromHistory;
/// <summary>
/// Sets or gets the text in the entry.
/// </summary>
@@ -109,15 +115,24 @@ namespace Terminal.Gui {
}
set {
ustring oldText = ustring.Make (text);
var oldText = ustring.Make (text);
text = TextModel.ToRunes (value);
if (!Secret && !isFromHistory) {
if (historyText == null)
historyText = new List<ustring> () { oldText };
if (idxhistoryText > 0 && idxhistoryText + 1 < historyText.Count)
historyText.RemoveRange (idxhistoryText + 1, historyText.Count - idxhistoryText - 1);
historyText.Add (ustring.Make (text));
idxhistoryText++;
}
Changed?.Invoke (this, oldText);
if (point > text.Count)
point = Math.Max (text.Count-1, 0);
point = Math.Max (DisplaySize (text, 0) - 1, 0);
// FIXME: this needs to be updated to use Rune.ColumnWidth
first = point > Frame.Width ? point - Frame.Width : 0;
//first = point > Frame.Width ? point - Frame.Width : 0;
Adjust ();
SetNeedsDisplay ();
}
}
@@ -171,14 +186,14 @@ namespace Terminal.Gui {
var tcount = text.Count;
for (int idx = 0; idx < tcount; idx++){
var rune = text [idx];
if (idx < first)
if (idx < p)
continue;
var cols = Rune.ColumnWidth (rune);
if (col == point && HasFocus && !Used && SelectedLength == 0)
Driver.SetAttribute (Colors.Menu.HotFocus);
else
Driver.SetAttribute (idx >= start && length > 0 && idx < start + length ? color.Focus : ColorScheme.Focus);
if (col + cols < width)
if (col + cols <= width)
Driver.AddRune ((Rune)(Secret ? '*' : rune));
col += cols;
}
@@ -204,10 +219,13 @@ namespace Terminal.Gui {
void Adjust ()
{
int offB = 0;
if (SuperView != null && SuperView.Frame.Right - Frame.Right < 0)
offB = SuperView.Frame.Right - Frame.Right - 1;
if (point < first)
first = point;
else if (first + point >= Frame.Width) {
first = point - (Frame.Width - 1);
else if (first + point >= Frame.Width + offB) {
first = point - (Frame.Width - 1 + offB);
}
SetNeedsDisplay ();
}
@@ -269,6 +287,22 @@ namespace Terminal.Gui {
}
break;
case Key.Home | Key.ShiftMask:
if (point > 0) {
int x = point;
point = 0;
PrepareSelection (x, point - x);
}
break;
case Key.End | Key.ShiftMask:
if (point < text.Count) {
int x = point;
point = text.Count;
PrepareSelection (x, point - x);
}
break;
// Home, C-A
case Key.Home:
case Key.ControlA:
@@ -277,6 +311,42 @@ namespace Terminal.Gui {
Adjust ();
break;
case Key.CursorLeft | Key.ShiftMask:
case Key.CursorUp | Key.ShiftMask:
if (point > 0) {
PrepareSelection (point--, -1);
}
break;
case Key.CursorRight | Key.ShiftMask:
case Key.CursorDown | Key.ShiftMask:
if (point < text.Count) {
PrepareSelection (point++, 1);
}
break;
case Key.CursorLeft | Key.ShiftMask | Key.CtrlMask:
case Key.CursorUp | Key.ShiftMask | Key.CtrlMask:
if (point > 0) {
int x = start > -1 ? start : point;
int sbw = WordBackward (point);
if (sbw != -1)
point = sbw;
PrepareSelection (x, sbw - x);
}
break;
case Key.CursorRight | Key.ShiftMask | Key.CtrlMask:
case Key.CursorDown | Key.ShiftMask | Key.CtrlMask:
if (point < text.Count) {
int x = start > -1 ? start : point;
int sfw = WordForward (point);
if (sfw != -1)
point = sfw;
PrepareSelection (x, sfw - x);
}
break;
case Key.CursorLeft:
case Key.ControlB:
ClearAllSelection ();
@@ -311,23 +381,53 @@ namespace Terminal.Gui {
Adjust ();
break;
case Key.ControlY: // Control-y, yank
if (Clipboard.Contents == null)
return true;
var clip = TextModel.ToRunes (Clipboard.Contents);
if (clip == null)
return true;
if (point == text.Count) {
// Undo
case Key.ControlZ:
if (historyText != null && historyText.Count > 0) {
isFromHistory = true;
if (idxhistoryText > 0)
idxhistoryText--;
if (idxhistoryText > -1)
Text = historyText [idxhistoryText];
point = text.Count;
SetText(text.Concat(clip).ToList());
} else {
point += clip.Count;
SetText(text.GetRange(0, oldCursorPos).Concat(clip).Concat(text.GetRange(oldCursorPos, text.Count - oldCursorPos)));
isFromHistory = false;
}
Adjust ();
break;
//Redo
case Key.ControlY: // Control-y, yank
if (historyText != null && historyText.Count > 0) {
isFromHistory = true;
if (idxhistoryText < historyText.Count - 1) {
idxhistoryText++;
if (idxhistoryText < historyText.Count) {
Text = historyText [idxhistoryText];
} else if (idxhistoryText == historyText.Count - 1) {
Text = historyText [historyText.Count - 1];
}
point = text.Count;
}
isFromHistory = false;
}
//if (Clipboard.Contents == null)
// return true;
//var clip = TextModel.ToRunes (Clipboard.Contents);
//if (clip == null)
// return true;
//if (point == text.Count) {
// point = text.Count;
// SetText(text.Concat(clip).ToList());
//} else {
// point += clip.Count;
// SetText(text.GetRange(0, oldCursorPos).Concat(clip).Concat(text.GetRange(oldCursorPos, text.Count - oldCursorPos)));
//}
//Adjust ();
break;
case Key.CursorLeft | Key.CtrlMask:
case (Key)((int)'b' + Key.AltMask):
ClearAllSelection ();
int bw = WordBackward (point);
@@ -336,6 +436,7 @@ namespace Terminal.Gui {
Adjust ();
break;
case Key.CursorRight | Key.CtrlMask:
case (Key)((int)'f' + Key.AltMask):
ClearAllSelection ();
int fw = WordForward (point);
@@ -344,20 +445,20 @@ namespace Terminal.Gui {
Adjust ();
break;
case Key.AltMask | Key.ControlI:
case Key.InsertChar:
Used = !Used;
SetNeedsDisplay ();
break;
case Key.AltMask | Key.ControlC:
case Key.ControlC:
Copy ();
break;
case Key.AltMask | Key.ControlX:
case Key.ControlX:
Cut ();
break;
case Key.AltMask | Key.ControlV:
case Key.ControlV:
Paste ();
break;
@@ -378,7 +479,7 @@ namespace Terminal.Gui {
var kbstr = TextModel.ToRunes (ustring.Make ((uint)kb.Key));
if (used) {
point++;
if (point == text.Count) {
if (point == text.Count + 1) {
SetText (text.Concat (kbstr).ToList ());
} else {
SetText (text.GetRange (0, oldCursorPos).Concat (kbstr).Concat (text.GetRange (oldCursorPos, Math.Min (text.Count - oldCursorPos, text.Count))));
@@ -476,7 +577,8 @@ namespace Terminal.Gui {
public override bool MouseEvent (MouseEvent ev)
{
if (!ev.Flags.HasFlag (MouseFlags.Button1Pressed) && !ev.Flags.HasFlag (MouseFlags.ReportMousePosition) &&
!ev.Flags.HasFlag (MouseFlags.Button1Released))
!ev.Flags.HasFlag (MouseFlags.Button1Released) && !ev.Flags.HasFlag (MouseFlags.Button1DoubleClicked) &&
!ev.Flags.HasFlag (MouseFlags.Button1TripleClicked))
return false;
if (ev.Flags == MouseFlags.Button1Pressed) {
@@ -493,13 +595,25 @@ namespace Terminal.Gui {
if (Application.mouseGrabView == null) {
Application.GrabMouse (this);
}
} else if (ev.Flags == MouseFlags.Button1Pressed) {
int x = PositionCursor (ev);
if (SelectedLength != 0)
ClearAllSelection ();
} else if (ev.Flags == MouseFlags.Button1Released) {
isButtonReleased = true;
Application.UngrabMouse ();
} else if (ev.Flags == MouseFlags.Button1DoubleClicked) {
int x = PositionCursor (ev);
int sbw = x;
if (x > 0 && (char)Text [x - 1] != ' ')
sbw = WordBackward (x);
if (sbw != -1) {
x = sbw;
PositionCursor (x);
}
int sfw = WordForward (x);
ClearAllSelection ();
PrepareSelection (sbw, sfw - sbw);
} else if (ev.Flags == MouseFlags.Button1TripleClicked) {
PositionCursor (0);
ClearAllSelection ();
PrepareSelection (0, text.Count);
}
SetNeedsDisplay ();
@@ -510,12 +624,16 @@ namespace Terminal.Gui {
{
// We could also set the cursor position.
int x;
if (Application.mouseGrabView == null) {
if (text.Count == 0)
x = ev.X - ev.OfX;
else
x = ev.X;
} else {
x = ev.X;// - (text.Count > Frame.Width ? text.Count - Frame.Width : 0);
}
return PositionCursor (x);
}
private int PositionCursor (int x)
{
point = first + x;
if (point > text.Count)
point = text.Count;
@@ -524,12 +642,12 @@ namespace Terminal.Gui {
return x;
}
void PrepareSelection (int x)
void PrepareSelection (int x, int direction = 0)
{
x = x + first < 0 ? 0 : x + first;
x = x + first < 0 ? 0 : x;
SelectedStart = SelectedStart == -1 && text.Count > 0 && x >= 0 && x <= text.Count ? x : SelectedStart;
if (SelectedStart > -1) {
SelectedLength = x <= text.Count ? x - SelectedStart : text.Count - SelectedStart;
SelectedLength = x + direction <= text.Count ? x + direction - SelectedStart : text.Count - SelectedStart;
SetSelectedStartSelectedLength ();
SelectedText = length > 0 ? ustring.Make (text).ToString ().Substring (
start < 0 ? 0 : start, length > text.Count ? text.Count : length) : "";
@@ -542,7 +660,7 @@ namespace Terminal.Gui {
/// </summary>
public void ClearAllSelection ()
{
if (SelectedLength == 0)
if (SelectedStart == -1 && SelectedLength == 0)
return;
SelectedStart = -1;
SelectedLength = 0;
@@ -601,10 +719,13 @@ namespace Terminal.Gui {
{
string actualText = Text.ToString ();
int start = SelectedStart == -1 ? CursorPosition : SelectedStart;
ustring cbTxt = Clipboard.Contents?.ToString () ?? "";
Text = actualText.Substring (0, start) +
Clipboard.Contents?.ToString () +
cbTxt +
actualText.Substring (start + SelectedLength, actualText.Length - start - SelectedLength);
point = start + cbTxt.Length;
SelectedLength = 0;
ClearAllSelection ();
SetNeedsDisplay ();
}