using System.Collections.ObjectModel; using System.Drawing; using Mindmagma.Curses; namespace SCI.CursesWrapper; public class Window { public const int InnerPadding = 1; /// /// Gets or sets the window position on the screen /// private Point _position; public Point Position { get { return _position; } set { if (ParentWindow is not null) { int addedPadding = ParentWindow.BorderEnabled? InnerPadding + 1 : 0; NCurses.WindowMove ( WindowId, ParentWindow.Position.Y + value.Y + addedPadding, ParentWindow.Position.X + value.X + addedPadding ); } else { NCurses.WindowMove ( WindowId, value.Y, value.X ); } _position = value; } } /// /// Gets or sets the width and height of the window /// private Size _windowSize; public Size WindowSize { get { return _windowSize; } set { _windowSize = value; } } /// /// Gets or sets the window background color pair /// private uint _backgroundColorId; public uint BackgroundColorId { get { return _backgroundColorId; } set { NCurses.WindowBackground (WindowId, value); Draw (); _backgroundColorId = value; } } private bool _borderEnabled; public bool BorderEnabled { get { return _borderEnabled; } set { SetBorder (value); Draw (); _borderEnabled = value; } } /// /// Sets the parent window /// private Window? _parentWindow; public Window? ParentWindow { get { return _parentWindow; }} /// /// Holds a list of children windows this window posesses /// private List _childWindows = new List (); public ReadOnlyCollection ChildWindows { get { return _childWindows.AsReadOnly (); }} /// /// Holds the pointer for this window /// private nint _windowId; public nint WindowId { get { return _windowId; }} /// /// Input handler assigned to this window /// protected InputHandler? _targetInputHandler; public InputHandler? TargetInputHandler { get { return _targetInputHandler; }} /// /// Event handler called when this window is active and a key is pressed /// public event InputHandler.KeypressEventHandler? OnKeyPress; /// /// Create new window by specifying X/Y and Width/Height geometry /// /// /// /// /// /// public Window (int x, int y, int width, int height, Window? parentWindow = null) { _Initialize (new Point (x, y), new Size (width, height), parentWindow); } /// /// Create new window by specifying geometry through Point and Size objects /// /// /// /// public Window (Point position, Size windowSize, Window? parentWindow = null) { _Initialize (position, windowSize, parentWindow); } /// /// Create new window by specifying X/Y and Width/Height geometry /// /// /// /// /// /// /// public Window (int x, int y, int width, int height, InputHandler targetInputHandler, Window? parentWindow = null) { _Initialize (new Point (x, y), new Size (width, height), parentWindow); RegisterInputHandler (targetInputHandler); SetWindowActive (); } /// /// Create new window by specifying geometry through Point and Size objects /// /// /// /// /// public Window (Point position, Size windowSize, InputHandler targetInputHandler, Window? parentWindow = null) { _Initialize (position, windowSize, parentWindow); RegisterInputHandler (targetInputHandler); SetWindowActive (); } /// /// Actual initialization function for this class /// /// /// /// private void _Initialize (Point position, Size windowSize, Window? parentWindow = null) { if (parentWindow is not null) { int addedPadding = parentWindow.BorderEnabled? InnerPadding + 1 : 0; _windowId = NCurses.DeriveWindow ( parentWindow.WindowId, windowSize.Height, windowSize.Width, position.Y + addedPadding, position.X + addedPadding ); parentWindow.AddChildWindow (this); } else { _windowId = NCurses.NewWindow ( windowSize.Height, windowSize.Width, position.Y, position.X ); } _position = position; _windowSize = windowSize; _parentWindow = parentWindow; NCurses.Keypad (WindowId, true); Draw (); } /// /// Destroys the window and its sub-windows when the Window object is destroyed /// ~Window () { Destroy (); } /// /// Adds a child window to this window /// /// public void AddChildWindow (Window child) { if (_childWindows.Contains (child)) return; _childWindows.Add (child); } /// /// Removes a child window from this window /// /// public void RemoveChildWindow (Window child) { _childWindows.Remove (child); } /// /// Discards all optimization options about drawn parts of this window. /// Call before drawing a sub window /// public void TouchWin () { NCurses.TouchWindow (WindowId); } /// /// Draws this window and all sub windows /// public void Draw () { NCurses.Refresh (); // TODO: Necessary? foreach (var window in ChildWindows) { window.Draw (); } if (ParentWindow is not null) ParentWindow.TouchWin (); NCurses.WindowRefresh (WindowId); } /// /// Destroys this window and all children windows /// public void Destroy () { // Catch double destroy calls if (WindowId == -1) throw new Exception ("Destroy called twice on object"); foreach (var window in _childWindows.ToList ()) { window.Destroy (); } UnregisterInputHandler (); // Prepare for screen clear NCurses.WindowBackground (WindowId, NCurses.ColorPair (-1)); SetBorder (false); // Clear window and redraw NCurses.ClearWindow (WindowId); Draw (); NCurses.DeleteWindow (WindowId); _windowId = -1; // Ensures sure the parent is updated too if (ParentWindow is not null) { ParentWindow.RemoveChildWindow (this); ParentWindow.Draw (); } } /// /// Register an input handler for this window to attach to OnKeyPress events /// /// InputHandler to register public void RegisterInputHandler (InputHandler inputHandler) { if (TargetInputHandler is not null) throw new Exception ( "Another input handler is already registered" ); _targetInputHandler = inputHandler; TargetInputHandler!.OnKeyPress += KeyPressHandler; } /// /// Detach from all OnKeyPress events and unset input handler /// public void UnregisterInputHandler () { if (TargetInputHandler is null) return; if (TargetInputHandler.ActiveWindow == this) TargetInputHandler.ActiveWindow = null; TargetInputHandler.OnKeyPress -= KeyPressHandler; } /// /// Handle key press events from the input handler /// /// /// private void KeyPressHandler (object sender, NCursesKeyPressEventArgs e) { if (e.SourceWindow != this) return; if (OnKeyPress is not null) { OnKeyPress (this, e); } } /// /// Tells the input handler this window is active /// public void SetWindowActive () { if (TargetInputHandler is null) return; TargetInputHandler.ActiveWindow = this; Draw (); } /// /// Enables or disables a border around this window /// /// Sets the status of the border /// If specified, uses this character as the top and bottom border /// If specified, uses this character as the left and right border public void SetBorder (bool enabled, char? horizontalChar = null, char? verticalChar = null) { if (horizontalChar is null) horizontalChar = (char) 0; if (verticalChar is null) verticalChar = (char) 0; if (enabled) { NCurses.Box (WindowId, (char) horizontalChar, (char) verticalChar); } else { NCurses.Box (WindowId, ' ', ' '); } } /// /// Calcaulates the usable inner width of this window /// /// Usable inner width of window in columns public int GetUsableWidth () { if (BorderEnabled) { return WindowSize.Width - 1 - InnerPadding; } else { return WindowSize.Width; } } /// /// Calcaulates the usable inner height of this window /// /// Usable inner height of window in rows public int GetUsableHeight () { if (BorderEnabled) { return WindowSize.Height - 1 - InnerPadding; } else { return WindowSize.Height; } } }