mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-26 15:57:56 +01:00
Add menu, ugly looking for now
This commit is contained in:
19
Core.cs
19
Core.cs
@@ -317,6 +317,25 @@ namespace Terminal {
|
|||||||
Driver.Clip = savedClip;
|
Driver.Clip = savedClip;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Utility function to draw strings that contain a hotkey
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="s">String to display, the underscoore before a letter flags the next letter as the hotkey.</param>
|
||||||
|
/// <param name="hotColor">Hot color.</param>
|
||||||
|
/// <param name="normalColor">Normal color.</param>
|
||||||
|
public void DrawHotString (string text, Attribute hotColor, Attribute normalColor)
|
||||||
|
{
|
||||||
|
Driver.SetAttribute (normalColor);
|
||||||
|
foreach (var c in text) {
|
||||||
|
if (c == '_') {
|
||||||
|
Driver.SetAttribute (hotColor);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Driver.AddCh (c);
|
||||||
|
Driver.SetAttribute (normalColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This moves the cursor to the specified column and row in the view.
|
/// This moves the cursor to the specified column and row in the view.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
14
Driver.cs
14
Driver.cs
@@ -52,6 +52,10 @@ namespace Terminal {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum SpecialChar {
|
||||||
|
HLine,
|
||||||
|
}
|
||||||
|
|
||||||
public abstract class ConsoleDriver {
|
public abstract class ConsoleDriver {
|
||||||
public abstract int Cols { get; }
|
public abstract int Cols { get; }
|
||||||
public abstract int Rows { get; }
|
public abstract int Rows { get; }
|
||||||
@@ -73,6 +77,7 @@ namespace Terminal {
|
|||||||
public abstract void SetColors (short foreColorId, short backgroundColorId);
|
public abstract void SetColors (short foreColorId, short backgroundColorId);
|
||||||
|
|
||||||
public abstract void DrawFrame (Rect region, bool fill);
|
public abstract void DrawFrame (Rect region, bool fill);
|
||||||
|
public abstract void AddSpecial (SpecialChar ch);
|
||||||
|
|
||||||
Rect clip;
|
Rect clip;
|
||||||
public Rect Clip {
|
public Rect Clip {
|
||||||
@@ -117,6 +122,15 @@ namespace Terminal {
|
|||||||
ccol++;
|
ccol++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void AddSpecial (SpecialChar ch)
|
||||||
|
{
|
||||||
|
switch (ch) {
|
||||||
|
case SpecialChar.HLine:
|
||||||
|
AddCh (Curses.ACS_HLINE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override void AddStr (string str)
|
public override void AddStr (string str)
|
||||||
{
|
{
|
||||||
// TODO; optimize this to determine if the str fits in the clip region, and if so, use Curses.addstr directly
|
// TODO; optimize this to determine if the str fits in the clip region, and if so, use Curses.addstr directly
|
||||||
|
|||||||
1
Event.cs
1
Event.cs
@@ -44,6 +44,7 @@ namespace Terminal {
|
|||||||
ControlY,
|
ControlY,
|
||||||
ControlZ,
|
ControlZ,
|
||||||
Esc = 27,
|
Esc = 27,
|
||||||
|
Enter = '\n',
|
||||||
Space = 32,
|
Space = 32,
|
||||||
Delete = 127,
|
Delete = 127,
|
||||||
|
|
||||||
|
|||||||
26
TODO.md
26
TODO.md
@@ -47,21 +47,17 @@ Unclear what to do about that right now.
|
|||||||
|
|
||||||
Needs to move to `ustring` from `NStack.Core` to get full Unicode support.
|
Needs to move to `ustring` from `NStack.Core` to get full Unicode support.
|
||||||
|
|
||||||
# Focus
|
Should get NStack.Core to move `ustring` to `System`.
|
||||||
|
|
||||||
When SetFocus is called, it need to ensure that the chain up the views is
|
|
||||||
focused as well, something that we got for free in the old Container/Widget
|
|
||||||
model, but needs revisiting in the new model.
|
|
||||||
|
|
||||||
# Bugs
|
|
||||||
|
|
||||||
On the demo, press tab twice, instead of selecting Ok, the first tab
|
|
||||||
does nothing, the second tab clears the screen.
|
|
||||||
|
|
||||||
=> Explanation: the Window gets a NeedsDisplay, so it displays
|
|
||||||
tiself, but the contentView does not have NeedsDisplay
|
|
||||||
set recursively, so it does not render any of the subviews
|
|
||||||
|
|
||||||
# Merge Responder into View
|
# Merge Responder into View
|
||||||
|
|
||||||
# Make HasFocus implicitly call SetNeedsDisplay
|
For now it is split, in case we want to introduce formal view controllers. But the design becomes very ugly.
|
||||||
|
|
||||||
|
# Bugs
|
||||||
|
|
||||||
|
# Mouse support
|
||||||
|
|
||||||
|
It is still pending.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,8 @@
|
|||||||
<Compile Include="Views\Label.cs" />
|
<Compile Include="Views\Label.cs" />
|
||||||
<Compile Include="Views\TextField.cs" />
|
<Compile Include="Views\TextField.cs" />
|
||||||
<Compile Include="Views\Button.cs" />
|
<Compile Include="Views\Button.cs" />
|
||||||
|
<Compile Include="Views\Checkbox.cs" />
|
||||||
|
<Compile Include="Views\Menu.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="mono-curses.dll">
|
<Reference Include="mono-curses.dll">
|
||||||
|
|||||||
126
Views/Checkbox.cs
Normal file
126
Views/Checkbox.cs
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Terminal {
|
||||||
|
public class CheckBox : View {
|
||||||
|
string text;
|
||||||
|
int hot_pos = -1;
|
||||||
|
char hot_key;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Toggled event, raised when the CheckButton is toggled.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Client code can hook up to this event, it is
|
||||||
|
/// raised when the checkbutton is activated either with
|
||||||
|
/// the mouse or the keyboard.
|
||||||
|
/// </remarks>
|
||||||
|
public event EventHandler Toggled;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Public constructor, creates a CheckButton based on
|
||||||
|
/// the given text at the given position.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The size of CheckButton is computed based on the
|
||||||
|
/// text length. This CheckButton is not toggled.
|
||||||
|
/// </remarks>
|
||||||
|
public CheckBox (int x, int y, string s) : this (x, y, s, false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Public constructor, creates a CheckButton based on
|
||||||
|
/// the given text at the given position and a state.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The size of CheckButton is computed based on the
|
||||||
|
/// text length.
|
||||||
|
/// </remarks>
|
||||||
|
public CheckBox (int x, int y, string s, bool is_checked) : base (new Rect (x, y, s.Length + 4, 1))
|
||||||
|
{
|
||||||
|
Checked = is_checked;
|
||||||
|
Text = s;
|
||||||
|
|
||||||
|
CanFocus = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The state of the checkbox.
|
||||||
|
/// </summary>
|
||||||
|
public bool Checked { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The text displayed by this widget.
|
||||||
|
/// </summary>
|
||||||
|
public string Text {
|
||||||
|
get {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
set {
|
||||||
|
text = value;
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
hot_pos = -1;
|
||||||
|
hot_key = (char)0;
|
||||||
|
foreach (char c in text) {
|
||||||
|
if (Char.IsUpper (c)) {
|
||||||
|
hot_key = c;
|
||||||
|
hot_pos = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Redraw (Rect region)
|
||||||
|
{
|
||||||
|
Driver.SetAttribute (HasFocus ? Colors.Base.Focus : Colors.Base.Normal);
|
||||||
|
Move (0, 0);
|
||||||
|
Driver.AddStr (Checked ? "[x] " : "[ ] ");
|
||||||
|
Move (4, 0);
|
||||||
|
Driver.AddStr (Text);
|
||||||
|
if (hot_pos != -1) {
|
||||||
|
Move (4 + hot_pos, 0);
|
||||||
|
Driver.SetAttribute (HasFocus ? Colors.Base.HotFocus : Colors.Base.HotNormal);
|
||||||
|
Driver.AddCh (hot_key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PositionCursor ()
|
||||||
|
{
|
||||||
|
Move (1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool ProcessKey (KeyEvent kb)
|
||||||
|
{
|
||||||
|
if (kb.KeyValue == ' ') {
|
||||||
|
Checked = !Checked;
|
||||||
|
|
||||||
|
if (Toggled != null)
|
||||||
|
Toggled (this, EventArgs.Empty);
|
||||||
|
|
||||||
|
SetNeedsDisplay ();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if false
|
||||||
|
public override void ProcessMouse (Curses.MouseEvent ev)
|
||||||
|
{
|
||||||
|
if ((ev.ButtonState & Curses.Event.Button1Clicked) != 0){
|
||||||
|
Container.SetFocus (this);
|
||||||
|
Container.Redraw ();
|
||||||
|
|
||||||
|
Checked = !Checked;
|
||||||
|
|
||||||
|
if (Toggled != null)
|
||||||
|
Toggled (this, EventArgs.Empty);
|
||||||
|
Redraw ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
245
Views/Menu.cs
Normal file
245
Views/Menu.cs
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
using System;
|
||||||
|
namespace Terminal {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A menu item has a title, an associated help text, and an action to execute on activation.
|
||||||
|
/// </summary>
|
||||||
|
public class MenuItem {
|
||||||
|
public MenuItem (string title, string help, Action action)
|
||||||
|
{
|
||||||
|
Title = title ?? "";
|
||||||
|
Help = help ?? "";
|
||||||
|
Action = action;
|
||||||
|
Width = Title.Length + Help.Length + 1;
|
||||||
|
}
|
||||||
|
public string Title { get; set; }
|
||||||
|
public string Help { get; set; }
|
||||||
|
public Action Action { get; set; }
|
||||||
|
public int Width { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A menu bar item contains other menu items.
|
||||||
|
/// </summary>
|
||||||
|
public class MenuBarItem {
|
||||||
|
public MenuBarItem (string title, MenuItem [] children)
|
||||||
|
{
|
||||||
|
Title = title ?? "";
|
||||||
|
Children = children;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Title { get; set; }
|
||||||
|
public MenuItem [] Children { get; set; }
|
||||||
|
public int Current { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A menu bar for your application.
|
||||||
|
/// </summary>
|
||||||
|
public class MenuBar : View {
|
||||||
|
public MenuBarItem [] Menus { get; set; }
|
||||||
|
int selected;
|
||||||
|
Action action;
|
||||||
|
|
||||||
|
public MenuBar (MenuBarItem [] menus) : base (new Rect (0, 0, Application.Driver.Cols, 1))
|
||||||
|
{
|
||||||
|
Menus = menus;
|
||||||
|
CanFocus = false;
|
||||||
|
selected = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Activates the menubar
|
||||||
|
/// </summary>
|
||||||
|
public void Activate (int idx)
|
||||||
|
{
|
||||||
|
if (idx < 0 || idx > Menus.Length)
|
||||||
|
throw new ArgumentException ("idx");
|
||||||
|
|
||||||
|
action = null;
|
||||||
|
selected = idx;
|
||||||
|
|
||||||
|
foreach (var m in Menus)
|
||||||
|
m.Current = 0;
|
||||||
|
|
||||||
|
// TODO: Application.Run (this);
|
||||||
|
selected = -1;
|
||||||
|
SuperView.SetNeedsDisplay ();
|
||||||
|
|
||||||
|
if (action != null)
|
||||||
|
action ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawMenu (int idx, int col, int line)
|
||||||
|
{
|
||||||
|
int max = 0;
|
||||||
|
var menu = Menus [idx];
|
||||||
|
|
||||||
|
if (menu.Children == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var m in menu.Children) {
|
||||||
|
if (m == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (m.Width > max)
|
||||||
|
max = m.Width;
|
||||||
|
}
|
||||||
|
max += 4;
|
||||||
|
DrawFrame (new Rect (col, line, max, menu.Children.Length + 2), true);
|
||||||
|
for (int i = 0; i < menu.Children.Length; i++) {
|
||||||
|
var item = menu.Children [i];
|
||||||
|
|
||||||
|
Move (line + 1 + i, col + 1);
|
||||||
|
Driver.SetAttribute (item == null ? Colors.Base.Focus : i == menu.Current ? Colors.Menu.MarkedSelected : Colors.Menu.Marked);
|
||||||
|
for (int p = 0; p < max - 2; p++)
|
||||||
|
if (item == null)
|
||||||
|
Driver.AddSpecial (SpecialChar.HLine);
|
||||||
|
else
|
||||||
|
Driver.AddCh (' ');
|
||||||
|
|
||||||
|
if (item == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Move (line + 1 + i, col + 2);
|
||||||
|
DrawHotString (item.Title,
|
||||||
|
i == menu.Current ? Colors.Menu.HotFocus: Colors.Menu.HotNormal,
|
||||||
|
i == menu.Current ? Colors.Menu.MarkedSelected : Colors.Menu.Marked);
|
||||||
|
|
||||||
|
// The help string
|
||||||
|
var l = item.Help.Length;
|
||||||
|
Move (col + max - l - 2, line + 1 + i);
|
||||||
|
Driver.AddStr (item.Help);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Redraw (Rect region)
|
||||||
|
{
|
||||||
|
Move (0, 0);
|
||||||
|
Driver.SetAttribute (Colors.Base.Focus);
|
||||||
|
for (int i = 0; i < Frame.Width; i++)
|
||||||
|
Driver.AddCh (' ');
|
||||||
|
|
||||||
|
Move (1, 0);
|
||||||
|
int pos = 0;
|
||||||
|
for (int i = 0; i < Menus.Length; i++) {
|
||||||
|
var menu = Menus [i];
|
||||||
|
if (i == selected) {
|
||||||
|
DrawMenu (i, pos, 1);
|
||||||
|
Driver.SetAttribute (Colors.Menu.MarkedSelected);
|
||||||
|
} else
|
||||||
|
Driver.SetAttribute (Colors.Menu.Focus);
|
||||||
|
|
||||||
|
Move (pos, 0);
|
||||||
|
Driver.AddCh (' ');
|
||||||
|
Driver.AddStr(menu.Title);
|
||||||
|
Driver.AddCh (' ');
|
||||||
|
if (HasFocus && i == selected)
|
||||||
|
Driver.SetAttribute (Colors.Menu.MarkedSelected);
|
||||||
|
else
|
||||||
|
Driver.SetAttribute (Colors.Menu.Marked);
|
||||||
|
Driver.AddStr (" ");
|
||||||
|
|
||||||
|
pos += menu.Title.Length + 4;
|
||||||
|
}
|
||||||
|
PositionCursor ();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PositionCursor ()
|
||||||
|
{
|
||||||
|
int pos = 0;
|
||||||
|
for (int i = 0; i < Menus.Length; i++) {
|
||||||
|
if (i == selected) {
|
||||||
|
pos++;
|
||||||
|
Move (pos, 0);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
pos += Menus [i].Title.Length + 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Move (0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Selected (MenuItem item)
|
||||||
|
{
|
||||||
|
// TODO: Running = false;
|
||||||
|
action = item.Action;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool ProcessKey (KeyEvent kb)
|
||||||
|
{
|
||||||
|
switch (kb.Key) {
|
||||||
|
case Key.CursorUp:
|
||||||
|
if (Menus [selected].Children == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int current = Menus [selected].Current;
|
||||||
|
do {
|
||||||
|
current--;
|
||||||
|
if (current < 0)
|
||||||
|
current = Menus [selected].Children.Length - 1;
|
||||||
|
} while (Menus [selected].Children [current] == null);
|
||||||
|
Menus [selected].Current = current;
|
||||||
|
|
||||||
|
SetNeedsDisplay ();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case Key.CursorDown:
|
||||||
|
if (Menus [selected].Children == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
do {
|
||||||
|
Menus [selected].Current = (Menus [selected].Current + 1) % Menus [selected].Children.Length;
|
||||||
|
} while (Menus [selected].Children [Menus [selected].Current] == null);
|
||||||
|
|
||||||
|
SetNeedsDisplay ();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Key.CursorLeft:
|
||||||
|
selected--;
|
||||||
|
if (selected < 0)
|
||||||
|
selected = Menus.Length - 1;
|
||||||
|
break;
|
||||||
|
case Key.CursorRight:
|
||||||
|
selected = (selected + 1) % Menus.Length;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Key.Enter:
|
||||||
|
if (Menus [selected].Children == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Selected (Menus [selected].Children [Menus [selected].Current]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Key.Esc:
|
||||||
|
case Key.ControlC:
|
||||||
|
//TODO: Running = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
var key = kb.KeyValue;
|
||||||
|
if ((key >= 'a' && key <= 'z') || (key >= 'A' && key <= 'Z') || (key >= '0' && key <= '9')) {
|
||||||
|
char c = Char.ToUpper ((char)key);
|
||||||
|
|
||||||
|
if (Menus [selected].Children == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
foreach (var mi in Menus [selected].Children) {
|
||||||
|
int p = mi.Title.IndexOf ('_');
|
||||||
|
if (p != -1 && p + 1 < mi.Title.Length) {
|
||||||
|
if (mi.Title [p + 1] == c) {
|
||||||
|
Selected (mi);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SetNeedsDisplay ();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
24
demo.cs
24
demo.cs
@@ -17,8 +17,9 @@ class Demo {
|
|||||||
new TextField (14, 2, 40, ""),
|
new TextField (14, 2, 40, ""),
|
||||||
new Label (3, 4, "Password: "),
|
new Label (3, 4, "Password: "),
|
||||||
new TextField (14, 4, 40, "") { Secret = true },
|
new TextField (14, 4, 40, "") { Secret = true },
|
||||||
new Button (3, 6, "Ok"),
|
new CheckBox (3, 6, "Remember me"),
|
||||||
new Button (10, 6, "Cancel")
|
new Button (3, 8, "Ok"),
|
||||||
|
new Button (10, 8, "Cancel")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,11 +27,28 @@ class Demo {
|
|||||||
{
|
{
|
||||||
Application.Init ();
|
Application.Init ();
|
||||||
var top = Application.Top;
|
var top = Application.Top;
|
||||||
var win = new Window (new Rect (0, 0, 80, 24), "Hello");
|
var tframe = top.Frame;
|
||||||
|
|
||||||
|
var win = new Window (new Rect (0, 1, tframe.Width, tframe.Height-1), "Hello");
|
||||||
|
var menu = new MenuBar (new MenuBarItem [] {
|
||||||
|
new MenuBarItem ("File", new MenuItem [] {
|
||||||
|
new MenuItem ("New", "", null),
|
||||||
|
new MenuItem ("Open", "", null),
|
||||||
|
new MenuItem ("Close", "", null),
|
||||||
|
new MenuItem ("Quit", "", null)
|
||||||
|
}),
|
||||||
|
new MenuBarItem ("Edit", new MenuItem [] {
|
||||||
|
new MenuItem ("Copy", "", null),
|
||||||
|
new MenuItem ("Cut", "", null),
|
||||||
|
new MenuItem ("Paste", "", null)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
ShowEntries (win);
|
ShowEntries (win);
|
||||||
|
|
||||||
// ShowTextAlignments (win);
|
// ShowTextAlignments (win);
|
||||||
top.Add (win);
|
top.Add (win);
|
||||||
|
top.Add (menu);
|
||||||
Application.Run ();
|
Application.Run ();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user