parent
be2c4fab03
commit
e7e71ff820
@ -1,189 +0,0 @@
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.IO.Pipes;
|
||||
using Microsoft.VisualBasic;
|
||||
using Mindmagma.Curses;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace SCI.CursesWrapper;
|
||||
|
||||
public class Helper {
|
||||
/// <summary>
|
||||
/// Create a centered NCurses window on the specified screen
|
||||
/// </summary>
|
||||
/// <param name="screen">Parent screen to attach window to</param>
|
||||
/// <param name="width">Width of columns of window</param>
|
||||
/// <param name="height">Height of rows of window</param>
|
||||
/// <param name="borders">If true, shows default borders around window</param>
|
||||
/// <returns>nint - Pointer of new window</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Throws exception if window is outside of screen bounds</exception>
|
||||
public static nint CreateCenteredWindow (nint screen, int width, int height, bool borders = true) {
|
||||
NCurses.GetMaxYX (screen, out int screenHeight, out int screenWidth);
|
||||
|
||||
int originY = (screenHeight / 2) - (height / 2);
|
||||
int originX = (screenWidth / 2) - (width / 2);
|
||||
|
||||
if (originX + width > screenWidth) throw new ArgumentOutOfRangeException ("width");
|
||||
if (originY + height > screenHeight) throw new ArgumentOutOfRangeException ("height");
|
||||
|
||||
if (originX < 0) throw new ArgumentOutOfRangeException ("originX");
|
||||
if (originY < 0) throw new ArgumentOutOfRangeException ("originY");
|
||||
|
||||
nint window = NCurses.NewWindow (height, width, originY, originX);
|
||||
if (borders) NCurses.Box (window, (char) 0, (char) 0);
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prints text horizontally centered to window (on a specific line);
|
||||
/// </summary>
|
||||
/// <param name="window">Parent window to print into</param>
|
||||
/// <param name="text">Text to display</param>
|
||||
/// <param name="y">Y posititon of the text</param>
|
||||
public static void PrintHCenteredText (nint window, string text, int y = 0) {
|
||||
NCurses.GetMaxYX (window, out int _, out int windowWidth);
|
||||
|
||||
var x = windowWidth / 2 - text.Length / 2;
|
||||
NCurses.MoveWindowAddString (window, y, x, text);
|
||||
}
|
||||
}
|
||||
|
||||
public class MessageBox {
|
||||
public enum MessageBoxButtons {
|
||||
OK,
|
||||
YesNo,
|
||||
RetryCancel,
|
||||
RetryIgnoreCancel
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows a message box with specified text and returns the selected button from zero index
|
||||
/// </summary>
|
||||
/// <param name="screen">Parent screen to show message box on</param>
|
||||
/// <param name="text">Text to show</param>
|
||||
/// <param name="buttons">Type of buttons to show</param>
|
||||
/// <returns>Button pressed by user, zero indexed</returns>
|
||||
public static int Show (nint screen, string text, MessageBoxButtons buttons = MessageBoxButtons.OK) {
|
||||
NCurses.GetMaxYX (screen, out int screenHeight, out int screenWidth);
|
||||
|
||||
// Set static box width, if width is larger than screen, reduce to screen size,
|
||||
// then calculate the message box and inner pad origin X coords
|
||||
var boxWidth = 50;
|
||||
if (boxWidth + 3 >= screenWidth) boxWidth = screenWidth - 3;
|
||||
int originX = (screenWidth / 2) - (boxWidth / 2);
|
||||
var padOriginX = originX + 2; // Add one for the border and for a single character padding
|
||||
|
||||
// Calculate sizes for the inner pad element
|
||||
var padWidth = boxWidth - 4; // Add four to the total box width due to the two character padding on both sides
|
||||
var totalTextLines = (int) Math.Ceiling ((double) text.Length / padWidth); // Get total amount of lines required to hold all text limited by box width
|
||||
var padHeight = totalTextLines;
|
||||
if (padHeight + 8 > screenHeight) { // If height required to show all text lines plus padding is larger than screen, reduce size of message box
|
||||
padHeight = screenHeight - 8;
|
||||
}
|
||||
|
||||
// Now calculate the height and Y origin of the text box and inner pad
|
||||
var boxHeight = padHeight + 6; // Add five to the pad height due to the title bar and two character padding top and bottom
|
||||
if (boxHeight + 2 > screenHeight) boxHeight = screenHeight - 2;
|
||||
int originY = (screenHeight / 2) - (boxHeight / 2);
|
||||
var padOriginY = originY + 3; // Add one for the border, one for the title and one for a single character padding
|
||||
|
||||
//if (text.Length > (totalTextRows * textCols)) text = text.Substring (0, (totalTextRows * textCols) - 1) + "$";
|
||||
|
||||
// Create message box and inner Pad
|
||||
var msgBoxWindow = Helper.CreateCenteredWindow (screen, boxWidth, boxHeight, true);
|
||||
var msgTextPad = NCurses.NewPad (padHeight, padWidth);
|
||||
|
||||
NCurses.Refresh ();
|
||||
|
||||
// Title, currently not in use
|
||||
Helper.PrintHCenteredText (msgBoxWindow, "Info", 1);
|
||||
NCurses.WindowRefresh (msgBoxWindow);
|
||||
|
||||
NCurses.WindowAddString (msgTextPad, text);
|
||||
|
||||
NCurses.PadRefresh (msgTextPad, 0, 0, padOriginY, padOriginX, padOriginY + padHeight, padOriginX + padWidth);
|
||||
|
||||
return InputHandler (msgBoxWindow, buttons);
|
||||
}
|
||||
|
||||
private static List<string> GetStringsFromButtonEnum (MessageBoxButtons buttons) {
|
||||
List<string> buttonStrings;
|
||||
|
||||
switch (buttons) {
|
||||
case MessageBoxButtons.OK:
|
||||
buttonStrings = new List<string> () { "OK" };
|
||||
break;
|
||||
case MessageBoxButtons.YesNo:
|
||||
buttonStrings = new List<string> () { "Yes", "No" };
|
||||
break;
|
||||
case MessageBoxButtons.RetryCancel:
|
||||
buttonStrings = new List<string> () { "Retry", "Cancel" };
|
||||
break;
|
||||
case MessageBoxButtons.RetryIgnoreCancel:
|
||||
buttonStrings = new List<string> () { "Retry", "Ignore", "Cancel" };
|
||||
break;
|
||||
default:
|
||||
throw new Exception ("Unknown button layout for message box");
|
||||
}
|
||||
|
||||
return buttonStrings;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws buttons specified by the buttons type
|
||||
/// </summary>
|
||||
/// <param name="window">Window to draw buttons on</param>
|
||||
/// <param name="buttons">Buttons to display</param>
|
||||
private static void DrawButtons (nint window, MessageBoxButtons buttons, int active = 0) {
|
||||
NCurses.GetMaxYX (window, out int windowHeight, out int windowWidth);
|
||||
|
||||
var buttonStrings = GetStringsFromButtonEnum (buttons);
|
||||
var totalStringLength = buttonStrings.Count - 1;
|
||||
foreach (var str in buttonStrings) totalStringLength += str.Length + 2;
|
||||
|
||||
NCurses.WindowMove (window, windowHeight - 2, (windowWidth / 2) - (totalStringLength / 2));
|
||||
|
||||
var index = 0;
|
||||
foreach (var str in buttonStrings) {
|
||||
if (index == active) {
|
||||
NCurses.WindowAddString (window, $"[{str}]");
|
||||
} else {
|
||||
NCurses.WindowAddString (window, $" {str} ");
|
||||
}
|
||||
|
||||
NCurses.WindowAddChar (window, ' ');
|
||||
|
||||
index ++;
|
||||
}
|
||||
|
||||
NCurses.WindowRefresh (window);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle the user selection of the buttons
|
||||
/// </summary>
|
||||
/// <param name="window">Window to draw buttons on</param>
|
||||
/// <param name="buttons">Buttons to display</param>
|
||||
/// <returns>User choice of button, zero indexed</returns>
|
||||
private static int InputHandler (nint window, MessageBoxButtons buttons) {
|
||||
DrawButtons (window, buttons);
|
||||
|
||||
int index = 0;
|
||||
int maxIndex = GetStringsFromButtonEnum (buttons).Count - 1;
|
||||
|
||||
int chr = 0;
|
||||
while ((chr = NCurses.GetChar ()) != 10) {
|
||||
if (chr == 260) { // Left
|
||||
index ++;
|
||||
if (index > maxIndex) index = 0;
|
||||
DrawButtons (window, buttons, index);
|
||||
} else if (chr == 261) { // Right
|
||||
index --;
|
||||
if (index < 0) index = maxIndex;
|
||||
DrawButtons (window, buttons, index);
|
||||
}
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
}
|
@ -1,114 +0,0 @@
|
||||
using Fahrplan;
|
||||
using Mindmagma.Curses;
|
||||
using Newtonsoft.Json;
|
||||
using SCI.CursesWrapper;
|
||||
|
||||
namespace ANSI_Fahrplan;
|
||||
|
||||
public class Playground {
|
||||
public static void Run (string [] args) {
|
||||
Console.WriteLine ("Program initializing Curses...");
|
||||
|
||||
// var jsonString = File.ReadAllText ("schedule.json");
|
||||
// var schedule = JsonConvert.DeserializeObject<Fahrplan.Root> (jsonString);
|
||||
|
||||
// foreach (var day in schedule.schedule.conference.days) {
|
||||
// Console.WriteLine ($"Day: {day.date}, {day.index}");
|
||||
// foreach (var room in day.rooms) {
|
||||
// Console.WriteLine ($" \\__ Room: {room.Key}");
|
||||
// foreach (var ev in room.Value) {
|
||||
// if (ev is null) continue;
|
||||
// Console.WriteLine ($" \\__ Event: {ev.title}");
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
var screen = NCurses.InitScreen ();
|
||||
NCurses.NoEcho ();
|
||||
NCurses.CBreak ();
|
||||
NCurses.SetCursor (0);
|
||||
|
||||
var hasColors = NCurses.HasColors ();
|
||||
if (hasColors) {
|
||||
NCurses.StartColor ();
|
||||
NCurses.InitPair (1, CursesColor.WHITE, CursesColor.BLUE);
|
||||
//ColorPairs.InitColors ();
|
||||
}
|
||||
|
||||
var win1 = NCurses.NewWindow (10, 20, 10, 10);
|
||||
NCurses.Box (win1, (char) 0, (char) 0);
|
||||
NCurses.WindowBackground (win1, ColorSchemes.Default ());
|
||||
|
||||
NCurses.MoveWindowAddString (win1, 1, 1, "Hello world!");
|
||||
|
||||
// Draw windows
|
||||
NCurses.Refresh ();
|
||||
NCurses.WindowRefresh (win1);
|
||||
|
||||
NCurses.GetChar ();
|
||||
NCurses.EndWin ();
|
||||
|
||||
/*var window = CreateWindowCentered (screen, 40, 12);
|
||||
NCurses.WindowBackground (window, NCurses.ColorPair (1));
|
||||
|
||||
NCurses.WindowAttributeOn (window, NCurses.ColorPair (1));*/
|
||||
|
||||
//NCurses.GetMaxYX (screen, out int screenHeight, out int screenWidth);
|
||||
//NCurses.MoveAddString (screenHeight - 1, 0, "NCurses Example");
|
||||
|
||||
/*var menuItems = new List<TopMenu.MenuItem> () {
|
||||
new TopMenu.MenuItem () { Label = "Test 1" },
|
||||
new TopMenu.MenuItem () { Label = "Test 2" },
|
||||
new TopMenu.MenuItem () { Label = "Test 3" },
|
||||
};
|
||||
var menu = new TopMenu (screen) { MenuItems = menuItems };
|
||||
|
||||
menu.Render ();*/
|
||||
|
||||
var msgBoxResponse = MessageBox.Show (screen, "Hello World, this is a message box text, yippee!! Let's make this text even longer, wooowiieeeee!", MessageBox.MessageBoxButtons.YesNo);
|
||||
NCurses.AddString ($"Input was {msgBoxResponse}");
|
||||
NCurses.Refresh ();
|
||||
|
||||
var inputTask = Task.Run (() => InputRoutine ());
|
||||
while (inputTask.Status == TaskStatus.WaitingToRun);
|
||||
|
||||
while (inputTask.Status == TaskStatus.Running) {
|
||||
NCurses.Refresh ();
|
||||
//NCurses.WindowRefresh (window);
|
||||
NCurses.Nap (1000 / 30);
|
||||
}
|
||||
|
||||
//NCurses.AttributeOff (NCurses.ColorPair (1));
|
||||
NCurses.EndWin ();
|
||||
}
|
||||
|
||||
static void InputRoutine () {
|
||||
int chr = 0;
|
||||
while (chr != CursesKey.ESC) {
|
||||
chr = NCurses.GetChar ();
|
||||
|
||||
/*if (chr == 265) {
|
||||
menu.Render (0);
|
||||
} else if (chr == 266) {
|
||||
menu.Render (1);
|
||||
} else if (chr == 267) {
|
||||
menu.Render (2);
|
||||
} else if (chr > 0) {
|
||||
menu.Render ();
|
||||
|
||||
NCurses.MoveAddString (2, 10, $"You pressed the key {chr} ");
|
||||
}*/
|
||||
|
||||
if (chr > 0) NCurses.MoveAddString (2, 10, $"You pressed the key {chr} ");
|
||||
}
|
||||
|
||||
/*NCurses.WindowBorder (window, ' ', ' ', ' ',' ',' ',' ',' ',' ');
|
||||
NCurses.WindowRefresh (window);
|
||||
NCurses.DeleteWindow (window);*/
|
||||
|
||||
NCurses.MoveAddString (10, 10, "Input routine exited");
|
||||
NCurses.Refresh ();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
using Mindmagma.Curses;
|
||||
|
||||
namespace ANSI_Fahrplan.Screens;
|
||||
|
||||
public abstract class Screen {
|
||||
private nint _rootWindow;
|
||||
public nint RootWindow { get { return _rootWindow; }}
|
||||
|
||||
public Screen (nint rootWindow) {
|
||||
_rootWindow = rootWindow;
|
||||
|
||||
PrepareWindow ();
|
||||
}
|
||||
|
||||
private void PrepareWindow () {
|
||||
NCurses.NoEcho ();
|
||||
NCurses.CBreak ();
|
||||
NCurses.SetCursor (0);
|
||||
|
||||
//NCurses.ClearWindow (RootWindow);
|
||||
NCurses.WindowMove (RootWindow, 0, 0);
|
||||
}
|
||||
|
||||
public abstract void Show ();
|
||||
|
||||
public abstract void Hide ();
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
- [x] Create JSON parser for Fahrplan
|
||||
- [] Add networking functions to pull up-to-date Fahrplan from web
|
||||
- [] User interface
|
||||
- [] Add landing page
|
||||
- [] Add schedule overview by
|
||||
- [] Day (List view, go left and right to change days)
|
||||
- [] Room ()
|
||||
- [] Category
|
||||
- [] Speakers
|
||||
- [] Today/Currently running
|
||||
- [] Timeline?
|
||||
- [] Add search interface
|
||||
- [] Add about page
|
@ -0,0 +1,64 @@
|
||||
using Mindmagma.Curses;
|
||||
|
||||
namespace SCI.CursesWrapper.UiElements;
|
||||
|
||||
public class FooterWindow : Window {
|
||||
/// <summary>
|
||||
/// Class constructor
|
||||
/// </summary>
|
||||
/// <param name="screen">Root NCurses screen ID</param>
|
||||
/// <param name="defaultTitle">Initial title to show</param>
|
||||
public FooterWindow (nint screen, string defaultTitle = "") :
|
||||
base (0, CursesWrapper.GetHeight (screen) - 1, CursesWrapper.GetWidth (screen), 1)
|
||||
{
|
||||
var colorSchemeNormal = ColorSchemes.TopMenuNormal ();
|
||||
BackgroundColorId = colorSchemeNormal;
|
||||
NCurses.WindowAttributeOn (WindowId, colorSchemeNormal);
|
||||
|
||||
DrawFooter (defaultTitle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw the footer window and insert the specified text
|
||||
/// </summary>
|
||||
/// <param name="title"></param>
|
||||
/// <param name="left"></param>
|
||||
/// <param name="right"></param>
|
||||
public void DrawFooter (string title, string left = "", string right = "") {
|
||||
Clear ();
|
||||
|
||||
// TODO: Describe the last-character-in-window bug(fix) here
|
||||
var pad =
|
||||
(WindowSize.Width / 2) -
|
||||
(title.Length / 2)
|
||||
- 1;
|
||||
|
||||
var finalString =
|
||||
" " + left.PadRight (pad) +
|
||||
title +
|
||||
(string.IsNullOrEmpty (right)?
|
||||
"" :
|
||||
right.PadLeft (pad)
|
||||
);
|
||||
|
||||
// String too long, leave out left and right text
|
||||
if (finalString.Length > WindowSize.Width - 1) {
|
||||
finalString = " ".PadLeft (pad) + title;
|
||||
}
|
||||
|
||||
// String still too long, leave out the padding and left align title
|
||||
if (finalString.Length > WindowSize.Width - 1) {
|
||||
finalString = " " + title;
|
||||
}
|
||||
|
||||
// String still too long, cut to size
|
||||
if (finalString.Length > WindowSize.Width - 1) {
|
||||
finalString = title.Substring (0, WindowSize.Width - 1);
|
||||
}
|
||||
|
||||
try {
|
||||
NCurses.MoveWindowAddString (this, 0, 0, finalString);
|
||||
Draw ();
|
||||
} catch (Exception) {};
|
||||
}
|
||||
}
|
@ -1,97 +0,0 @@
|
||||
using Mindmagma.Curses;
|
||||
|
||||
namespace SCI.CursesWrapper.UiElements;
|
||||
|
||||
public class InputBox {
|
||||
public delegate void InputCompleted (string input);
|
||||
|
||||
public void RequestInput (nint screen, InputHandler inputHandler, InputCompleted callback, string question) {
|
||||
const int boxPaddingTop = 1;
|
||||
const int boxPaddingSides = 2;
|
||||
const int boxPaddingBottom = 2;
|
||||
|
||||
const int boxMarginTopBottom = 1;
|
||||
const int boxMarginSides = 2;
|
||||
|
||||
NCurses.GetMaxYX (screen, out int height, out int width);
|
||||
|
||||
// Contents inside the question box may not be larger than these
|
||||
int maxInnerWidth = width - (boxPaddingSides * 2) - (boxMarginSides * 2);
|
||||
int maxInnerHeight = height - boxPaddingTop - boxPaddingBottom - boxMarginTopBottom;
|
||||
|
||||
using var strReader = new StringReader (question);
|
||||
string? inStr;
|
||||
int innerWidth = 1;
|
||||
int innerHeight = 1;
|
||||
while ((inStr = strReader.ReadLine ()) is not null) {
|
||||
if (inStr.Length > innerWidth) innerWidth = inStr.Length;
|
||||
|
||||
if (innerWidth > maxInnerWidth) {
|
||||
innerWidth = maxInnerWidth;
|
||||
innerHeight += (int) Math.Ceiling (inStr.Length / (double) maxInnerWidth);
|
||||
} else {
|
||||
innerHeight ++;
|
||||
}
|
||||
}
|
||||
|
||||
int boxWidth = innerWidth + (boxPaddingSides * 2);
|
||||
int boxHeight = innerHeight + boxPaddingTop + boxPaddingBottom;
|
||||
int boxAnchorX = (width / 2) - (boxWidth / 2);
|
||||
int boxAnchorY = (height / 2) - (boxHeight / 2);
|
||||
|
||||
var boxWin = NCurses.SubWindow (screen, boxHeight, boxWidth, boxAnchorY, boxAnchorX);
|
||||
NCurses.Box (boxWin, (char) 0, (char) 0);
|
||||
|
||||
NCurses.Refresh ();
|
||||
NCurses.WindowRefresh (boxWin);
|
||||
|
||||
int questionTextWinAnchorX = boxAnchorX + boxPaddingSides;
|
||||
int questionTextWinAnchorY = boxAnchorY + boxPaddingTop;
|
||||
|
||||
var questionTextWin = NCurses.SubWindow (boxWin, innerHeight, innerWidth, questionTextWinAnchorY, questionTextWinAnchorX);
|
||||
NCurses.WindowAddString (questionTextWin, question.Trim ());
|
||||
|
||||
NCurses.WindowRefresh (questionTextWin);
|
||||
|
||||
int inputFieldWinX = questionTextWinAnchorX;
|
||||
int inputFieldWinY = boxAnchorY + boxHeight - boxMarginTopBottom - 1;
|
||||
|
||||
var inputFieldWin = NCurses.SubWindow (boxWin, 1, innerWidth, inputFieldWinY, inputFieldWinX);
|
||||
NCurses.WindowBackground (inputFieldWin, ColorSchemes.TextInputField ());
|
||||
NCurses.WindowAttributeSet (inputFieldWin, ColorSchemes.TextInputField ());
|
||||
|
||||
NCurses.WindowRefresh (inputFieldWin);
|
||||
|
||||
NCurses.MoveWindow (inputFieldWin, inputFieldWinY, inputFieldWinX);
|
||||
NCurses.SetCursor (1);
|
||||
NCurses.Echo ();
|
||||
|
||||
var userInput = "";
|
||||
InputHandler.KeypressEventHandler handlerFunction = (object sender, NCursesKeyPressEventArgs e) => {
|
||||
if (e.KeyCode == '\n' || e.KeyCode == CursesKey.ENTER) {
|
||||
callback (userInput);
|
||||
}
|
||||
|
||||
if (e.KeyCode == '\n' || e.KeyCode == CursesKey.ENTER || e.KeyCode == CursesKey.ESC) {
|
||||
NCurses.SetCursor (0);
|
||||
NCurses.NoEcho ();
|
||||
|
||||
//inputHandler.DisableRawEventHandler (screen);
|
||||
|
||||
NCurses.MoveWindow (screen, 0, 0);
|
||||
NCurses.DeleteWindow (inputFieldWin);
|
||||
NCurses.DeleteWindow (questionTextWin);
|
||||
NCurses.DeleteWindow (boxWin);
|
||||
NCurses.Refresh ();
|
||||
} else if (e.KeyCode == CursesKey.BACKSPACE) {
|
||||
userInput = userInput.Substring (0, userInput.Length - 2);
|
||||
} else if (char.IsAscii ((char) e.KeyCode)) {
|
||||
userInput += (char) e.KeyCode;
|
||||
}
|
||||
|
||||
NCurses.MoveWindow (inputFieldWin, inputFieldWinY, inputFieldWinX);
|
||||
};
|
||||
|
||||
//inputHandler.EnableRawEventHandler (handlerFunction, inputFieldWin);
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
namespace ANSI_Fahrplan.UiElements;
|
||||
|
||||
public abstract class UiElement {
|
||||
private nint _screen;
|
||||
public nint Screen { get { return _screen; }}
|
||||
|
||||
protected nint contentWindow { get; set; }
|
||||
|
||||
public UiElement (nint screen) {
|
||||
_screen = screen;
|
||||
}
|
||||
}
|
Loading…
Reference in new issue