mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-26 07:47:54 +01:00
Start event handling
This commit is contained in:
158
Core.cs
158
Core.cs
@@ -3,6 +3,8 @@
|
||||
// Pending:
|
||||
// - Check for NeedDisplay on the hierarchy and repaint
|
||||
// - Layout support
|
||||
// - "Colors" type or "Attributes" type?
|
||||
// - What to surface as "BackgroundCOlor" when clearing a window, an attribute or colors?
|
||||
//
|
||||
// Optimziations
|
||||
// - Add rendering limitation to the exposed area
|
||||
@@ -17,7 +19,79 @@ namespace Terminal {
|
||||
public bool HasFocus { get; internal set; }
|
||||
|
||||
// Key handling
|
||||
public virtual void KeyDown (Event.Key kb) { }
|
||||
/// <summary>
|
||||
/// This method can be overwritten by view that
|
||||
/// want to provide accelerator functionality
|
||||
/// (Alt-key for example).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Before keys are sent to the subview on the
|
||||
/// current view, all the views are
|
||||
/// processed and the key is passed to the widgets
|
||||
/// to allow some of them to process the keystroke
|
||||
/// as a hot-key. </para>
|
||||
/// <para>
|
||||
/// For example, if you implement a button that
|
||||
/// has a hotkey ok "o", you would catch the
|
||||
/// combination Alt-o here. If the event is
|
||||
/// caught, you must return true to stop the
|
||||
/// keystroke from being dispatched to other
|
||||
/// views.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
|
||||
public virtual bool ProcessHotKey (KeyEvent kb)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If the view is focused, gives the view a
|
||||
/// chance to process the keystroke.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Views can override this method if they are
|
||||
/// interested in processing the given keystroke.
|
||||
/// If they consume the keystroke, they must
|
||||
/// return true to stop the keystroke from being
|
||||
/// processed by other widgets or consumed by the
|
||||
/// widget engine. If they return false, the
|
||||
/// keystroke will be passed using the ProcessColdKey
|
||||
/// method to other views to process.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public virtual bool ProcessKey (KeyEvent kb)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method can be overwritten by views that
|
||||
/// want to provide accelerator functionality
|
||||
/// (Alt-key for example), but without
|
||||
/// interefering with normal ProcessKey behavior.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// After keys are sent to the subviews on the
|
||||
/// current view, all the view are
|
||||
/// processed and the key is passed to the views
|
||||
/// to allow some of them to process the keystroke
|
||||
/// as a cold-key. </para>
|
||||
/// <para>
|
||||
/// This functionality is used, for example, by
|
||||
/// default buttons to act on the enter key.
|
||||
/// Processing this as a hot-key would prevent
|
||||
/// non-default buttons from consuming the enter
|
||||
/// keypress when they have the focus.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public virtual bool ProcessColdKey (KeyEvent kb)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Mouse events
|
||||
public virtual void MouseEvent (Event.Mouse me) { }
|
||||
@@ -57,6 +131,8 @@ namespace Terminal {
|
||||
}
|
||||
}
|
||||
|
||||
public View SuperView => container;
|
||||
|
||||
public View (Rect frame)
|
||||
{
|
||||
this.Frame = frame;
|
||||
@@ -223,6 +299,12 @@ namespace Terminal {
|
||||
Move (frame.X, frame.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the currently focused view inside this view, or null if nothing is focused.
|
||||
/// </summary>
|
||||
/// <value>The focused.</value>
|
||||
public View Focused => focused;
|
||||
|
||||
/// <summary>
|
||||
/// Displays the specified character in the specified column and row.
|
||||
/// </summary>
|
||||
@@ -305,6 +387,9 @@ namespace Terminal {
|
||||
/// </summary>
|
||||
public void FocusFirst ()
|
||||
{
|
||||
if (subviews == null)
|
||||
return;
|
||||
|
||||
foreach (var view in subviews) {
|
||||
if (view.CanFocus) {
|
||||
SetFocus (view);
|
||||
@@ -318,6 +403,9 @@ namespace Terminal {
|
||||
/// </summary>
|
||||
public void FocusLast ()
|
||||
{
|
||||
if (subviews == null)
|
||||
return;
|
||||
|
||||
for (int i = subviews.Count; i > 0;) {
|
||||
i--;
|
||||
|
||||
@@ -360,6 +448,7 @@ namespace Terminal {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (focused != null) {
|
||||
focused.HasFocus = false;
|
||||
focused = null;
|
||||
@@ -425,6 +514,48 @@ namespace Terminal {
|
||||
return new Toplevel (new Rect (0, 0, Driver.Cols, Driver.Rows));
|
||||
}
|
||||
|
||||
public override bool CanFocus {
|
||||
get => true;
|
||||
}
|
||||
|
||||
public override bool ProcessKey (KeyEvent kb)
|
||||
{
|
||||
if (ProcessHotKey (kb))
|
||||
return true;
|
||||
|
||||
// Process the key normally
|
||||
if (Focused?.ProcessKey (kb) == true)
|
||||
return true;
|
||||
|
||||
if (ProcessColdKey (kb))
|
||||
return true;
|
||||
|
||||
switch (kb.Key) {
|
||||
case Key.ControlC:
|
||||
// TODO: stop current execution of this container
|
||||
break;
|
||||
case Key.ControlZ:
|
||||
// TODO: should suspend
|
||||
// console_csharp_send_sigtstp ();
|
||||
break;
|
||||
case Key.Tab:
|
||||
var old = Focused;
|
||||
if (!FocusNext ())
|
||||
FocusNext ();
|
||||
old?.SetNeedsDisplay ();
|
||||
Focused?.SetNeedsDisplay ();
|
||||
break;
|
||||
case Key.BackTab:
|
||||
old = Focused;
|
||||
if (!FocusPrev ())
|
||||
FocusPrev ();
|
||||
old?.SetNeedsDisplay ();
|
||||
Focused?.SetNeedsDisplay ();
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#if false
|
||||
public override void Redraw ()
|
||||
{
|
||||
@@ -460,7 +591,7 @@ namespace Terminal {
|
||||
base.Add(contentView);
|
||||
}
|
||||
|
||||
public IEnumerator GetEnumerator ()
|
||||
public new IEnumerator GetEnumerator ()
|
||||
{
|
||||
return contentView.GetEnumerator ();
|
||||
}
|
||||
@@ -527,16 +658,10 @@ namespace Terminal {
|
||||
if (Top != null)
|
||||
return;
|
||||
|
||||
Driver.Init ();
|
||||
Driver.Init (TerminalResized);
|
||||
MainLoop = new Mono.Terminal.MainLoop ();
|
||||
Top = Toplevel.Create ();
|
||||
focus = Top;
|
||||
|
||||
MainLoop.AddWatch (0, Mono.Terminal.MainLoop.Condition.PollIn, x => {
|
||||
//ProcessChar ();
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public class RunState : IDisposable {
|
||||
@@ -561,6 +686,10 @@ namespace Terminal {
|
||||
}
|
||||
}
|
||||
|
||||
static void KeyEvent (Key key)
|
||||
{
|
||||
}
|
||||
|
||||
static public RunState Begin (Toplevel toplevel)
|
||||
{
|
||||
if (toplevel == null)
|
||||
@@ -568,10 +697,8 @@ namespace Terminal {
|
||||
var rs = new RunState (toplevel);
|
||||
|
||||
Init ();
|
||||
Driver.PrepareToRun ();
|
||||
|
||||
toplevels.Push (toplevel);
|
||||
|
||||
Driver.PrepareToRun (MainLoop, toplevel);
|
||||
toplevel.LayoutSubviews ();
|
||||
toplevel.FocusFirst ();
|
||||
Redraw (toplevel);
|
||||
@@ -674,5 +801,12 @@ namespace Terminal {
|
||||
RunLoop (runToken);
|
||||
End (runToken);
|
||||
}
|
||||
|
||||
static void TerminalResized ()
|
||||
{
|
||||
foreach (var t in toplevels) {
|
||||
t.Frame = new Rect (0, 0, Driver.Cols, Driver.Rows);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
99
Driver.cs
99
Driver.cs
@@ -1,10 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Mono.Terminal;
|
||||
using Unix.Terminal;
|
||||
|
||||
namespace Terminal {
|
||||
public enum Color
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Basic colors that can be used to set the foreground and background colors in console applications. These can only be
|
||||
/// </summary>
|
||||
public enum Color {
|
||||
Black,
|
||||
Blue,
|
||||
Green,
|
||||
@@ -49,13 +53,13 @@ namespace Terminal {
|
||||
}
|
||||
|
||||
public abstract class ConsoleDriver {
|
||||
public abstract int Cols {get;}
|
||||
public abstract int Rows {get;}
|
||||
public abstract void Init ();
|
||||
public abstract int Cols { get; }
|
||||
public abstract int Rows { get; }
|
||||
public abstract void Init (Action terminalResized);
|
||||
public abstract void Move (int col, int row);
|
||||
public abstract void AddCh (int ch);
|
||||
public abstract void AddStr (string str);
|
||||
public abstract void PrepareToRun ();
|
||||
public abstract void PrepareToRun (MainLoop mainLoop, Responder target);
|
||||
public abstract void Refresh ();
|
||||
public abstract void End ();
|
||||
public abstract void RedrawTop ();
|
||||
@@ -78,6 +82,8 @@ namespace Terminal {
|
||||
}
|
||||
|
||||
public class CursesDriver : ConsoleDriver {
|
||||
Action terminalResized;
|
||||
|
||||
public override int Cols => Curses.Cols;
|
||||
public override int Rows => Curses.Lines;
|
||||
|
||||
@@ -115,12 +121,12 @@ namespace Terminal {
|
||||
{
|
||||
// TODO; optimize this to determine if the str fits in the clip region, and if so, use Curses.addstr directly
|
||||
foreach (var c in str)
|
||||
AddCh ((int) c);
|
||||
AddCh ((int)c);
|
||||
}
|
||||
|
||||
public override void Refresh() => Curses.refresh ();
|
||||
public override void End() => Curses.endwin ();
|
||||
public override void RedrawTop() => window.redrawwin ();
|
||||
public override void Refresh () => Curses.refresh ();
|
||||
public override void End () => Curses.endwin ();
|
||||
public override void RedrawTop () => window.redrawwin ();
|
||||
public override void SetAttribute (Attribute c) => Curses.attrset (c.value);
|
||||
public Curses.Window window;
|
||||
|
||||
@@ -135,7 +141,7 @@ namespace Terminal {
|
||||
|
||||
public override void SetColors (ConsoleColor foreground, ConsoleColor background)
|
||||
{
|
||||
int f = (short) foreground;
|
||||
int f = (short)foreground;
|
||||
int b = (short)background;
|
||||
var v = colorPairs [f, b];
|
||||
if ((v & 0x10000) == 0) {
|
||||
@@ -152,16 +158,80 @@ namespace Terminal {
|
||||
Dictionary<int, int> rawPairs = new Dictionary<int, int> ();
|
||||
public override void SetColors (short foreColorId, short backgroundColorId)
|
||||
{
|
||||
int key = (((ushort)foreColorId << 16)) | (ushort) backgroundColorId;
|
||||
int key = (((ushort)foreColorId << 16)) | (ushort)backgroundColorId;
|
||||
if (!rawPairs.TryGetValue (key, out var v)) {
|
||||
v = MakeColor (foreColorId, backgroundColorId);
|
||||
rawPairs [key] = v;
|
||||
}
|
||||
SetAttribute (v);
|
||||
}
|
||||
public override void PrepareToRun()
|
||||
|
||||
static Key MapCursesKey (int cursesKey)
|
||||
{
|
||||
switch (cursesKey) {
|
||||
case Curses.KeyF1: return Key.F1;
|
||||
case Curses.KeyF2: return Key.F2;
|
||||
case Curses.KeyF3: return Key.F3;
|
||||
case Curses.KeyF4: return Key.F4;
|
||||
case Curses.KeyF5: return Key.F5;
|
||||
case Curses.KeyF6: return Key.F6;
|
||||
case Curses.KeyF7: return Key.F7;
|
||||
case Curses.KeyF8: return Key.F8;
|
||||
case Curses.KeyF9: return Key.F9;
|
||||
case Curses.KeyF10: return Key.F10;
|
||||
case Curses.KeyUp: return Key.CursorUp;
|
||||
case Curses.KeyDown: return Key.CursorDown;
|
||||
case Curses.KeyLeft: return Key.CursorLeft;
|
||||
case Curses.KeyRight: return Key.CursorRight;
|
||||
case Curses.KeyHome: return Key.Home;
|
||||
case Curses.KeyEnd: return Key.End;
|
||||
case Curses.KeyNPage: return Key.PageDown;
|
||||
case Curses.KeyPPage: return Key.PageUp;
|
||||
case Curses.KeyDeleteChar: return Key.DeleteChar;
|
||||
case Curses.KeyInsertChar: return Key.InsertChar;
|
||||
case Curses.KeyBackTab: return Key.BackTab;
|
||||
default: return Key.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessInput (Responder handler)
|
||||
{
|
||||
var code = Curses.getch ();
|
||||
if ((code == -1) || (code == Curses.KeyResize)) {
|
||||
if (Curses.CheckWinChange ()) {
|
||||
terminalResized ();
|
||||
}
|
||||
}
|
||||
if (code == Curses.KeyMouse) {
|
||||
// TODO
|
||||
// Curses.MouseEvent ev;
|
||||
// Curses.getmouse (out ev);
|
||||
// handler.HandleMouse ();
|
||||
return;
|
||||
}
|
||||
|
||||
// ESC+letter is Alt-Letter.
|
||||
if (code == 27) {
|
||||
Curses.timeout (100);
|
||||
int k = Curses.getch ();
|
||||
if (k != Curses.ERR && k != 27) {
|
||||
var mapped = MapCursesKey (k) | Key.AltMask;
|
||||
handler.ProcessKey (new KeyEvent (mapped));
|
||||
}
|
||||
} else {
|
||||
handler.ProcessKey (new KeyEvent (MapCursesKey (code)));
|
||||
}
|
||||
}
|
||||
|
||||
public override void PrepareToRun (MainLoop mainLoop, Responder handler)
|
||||
{
|
||||
Curses.timeout (-1);
|
||||
|
||||
mainLoop.AddWatch (0, Mono.Terminal.MainLoop.Condition.PollIn, x => {
|
||||
ProcessInput (handler);
|
||||
return true;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public override void DrawFrame (Rect region, bool fill)
|
||||
@@ -192,7 +262,7 @@ namespace Terminal {
|
||||
AddCh (Curses.ACS_LRCORNER);
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
public override void Init(Action terminalResized)
|
||||
{
|
||||
if (window != null)
|
||||
return;
|
||||
@@ -205,6 +275,7 @@ namespace Terminal {
|
||||
Curses.raw ();
|
||||
Curses.noecho ();
|
||||
Curses.Window.Standard.keypad (true);
|
||||
this.terminalResized = terminalResized;
|
||||
|
||||
Colors.Base = new ColorScheme ();
|
||||
Colors.Dialog = new ColorScheme ();
|
||||
|
||||
88
Event.cs
88
Event.cs
@@ -1,9 +1,95 @@
|
||||
namespace Terminal {
|
||||
|
||||
/// <summary>
|
||||
/// The Key enumeration contains special encoding for some keys, but can also
|
||||
/// encode all the unicode values that can be passed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// If the SpecialMask is set, then the value is that of the special mask,
|
||||
/// otherwise, the value is the one of the lower bits (as extracted by CharMask)
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Control keys are the values between 1 and 26 corresponding to Control-A to Control-Z
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public enum Key : uint {
|
||||
CharMask = 0xfffff,
|
||||
SpecialMask = 0xfff00000,
|
||||
ControlA = 1,
|
||||
ControlB,
|
||||
ControlC,
|
||||
ControlD,
|
||||
ControlE,
|
||||
ControlF,
|
||||
ControlG,
|
||||
ControlH,
|
||||
ControlI,
|
||||
Tab = ControlI,
|
||||
ControlJ,
|
||||
ControlK,
|
||||
ControlL,
|
||||
ControlM,
|
||||
ControlN,
|
||||
ControlO,
|
||||
ControlP,
|
||||
ControlQ,
|
||||
ControlR,
|
||||
ControlS,
|
||||
ControlT,
|
||||
ControlU,
|
||||
ControlV,
|
||||
ControlW,
|
||||
ControlX,
|
||||
ControlY,
|
||||
ControlZ,
|
||||
Esc = 27,
|
||||
Space = 32,
|
||||
Delete = 127,
|
||||
|
||||
AltMask = 0x80000000,
|
||||
|
||||
Backspace = 0x100000,
|
||||
CursorUp,
|
||||
CursorDown,
|
||||
CursorLeft,
|
||||
CursorRight,
|
||||
PageUp,
|
||||
PageDown,
|
||||
Home,
|
||||
End,
|
||||
DeleteChar,
|
||||
InsertChar,
|
||||
F1,
|
||||
F2,
|
||||
F3,
|
||||
F4,
|
||||
F5,
|
||||
F6,
|
||||
F7,
|
||||
F8,
|
||||
F9,
|
||||
F10,
|
||||
BackTab,
|
||||
Unknown
|
||||
}
|
||||
|
||||
public struct KeyEvent {
|
||||
public Key Key;
|
||||
public int KeyValue => (int)KeyValue;
|
||||
public bool IsAlt => (Key & Key.AltMask) != 0;
|
||||
public bool IsCtrl => ((uint)Key >= 1) && ((uint)Key <= 26);
|
||||
|
||||
public KeyEvent (Key k)
|
||||
{
|
||||
Key = k;
|
||||
}
|
||||
}
|
||||
|
||||
public class Event {
|
||||
public class Key : Event {
|
||||
public int Code { get; private set; }
|
||||
|
||||
public bool Alt { get; private set; }
|
||||
public Key (int code)
|
||||
{
|
||||
Code = code;
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
<Compile Include="Types\Size.cs" />
|
||||
<Compile Include="demo.cs" />
|
||||
<Compile Include="Views\Label.cs" />
|
||||
<Compile Include="Views\TextField.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="mono-curses.dll">
|
||||
|
||||
321
Views/TextField.cs
Normal file
321
Views/TextField.cs
Normal file
@@ -0,0 +1,321 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Terminal {
|
||||
/// <summary>
|
||||
/// Text data entry widget
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The Entry widget provides Emacs-like editing
|
||||
/// functionality, and mouse support.
|
||||
/// </remarks>
|
||||
public class TextField : View {
|
||||
string text, kill;
|
||||
int first, point;
|
||||
bool used;
|
||||
|
||||
/// <summary>
|
||||
/// Changed event, raised when the text has clicked.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Client code can hook up to this event, it is
|
||||
/// raised when the text in the entry changes.
|
||||
/// </remarks>
|
||||
public event EventHandler Changed;
|
||||
|
||||
/// <summary>
|
||||
/// Public constructor.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// </remarks>
|
||||
public TextField (int x, int y, int w, string s) : base (new Rect (x, y, w, 1))
|
||||
{
|
||||
if (s == null)
|
||||
s = "";
|
||||
|
||||
text = s;
|
||||
point = s.Length;
|
||||
first = point > w ? point - w : 0;
|
||||
CanFocus = true;
|
||||
Color = Colors.Dialog.Focus;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets or gets the text in the entry.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// </remarks>
|
||||
public string Text {
|
||||
get {
|
||||
return text;
|
||||
}
|
||||
|
||||
set {
|
||||
text = value;
|
||||
if (point > text.Length)
|
||||
point = text.Length;
|
||||
first = point > Frame.Width ? point - Frame.Width : 0;
|
||||
SetNeedsDisplay ();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the secret property.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This makes the text entry suitable for entering passwords.
|
||||
/// </remarks>
|
||||
public bool Secret { get; set; }
|
||||
|
||||
Attribute color;
|
||||
/// <summary>
|
||||
/// Sets the color attribute to use (includes foreground and background).
|
||||
/// </summary>
|
||||
/// <value>The color.</value>
|
||||
public Attribute Color {
|
||||
get => color;
|
||||
set {
|
||||
color = value;
|
||||
SetNeedsDisplay ();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The current cursor position.
|
||||
/// </summary>
|
||||
public int CursorPosition { get { return point; } }
|
||||
|
||||
/// <summary>
|
||||
/// Sets the cursor position.
|
||||
/// </summary>
|
||||
public override void PositionCursor ()
|
||||
{
|
||||
Move (point - first, 0);
|
||||
}
|
||||
|
||||
public override void Redraw (Rect region)
|
||||
{
|
||||
Driver.SetAttribute (Color);
|
||||
Move (0, 0);
|
||||
|
||||
for (int i = 0; i < Frame.Width; i++) {
|
||||
int p = first + i;
|
||||
|
||||
if (p < text.Length) {
|
||||
Driver.AddCh (Secret ? '*' : text [p]);
|
||||
} else
|
||||
Driver.AddCh (' ');
|
||||
}
|
||||
PositionCursor ();
|
||||
}
|
||||
|
||||
void Adjust ()
|
||||
{
|
||||
if (point < first)
|
||||
first = point;
|
||||
else if (first + point >= Frame.Width)
|
||||
first = point - (Frame.Width / 3);
|
||||
Redraw (Bounds);
|
||||
Driver.Refresh ();
|
||||
}
|
||||
|
||||
void SetText (string new_text)
|
||||
{
|
||||
text = new_text;
|
||||
if (Changed != null)
|
||||
Changed (this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public override bool CanFocus {
|
||||
get => true;
|
||||
set { base.CanFocus = value; }
|
||||
}
|
||||
|
||||
public override bool ProcessKey (KeyEvent kb)
|
||||
{
|
||||
switch (kb.Key) {
|
||||
case Key.Delete:
|
||||
case Key.Backspace:
|
||||
if (point == 0)
|
||||
return true;
|
||||
|
||||
SetText (text.Substring (0, point - 1) + text.Substring (point));
|
||||
point--;
|
||||
Adjust ();
|
||||
break;
|
||||
|
||||
// Home, C-A
|
||||
case Key.Home:
|
||||
case Key.ControlA:
|
||||
point = 0;
|
||||
Adjust ();
|
||||
break;
|
||||
|
||||
case Key.CursorLeft:
|
||||
case Key.ControlB:
|
||||
if (point > 0) {
|
||||
point--;
|
||||
Adjust ();
|
||||
}
|
||||
break;
|
||||
|
||||
case Key.ControlD: // Delete
|
||||
if (point == text.Length)
|
||||
break;
|
||||
SetText (text.Substring (0, point) + text.Substring (point + 1));
|
||||
Adjust ();
|
||||
break;
|
||||
|
||||
case Key.ControlE: // End
|
||||
point = text.Length;
|
||||
Adjust ();
|
||||
break;
|
||||
|
||||
case Key.CursorRight:
|
||||
case Key.ControlF:
|
||||
if (point == text.Length)
|
||||
break;
|
||||
point++;
|
||||
Adjust ();
|
||||
break;
|
||||
|
||||
case Key.ControlK: // kill-to-end
|
||||
kill = text.Substring (point);
|
||||
SetText (text.Substring (0, point));
|
||||
Adjust ();
|
||||
break;
|
||||
|
||||
case Key.ControlY: // Control-y, yank
|
||||
if (kill == null)
|
||||
return true;
|
||||
|
||||
if (point == text.Length) {
|
||||
SetText (text + kill);
|
||||
point = text.Length;
|
||||
} else {
|
||||
SetText (text.Substring (0, point) + kill + text.Substring (point));
|
||||
point += kill.Length;
|
||||
}
|
||||
Adjust ();
|
||||
break;
|
||||
|
||||
case (Key)((int)'b' + Key.AltMask):
|
||||
int bw = WordBackward (point);
|
||||
if (bw != -1)
|
||||
point = bw;
|
||||
Adjust ();
|
||||
break;
|
||||
|
||||
case (Key)((int)'f' + Key.AltMask):
|
||||
int fw = WordForward (point);
|
||||
if (fw != -1)
|
||||
point = fw;
|
||||
Adjust ();
|
||||
break;
|
||||
|
||||
default:
|
||||
// Ignore other control characters.
|
||||
if (kb.Key < Key.Space || kb.Key > Key.CharMask)
|
||||
return false;
|
||||
|
||||
if (used) {
|
||||
if (point == text.Length) {
|
||||
SetText (text + (char)kb.Key);
|
||||
} else {
|
||||
SetText (text.Substring (0, point) + (char)kb.Key + text.Substring (point));
|
||||
}
|
||||
point++;
|
||||
} else {
|
||||
SetText ("" + (char)kb.Key);
|
||||
first = 0;
|
||||
point = 1;
|
||||
}
|
||||
used = true;
|
||||
Adjust ();
|
||||
return true;
|
||||
}
|
||||
used = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
int WordForward (int p)
|
||||
{
|
||||
if (p >= text.Length)
|
||||
return -1;
|
||||
|
||||
int i = p;
|
||||
if (Char.IsPunctuation (text [p]) || Char.IsWhiteSpace (text [p])) {
|
||||
for (; i < text.Length; i++) {
|
||||
if (Char.IsLetterOrDigit (text [i]))
|
||||
break;
|
||||
}
|
||||
for (; i < text.Length; i++) {
|
||||
if (!Char.IsLetterOrDigit (text [i]))
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
for (; i < text.Length; i++) {
|
||||
if (!Char.IsLetterOrDigit (text [i]))
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i != p)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int WordBackward (int p)
|
||||
{
|
||||
if (p == 0)
|
||||
return -1;
|
||||
|
||||
int i = p - 1;
|
||||
if (i == 0)
|
||||
return 0;
|
||||
|
||||
if (Char.IsPunctuation (text [i]) || Char.IsSymbol (text [i]) || Char.IsWhiteSpace (text [i])) {
|
||||
for (; i >= 0; i--) {
|
||||
if (Char.IsLetterOrDigit (text [i]))
|
||||
break;
|
||||
}
|
||||
for (; i >= 0; i--) {
|
||||
if (!Char.IsLetterOrDigit (text [i]))
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
for (; i >= 0; i--) {
|
||||
if (!Char.IsLetterOrDigit (text [i]))
|
||||
break;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
|
||||
if (i != p)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if false
|
||||
public override void ProcessMouse (Curses.MouseEvent ev)
|
||||
{
|
||||
if ((ev.ButtonState & Curses.Event.Button1Clicked) == 0)
|
||||
return;
|
||||
|
||||
.SetFocus (this);
|
||||
|
||||
// We could also set the cursor position.
|
||||
point = first + (ev.X - x);
|
||||
if (point > text.Length)
|
||||
point = text.Length;
|
||||
if (point < first)
|
||||
point = 0;
|
||||
|
||||
SetNeedsDisplay ();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
4
demo.cs
4
demo.cs
@@ -9,7 +9,9 @@ class Demo {
|
||||
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 Label (new Rect (0, 12, 40, 3), "4-Hello world, how are you doing today") { TextAlignment = TextAlignment.Justified},
|
||||
new Label (3, 14, "Login: "),
|
||||
new TextField (10, 14, 40, "")
|
||||
};
|
||||
top.Add (win);
|
||||
Application.Run ();
|
||||
|
||||
Reference in New Issue
Block a user