diff --git a/Example/demo.cs.orig b/Example/demo.cs.orig new file mode 100644 index 000000000..f3363488b --- /dev/null +++ b/Example/demo.cs.orig @@ -0,0 +1,611 @@ +using Terminal.Gui; +using System; +using Mono.Terminal; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Reflection; +using NStack; +using System.Text; + +static class Demo { + //class Box10x : View, IScrollView { + class Box10x : View { + int w = 40; + int h = 50; + + public bool WantCursorPosition { get; set; } = false; + + public Box10x (int x, int y) : base (new Rect (x, y, 20, 10)) + { + } + + public Size GetContentSize () + { + return new Size (w, h); + } + + public void SetCursorPosition (Point pos) + { + throw new NotImplementedException (); + } + + public override void Redraw (Rect region) + { + //Point pos = new Point (region.X, region.Y); + Driver.SetAttribute (ColorScheme.Focus); + + for (int y = 0; y < h; y++) { + Move (0, y); + Driver.AddStr (y.ToString ()); + for (int x = 0; x < w - y.ToString ().Length; x++) { + //Driver.AddRune ((Rune)('0' + (x + y) % 10)); + if (y.ToString ().Length < w) + Driver.AddStr (" "); + } + } + //Move (pos.X, pos.Y); + } + } + + class Filler : View { + public Filler (Rect rect) : base (rect) + { + } + + public override void Redraw (Rect region) + { + Driver.SetAttribute (ColorScheme.Focus); + var f = Frame; + + for (int y = 0; y < f.Width; y++) { + Move (0, y); + for (int x = 0; x < f.Height; x++) { + 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: + r = 'o'; + break; + default: + r = 'O'; + break; + } + Driver.AddRune (r); + } + } + } + } + + static void ShowTextAlignments () + { +<<<<<<< HEAD + var container = new Window ($"Show Text Alignments") { + X = 0, + Y = 0, + Width = Dim.Fill (), + Height = Dim.Fill () + }; + container.OnKeyUp += (KeyEvent ke) => { + if (ke.Key == Key.Esc) + container.Running = false; + }; +======= + var container = new Dialog ( + "Text Alignments", 70, 20, + new Button ("Ok", is_default: true) { Clicked = () => { Application.RequestStop (); } }, + new Button ("Cancel") { Clicked = () => { Application.RequestStop (); } }); + +>>>>>>> cb40c5c2491a559658481d20dd4b6a3343c0183f + + int i = 0; + string txt = "Hello world, how are you doing today?"; + container.Add ( +<<<<<<< HEAD + new Label ($"{i+1}-{txt}") { TextAlignment = TextAlignment.Left, Y = 3, Width = Dim.Fill () }, + new Label ($"{i+2}-{txt}") { TextAlignment = TextAlignment.Right, Y = 5, Width = Dim.Fill () }, + new Label ($"{i+3}-{txt}") { TextAlignment = TextAlignment.Centered, Y = 7, Width = Dim.Fill () }, + new Label ($"{i+4}-{txt}") { TextAlignment = TextAlignment.Justified, Y = 9, Width = Dim.Fill () } +======= + new Label (new Rect (0, 1, 50, 3), $"{i+1}-{txt}") { TextAlignment = TextAlignment.Left }, + new Label (new Rect (0, 3, 50, 3), $"{i+2}-{txt}") { TextAlignment = TextAlignment.Right }, + new Label (new Rect (0, 5, 50, 3), $"{i+3}-{txt}") { TextAlignment = TextAlignment.Centered }, + new Label (new Rect (0, 7, 50, 3), $"{i+4}-{txt}") { TextAlignment = TextAlignment.Justified } +>>>>>>> cb40c5c2491a559658481d20dd4b6a3343c0183f + ); + + Application.Run (container); + } + + static void ShowEntries (View container) + { + var scrollView = new ScrollView (new Rect (50, 10, 20, 8)) { + ContentSize = new Size (20, 50), + //ContentOffset = new Point (0, 0), + ShowVerticalScrollIndicator = true, + ShowHorizontalScrollIndicator = true + }; +#if false + scrollView.Add (new Box10x (0, 0)); +#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)) { + ContentSize = new Size (100, 100), + ShowVerticalScrollIndicator = true, + ShowHorizontalScrollIndicator = true + }; + scrollView2.Add (new Box10x (0, 0)); + var progress = new ProgressBar (new Rect (68, 1, 10, 1)); + bool timer (MainLoop caller) + { + progress.Pulse (); + return true; + } + + Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (300), timer); + + + // A little convoluted, this is because I am using this to test the + // layout based on referencing elements of another view: + + var login = new Label ("Login: ") { X = 3, Y = 6 }; + var password = new Label ("Password: ") { + X = Pos.Left (login), + Y = Pos.Bottom (login) + 1 + }; + var loginText = new TextField ("") { + X = Pos.Right (password), + Y = Pos.Top (login), + Width = 40 + }; + + var passText = new TextField ("") { + Secret = true, + X = Pos.Left (loginText), + Y = Pos.Top (password), + Width = Dim.Width (loginText) + }; + + var tf = new Button (3, 19, "Ok"); + // Add some content + container.Add ( + login, + loginText, + password, + passText, + new FrameView (new Rect (3, 10, 25, 6), "Options"){ + new CheckBox (1, 0, "Remember me"), + new RadioGroup (1, 2, new [] { "_Personal", "_Company" }), + }, + new ListView (new Rect (59, 6, 16, 4), new string [] { + "First row", + "<>", + "This is a very long row that should overflow what is shown", + "4th", + "There is an empty slot on the second row", + "Whoa", + "This is so cool" + }), + scrollView, + scrollView2, + tf, + new Button (10, 19, "Cancel"), + new TimeField (3, 20, DateTime.Now), + new TimeField (23, 20, DateTime.Now, true), + new DateField (3, 22, DateTime.Now), + new DateField (23, 22, DateTime.Now, true), + progress, + new Label (3, 24, "Press F9 (on Unix, ESC+9 is an alias) to activate the menubar"), + menuKeysStyle, + menuAutoMouseNav + + ); + container.SendSubviewToBack (tf); + } + + public static Label ml2; + + static void NewFile () + { + var d = new Dialog ( + "New File", 50, 20, + new Button ("Ok", is_default: true) { Clicked = () => { Application.RequestStop (); } }, + new Button ("Cancel") { Clicked = () => { Application.RequestStop (); } }); + ml2 = new Label (1, 1, "Mouse Debug Line"); + d.Add (ml2); + Application.Run (d); + } + + // + // Creates a nested editor + static void Editor (Toplevel top) + { + var tframe = top.Frame; + var ntop = new Toplevel (tframe); + var menu = new MenuBar (new MenuBarItem [] { + new MenuBarItem ("_File", new MenuItem [] { + new MenuItem ("_Close", "", () => {Application.RequestStop ();}), + }), + new MenuBarItem ("_Edit", new MenuItem [] { + new MenuItem ("_Copy", "", null), + new MenuItem ("C_ut", "", null), + new MenuItem ("_Paste", "", null) + }), + }); + ntop.Add (menu); + + string fname = null; + 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") { + X = 0, + Y = 1, + Width = Dim.Fill (), + Height = Dim.Fill () + }; + ntop.Add (win); + + 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); + + Application.Run (ntop); + } + + static bool Quit () + { + var n = MessageBox.Query (50, 7, "Quit Demo", "Are you sure you want to quit this demo?", "Yes", "No"); + return n == 0; + } + + static void Close () + { + 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 + // it introduces a new brace instead, and does not indent. Then watch me fight + // the editor as more oddities happen. + + public static void Open () + { + var d = new OpenDialog ("Open", "Open a file") { AllowsMultipleSelection = true }; + Application.Run (d); + + if (!d.Canceled) + MessageBox.Query (50, 7, "Selected File", string.Join (", ", d.FilePaths), "Ok"); + } + + public static void ShowHex (Toplevel top) + { + var tframe = top.Frame; + var ntop = new Toplevel (tframe); + var menu = new MenuBar (new MenuBarItem [] { + new MenuBarItem ("_File", new MenuItem [] { + new MenuItem ("_Close", "", () => {Application.RequestStop ();}), + }), + }); + ntop.Add (menu); + + var win = new Window ("/etc/passwd") { + X = 0, + Y = 1, + Width = Dim.Fill (), + Height = Dim.Fill () + }; + ntop.Add (win); + + var source = System.IO.File.OpenRead ("/etc/passwd"); + var hex = new HexView (source) { + X = 0, + Y = 0, + Width = Dim.Fill (), + Height = Dim.Fill () + }; + 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"); + } + + static void MenuKeysStyle_Toggled (object sender, EventArgs e) + { + menu.UseKeysUpDownAsKeysLeftRight = menuKeysStyle.Checked; + } + + static void MenuAutoMouseNav_Toggled (object sender, EventArgs e) + { + menu.WantMousePositionReports = menuAutoMouseNav.Checked; + } + + + 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 (); + } + } + + static void Help () + { + MessageBox.Query (50, 7, "Help", "This is a small help\nBe kind.", "Ok"); + } + + #region Selection Demo + + static void ListSelectionDemo (bool multiple) + { + var d = new Dialog ("Selection Demo", 60, 20, + new Button ("Ok", is_default: true) { Clicked = () => { Application.RequestStop (); } }, + new Button ("Cancel") { Clicked = () => { Application.RequestStop (); } }); + + var animals = new List () { "Alpaca", "Llama", "Lion", "Shark", "Goat" }; + var msg = new Label ("Use space bar or control-t to toggle selection") { + X = 1, + Y = 1, + Width = Dim.Fill () - 1, + Height = 1 + }; + + var list = new ListView (animals) { + X = 1, + Y = 3, + Width = Dim.Fill () - 4, + Height = Dim.Fill () - 4, + AllowsMarking = true, + AllowsMultipleSelection = multiple + }; + d.Add (msg, list); + Application.Run (d); + + var result = ""; + for (int i = 0; i < animals.Count; i++) { + if (list.Source.IsMarked (i)) { + result += animals [i] + " "; + } + } + MessageBox.Query (60, 10, "Selected Animals", result == "" ? "No animals selected" : result, "Ok"); + } + #endregion + + + #region OnKeyDown / OnKeyUp Demo + private static void OnKeyDownUpDemo () + { + var container = new Dialog ( + "OnKeyDown & OnKeyUp demo", 80, 20, + new Button ("Close") { Clicked = () => { Application.RequestStop (); } }) { + Width = Dim.Fill (), + Height = Dim.Fill (), + }; + + var list = new List (); + var listView = new ListView (list) { + X = 0, + Y = 0, + Width = Dim.Fill () - 1, + Height = Dim.Fill () - 2, + }; + listView.ColorScheme = Colors.TopLevel; + container.Add (listView); + + void KeyUpDown (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}")}"); + } + listView.MoveDown (); + } + + container.OnKeyDown += (KeyEvent keyEvent) => KeyUpDown (keyEvent, "Down"); + container.OnKeyUp += (KeyEvent keyEvent) => KeyUpDown (keyEvent, "Up"); + Application.Run (container); + } + #endregion + + 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; + + //Open (); +#if true + int margin = 3; + var win = new Window ("Hello") { + X = 1, + Y = 1, + + Width = Dim.Fill () - margin, + Height = Dim.Fill () - margin + }; +#else + var tframe = top.Frame; + + var win = new Window (new Rect (0, 1, tframe.Width, tframe.Height - 1), "Hello"); +#endif + 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 ()), + new MenuItem ("_Disabled", "", () => { }, () => false), + null, + new MenuItem ("_Quit", "", () => { if (Quit ()) top.Running = false; }) + }), + new MenuBarItem ("_Edit", new MenuItem [] { + 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 MenuItem ("Select _Multiple Items", "", () => ListSelectionDemo (true)), + new MenuItem ("Select _Single Item", "", () => ListSelectionDemo (false)), + }), + new MenuBarItem ("A_ssorted", new MenuItem [] { + new MenuItem ("_Show text alignments", "", () => ShowTextAlignments ()), + new MenuItem ("_OnKeyDown/Up", "", () => OnKeyDownUpDemo ()) + }), + new MenuBarItem ("_Test Menu and SubMenus", new MenuItem [] { + new MenuItem ("SubMenu1Item_1", + new MenuBarItem (new MenuItem[] { + new MenuItem ("SubMenu2Item_1", + new MenuBarItem (new MenuItem [] { + new MenuItem ("SubMenu3Item_1", + new MenuBarItem (new MenuItem [] { menuItems [2] }) + ) + }) + ) + }) + ) + }), + new MenuBarItem ("_About...", "Demonstrates top-level menu item", () => MessageBox.ErrorQuery (50, 7, "About Demo", "This is a demo app for gui.cs", "Ok")), + }); + + 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; + ml = new Label (new Rect (3, 17, 47, 1), "Mouse: "); + Application.RootMouseEvent += delegate (MouseEvent me) { + ml.TextColor = Colors.TopLevel.Normal; + 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); + + var drag = new Label ("Drag: ") { X = 70, Y = 24 }; + var dragText = new TextField ("") { + X = Pos.Right (drag), + Y = Pos.Top (drag), + Width = 40 + }; + + var statusBar = new StatusBar (new StatusItem [] { + 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; }), + }) { + Parent = null, + }; + + win.Add (drag, dragText); +#if true + // FIXED: This currently causes a stack overflow, because it is referencing a window that has not had its size allocated yet + + var bottom = new Label ("This should go on the bottom of the same top-level!"); + win.Add (bottom); + var bottom2 = new Label ("This should go on the bottom of another top-level!"); + top.Add (bottom2); + + Application.OnLoad = () => { + bottom.X = win.X; + bottom.Y = Pos.Bottom (win) - Pos.Top (win) - margin; + bottom2.X = Pos.Left (win); + bottom2.Y = Pos.Bottom (win); + }; +#endif + + + top.Add (win); + //top.Add (menu); + top.Add (menu, statusBar); + Application.Run (); + } +} diff --git a/Terminal.Gui/Core.cs b/Terminal.Gui/Core.cs index 0b9582efc..783a33475 100644 --- a/Terminal.Gui/Core.cs +++ b/Terminal.Gui/Core.cs @@ -1545,12 +1545,12 @@ namespace Terminal.Gui { /// /// Check id current toplevel has menu bar /// - public bool HasMenuBar { get; set; } + public MenuBar MenuBar { get; set; } /// /// Check id current toplevel has status bar /// - public bool HasStatusBar { get; set; } + public StatusBar StatusBar { get; set; } /// public override bool ProcessKey (KeyEvent keyEvent) @@ -1609,9 +1609,9 @@ namespace Terminal.Gui { { if (this == Application.Top) { if (view is MenuBar) - HasMenuBar = true; + MenuBar = view as MenuBar; if (view is StatusBar) - HasStatusBar = true; + StatusBar = view as StatusBar; } base.Add (view); } @@ -1621,9 +1621,9 @@ namespace Terminal.Gui { { if (this == Application.Top) { if (view is MenuBar) - HasMenuBar = true; + MenuBar = null; if (view is StatusBar) - HasStatusBar = true; + StatusBar = null; } base.Remove (view); } @@ -1632,8 +1632,8 @@ namespace Terminal.Gui { public override void RemoveAll () { if (this == Application.Top) { - HasMenuBar = false; - HasStatusBar = false; + MenuBar = null; + StatusBar = null; } base.RemoveAll (); } @@ -1644,15 +1644,15 @@ namespace Terminal.Gui { nx = nx + top.Frame.Width > Driver.Cols ? Math.Max (Driver.Cols - top.Frame.Width, 0) : nx; bool m, s; if (SuperView == null || SuperView.GetType() != typeof(Toplevel)) - m = Application.Top.HasMenuBar; + m = Application.Top.MenuBar != null; else - m = ((Toplevel)SuperView).HasMenuBar; + m = ((Toplevel)SuperView).MenuBar != null; int l = m ? 1 : 0; ny = Math.Max (y, l); - if (SuperView == null) + if (SuperView == null || SuperView.GetType() != typeof(Toplevel)) s = Application.Top.HasStatusBar; else - s = ((Toplevel)SuperView).HasStatusBar; + s = ((Toplevel)SuperView).StatusBar != null; l = s ? Driver.Rows - 1 : Driver.Rows; ny = Math.Min (ny, l); ny = ny + top.Frame.Height > l ? Math.Max (l - top.Frame.Height, m ? 1 : 0) : ny; @@ -1674,9 +1674,15 @@ namespace Terminal.Gui { top.X = nx; top.Y = ny; } - if (HasStatusBar && ny + top.Frame.Height > Driver.Rows - 1) { - if (top.Height is Dim.DimFill) - top.Height = Dim.Fill () - 1; + if (StatusBar != null) { + if (ny + top.Frame.Height > Driver.Rows - 1) { + if (top.Height is Dim.DimFill) + top.Height = Dim.Fill () - 1; + } + if (StatusBar.Frame.Y != Driver.Rows - 1) { + StatusBar.Y = Driver.Rows - 1; + SetNeedsDisplay (); + } } } } @@ -1688,7 +1694,7 @@ namespace Terminal.Gui { { Application.CurrentView = this; - if (this == Application.Top) { + if (this == Application.Top || this == Application.Current) { if (!NeedDisplay.IsEmpty) { Driver.SetAttribute (Colors.TopLevel.Normal); Clear (region); diff --git a/Terminal.Gui/Drivers/CursesDriver.cs b/Terminal.Gui/Drivers/CursesDriver.cs index 95a658108..2e8e7059a 100644 --- a/Terminal.Gui/Drivers/CursesDriver.cs +++ b/Terminal.Gui/Drivers/CursesDriver.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Runtime.InteropServices; +using System.Threading.Tasks; using Mono.Terminal; using NStack; using Unix.Terminal; @@ -183,12 +184,185 @@ namespace Terminal.Gui { } } - static MouseEvent ToDriverMouse (Curses.MouseEvent cev) + Curses.Event? LastMouseButtonPressed = null; + bool IsButtonPressed = false; + bool cancelButtonClicked = false; + Point point; + + MouseEvent ToDriverMouse (Curses.MouseEvent cev) + { + MouseFlags mouseFlag = MouseFlags.AllEvents; + + if (LastMouseButtonPressed != null && cev.ButtonState != Curses.Event.ReportMousePosition) { + LastMouseButtonPressed = null; + IsButtonPressed = false; + } + + + if ((cev.ButtonState == Curses.Event.Button1Clicked || cev.ButtonState == Curses.Event.Button2Clicked || + cev.ButtonState == Curses.Event.Button3Clicked) && + LastMouseButtonPressed == null) { + + IsButtonPressed = false; + mouseFlag = ProcessButtonClickedEvent (cev, mouseFlag); + + } else if (((cev.ButtonState == Curses.Event.Button1Pressed || cev.ButtonState == Curses.Event.Button2Pressed || + cev.ButtonState == Curses.Event.Button3Pressed) && LastMouseButtonPressed == null) || + IsButtonPressed && cev.ButtonState == Curses.Event.ReportMousePosition) { + + mouseFlag = (MouseFlags)cev.ButtonState; + if (cev.ButtonState != Curses.Event.ReportMousePosition) + LastMouseButtonPressed = cev.ButtonState; + IsButtonPressed = true; + + if (cev.ButtonState == Curses.Event.ReportMousePosition) { + mouseFlag = (MouseFlags)LastMouseButtonPressed | MouseFlags.ReportMousePosition; + point = new Point (); + cancelButtonClicked = true; + } else { + point = new Point () { + X = cev.X, + Y = cev.Y + }; + } + + if ((mouseFlag & MouseFlags.ReportMousePosition) == 0) { + Task.Run (async () => { + while (IsButtonPressed && LastMouseButtonPressed != null) { + await Task.Delay (200); + var me = new MouseEvent () { + X = cev.X, + Y = cev.Y, + Flags = mouseFlag + }; + + var view = Application.wantContinuousButtonPressedView; + if (view == null) + break; + if (IsButtonPressed && LastMouseButtonPressed != null && (mouseFlag & MouseFlags.ReportMousePosition) == 0) { + mouseHandler (me); + mainLoop.Driver.Wakeup (); + } + } + }); + } + + + } else if ((cev.ButtonState == Curses.Event.Button1Released || cev.ButtonState == Curses.Event.Button2Released || + cev.ButtonState == Curses.Event.Button3Released)) { + + mouseFlag = ProcessButtonReleasedEvent (cev, mouseFlag); + IsButtonPressed = false; + + } else if (cev.ButtonState == Curses.Event.Button4Pressed) { + + mouseFlag = MouseFlags.WheeledUp; + + } else if (cev.ButtonState == Curses.Event.ReportMousePosition && cev.X == point.X && cev.Y == point.Y) { + + mouseFlag = MouseFlags.WheeledDown; + + } + else if (cev.ButtonState == Curses.Event.ReportMousePosition) { + + mouseFlag = MouseFlags.ReportMousePosition; + } else { + mouseFlag = (MouseFlags)cev.ButtonState; + } + + point = new Point () { + X = cev.X, + Y = cev.Y + }; + + + + if (cev.ID != 0) + mouseFlag = MouseFlags.WheeledDown; + + return new MouseEvent () { + X = cev.X, + Y = cev.Y, + //Flags = (MouseFlags)cev.ButtonState + Flags = mouseFlag + }; + } + + private MouseFlags ProcessButtonClickedEvent (Curses.MouseEvent cev, MouseFlags mf) + { + LastMouseButtonPressed = cev.ButtonState; + mf = GetButtonState (cev, true); + mouseHandler (ProcessButtonState (cev, mf)); + if (LastMouseButtonPressed != null && LastMouseButtonPressed == cev.ButtonState) { + mf = GetButtonState (cev, false); + mouseHandler (ProcessButtonState (cev, mf)); + if (LastMouseButtonPressed != null && LastMouseButtonPressed == cev.ButtonState) { + mf = (MouseFlags)cev.ButtonState; + } + } + LastMouseButtonPressed = null; + return mf; + } + + private MouseFlags ProcessButtonReleasedEvent (Curses.MouseEvent cev, MouseFlags mf) + { + mf = (MouseFlags)cev.ButtonState; + mouseHandler (ProcessButtonState (cev, mf)); + if (!cancelButtonClicked && LastMouseButtonPressed == null) + mf = GetButtonState (cev); + else + cancelButtonClicked = false; + return mf; + } + + MouseFlags GetButtonState (Curses.MouseEvent cev, bool pressed = false) + { + MouseFlags mf = default; + switch (cev.ButtonState) { + case Curses.Event.Button1Clicked: + if (pressed) + mf = MouseFlags.Button1Pressed; + else + mf = MouseFlags.Button1Released; + break; + + case Curses.Event.Button2Clicked: + if (pressed) + mf = MouseFlags.Button2Pressed; + else + mf = MouseFlags.Button2Released; + break; + + case Curses.Event.Button3Clicked: + if (pressed) + mf = MouseFlags.Button3Pressed; + else + mf = MouseFlags.Button3Released; + break; + + case Curses.Event.Button1Released: + mf = MouseFlags.Button1Clicked; + break; + + case Curses.Event.Button2Released: + mf = MouseFlags.Button2Clicked; + break; + + case Curses.Event.Button3Released: + mf = MouseFlags.Button3Clicked; + break; + + + } + return mf; + } + + MouseEvent ProcessButtonState (Curses.MouseEvent cev, MouseFlags mf) { return new MouseEvent () { X = cev.X, Y = cev.Y, - Flags = (MouseFlags)cev.ButtonState + Flags = mf }; } @@ -252,10 +426,15 @@ namespace Terminal.Gui { keyUpHandler (new KeyEvent ((Key)wch)); } + Action mouseHandler; + MainLoop mainLoop; + public override void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler) { // Note: Curses doesn't support keydown/up events and thus any passed keyDown/UpHandlers will never be called - Curses.timeout (-1); + Curses.timeout (0); + this.mouseHandler = mouseHandler; + this.mainLoop = mainLoop; (mainLoop.Driver as Mono.Terminal.UnixMainLoop).AddWatch (0, Mono.Terminal.UnixMainLoop.Condition.PollIn, x => { ProcessInput (keyHandler, keyUpHandler, mouseHandler); @@ -425,21 +604,21 @@ namespace Terminal.Gui { Console.Out.Flush (); } - int lastMouseInterval; - bool mouseGrabbed; + //int lastMouseInterval; + //bool mouseGrabbed; public override void UncookMouse () { - if (mouseGrabbed) - return; - lastMouseInterval = Curses.mouseinterval (0); - mouseGrabbed = true; + //if (mouseGrabbed) + // return; + //lastMouseInterval = Curses.mouseinterval (0); + //mouseGrabbed = true; } public override void CookMouse () { - mouseGrabbed = false; - Curses.mouseinterval (lastMouseInterval); + //mouseGrabbed = false; + //Curses.mouseinterval (lastMouseInterval); } } diff --git a/Terminal.Gui/MonoCurses/constants.cs b/Terminal.Gui/MonoCurses/constants.cs index df19c30e2..8eed47967 100644 --- a/Terminal.Gui/MonoCurses/constants.cs +++ b/Terminal.Gui/MonoCurses/constants.cs @@ -2,6 +2,8 @@ * This file is autogenerated by the attrib.c program, do not edit */ +#define XTERM1006 + using System; namespace Unix.Terminal { @@ -77,6 +79,15 @@ namespace Unix.Terminal { ReportMousePosition = unchecked((int)0x8000000), AllEvents = unchecked((int)0x7ffffff), } +#if XTERM1006 + public const int LeftRightUpNPagePPage= unchecked((int)0x8); + public const int DownEnd = unchecked((int)0x6); + public const int Home = unchecked((int)0x7); +#else + public const int LeftRightUpNPagePPage= unchecked((int)0x0); + public const int DownEnd = unchecked((int)0x0); + public const int Home = unchecked((int)0x0); +#endif public const int ERR = unchecked((int)0xffffffff); public const int KeyBackspace = unchecked((int)0x107); public const int KeyUp = unchecked((int)0x103); @@ -110,30 +121,30 @@ namespace Unix.Terminal { 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 AltKeyUp = unchecked((int)0x234 + LeftRightUpNPagePPage); + public const int AltKeyDown = unchecked((int)0x20b + DownEnd); + public const int AltKeyLeft = unchecked((int)0x21f + LeftRightUpNPagePPage); + public const int AltKeyRight = unchecked((int)0x22e + LeftRightUpNPagePPage); + public const int AltKeyNPage = unchecked((int)0x224 + LeftRightUpNPagePPage); + public const int AltKeyPPage = unchecked((int)0x229 + LeftRightUpNPagePPage); + public const int AltKeyHome = unchecked((int)0x215 + Home); + public const int AltKeyEnd = unchecked((int)0x210 + DownEnd); + public const int CtrlKeyUp = unchecked((int)0x236 + LeftRightUpNPagePPage); + public const int CtrlKeyDown = unchecked((int)0x20d + DownEnd); + public const int CtrlKeyLeft = unchecked((int)0x221 + LeftRightUpNPagePPage); + public const int CtrlKeyRight = unchecked((int)0x230 + LeftRightUpNPagePPage); + public const int CtrlKeyNPage = unchecked((int)0x226 + LeftRightUpNPagePPage); + public const int CtrlKeyPPage = unchecked((int)0x22b + LeftRightUpNPagePPage); + public const int CtrlKeyHome = unchecked((int)0x217 + Home); + public const int CtrlKeyEnd = unchecked((int)0x212 + DownEnd); + public const int ShiftCtrlKeyUp = unchecked((int)0x237 + LeftRightUpNPagePPage); + public const int ShiftCtrlKeyDown = unchecked((int)0x20e + DownEnd); + public const int ShiftCtrlKeyLeft = unchecked((int)0x222 + LeftRightUpNPagePPage); + public const int ShiftCtrlKeyRight = unchecked((int)0x231 + LeftRightUpNPagePPage); + public const int ShiftCtrlKeyNPage = unchecked((int)0x227 + LeftRightUpNPagePPage); + public const int ShiftCtrlKeyPPage = unchecked((int)0x22c + LeftRightUpNPagePPage); + public const int ShiftCtrlKeyHome = unchecked((int)0x218 + Home); + public const int ShiftCtrlKeyEnd = unchecked((int)0x213 + DownEnd); public const int LC_ALL = 6; static public int ColorPair(int n){ diff --git a/Terminal.Gui/Views/ListView.cs b/Terminal.Gui/Views/ListView.cs index 309c408e0..7262fa478 100644 --- a/Terminal.Gui/Views/ListView.cs +++ b/Terminal.Gui/Views/ListView.cs @@ -63,6 +63,12 @@ namespace Terminal.Gui { /// Item index. /// If set to true value. void SetMark (int item, bool value); + + /// + /// Return the source as IList. + /// + /// + IList ToList (); } /// @@ -257,7 +263,7 @@ namespace Terminal.Gui { /// Redraws the ListView /// /// Region. - public override void Redraw(Rect region) + public override void Redraw (Rect region) { var current = ColorScheme.Focus; Driver.SetAttribute (current); @@ -279,12 +285,12 @@ namespace Terminal.Gui { Move (0, row); if (source == null || item >= source.Count) { for (int c = 0; c < f.Width; c++) - Driver.AddRune(' '); + Driver.AddRune (' '); } else { if (allowsMarking) { Driver.AddStr (source.IsMarked (item) ? (AllowsMultipleSelection ? "[x] " : "(o)") : (AllowsMultipleSelection ? "[ ] " : "( )")); } - Source.Render(this, Driver, isSelected, item, col, row, f.Width-col); + Source.Render (this, Driver, isSelected, item, col, row, f.Width - col); } } } @@ -292,12 +298,12 @@ namespace Terminal.Gui { /// /// This event is raised when the cursor selection has changed. /// - public event Action SelectedChanged; + public event EventHandler SelectedChanged; /// /// This event is raised on Enter key or Double Click to open the selected item. /// - public event EventHandler OpenSelectedItem; + public event EventHandler OpenSelectedItem; /// /// Handles cursor movement for this view, passes all other events. @@ -312,27 +318,27 @@ namespace Terminal.Gui { switch (kb.Key) { case Key.CursorUp: case Key.ControlP: - return MoveUp(); + return MoveUp (); case Key.CursorDown: case Key.ControlN: - return MoveDown(); + return MoveDown (); case Key.ControlV: case Key.PageDown: - return MovePageDown(); + return MovePageDown (); case Key.PageUp: - return MovePageUp(); + return MovePageUp (); case Key.Space: - if (MarkUnmarkRow()) + if (MarkUnmarkRow ()) return true; else break; case Key.Enter: - OpenSelectedItem?.Invoke (this, new EventArgs ()); + OnOpenSelectedItem (); break; } @@ -340,7 +346,7 @@ namespace Terminal.Gui { } /// - /// + /// Prevents marking if it's not allowed mark and if it's not allows multiple selection. /// /// public virtual bool AllowsAll () @@ -359,13 +365,14 @@ namespace Terminal.Gui { } /// - /// + /// Marks an unmarked row. /// /// - public virtual bool MarkUnmarkRow(){ + public virtual bool MarkUnmarkRow () + { if (AllowsAll ()) { - Source.SetMark(SelectedItem, !Source.IsMarked(SelectedItem)); - SetNeedsDisplay(); + Source.SetMark (SelectedItem, !Source.IsMarked (SelectedItem)); + SetNeedsDisplay (); return true; } @@ -373,84 +380,114 @@ namespace Terminal.Gui { } /// - /// + /// Moves to the next page. /// /// - public virtual bool MovePageUp(){ + public virtual bool MovePageUp () + { int n = (selected - Frame.Height); if (n < 0) n = 0; - if (n != selected){ + if (n != selected) { selected = n; top = selected; - if (SelectedChanged != null) - SelectedChanged(); - SetNeedsDisplay(); + OnSelectedChanged (); + SetNeedsDisplay (); } return true; } /// - /// + /// Moves to the previous page. /// /// - public virtual bool MovePageDown(){ + public virtual bool MovePageDown () + { var n = (selected + Frame.Height); if (n > source.Count) n = source.Count - 1; - if (n != selected){ + if (n != selected) { selected = n; if (source.Count >= Frame.Height) top = selected; else top = 0; - if (SelectedChanged != null) - SelectedChanged(); - SetNeedsDisplay(); + OnSelectedChanged (); + SetNeedsDisplay (); } return true; } /// - /// + /// Moves to the next row. /// /// - public virtual bool MoveDown(){ - if (selected + 1 < source.Count){ + public virtual bool MoveDown () + { + if (selected + 1 < source.Count) { selected++; if (selected >= top + Frame.Height) top++; - if (SelectedChanged != null) - SelectedChanged(); - SetNeedsDisplay(); + OnSelectedChanged (); + SetNeedsDisplay (); } return true; } /// - /// + /// Moves to the previous row. /// /// - public virtual bool MoveUp(){ - if (selected > 0){ + public virtual bool MoveUp () + { + if (selected > 0) { selected--; if (selected < top) top = selected; - if (SelectedChanged != null) - SelectedChanged(); - SetNeedsDisplay(); + OnSelectedChanged (); + SetNeedsDisplay (); } return true; } + int lastSelectedItem = -1; + + /// + /// Invokes the SelectedChanged event if it is defined. + /// + /// + public virtual bool OnSelectedChanged () + { + if (selected != lastSelectedItem) { + var value = source.ToList () [selected]; + SelectedChanged?.Invoke (this, new ListViewItemEventArgs (selected, value)); + lastSelectedItem = selected; + return true; + } + + return false; + } + + /// + /// Invokes the OnOpenSelectedItem event if it is defined. + /// + /// + public virtual bool OnOpenSelectedItem () + { + var value = source.ToList () [selected]; + OpenSelectedItem?.Invoke (this, new ListViewItemEventArgs (selected, value)); + + return true; + } + /// /// Positions the cursor in this view /// - public override void PositionCursor() + public override void PositionCursor () { if (allowsMarking) Move (1, selected - top); @@ -461,7 +498,8 @@ namespace Terminal.Gui { /// public override bool MouseEvent(MouseEvent me) { - if (!me.Flags.HasFlag (MouseFlags.Button1Clicked) && !me.Flags.HasFlag (MouseFlags.Button1DoubleClicked)) + if (!me.Flags.HasFlag (MouseFlags.Button1Clicked) && !me.Flags.HasFlag (MouseFlags.Button1DoubleClicked) && + me.Flags != MouseFlags.WheeledDown && me.Flags != MouseFlags.WheeledUp) return false; if (!HasFocus) @@ -470,6 +508,14 @@ namespace Terminal.Gui { if (source == null) return false; + if (me.Flags == MouseFlags.WheeledDown) { + MoveDown (); + return true; + } else if (me.Flags == MouseFlags.WheeledUp) { + MoveUp (); + return true; + } + if (me.Y + top >= source.Count) return true; @@ -479,10 +525,10 @@ namespace Terminal.Gui { SetNeedsDisplay (); return true; } - SelectedChanged?.Invoke (); + OnSelectedChanged (); SetNeedsDisplay (); if (me.Flags == MouseFlags.Button1DoubleClicked) - OpenSelectedItem?.Invoke (this, new EventArgs ()); + OnOpenSelectedItem (); return true; } } @@ -497,7 +543,7 @@ namespace Terminal.Gui { int count; /// - /// constructor + /// Constructor based on a source. /// /// public ListWrapper (IList source) @@ -508,7 +554,7 @@ namespace Terminal.Gui { } /// - /// Count of items. + /// Returns the amount of items in the source. /// public int Count => src.Count; @@ -519,7 +565,7 @@ namespace Terminal.Gui { for (int i = 0; i < byteLen;) { (var rune, var size) = Utf8.DecodeRune (ustr, i, i - byteLen); var count = Rune.ColumnWidth (rune); - if (used+count >= width) + if (used + count >= width) break; driver.AddRune (rune); used += count; @@ -531,15 +577,15 @@ namespace Terminal.Gui { } /// - /// Renders an item in the the list. + /// Method that render to the appropriate type based on the type of the item passed to it. /// - /// - /// - /// - /// - /// - /// - /// + /// The ListView. + /// The driver used by the caller. + /// Informs if it's marked or not. + /// The item. + /// The col where to move. + /// The line where to move. + /// The item width. public void Render (ListView container, ConsoleDriver driver, bool marked, int item, int col, int line, int width) { container.Move (col, line); @@ -553,10 +599,10 @@ namespace Terminal.Gui { } /// - /// Returns true of the item is marked. false if not. + /// Returns true if the item is marked, false otherwise. /// - /// - /// + /// The item. + /// trueIf is marked.falseotherwise. public bool IsMarked (int item) { if (item >= 0 && item < count) @@ -565,14 +611,48 @@ namespace Terminal.Gui { } /// - /// Sets the marked state of an item. + /// Sets the item as marked or unmarked based on the value is true or false, respectively. /// - /// - /// + /// The item + /// Marks the item.Unmarked the item.The value. public void SetMark (int item, bool value) { if (item >= 0 && item < count) marks [item] = value; } + + /// + /// Returns the source as IList. + /// + /// + public IList ToList () + { + return src; + } + } + + /// + /// Gets the item and value to use in an event handler. + /// + public class ListViewItemEventArgs : EventArgs { + /// + /// The item. + /// + public int Item { get; } + /// + /// The item value. + /// + public object Value { get; } + + /// + /// Constructor to sets the item and value passed from. + /// + /// The item. + /// The item value + public ListViewItemEventArgs (int item, object value) + { + Item = item; + Value = value; + } } } diff --git a/Terminal.Gui/Views/Menu.cs b/Terminal.Gui/Views/Menu.cs index 16b7c0b87..d6438a8a4 100644 --- a/Terminal.Gui/Views/Menu.cs +++ b/Terminal.Gui/Views/Menu.cs @@ -562,6 +562,7 @@ namespace Terminal.Gui { } bool openedByAltKey; + /// public override bool OnKeyDown (KeyEvent keyEvent) { diff --git a/Terminal.Gui/Views/StatusBar.cs b/Terminal.Gui/Views/StatusBar.cs index e204f97e5..789781815 100644 --- a/Terminal.Gui/Views/StatusBar.cs +++ b/Terminal.Gui/Views/StatusBar.cs @@ -107,6 +107,10 @@ namespace Terminal.Gui { Items = items; CanFocus = false; ColorScheme = Colors.Menu; + X = 0; + Y = Driver.Rows - 1; + Width = Dim.Fill (); + Height = 1; Application.Loaded += (sender, e) => { X = 0; @@ -141,11 +145,11 @@ namespace Terminal.Gui { /// public override void Redraw (Rect region) { - if (Frame.Y != Driver.Rows - 1) { - Frame = new Rect (Frame.X, Driver.Rows - 1, Frame.Width, Frame.Height); - Y = Driver.Rows - 1; - SetNeedsDisplay (); - } + //if (Frame.Y != Driver.Rows - 1) { + // Frame = new Rect (Frame.X, Driver.Rows - 1, Frame.Width, Frame.Height); + // Y = Driver.Rows - 1; + // SetNeedsDisplay (); + //} Move (0, 0); Driver.SetAttribute (ColorScheme.Normal); diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs index 781866ef5..71a53ccd7 100644 --- a/Terminal.Gui/Views/TextField.cs +++ b/Terminal.Gui/Views/TextField.cs @@ -28,6 +28,11 @@ namespace Terminal.Gui { /// public bool Used { get => used; set { used = value; } } + /// + /// If set to true its not allow any changes in the text. + /// + public bool ReadOnly { get; set; } = false; + /// /// Changed event, raised when the text has clicked. /// @@ -95,7 +100,7 @@ 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 (); } } @@ -115,6 +120,9 @@ namespace Terminal.Gui { } set { + if (ReadOnly) + return; + var oldText = ustring.Make (text); text = TextModel.ToRunes (value); if (!Secret && !isFromHistory) { @@ -184,13 +192,16 @@ namespace Terminal.Gui { int col = 0; int width = Frame.Width; var tcount = text.Count; + var roc = new Attribute (Color.DarkGray, Color.Gray); for (int idx = 0; idx < tcount; idx++){ var rune = text [idx]; if (idx < p) continue; var cols = Rune.ColumnWidth (rune); - if (col == point && HasFocus && !Used && SelectedLength == 0) + if (col == point && HasFocus && !Used && SelectedLength == 0 && !ReadOnly) Driver.SetAttribute (Colors.Menu.HotFocus); + else if (ReadOnly) + Driver.SetAttribute (idx >= start && length > 0 && idx < start + length ? color.Focus : roc); else Driver.SetAttribute (idx >= start && length > 0 && idx < start + length ? color.Focus : ColorScheme.Focus); if (col + cols <= width) @@ -261,6 +272,9 @@ namespace Terminal.Gui { switch (kb.Key) { case Key.DeleteChar: case Key.ControlD: + if (ReadOnly) + return true; + if (SelectedLength == 0) { if (text.Count == 0 || text.Count == point) return true; @@ -275,6 +289,9 @@ namespace Terminal.Gui { case Key.Delete: case Key.Backspace: + if (ReadOnly) + return true; + if (SelectedLength == 0) { if (point == 0) return true; @@ -373,6 +390,9 @@ namespace Terminal.Gui { break; case Key.ControlK: // kill-to-end + if (ReadOnly) + return true; + ClearAllSelection (); if (point >= text.Count) return true; @@ -383,6 +403,9 @@ namespace Terminal.Gui { // Undo case Key.ControlZ: + if (ReadOnly) + return true; + if (historyText != null && historyText.Count > 0) { isFromHistory = true; if (idxhistoryText > 0) @@ -396,6 +419,9 @@ namespace Terminal.Gui { //Redo case Key.ControlY: // Control-y, yank + if (ReadOnly) + return true; + if (historyText != null && historyText.Count > 0) { isFromHistory = true; if (idxhistoryText < historyText.Count - 1) { @@ -455,6 +481,9 @@ namespace Terminal.Gui { break; case Key.ControlX: + if (ReadOnly) + return true; + Cut (); break; @@ -472,6 +501,9 @@ namespace Terminal.Gui { if (kb.Key < Key.Space || kb.Key > Key.CharMask) return false; + if (ReadOnly) + return true; + if (SelectedLength != 0) { DeleteSelectedText (); oldCursorPos = point; @@ -639,7 +671,7 @@ namespace Terminal.Gui { point = text.Count; if (point < first) point = 0; - return x; + return point; } void PrepareSelection (int x, int direction = 0) @@ -682,8 +714,11 @@ namespace Terminal.Gui { /// /// Copy the selected text to the clipboard. /// - public void Copy () + public virtual void Copy () { + if (Secret) + return; + if (SelectedLength != 0) { Clipboard.Contents = SelectedText; } @@ -692,7 +727,7 @@ namespace Terminal.Gui { /// /// Cut the selected text to the clipboard. /// - public void Cut () + public virtual void Cut () { if (SelectedLength != 0) { Clipboard.Contents = SelectedText; @@ -715,8 +750,11 @@ namespace Terminal.Gui { /// /// Paste the selected text from the clipboard. /// - public void Paste () + public virtual void Paste () { + if (ReadOnly) + return; + string actualText = Text.ToString (); int start = SelectedStart == -1 ? CursorPosition : SelectedStart; ustring cbTxt = Clipboard.Contents?.ToString () ?? ""; diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index 0d6f35a5c..738a6f35f 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -39,6 +39,7 @@ namespace Terminal.Gui { if (file == null) throw new ArgumentNullException (nameof (file)); try { + FilePath = file; var stream = File.OpenRead (file); } catch { return false; @@ -47,6 +48,19 @@ namespace Terminal.Gui { return true; } + public bool CloseFile () + { + if (FilePath == null) + throw new ArgumentNullException (nameof (FilePath)); + try { + FilePath = null; + lines = new List> (); + } catch { + return false; + } + return true; + } + // Turns the ustring into runes, this does not split the // contents on a newline if it is present. internal static List ToRunes (ustring str) @@ -120,6 +134,8 @@ namespace Terminal.Gui { return sb.ToString (); } + public string FilePath { get; set; } + /// /// The number of text lines in the model /// @@ -351,6 +367,18 @@ namespace Terminal.Gui { SetNeedsDisplay (); } + /// + /// Closes the contents of the stream into the TextView. + /// + /// true, if stream was closed, false otherwise. + public bool CloseFile() + { + ResetPosition (); + var res = model.CloseFile (); + SetNeedsDisplay (); + return res; + } + /// /// The current cursor row. /// diff --git a/UICatalog/Program.cs b/UICatalog/Program.cs index 17315ac5f..a571f99c2 100644 --- a/UICatalog/Program.cs +++ b/UICatalog/Program.cs @@ -1,5 +1,6 @@ using NStack; using System; +using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; @@ -119,7 +120,7 @@ namespace UICatalog { _rightPane.Add (_scenarioListView); _categoryListView.SelectedItem = 0; - CategoryListView_SelectedChanged (); + _categoryListView.OnSelectedChanged (); _statusBar = new StatusBar (new StatusItem [] { //new StatusItem(Key.F1, "~F1~ Help", () => Help()), @@ -148,7 +149,9 @@ namespace UICatalog { } _top = Application.Top; + _top.KeyUp += KeyUpHandler; + _top.Add (_menu); _top.Add (_leftPane); _top.Add (_rightPane); @@ -227,6 +230,12 @@ namespace UICatalog { used++; } } + + public IList ToList () + { + return Scenarios; + } + } /// @@ -244,7 +253,7 @@ namespace UICatalog { // break; //case Key.Enter: // break; - //} + //}< } else if (a.KeyEvent.Key == Key.Tab || a.KeyEvent.Key == Key.BackTab) { // BUGBUG: Work around Issue #434 by implementing our own TAB navigation if (_top.MostFocused == _categoryListView) @@ -254,7 +263,7 @@ namespace UICatalog { } } - private static void CategoryListView_SelectedChanged () + private static void CategoryListView_SelectedChanged (object sender, ListViewItemEventArgs e) { var item = _categories [_categoryListView.SelectedItem]; List newlist; diff --git a/UICatalog/Scenarios/ComputedLayout.cs b/UICatalog/Scenarios/ComputedLayout.cs index 77f3d514b..d66194f22 100644 --- a/UICatalog/Scenarios/ComputedLayout.cs +++ b/UICatalog/Scenarios/ComputedLayout.cs @@ -98,6 +98,36 @@ namespace UICatalog { Y = Pos.At(10) }; Win.Add (absoluteButton); + + // Centering multiple controls horizontally. + // This is intentionally convoluted to illustrate potential bugs. + var bottomLabel = new Label ("This should be the last line (Bug #xxx).") { + TextAlignment = Terminal.Gui.TextAlignment.Centered, + ColorScheme = Colors.TopLevel, + Width = Dim.Fill (), + X = Pos.Center (), + Y = Pos.Bottom (Win) - 3 // BUGBUG: -1 should be just above border; but it has to be -3 + }; + + var centerButton = new Button ("Center") { + X = Pos.Center (), + Y = Pos.Top(bottomLabel) - 1 + }; + var leftButton = new Button ("Left") { + Y = Pos.Top (bottomLabel) - 1 + }; + var rightButton = new Button ("Right") { + Y = Pos.Top (bottomLabel) - 1 + }; + + leftButton.X = Pos.Left (centerButton) - leftButton.Frame.Width - 5; + rightButton.X = Pos.Right (centerButton) + 5; + + Win.Add (bottomLabel); + Win.Add (leftButton); + Win.Add (centerButton); + Win.Add (rightButton); + } public override void Run () diff --git a/UICatalog/Scenarios/Keys.cs b/UICatalog/Scenarios/Keys.cs index 4f3b4da4e..3584709b6 100644 --- a/UICatalog/Scenarios/Keys.cs +++ b/UICatalog/Scenarios/Keys.cs @@ -43,8 +43,8 @@ namespace UICatalog { public override bool ProcessColdKey (KeyEvent keyEvent) { _processColdKeyList.Add (keyEvent.ToString ()); - return base.ProcessColdKey (keyEvent); + return base.ProcessColdKey (keyEvent); } }