Merge pull request #552 from tig/drawwindowframe

New "Draw Window Frame"
This commit is contained in:
Charlie Kindel
2020-05-27 11:39:07 -06:00
committed by GitHub
7 changed files with 221 additions and 142 deletions

View File

@@ -798,14 +798,14 @@ namespace Terminal.Gui {
}
// Converts a rectangle in view coordinates to screen coordinates.
Rect RectToScreen (Rect rect)
internal Rect RectToScreen (Rect rect)
{
ViewToScreen (rect.X, rect.Y, out var x, out var y, clipped: false);
return new Rect (x, y, rect.Width, rect.Height);
}
// Clips a rectangle in screen coordinates to the dimensions currently available on the screen
Rect ScreenClip (Rect rect)
internal Rect ScreenClip (Rect rect)
{
var x = rect.X < 0 ? 0 : rect.X;
var y = rect.Y < 0 ? 0 : rect.Y;
@@ -1082,7 +1082,7 @@ namespace Terminal.Gui {
/// Constructs.
/// </summary>
/// <param name="ke"></param>
public KeyEventEventArgs(KeyEvent ke) => KeyEvent = ke;
public KeyEventEventArgs (KeyEvent ke) => KeyEvent = ke;
/// <summary>
/// The <see cref="KeyEvent"/> for the event.
/// </summary>
@@ -1683,13 +1683,13 @@ namespace Terminal.Gui {
nx = Math.Max (x, 0);
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))
if (SuperView == null || SuperView.GetType () != typeof (Toplevel))
m = Application.Top.MenuBar != null;
else
m = ((Toplevel)SuperView).MenuBar != null;
int l = m ? 1 : 0;
ny = Math.Max (y, l);
if (SuperView == null || SuperView.GetType() != typeof(Toplevel))
if (SuperView == null || SuperView.GetType () != typeof (Toplevel))
s = Application.Top.StatusBar != null;
else
s = ((Toplevel)SuperView).StatusBar != null;
@@ -1867,11 +1867,6 @@ namespace Terminal.Gui {
return contentView.GetEnumerator ();
}
void DrawFrame (bool fill = true)
{
DrawFrame (new Rect (0, 0, Frame.Width, Frame.Height), padding, fill: fill);
}
/// <summary>
/// Add the specified view to the <see cref="ContentView"/>.
/// </summary>
@@ -1916,30 +1911,24 @@ namespace Terminal.Gui {
public override void Redraw (Rect bounds)
{
Application.CurrentView = this;
var scrRect = RectToScreen (new Rect (0, 0, Frame.Width, Frame.Height));
var savedClip = Driver.Clip;
Driver.Clip = ScreenClip (RectToScreen (Bounds));
if (NeedDisplay != null && !NeedDisplay.IsEmpty) {
DrawFrameWindow ();
Driver.SetAttribute (ColorScheme.Normal);
Driver.DrawFrame (scrRect, padding, true);
}
contentView.Redraw (contentView.Bounds);
ClearNeedsDisplay ();
DrawFrameWindow (false);
Driver.SetAttribute (ColorScheme.Normal);
Driver.DrawFrame (scrRect, padding, false);
void DrawFrameWindow (bool fill = true)
{
Driver.SetAttribute (ColorScheme.Normal);
DrawFrame (fill);
if (HasFocus)
Driver.SetAttribute (ColorScheme.HotNormal);
var width = Frame.Width - (padding + 2) * 2;
if (Title != null && width > 4) {
Move (1 + padding, padding);
Driver.AddRune (' ');
var str = Title.Length >= width ? Title [0, width - 2] : Title;
Driver.AddStr (str);
Driver.AddRune (' ');
}
Driver.SetAttribute (ColorScheme.Normal);
}
if (HasFocus)
Driver.SetAttribute (ColorScheme.HotNormal);
Driver.DrawWindowTitle (scrRect, Title, padding, padding, padding, padding);
Driver.Clip = savedClip;
Driver.SetAttribute (ColorScheme.Normal);
}
//
@@ -2415,7 +2404,7 @@ namespace Terminal.Gui {
if (toplevel.LayoutStyle == LayoutStyle.Computed)
toplevel.RelativeLayout (new Rect (0, 0, Driver.Cols, Driver.Rows));
toplevel.LayoutSubviews ();
Loaded?.Invoke (null, new ResizedEventArgs () { Rows = Driver.Rows, Cols = Driver.Cols } );
Loaded?.Invoke (null, new ResizedEventArgs () { Rows = Driver.Rows, Cols = Driver.Cols });
toplevel.WillPresent ();
Redraw (toplevel);
toplevel.PositionCursor ();

View File

@@ -8,6 +8,7 @@ using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using Mono.Terminal;
using NStack;
using Unix.Terminal;
@@ -525,91 +526,173 @@ namespace Terminal.Gui {
TerminalResized = terminalResized;
}
// Useful for debugging (e.g. change to `*`
const char clearChar = ' ';
/// <summary>
/// Draws the title for a Window-style view incorporating padding.
/// </summary>
/// <param name="region">Screen relative region where the frame will be drawn.</param>
/// <param name="title">The title for the window. The title will only be drawn if <c>title</c> is not null or empty and paddingTop is greater than 0.</param>
/// <param name="paddingLeft">Number of columns to pad on the left (if 0 the border will not appear on the left).</param>
/// <param name="paddingTop">Number of rows to pad on the top (if 0 the border and title will not appear on the top).</param>
/// <param name="paddingRight">Number of columns to pad on the right (if 0 the border will not appear on the right).</param>
/// <param name="paddingBottom">Number of rows to pad on the bottom (if 0 the border will not appear on the bottom).</param>
/// <param name="textAlignment">Not yet immplemented.</param>
/// <remarks></remarks>
public virtual void DrawWindowTitle (Rect region, ustring title, int paddingLeft, int paddingTop, int paddingRight, int paddingBottom, TextAlignment textAlignment = TextAlignment.Left)
{
var width = region.Width - (paddingLeft + 2) * 2;
if (!ustring.IsNullOrEmpty(title) && width > 4 && region.Y + paddingTop <= region.Y + paddingBottom) {
Move (region.X + 1 + paddingLeft, region.Y + paddingTop);
AddRune (' ');
var str = title.Length >= width ? title [0, width - 2] : title;
AddStr (str);
AddRune (' ');
}
}
/// <summary>
/// Draws a frame for a window with padding aand n optional visible border inside the padding.
/// </summary>
/// <param name="region">Screen relative region where the frame will be drawn.</param>
/// <param name="paddingLeft">Number of columns to pad on the left (if 0 the border will not appear on the left).</param>
/// <param name="paddingTop">Number of rows to pad on the top (if 0 the border and title will not appear on the top).</param>
/// <param name="paddingRight">Number of columns to pad on the right (if 0 the border will not appear on the right).</param>
/// <param name="paddingBottom">Number of rows to pad on the bottom (if 0 the border will not appear on the bottom).</param>
/// <param name="border">If set to <c>true</c> and any padding dimension is > 0 the border will be drawn.</param>
/// <param name="fill">If set to <c>true</c> it will clear the content area (the area inside the padding) with the current color, otherwise the content area will be left untouched.</param>
public virtual void DrawWindowFrame (Rect region, int paddingLeft = 0, int paddingTop = 0, int paddingRight = 0, int paddingBottom = 0, bool border = true, bool fill = false)
{
void AddRuneAt (int col, int row, Rune ch)
{
Move (col, row);
AddRune (ch);
}
int fwidth = (int)(region.Width - (paddingRight + paddingLeft));
int fheight = (int)(region.Height - (paddingBottom + paddingTop));
int fleft = region.X + paddingLeft;
int fright = fleft + fwidth + 1;
int ftop = region.Y + paddingTop;
int fbottom = ftop + fheight + 1;
Rune hLine = border ? HLine : clearChar;
Rune vLine = border ? VLine : clearChar;
Rune uRCorner = border ? URCorner : clearChar;
Rune uLCorner = border ? ULCorner : clearChar;
Rune lLCorner = border ? LLCorner : clearChar;
Rune lRCorner = border ? LRCorner : clearChar;
// Outside top
if (paddingTop > 1) {
for (int r = region.Y; r < ftop; r++) {
for (int c = region.X; c <= fright + paddingRight; c++) {
AddRuneAt (c, r, clearChar);
}
}
}
// Outside top-left
for (int c = region.X; c <= fleft; c++) {
AddRuneAt (c, ftop, clearChar);
}
// Frame top-left corner
AddRuneAt (fleft, ftop, paddingTop >= 0 ? (paddingLeft >= 0 ? uLCorner : hLine) : clearChar);
// Frame top
for (int c = fleft + 1; c <= fright; c++) {
AddRuneAt (c, ftop, paddingTop > 0 ? hLine : clearChar);
}
// Frame top-right corner
if (fright > fleft) {
AddRuneAt (fright, ftop, paddingTop >= 0 ? (paddingRight >= 0 ? uRCorner : hLine) : clearChar);
}
// Outside top-right corner
for (int c = fright + 1; c < fright + paddingRight; c++) {
AddRuneAt (c, ftop, clearChar);
}
// Left, Fill, Right
if (fbottom > ftop) {
for (int r = ftop + 1; r < fbottom; r++) {
// Outside left
for (int c = region.X; c < fleft; c++) {
AddRuneAt (c, r, clearChar);
}
// Frame left
AddRuneAt (fleft, r, paddingLeft > 0 ? vLine : clearChar);
// Fill
if (fill) {
for (int x = fleft + 1; x < fright; x++) {
AddRuneAt (x, r, clearChar);
}
}
// Frame right
if (fright > fleft) {
AddRuneAt (fright, r, paddingRight > 0 ? vLine : clearChar);
}
// Outside right
for (int c = fright + 1; c < fright + paddingRight; c++) {
AddRuneAt (c, r, clearChar);
}
}
// Outside Bottom
for (int c = region.X; c < fleft; c++) {
AddRuneAt (c, fbottom, clearChar);
}
// Frame bottom-left
AddRuneAt (fleft, fbottom, paddingLeft > 0 ? lLCorner : clearChar);
if (fright > fleft) {
// Frame bottom
for (int c = fleft + 1; c < fright; c++) {
AddRuneAt (c, fbottom, paddingBottom > 0 ? hLine : clearChar);
}
// Frame bottom-right
AddRuneAt (fright, fbottom, paddingRight > 0 ? (paddingBottom > 0 ? lRCorner : hLine) : clearChar);
}
// Outside right
for (int c = fright + 1; c < fright + paddingRight; c++) {
AddRuneAt (c, fbottom, clearChar);
}
}
// Out bottom - ensure top is always drawn if we overlap
if (paddingBottom > 0) {
for (int r = fbottom + 1; r < fbottom + paddingBottom; r++) {
for (int c = region.X; c <= fright + paddingRight; c++) {
AddRuneAt (c, r, clearChar);
}
}
}
}
/// <summary>
/// Draws a frame on the specified region with the specified padding around the frame.
/// </summary>
/// <param name="region">Region where the frame will be drawn..</param>
/// <param name="padding">Padding to add on the sides.</param>
/// <param name="fill">If set to <c>true</c> it will clear the contents with the current color, otherwise the contents will be left untouched.</param>
/// <remarks>This is a legacy/depcrecated API. Use <see cref="DrawWindowFrame(Rect, int, int, int, int, bool, bool)"/>.</remarks>
/// <remarks>A padding value of 0 means there is actually a 1 cell border.</remarks>
public virtual void DrawFrame (Rect region, int padding, bool fill)
{
int width = region.Width;
int height = region.Height;
int b;
int fwidth = width - padding * 2;
int fheight = height - 1 - padding;
Move (region.X, region.Y);
if (padding > 0) {
for (int l = 0; l < padding; l++)
for (b = region.X; b < region.X + width; b++) {
AddRune (' ');
Move (b + 1, region.Y);
}
}
Move (region.X, region.Y + padding);
for (int c = 0; c < padding; c++) {
AddRune (' ');
Move (region.X + 1, region.Y + padding);
}
AddRune (ULCorner);
for (b = region.X; b < region.X + fwidth - 2; b++) {
AddRune (HLine);
Move (b + (padding > 0 ? padding + 2 : 2), region.Y + padding);
}
AddRune (URCorner);
for (int c = 0; c < padding; c++) {
AddRune (' ');
Move (region.X + 1, region.Y + padding);
}
for (b = 1 + padding; b < fheight; b++) {
Move (region.X, region.Y + b);
for (int c = 0; c < padding; c++) {
AddRune (' ');
Move (region.X + 1, region.Y + b);
}
AddRune (VLine);
if (fill) {
for (int x = region.X + 1; x < region.X + fwidth - 1; x++) {
AddRune (' ');
Move (x + (padding > 0 ? padding + 1 : 1), region.Y + b);
}
} else {
if (padding > 0)
Move (region.X + fwidth, region.Y + b);
else
Move (region.X + fwidth - 1, region.Y + b);
}
AddRune (VLine);
for (int c = 0; c < padding; c++) {
AddRune (' ');
Move (region.X + 1, region.Y + b);
}
}
Move (region.X, region.Y + fheight);
for (int c = 0; c < padding; c++) {
AddRune (' ');
Move (region.X + 1, region.Y + b);
}
AddRune (LLCorner);
for (b = region.X; b < region.X + fwidth - 2; b++) {
AddRune (HLine);
Move (b + (padding > 0 ? padding + 2 : 2), region.Y + fheight);
}
AddRune (LRCorner);
for (int c = 0; c < padding; c++) {
AddRune (' ');
Move (region.X + 1, region.Y);
}
if (padding > 0) {
Move (region.X, region.Y + height - padding);
for (int l = 0; l < padding; l++) {
for (b = region.X; b < region.X + width; b++) {
AddRune (' ');
Move (b + 1, region.Y + height - padding);
}
}
}
// DrawFrame assumes the frame is always at least one row/col thick
// DrawWindowFrame assumes a padding of 0 means NO padding
padding++;
DrawWindowFrame (new Rect (region.X - 1, region.Y - 1, region.Width, region.Height), padding, padding, padding, padding, fill: fill);
}

View File

@@ -134,23 +134,26 @@ namespace Terminal.Gui {
///<inheritdoc cref="Redraw(Rect)"/>
public override void Redraw (Rect bounds)
{
if (!NeedDisplay.IsEmpty) {
Driver.SetAttribute (ColorScheme.Normal);
DrawFrame ();
if (HasFocus)
Driver.SetAttribute (ColorScheme.Normal);
var width = Frame.Width;
if (Title != null && width > 4) {
Move (1, 0);
Driver.AddRune (' ');
var str = Title.Length > width ? Title [0, width - 4] : Title;
Driver.AddStr (str);
Driver.AddRune (' ');
}
var padding = 0;
Application.CurrentView = this;
var scrRect = RectToScreen (new Rect (0, 0, Frame.Width, Frame.Height));
var savedClip = Driver.Clip;
Driver.Clip = ScreenClip (RectToScreen (Bounds));
if (NeedDisplay != null && !NeedDisplay.IsEmpty) {
Driver.SetAttribute (ColorScheme.Normal);
Driver.DrawFrame (scrRect, padding, true);
}
contentView.Redraw (contentView.Bounds);
ClearNeedsDisplay ();
Driver.SetAttribute (ColorScheme.Normal);
Driver.DrawFrame (scrRect, padding, false);
if (HasFocus)
Driver.SetAttribute (ColorScheme.HotNormal);
Driver.DrawWindowTitle (scrRect, Title, padding, padding, padding, padding);
Driver.Clip = savedClip;
Driver.SetAttribute (ColorScheme.Normal);
}
}
}

View File

@@ -73,10 +73,10 @@ namespace UICatalog {
string txt = "Resize the terminal to see computed layout in action.";
var labelList = new List<Label> ();
labelList.Add (new Label ($"The lines below show different TextAlignments"));
labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Left, Width = Dim.Fill (1), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()), ColorScheme = Colors.Dialog });
labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Right, Width = Dim.Fill (1), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()), ColorScheme = Colors.Dialog });
labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Centered, Width = Dim.Fill (1), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()), ColorScheme = Colors.Dialog });
labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Justified, Width = Dim.Fill (1), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()), ColorScheme = Colors.Dialog });
labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Left, Width = Dim.Fill (), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()), ColorScheme = Colors.Dialog });
labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Right, Width = Dim.Fill (), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()), ColorScheme = Colors.Dialog });
labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Centered, Width = Dim.Fill (), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()), ColorScheme = Colors.Dialog });
labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Justified, Width = Dim.Fill (), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()), ColorScheme = Colors.Dialog });
subWin.Add (labelList.ToArray ());

View File

@@ -23,7 +23,7 @@ namespace UICatalog {
internal ProgressBar PulseProgressBar { get; private set; }
internal Action StartBtnClick;
internal Action StopBtnClick;
internal Action PulseBtnClick;
internal Action PulseBtnClick = null;
private Label _startedLabel;
internal bool Started {
get {

View File

@@ -28,15 +28,15 @@ namespace UICatalog {
public override void Setup ()
{
int margin = 3;
int padding = 1;
int margin = 2;
int padding = 0;
int height = 10;
var listWin = new List<View> ();
Win = new Window ($"{listWin.Count} - Scenario: {GetName ()}", padding) {
X = margin,
Y = margin,
Width = Dim.Fill (margin),
Height = height,
X = Pos.Center (),
Y = 1,
Width = Dim.Fill (10),
Height = Dim.Percent (20),
};
Win.ColorScheme = Colors.Dialog;
Win.Add (new Button ("Press me!") {
@@ -52,7 +52,7 @@ namespace UICatalog {
Window win = null;
win = new Window ($"{listWin.Count} - Scenario: {GetName ()}", padding) {
X = margin,
Y = Pos.Bottom(listWin.Last()) + (margin/2),
Y = Pos.Bottom (listWin.Last ()) + (margin),
Width = Dim.Fill (margin),
Height = height,
};
@@ -63,17 +63,19 @@ namespace UICatalog {
ColorScheme = Colors.Error,
Clicked = () => MessageBox.ErrorQuery (30, 10, win.Title.ToString (), "Neat?", "Yes", "No")
});
var subWin = new Window("Sub Window") {
var subWin = new Window ("Sub Window") {
X = Pos.Percent (0),
Y = Pos.AnchorEnd() - 5,
Y = Pos.AnchorEnd () - 5,
Width = Dim.Percent (50),
Height = 5,
ColorScheme = Colors.Base,
};
subWin.Add (new TextField (win.Title.ToString ()));
subWin.Add (new TextField (win.Title.ToString ()) {
ColorScheme = Colors.Error
});
win.Add (subWin);
var frameView = new FrameView ("This is a Sub-FrameView") {
X = Pos.Percent(50),
X = Pos.Percent (50),
Y = Pos.AnchorEnd () - 5,
Width = Dim.Percent (100),
Height = 5,
@@ -108,13 +110,15 @@ namespace UICatalog {
Height = Dim.Fill () - 1,
ColorScheme = Colors.Base,
};
subWinFV.Add (new TextField (frame.Title.ToString ()));
subWinFV.Add (new TextField (frame.Title.ToString ()) {
ColorScheme = Colors.Error
});
frame.Add (subWinFV);
var frameViewFV = new FrameView ("this is a Sub-FrameView") {
X = Pos.Percent (50),
Y = Pos.AnchorEnd () - (height - 4),
Width = Dim.Percent (100),
Height = Dim.Fill() - 1,
Height = Dim.Fill () - 1,
ColorScheme = Colors.Base,
};
frameViewFV.Add (new TextField ("Edit Me"));
@@ -124,12 +128,12 @@ namespace UICatalog {
// it's client area. #522
frameViewFV.Add (new CheckBox (0, 2, "Or, Check me"));
frame.Add (new CheckBox ("No, Check me!") {
frame.Add (new CheckBox ("No, Check me!") {
X = 0,
Y = Pos.AnchorEnd() - 1, // BUGBUG: #522 If I don't do the -1 it doesn't draw, but it should!
Y = Pos.AnchorEnd () - 1, // BUGBUG: #522 If I don't do the -1 it doesn't draw, but it should!
});
frame.Add (new CheckBox ("Really, Check me!") {
X = Pos.Percent(50),
X = Pos.Percent (50),
Y = Pos.AnchorEnd () - 1, // BUGBUG: #522 If I don't do the -1 it doesn't draw, but it should!
});

View File

@@ -104,10 +104,10 @@ namespace UICatalog {
_categories = Scenario.GetAllCategories ();
_categoryListView = new ListView (_categories) {
X = 1,
X = 0,
Y = 0,
Width = Dim.Fill (0),
Height = Dim.Fill (2),
Height = Dim.Fill (0),
AllowsMarking = false,
CanFocus = true,
};