mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-30 17:57:57 +01:00
Mouse text selection with cut, copy and paste on text fields
This commit is contained in:
220
Example/demo.cs
220
Example/demo.cs
@@ -3,7 +3,10 @@ using System;
|
||||
using Mono.Terminal;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using NStack;
|
||||
|
||||
static class Demo {
|
||||
class Box10x : View {
|
||||
@@ -42,6 +45,9 @@ static class Demo {
|
||||
Rune r;
|
||||
switch (x % 3) {
|
||||
case 0:
|
||||
Driver.AddRune (y.ToString ().ToCharArray (0, 1) [0]);
|
||||
if (y > 9)
|
||||
Driver.AddRune (y.ToString ().ToCharArray (1, 1) [0]);
|
||||
r = '.';
|
||||
break;
|
||||
case 1:
|
||||
@@ -60,11 +66,15 @@ static class Demo {
|
||||
|
||||
static void ShowTextAlignments (View container)
|
||||
{
|
||||
int i = 0;
|
||||
string txt = "Hello world, how are you doing today";
|
||||
container.Add (
|
||||
new Label (new Rect (0, 0, 40, 3), "1-Hello world, how are you doing today") { TextAlignment = TextAlignment.Left },
|
||||
new Label (new Rect (0, 4, 40, 3), "2-Hello world, how are you doing today") { TextAlignment = TextAlignment.Right },
|
||||
new Label (new Rect (0, 8, 40, 3), "3-Hello world, how are you doing today") { TextAlignment = TextAlignment.Centered },
|
||||
new Label (new Rect (0, 12, 40, 3), "4-Hello world, how are you doing today") { TextAlignment = TextAlignment.Justified });
|
||||
new FrameView (new Rect (75, 1, txt.Length + 6, 20), "Text Alignments") {
|
||||
new Label(new Rect(0, 1, 40, 3), $"{i+1}-{txt}") { TextAlignment = TextAlignment.Left },
|
||||
new Label(new Rect(0, 5, 40, 3), $"{i+2}-{txt}") { TextAlignment = TextAlignment.Right },
|
||||
new Label(new Rect(0, 9, 40, 3), $"{i+3}-{txt}") { TextAlignment = TextAlignment.Centered },
|
||||
new Label(new Rect(0, 13, 40, 3), $"{i+4}-{txt}") { TextAlignment = TextAlignment.Justified }
|
||||
});
|
||||
}
|
||||
|
||||
static void ShowEntries (View container)
|
||||
@@ -75,9 +85,11 @@ static class Demo {
|
||||
ShowVerticalScrollIndicator = true,
|
||||
ShowHorizontalScrollIndicator = true
|
||||
};
|
||||
|
||||
#if false
|
||||
scrollView.Add (new Box10x (0, 0));
|
||||
//scrollView.Add (new Filler (new Rect (0, 0, 40, 40)));
|
||||
#else
|
||||
scrollView.Add (new Filler (new Rect (0, 0, 40, 40)));
|
||||
#endif
|
||||
|
||||
// This is just to debug the visuals of the scrollview when small
|
||||
var scrollView2 = new ScrollView (new Rect (72, 10, 3, 3)) {
|
||||
@@ -143,7 +155,9 @@ static class Demo {
|
||||
new TimeField (3, 20, DateTime.Now),
|
||||
new TimeField (23, 20, DateTime.Now, true),
|
||||
progress,
|
||||
new Label (3, 22, "Press F9 (on Unix, ESC+9 is an alias) to activate the menubar")
|
||||
new Label (3, 24, "Press F9 (on Unix, ESC+9 is an alias) to activate the menubar"),
|
||||
menuKeysStyle,
|
||||
menuAutoMouseNav
|
||||
|
||||
);
|
||||
|
||||
@@ -164,10 +178,11 @@ static class Demo {
|
||||
|
||||
//
|
||||
// Creates a nested editor
|
||||
static void Editor(Toplevel top) {
|
||||
static void Editor (Toplevel top)
|
||||
{
|
||||
var tframe = top.Frame;
|
||||
var ntop = new Toplevel(tframe);
|
||||
var menu = new MenuBar(new MenuBarItem[] {
|
||||
var ntop = new Toplevel (tframe);
|
||||
var menu = new MenuBar (new MenuBarItem [] {
|
||||
new MenuBarItem ("_File", new MenuItem [] {
|
||||
new MenuItem ("_Close", "", () => {Application.RequestStop ();}),
|
||||
}),
|
||||
@@ -177,25 +192,25 @@ static class Demo {
|
||||
new MenuItem ("_Paste", "", null)
|
||||
}),
|
||||
});
|
||||
ntop.Add(menu);
|
||||
ntop.Add (menu);
|
||||
|
||||
string fname = null;
|
||||
foreach (var s in new[] { "/etc/passwd", "c:\\windows\\win.ini" })
|
||||
if (System.IO.File.Exists(s)) {
|
||||
foreach (var s in new [] { "/etc/passwd", "c:\\windows\\win.ini" })
|
||||
if (System.IO.File.Exists (s)) {
|
||||
fname = s;
|
||||
break;
|
||||
}
|
||||
|
||||
var win = new Window(fname ?? "Untitled") {
|
||||
var win = new Window (fname ?? "Untitled") {
|
||||
X = 0,
|
||||
Y = 1,
|
||||
Width = Dim.Fill(),
|
||||
Height = Dim.Fill()
|
||||
Width = Dim.Fill (),
|
||||
Height = Dim.Fill ()
|
||||
};
|
||||
ntop.Add(win);
|
||||
ntop.Add (win);
|
||||
|
||||
var text = new TextView (new Rect (0, 0, tframe.Width - 2, tframe.Height - 3));
|
||||
|
||||
var text = new TextView(new Rect(0, 0, tframe.Width - 2, tframe.Height - 3));
|
||||
|
||||
if (fname != null)
|
||||
text.Text = System.IO.File.ReadAllText (fname);
|
||||
win.Add (text);
|
||||
@@ -211,7 +226,7 @@ static 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");
|
||||
}
|
||||
|
||||
// Watch what happens when I try to introduce a newline after the first open brace
|
||||
@@ -223,7 +238,7 @@ static class Demo {
|
||||
var d = new OpenDialog ("Open", "Open a file");
|
||||
Application.Run (d);
|
||||
|
||||
MessageBox.Query (50, 7, "Selected File", string.Join (", ", d.FilePaths), "ok");
|
||||
MessageBox.Query (50, 7, "Selected File", string.Join (", ", d.FilePaths), "Ok");
|
||||
}
|
||||
|
||||
public static void ShowHex (Toplevel top)
|
||||
@@ -254,9 +269,103 @@ static class Demo {
|
||||
};
|
||||
win.Add (hex);
|
||||
Application.Run (ntop);
|
||||
|
||||
|
||||
}
|
||||
|
||||
public class MenuItemDetails : MenuItem {
|
||||
ustring title;
|
||||
string help;
|
||||
Action action;
|
||||
|
||||
public MenuItemDetails (ustring title, string help, Action action) : base (title, help, action)
|
||||
{
|
||||
this.title = title;
|
||||
this.help = help;
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public static MenuItemDetails Instance (MenuItem mi)
|
||||
{
|
||||
return (MenuItemDetails)mi.GetMenuItem ();
|
||||
}
|
||||
}
|
||||
|
||||
public delegate MenuItem MenuItemDelegate (MenuItemDetails menuItem);
|
||||
|
||||
public static void ShowMenuItem (MenuItem mi)
|
||||
{
|
||||
BindingFlags flags = BindingFlags.Public | BindingFlags.Static;
|
||||
MethodInfo minfo = typeof (MenuItemDetails).GetMethod ("Instance", flags);
|
||||
MenuItemDelegate mid = (MenuItemDelegate)Delegate.CreateDelegate (typeof (MenuItemDelegate), minfo);
|
||||
MessageBox.Query (70, 7, mi.Title.ToString (),
|
||||
$"{mi.Title.ToString ()} selected. Is from submenu: {mi.GetMenuBarItem ()}", "Ok");
|
||||
}
|
||||
|
||||
private static void MenuKeysStyle_Toggled (object sender, EventArgs e)
|
||||
{
|
||||
menu.UseKeysUpDownAsKeysLeftRight = menuKeysStyle.Checked;
|
||||
}
|
||||
|
||||
private static void MenuAutoMouseNav_Toggled (object sender, EventArgs e)
|
||||
{
|
||||
menu.WantMousePositionReports = menuAutoMouseNav.Checked;
|
||||
}
|
||||
|
||||
//private static TextField GetTextFieldSelText (View vt)
|
||||
//{
|
||||
// TextField textField;
|
||||
// foreach (View v in vt.Subviews) {
|
||||
// if (v is TextField && ((TextField)v).SelText != "")
|
||||
// return v as TextField;
|
||||
// else
|
||||
// textField = GetTextFieldSelText (v);
|
||||
// if (textField != null)
|
||||
// return textField;
|
||||
// }
|
||||
// return null;
|
||||
//}
|
||||
|
||||
private static void Copy ()
|
||||
{
|
||||
TextField textField = menu.LastFocused as TextField;
|
||||
if (textField != null && textField.SelLength > 0) {
|
||||
Clipboard.Contents = textField.SelText;
|
||||
textField.SelLength = 0;
|
||||
textField.SetNeedsDisplay ();
|
||||
}
|
||||
}
|
||||
|
||||
private static void Cut ()
|
||||
{
|
||||
TextField textField = menu.LastFocused as TextField;
|
||||
if (textField != null && textField.SelLength > 0) {
|
||||
Clipboard.Contents = textField.SelText;
|
||||
string actualText = textField.Text.ToString ();
|
||||
string newText = actualText.Substring (0, textField.SelStart) +
|
||||
actualText.Substring (textField.SelStart + textField.SelLength, actualText.Length - textField.SelStart - textField.SelLength);
|
||||
textField.Text = newText;
|
||||
textField.SelLength = 0;
|
||||
textField.CursorPosition = textField.SelStart == -1 ? textField.CursorPosition : textField.SelStart;
|
||||
textField.SetNeedsDisplay ();
|
||||
}
|
||||
}
|
||||
|
||||
private static void Paste ()
|
||||
{
|
||||
TextField textField = menu.LastFocused as TextField;
|
||||
if (textField != null) {
|
||||
string actualText = textField.Text.ToString ();
|
||||
int start = textField.SelStart == -1 ? textField.CursorPosition : textField.SelStart;
|
||||
string newText = actualText.Substring (0, start) +
|
||||
Clipboard.Contents?.ToString() +
|
||||
actualText.Substring (start + textField.SelLength, actualText.Length - start - textField.SelLength);
|
||||
textField.Text = newText;
|
||||
textField.SelLength = 0;
|
||||
textField.SetNeedsDisplay ();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#region Selection Demo
|
||||
|
||||
static void ListSelectionDemo ()
|
||||
@@ -295,14 +404,19 @@ static class Demo {
|
||||
|
||||
|
||||
public static Label ml;
|
||||
public static MenuBar menu;
|
||||
public static CheckBox menuKeysStyle;
|
||||
public static CheckBox menuAutoMouseNav;
|
||||
static void Main ()
|
||||
{
|
||||
if (Debugger.IsAttached)
|
||||
CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.GetCultureInfo ("en-US");
|
||||
|
||||
//Application.UseSystemConsole = true;
|
||||
Application.Init ();
|
||||
|
||||
|
||||
var top = Application.Top;
|
||||
|
||||
var tframe = top.Frame;
|
||||
|
||||
//Open ();
|
||||
#if true
|
||||
var win = new Window ("Hello") {
|
||||
@@ -312,27 +426,63 @@ static class Demo {
|
||||
Height = Dim.Fill ()
|
||||
};
|
||||
#else
|
||||
var tframe = top.Frame;
|
||||
|
||||
var win = new Window (new Rect (0, 1, tframe.Width, tframe.Height - 1), "Hello");
|
||||
#endif
|
||||
var menu = new MenuBar (new MenuBarItem [] {
|
||||
MenuItemDetails [] menuItems = {
|
||||
new MenuItemDetails ("F_ind", "", null),
|
||||
new MenuItemDetails ("_Replace", "", null),
|
||||
new MenuItemDetails ("_Item1", "", null),
|
||||
new MenuItemDetails ("_Not From Sub Menu", "", null)
|
||||
};
|
||||
|
||||
menuItems [0].Action = () => ShowMenuItem (menuItems [0]);
|
||||
menuItems [1].Action = () => ShowMenuItem (menuItems [1]);
|
||||
menuItems [2].Action = () => ShowMenuItem (menuItems [2]);
|
||||
menuItems [3].Action = () => ShowMenuItem (menuItems [3]);
|
||||
|
||||
menu = new MenuBar (new MenuBarItem [] {
|
||||
new MenuBarItem ("_File", new MenuItem [] {
|
||||
new MenuItem ("Text Editor Demo", "", () => { Editor (top); }),
|
||||
new MenuItem ("_New", "Creates new file", NewFile),
|
||||
new MenuItem ("_Open", "", Open),
|
||||
new MenuItem ("_Hex", "", () => ShowHex (top)),
|
||||
new MenuItem ("_Close", "", () => Close ()),
|
||||
null,
|
||||
new MenuItem ("_Quit", "", () => { if (Quit ()) top.Running = false; })
|
||||
}),
|
||||
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),
|
||||
new MenuItem ("_Find and Replace",
|
||||
new MenuBarItem (new MenuItem[] {menuItems [0], menuItems [1] })),
|
||||
menuItems[3]
|
||||
}),
|
||||
new MenuBarItem ("_List Demos", new MenuItem [] {
|
||||
new MenuBarItem ("_List Demos", new MenuItem [] {
|
||||
new MenuItem ("Select Items", "", ListSelectionDemo),
|
||||
}),
|
||||
new MenuBarItem ("Test Menu and SubMenus", new MenuItem [] {
|
||||
new MenuItem ("SubMenu1Item1",
|
||||
new MenuBarItem (new MenuItem[] {
|
||||
new MenuItem ("SubMenu2Item1",
|
||||
new MenuBarItem (new MenuItem [] {
|
||||
new MenuItem ("SubMenu3Item1",
|
||||
new MenuBarItem (new MenuItem [] { menuItems [2] })
|
||||
)
|
||||
})
|
||||
)
|
||||
})
|
||||
)
|
||||
}),
|
||||
});
|
||||
|
||||
menuKeysStyle = new CheckBox (3, 25, "UseKeysUpDownAsKeysLeftRight", true);
|
||||
menuKeysStyle.Toggled += MenuKeysStyle_Toggled;
|
||||
menuAutoMouseNav = new CheckBox (40, 25, "UseMenuAutoNavigation", true);
|
||||
menuAutoMouseNav.Toggled += MenuAutoMouseNav_Toggled;
|
||||
|
||||
ShowEntries (win);
|
||||
|
||||
int count = 0;
|
||||
@@ -341,14 +491,14 @@ static class Demo {
|
||||
|
||||
ml.Text = $"Mouse: ({me.X},{me.Y}) - {me.Flags} {count++}";
|
||||
};
|
||||
|
||||
|
||||
var test = new Label (3, 18, "Se iniciará el análisis");
|
||||
win.Add (test);
|
||||
win.Add (ml);
|
||||
|
||||
// ShowTextAlignments (win);
|
||||
|
||||
ShowTextAlignments (win);
|
||||
top.Add (win);
|
||||
top.Add (menu);
|
||||
Application.Run ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,6 +228,8 @@ namespace Terminal.Gui {
|
||||
View container = null;
|
||||
View focused = null;
|
||||
Direction focusDirection;
|
||||
public event EventHandler OnEnter;
|
||||
public event EventHandler OnLeave;
|
||||
|
||||
internal Direction FocusDirection {
|
||||
get => SuperView?.FocusDirection ?? focusDirection;
|
||||
@@ -430,7 +432,7 @@ namespace Terminal.Gui {
|
||||
SetNeedsDisplay (Bounds);
|
||||
}
|
||||
|
||||
bool layoutNeeded = true;
|
||||
internal bool layoutNeeded = true;
|
||||
|
||||
internal void SetNeedsLayout ()
|
||||
{
|
||||
@@ -826,11 +828,16 @@ namespace Terminal.Gui {
|
||||
}
|
||||
internal set {
|
||||
if (base.HasFocus != value)
|
||||
if (value == true)
|
||||
OnEnter?.Invoke (this, new EventArgs ());
|
||||
else
|
||||
OnLeave?.Invoke (this, new EventArgs ());
|
||||
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 ());
|
||||
focused.HasFocus = false;
|
||||
focused = null;
|
||||
}
|
||||
@@ -1283,6 +1290,19 @@ namespace Terminal.Gui {
|
||||
/// toplevel and then invoke <see cref="M:Terminal.Gui.Application.Run"/> with the
|
||||
/// new toplevel.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// TopLevels can also opt-in to more sophisticated initialization
|
||||
/// by implementing <see cref="ISupportInitialize"/>. When they do
|
||||
/// so, the <see cref="ISupportInitialize.BeginInit"/> and
|
||||
/// <see cref="ISupportInitialize.EndInit"/> methods will be called
|
||||
/// before running the view.
|
||||
/// If first-run-only initialization is preferred, the <see cref="ISupportInitializeNotification"/>
|
||||
/// can be implemented too, in which case the <see cref="ISupportInitialize"/>
|
||||
/// methods will only be called if <see cref="ISupportInitializeNotification.IsInitialized"/>
|
||||
/// is <see langword="false"/>. This allows proper View inheritance hierarchies
|
||||
/// to override base class layout code optimally by doing so only on first run,
|
||||
/// instead of on every run.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class Toplevel : View {
|
||||
/// <summary>
|
||||
@@ -1799,25 +1819,25 @@ namespace Terminal.Gui {
|
||||
{
|
||||
var chain = toplevels.ToList();
|
||||
foreach (var topLevel in chain) {
|
||||
if (topLevel.Modal)
|
||||
break;
|
||||
if (topLevel.ProcessHotKey (ke))
|
||||
return;
|
||||
if (topLevel.Modal)
|
||||
break;
|
||||
}
|
||||
|
||||
foreach (var topLevel in chain) {
|
||||
if (topLevel.Modal)
|
||||
break;
|
||||
if (topLevel.ProcessKey (ke))
|
||||
return;
|
||||
if (topLevel.Modal)
|
||||
break;
|
||||
}
|
||||
|
||||
foreach (var topLevel in chain) {
|
||||
if (topLevel.Modal)
|
||||
break;
|
||||
// Process the key normally
|
||||
if (topLevel.ProcessColdKey (ke))
|
||||
return;
|
||||
if (topLevel.Modal)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2041,11 +2061,28 @@ namespace Terminal.Gui {
|
||||
DrawBounds (state.Toplevel);
|
||||
state.Toplevel.PositionCursor ();
|
||||
Driver.Refresh ();
|
||||
} else if (CheckLayoutNeeded (state.Toplevel)) {
|
||||
TerminalResized ();
|
||||
layoutNeeded = false;
|
||||
} else
|
||||
Driver.UpdateCursor ();
|
||||
}
|
||||
}
|
||||
|
||||
static bool layoutNeeded;
|
||||
static bool CheckLayoutNeeded (View view)
|
||||
{
|
||||
if (view.layoutNeeded)
|
||||
return layoutNeeded = view.layoutNeeded;
|
||||
|
||||
for (int i = 0; view.Subviews.Count > i; i++) {
|
||||
CheckLayoutNeeded (view.Subviews [i]);
|
||||
if (layoutNeeded)
|
||||
return layoutNeeded;
|
||||
}
|
||||
return layoutNeeded;
|
||||
}
|
||||
|
||||
internal static bool DebugDrawBounds;
|
||||
|
||||
// Need to look into why this does not work properly.
|
||||
|
||||
@@ -142,6 +142,10 @@ namespace Terminal.Gui {
|
||||
/// </summary>
|
||||
public Attribute Normal;
|
||||
/// <summary>
|
||||
/// The default color for text, when the view is disabled.
|
||||
/// </summary>
|
||||
public Attribute Disabled;
|
||||
/// <summary>
|
||||
/// The color for text when the view has the focus.
|
||||
/// </summary>
|
||||
public Attribute Focus;
|
||||
|
||||
@@ -264,6 +264,7 @@ namespace Terminal.Gui {
|
||||
Colors.Menu.Focus = Curses.A_BOLD | MakeColor (Curses.COLOR_WHITE, Curses.COLOR_BLACK);
|
||||
Colors.Menu.HotNormal = Curses.A_BOLD | MakeColor (Curses.COLOR_YELLOW, Curses.COLOR_CYAN);
|
||||
Colors.Menu.Normal = Curses.A_BOLD | MakeColor (Curses.COLOR_WHITE, Curses.COLOR_CYAN);
|
||||
Colors.Menu.Disabled = MakeColor(Curses.COLOR_WHITE, Curses.COLOR_CYAN);
|
||||
|
||||
Colors.Dialog.Normal = MakeColor (Curses.COLOR_BLACK, Curses.COLOR_WHITE);
|
||||
Colors.Dialog.Focus = MakeColor (Curses.COLOR_BLACK, Curses.COLOR_CYAN);
|
||||
|
||||
@@ -144,6 +144,7 @@ namespace Terminal.Gui {
|
||||
Colors.Menu.Focus = MakeColor (ConsoleColor.White, ConsoleColor.Black);
|
||||
Colors.Menu.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.Cyan);
|
||||
Colors.Menu.Normal = MakeColor (ConsoleColor.White, ConsoleColor.Cyan);
|
||||
Colors.Menu.Disabled = MakeColor(ConsoleColor.DarkGray, ConsoleColor.Cyan);
|
||||
|
||||
Colors.Dialog.Normal = MakeColor (ConsoleColor.Black, ConsoleColor.Gray);
|
||||
Colors.Dialog.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Cyan);
|
||||
|
||||
@@ -553,7 +553,7 @@ namespace Terminal.Gui {
|
||||
|
||||
case WindowsConsole.EventType.WindowBufferSize:
|
||||
cols = inputEvent.WindowBufferSizeEvent.size.X;
|
||||
rows = inputEvent.WindowBufferSizeEvent.size.Y - 1;
|
||||
rows = inputEvent.WindowBufferSizeEvent.size.Y;
|
||||
ResizeScreen ();
|
||||
UpdateOffScreen ();
|
||||
TerminalResized?.Invoke();
|
||||
@@ -566,7 +566,9 @@ namespace Terminal.Gui {
|
||||
|
||||
private MouseEvent ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent)
|
||||
{
|
||||
MouseFlags mouseFlag = MouseFlags.AllEvents;
|
||||
MouseFlags mouseFlag = 0;
|
||||
|
||||
|
||||
|
||||
// The ButtonState member of the MouseEvent structure has bit corresponding to each mouse button.
|
||||
// This will tell when a mouse button is pressed. When the button is released this event will
|
||||
@@ -577,7 +579,8 @@ namespace Terminal.Gui {
|
||||
LastMouseButtonPressed = null;
|
||||
}
|
||||
|
||||
if (mouseEvent.EventFlags == 0 && LastMouseButtonPressed == null) {
|
||||
if (mouseEvent.EventFlags == 0 && LastMouseButtonPressed == null ||
|
||||
mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved && mouseEvent.ButtonState != 0) {
|
||||
switch (mouseEvent.ButtonState) {
|
||||
case WindowsConsole.ButtonState.Button1Pressed:
|
||||
mouseFlag = MouseFlags.Button1Pressed;
|
||||
@@ -587,8 +590,8 @@ namespace Terminal.Gui {
|
||||
mouseFlag = MouseFlags.Button2Pressed;
|
||||
break;
|
||||
|
||||
case WindowsConsole.ButtonState.Button3Pressed:
|
||||
mouseFlag = MouseFlags.Button3Pressed;
|
||||
case WindowsConsole.ButtonState.RightmostButtonPressed:
|
||||
mouseFlag = MouseFlags.Button4Pressed;
|
||||
break;
|
||||
}
|
||||
LastMouseButtonPressed = mouseEvent.ButtonState;
|
||||
@@ -602,11 +605,14 @@ namespace Terminal.Gui {
|
||||
mouseFlag = MouseFlags.Button2Clicked;
|
||||
break;
|
||||
|
||||
case WindowsConsole.ButtonState.Button3Pressed:
|
||||
mouseFlag = MouseFlags.Button3Clicked;
|
||||
case WindowsConsole.ButtonState.RightmostButtonPressed:
|
||||
mouseFlag = MouseFlags.Button4Clicked;
|
||||
break;
|
||||
}
|
||||
LastMouseButtonPressed = null;
|
||||
} else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseWheeled) {
|
||||
mouseFlag = MouseFlags.ButtonWheeled;
|
||||
LastMouseButtonPressed = null;
|
||||
} else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved) {
|
||||
mouseFlag = MouseFlags.ReportMousePosition;
|
||||
}
|
||||
@@ -759,6 +765,7 @@ namespace Terminal.Gui {
|
||||
Colors.Menu.Focus = MakeColor (ConsoleColor.White, ConsoleColor.Black);
|
||||
Colors.Menu.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.Cyan);
|
||||
Colors.Menu.Normal = MakeColor (ConsoleColor.White, ConsoleColor.Cyan);
|
||||
Colors.Menu.Disabled = MakeColor(ConsoleColor.DarkGray, ConsoleColor.Cyan);
|
||||
|
||||
Colors.Dialog.Normal = MakeColor (ConsoleColor.Black, ConsoleColor.Gray);
|
||||
Colors.Dialog.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Cyan);
|
||||
|
||||
@@ -400,11 +400,11 @@ namespace Terminal.Gui {
|
||||
/// </summary>
|
||||
Button4TripleClicked = unchecked((int)0x400000),
|
||||
/// <summary>
|
||||
/// The fourth button was pressed.
|
||||
/// Flag: the shift key was pressed when the mouse button took place.
|
||||
/// </summary>
|
||||
ButtonShift = unchecked((int)0x2000000),
|
||||
/// <summary>
|
||||
/// Flag: the shift key was pressed when the mouse button took place.
|
||||
/// Flag: the ctrl key was pressed when the mouse button took place.
|
||||
/// </summary>
|
||||
ButtonCtrl = unchecked((int)0x1000000),
|
||||
/// <summary>
|
||||
@@ -416,6 +416,10 @@ namespace Terminal.Gui {
|
||||
/// </summary>
|
||||
ReportMousePosition = unchecked((int)0x8000000),
|
||||
/// <summary>
|
||||
/// Vertical button wheeled.
|
||||
/// </summary>
|
||||
ButtonWheeled = unchecked((int)0x4040),
|
||||
/// <summary>
|
||||
/// Mask that captures all the events.
|
||||
/// </summary>
|
||||
AllEvents = unchecked((int)0x7ffffff),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -35,7 +35,7 @@ namespace Terminal.Gui {
|
||||
/// Client code can hook up to this event, it is
|
||||
/// raised when the text in the entry changes.
|
||||
/// </remarks>
|
||||
public event EventHandler Changed;
|
||||
public event EventHandler<ustring> Changed;
|
||||
|
||||
/// <summary>
|
||||
/// Public constructor that creates a text field, with layout controlled with X, Y, Width and Height.
|
||||
@@ -58,6 +58,7 @@ namespace Terminal.Gui {
|
||||
this.text = TextModel.ToRunes (text);
|
||||
point = text.Length;
|
||||
CanFocus = true;
|
||||
WantMousePositionReports = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -98,7 +99,10 @@ namespace Terminal.Gui {
|
||||
}
|
||||
|
||||
set {
|
||||
ustring oldText = ustring.Make (text);
|
||||
text = TextModel.ToRunes (value);
|
||||
Changed?.Invoke (this, oldText);
|
||||
|
||||
if (point > text.Count)
|
||||
point = Math.Max (text.Count-1, 0);
|
||||
|
||||
@@ -145,6 +149,9 @@ namespace Terminal.Gui {
|
||||
|
||||
public override void Redraw (Rect region)
|
||||
{
|
||||
ColorScheme color = Colors.Menu;
|
||||
SetSelStartSelLength ();
|
||||
|
||||
Driver.SetAttribute (ColorScheme.Focus);
|
||||
Move (0, 0);
|
||||
|
||||
@@ -157,11 +164,14 @@ namespace Terminal.Gui {
|
||||
if (idx < first)
|
||||
continue;
|
||||
var cols = Rune.ColumnWidth (rune);
|
||||
System.Diagnostics.Debug.WriteLine ($"SelStart: {SelStart}, SelLength: {SelLength} SelText: {SelText}");
|
||||
Driver.SetAttribute (idx >= start && length > 0 && idx < start + length ? color.Focus : ColorScheme.Focus);
|
||||
if (col + cols < width)
|
||||
Driver.AddRune ((Rune)(Secret ? '*' : rune));
|
||||
col += cols;
|
||||
}
|
||||
|
||||
Driver.SetAttribute (ColorScheme.Focus);
|
||||
for (int i = col; i < Frame.Width; i++)
|
||||
Driver.AddRune (' ');
|
||||
|
||||
@@ -193,8 +203,6 @@ namespace Terminal.Gui {
|
||||
void SetText (List<Rune> newText)
|
||||
{
|
||||
text = newText;
|
||||
if (Changed != null)
|
||||
Changed (this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
void SetText (IEnumerable<Rune> newText)
|
||||
@@ -215,6 +223,9 @@ namespace Terminal.Gui {
|
||||
|
||||
public override bool ProcessKey (KeyEvent kb)
|
||||
{
|
||||
if (selResetNeeded || SelStart > -1)
|
||||
ClearAllSelection ();
|
||||
|
||||
// remember current cursor position
|
||||
// because the new calculated cursor position is needed to be set BEFORE the change event is triggert
|
||||
// Needed for the Elmish Wrapper issue https://github.com/DieselMeister/Terminal.Gui.Elmish/issues/2
|
||||
@@ -222,6 +233,7 @@ namespace Terminal.Gui {
|
||||
|
||||
switch (kb.Key) {
|
||||
case Key.DeleteChar:
|
||||
// TODO: If text length greater than 0 delete all selected text and reposition the cursor
|
||||
case Key.ControlD:
|
||||
if (text.Count == 0 || text.Count== point)
|
||||
return true;
|
||||
@@ -232,6 +244,7 @@ namespace Terminal.Gui {
|
||||
|
||||
case Key.Delete:
|
||||
case Key.Backspace:
|
||||
// TODO: If text length greater than 0 delete all selected text and reposition the cursor
|
||||
if (point == 0)
|
||||
return true;
|
||||
|
||||
@@ -401,9 +414,18 @@ namespace Terminal.Gui {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public override bool MouseEvent (MouseEvent ev)
|
||||
public int SelStart { get; set; } = -1;
|
||||
|
||||
public int SelLength { get; set; } = 0;
|
||||
|
||||
public ustring SelText { get; set; }
|
||||
|
||||
bool selResetNeeded;
|
||||
int start, length;
|
||||
|
||||
public override bool MouseEvent (MouseEvent ev)
|
||||
{
|
||||
if (!ev.Flags.HasFlag (MouseFlags.Button1Clicked))
|
||||
if (!ev.Flags.HasFlag (MouseFlags.Button1Clicked) && !ev.Flags.HasFlag (MouseFlags.Button1Pressed))
|
||||
return false;
|
||||
|
||||
if (!HasFocus)
|
||||
@@ -416,10 +438,41 @@ namespace Terminal.Gui {
|
||||
if (point < first)
|
||||
point = 0;
|
||||
|
||||
// FIXME: If mouse exits out of view with left button pressed I can't change selResetNeeded to true
|
||||
if (ev.Flags.HasFlag (MouseFlags.Button1Clicked) && SelStart > -1)
|
||||
selResetNeeded = true;
|
||||
else if (ev.Flags.HasFlag (MouseFlags.Button1Pressed)) {
|
||||
if (selResetNeeded)
|
||||
ClearAllSelection ();
|
||||
selResetNeeded = false;
|
||||
SelStart = SelStart == -1 && text.Count > 0 && ev.X >= 0 && ev.X <= text.Count ? ev.X : SelStart;
|
||||
if (SelStart > -1) {
|
||||
SelLength = SelStart > -1 && ev.X <= text.Count ? ev.X - SelStart : text.Count - SelStart;
|
||||
SetSelStartSelLength ();
|
||||
SelText = length > 0 ? ustring.Make (text).ToString ().Substring (start, length) : "";
|
||||
}
|
||||
}
|
||||
|
||||
SetNeedsDisplay ();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClearAllSelection ()
|
||||
{
|
||||
SelStart = -1;
|
||||
SelLength = 0;
|
||||
SelText = "";
|
||||
}
|
||||
|
||||
void SetSelStartSelLength ()
|
||||
{
|
||||
if (SelLength < 0) {
|
||||
start = SelLength + SelStart;
|
||||
length = Math.Abs (SelLength);
|
||||
} else {
|
||||
start = SelStart;
|
||||
length = SelLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user