Fixing many clipping issues (#580)

* almost got clip rect working

* fixes many bugs related to clipping incl #399

* Merge tweaks.
This commit is contained in:
Charlie Kindel
2020-05-29 17:41:49 -06:00
committed by GitHub
parent bd999a4823
commit 1024f073b2
13 changed files with 278 additions and 96 deletions

View File

@@ -4,10 +4,13 @@
// Authors:
// Miguel de Icaza (miguel@gnome.org)
//
// Define this to enable diagnostics drawing for Window Frames
//#define DRAW_WINDOW_FRAME_DIAGNOSTICS
using NStack;
using System;
using System.Runtime.CompilerServices;
namespace Terminal.Gui {
/// <summary>
@@ -547,6 +550,17 @@ namespace Terminal.Gui {
}
}
#if DRAW_WINDOW_FRAME_DIAGNOSTICS
const char leftChar = 'L';
const char rightChar = 'R';
const char topChar = 'T';
const char bottomChar = 'B';
#else
const char leftChar = clearChar;
const char rightChar = clearChar;
const char topChar = clearChar;
const char bottomChar = clearChar;
#endif
/// <summary>
/// Draws a frame for a window with padding aand n optional visible border inside the padding.
/// </summary>
@@ -565,11 +579,22 @@ namespace Terminal.Gui {
AddRune (ch);
}
// fwidth is count of hLine chars
int fwidth = (int)(region.Width - (paddingRight + paddingLeft));
// fheight is count of vLine chars
int fheight = (int)(region.Height - (paddingBottom + paddingTop));
int fleft = region.X + paddingLeft;
// fleft is location of left frame line
int fleft = region.X + paddingLeft - 1;
// fright is location of right frame line
int fright = fleft + fwidth + 1;
int ftop = region.Y + paddingTop;
// ftop is location of top frame line
int ftop = region.Y + paddingTop - 1;
// fbottom is locaiton of bottom frame line
int fbottom = ftop + fheight + 1;
Rune hLine = border ? HLine : clearChar;
@@ -582,33 +607,33 @@ namespace Terminal.Gui {
// 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);
for (int c = region.X; c < region.X + region.Width; c++) {
AddRuneAt (c, r, topChar);
}
}
}
// Outside top-left
for (int c = region.X; c <= fleft; c++) {
AddRuneAt (c, ftop, clearChar);
for (int c = region.X; c < fleft; c++) {
AddRuneAt (c, ftop, leftChar);
}
// Frame top-left corner
AddRuneAt (fleft, ftop, paddingTop >= 0 ? (paddingLeft >= 0 ? uLCorner : hLine) : clearChar);
AddRuneAt (fleft, ftop, paddingTop >= 0 ? (paddingLeft >= 0 ? uLCorner : hLine) : leftChar);
// Frame top
for (int c = fleft + 1; c <= fright; c++) {
AddRuneAt (c, ftop, paddingTop > 0 ? hLine : clearChar);
for (int c = fleft + 1; c < fleft + 1 + fwidth; c++) {
AddRuneAt (c, ftop, paddingTop > 0 ? hLine : topChar);
}
// Frame top-right corner
if (fright > fleft) {
AddRuneAt (fright, ftop, paddingTop >= 0 ? (paddingRight >= 0 ? uRCorner : hLine) : clearChar);
AddRuneAt (fright, ftop, paddingTop >= 0 ? (paddingRight >= 0 ? uRCorner : hLine) : rightChar);
}
// Outside top-right corner
for (int c = fright + 1; c < fright + paddingRight; c++) {
AddRuneAt (c, ftop, clearChar);
AddRuneAt (c, ftop, rightChar);
}
// Left, Fill, Right
@@ -616,11 +641,11 @@ namespace Terminal.Gui {
for (int r = ftop + 1; r < fbottom; r++) {
// Outside left
for (int c = region.X; c < fleft; c++) {
AddRuneAt (c, r, clearChar);
AddRuneAt (c, r, leftChar);
}
// Frame left
AddRuneAt (fleft, r, paddingLeft > 0 ? vLine : clearChar);
AddRuneAt (fleft, r, paddingLeft > 0 ? vLine : leftChar);
// Fill
if (fill) {
@@ -631,44 +656,54 @@ namespace Terminal.Gui {
// Frame right
if (fright > fleft) {
AddRuneAt (fright, r, paddingRight > 0 ? vLine : clearChar);
#if DRAW_WINDOW_FRAME_DIAGNOSTICS
var v = (char)(((int)'0') + ((r - ftop) % 10)); // vLine;
#else
var v = vLine;
#endif
AddRuneAt (fright, r, paddingRight > 0 ? v : rightChar);
}
// Outside right
for (int c = fright + 1; c < fright + paddingRight; c++) {
AddRuneAt (c, r, clearChar);
AddRuneAt (c, r, rightChar);
}
}
// Outside Bottom
for (int c = region.X; c < fleft; c++) {
AddRuneAt (c, fbottom, clearChar);
for (int c = region.X; c < region.X + region.Width; c++) {
AddRuneAt (c, fbottom, leftChar);
}
// Frame bottom-left
AddRuneAt (fleft, fbottom, paddingLeft > 0 ? lLCorner : clearChar);
AddRuneAt (fleft, fbottom, paddingLeft > 0 ? lLCorner : leftChar);
if (fright > fleft) {
// Frame bottom
for (int c = fleft + 1; c < fright; c++) {
AddRuneAt (c, fbottom, paddingBottom > 0 ? hLine : clearChar);
#if DRAW_WINDOW_FRAME_DIAGNOSTICS
var h = (char)(((int)'0') + ((c - fleft) % 10)); // hLine;
#else
var h = hLine;
#endif
AddRuneAt (c, fbottom, paddingBottom > 0 ? h : bottomChar);
}
// Frame bottom-right
AddRuneAt (fright, fbottom, paddingRight > 0 ? (paddingBottom > 0 ? lRCorner : hLine) : clearChar);
AddRuneAt (fright, fbottom, paddingRight > 0 ? (paddingBottom > 0 ? lRCorner : hLine) : rightChar);
}
// Outside right
for (int c = fright + 1; c < fright + paddingRight; c++) {
AddRuneAt (c, fbottom, clearChar);
AddRuneAt (c, fbottom, rightChar);
}
}
// 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);
for (int c = region.X; c < region.X + region.Width; c++) {
AddRuneAt (c, r, bottomChar);
}
}
}
@@ -677,17 +712,19 @@ namespace Terminal.Gui {
/// <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="region">Screen relative 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>
/// <remarks>This API has been superceded by <see cref="DrawWindowFrame(Rect, int, int, int, int, bool, bool)"/>.</remarks>
/// <remarks>This API is equivlalent to calling <c>DrawWindowFrame(Rect, p - 1, p - 1, p - 1, p - 1)</c>. In other words,
/// A padding value of 0 means there is actually a one cell border.
/// </remarks>
public virtual void DrawFrame (Rect region, int padding, bool fill)
{
// 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);
// DrawFrame assumes the border is always at least one row/col thick
// DrawWindowFrame assumes a padding of 0 means NO padding and no frame
DrawWindowFrame (new Rect (region.X, region.Y, region.Width, region.Height),
padding + 1, padding + 1, padding + 1, padding + 1, fill: fill);
}

View File

@@ -665,7 +665,7 @@ namespace Terminal.Gui {
{
var bscreen = RectToScreen (rect);
var previous = Driver.Clip;
Driver.Clip = ScreenClip (RectToScreen (Bounds));
Driver.Clip = ScreenClip (RectToScreen (Bounds));
return previous;
}
@@ -678,8 +678,7 @@ namespace Terminal.Gui {
public void DrawFrame (Rect rect, int padding = 0, bool fill = false)
{
var scrRect = RectToScreen (rect);
var savedClip = Driver.Clip;
Driver.Clip = ScreenClip (RectToScreen (Bounds));
var savedClip = ClipToBounds ();
Driver.DrawFrame (scrRect, padding, fill);
Driver.Clip = savedClip;
}
@@ -890,7 +889,15 @@ namespace Terminal.Gui {
if (view.layoutNeeded)
view.LayoutSubviews ();
Application.CurrentView = view;
view.Redraw (view.Bounds);
// Ensure we don't make the Driver's clip rect any bigger
if (Driver.Clip.IsEmpty || Driver.Clip.Contains(RectToScreen (view.Frame))) {
var savedClip = view.ClipToBounds ();
view.Redraw (view.Bounds);
Driver.Clip = savedClip;
} else {
view.Redraw (view.Bounds);
}
}
view.NeedDisplay = Rect.Empty;
view.childNeedsDisplay = false;

View File

@@ -40,6 +40,7 @@ namespace Terminal.Gui {
Driver.AddRune ('x');
}
}
base.Redraw (region);
}
#endif
}
@@ -153,16 +154,23 @@ namespace Terminal.Gui {
///<inheritdoc cref="Redraw"/>
public override void Redraw (Rect bounds)
{
//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));
var scrRect = RectToScreen (new Rect(0, 0, Frame.Width, Frame.Height));
// BUGBUG: Why do we draw the frame twice? This call is here to clear the content area, I think. Why not just clear that area?
if (NeedDisplay != null && !NeedDisplay.IsEmpty) {
Driver.SetAttribute (ColorScheme.Normal);
Driver.DrawFrame (scrRect, padding, true);
}
contentView.Redraw (contentView.Bounds);
if (Driver.Clip.IsEmpty || Driver.Clip.Contains (contentView.RectToScreen (contentView.Frame))) {
var savedClip = ClipToBounds ();
contentView.Redraw (contentView.Bounds);
Driver.Clip = savedClip;
} else {
contentView.Redraw (contentView.Bounds);
}
ClearNeedsDisplay ();
Driver.SetAttribute (ColorScheme.Normal);
Driver.DrawFrame (scrRect, padding, false);
@@ -170,7 +178,6 @@ namespace Terminal.Gui {
if (HasFocus)
Driver.SetAttribute (ColorScheme.HotNormal);
Driver.DrawWindowTitle (scrRect, Title, padding, padding, padding, padding);
Driver.Clip = savedClip;
Driver.SetAttribute (ColorScheme.Normal);
}

View File

@@ -39,6 +39,7 @@ namespace Terminal.Gui {
get => is_default;
set {
is_default = value;
SetWidthHeight (Text, is_default);
Update ();
}
}
@@ -65,20 +66,12 @@ namespace Terminal.Gui {
public Button (ustring text, bool is_default = false) : base ()
{
CanFocus = true;
Text = text ?? string.Empty;
this.IsDefault = is_default;
Text = text;
int w = SetWidthHeight (text, is_default);
Frame = new Rect (0, 0, w, 1);
}
int SetWidthHeight (ustring text, bool is_default)
{
int w = text.Length + 4 + (is_default ? 2 : 0);
Width = w;
Height = 1;
return w;
}
/// <summary>
/// Initializes a new instance of <see cref="Button"/> at the given coordinates, based on the given text
/// </summary>
@@ -91,6 +84,35 @@ namespace Terminal.Gui {
/// <param name="text">The button's text</param>
public Button (int x, int y, ustring text) : this (x, y, text, false) { }
/// <summary>
/// Initializes a new instance of <see cref="Button"/> at the given coordinates, based on the given text, and with the specified <see cref="IsDefault"/> value
/// </summary>
/// <remarks>
/// If the value for is_default is true, a special
/// decoration is used, and the enter key on a
/// dialog would implicitly activate this button.
/// </remarks>
/// <param name="x">X position where the button will be shown.</param>
/// <param name="y">Y position where the button will be shown.</param>
/// <param name="text">The button's text</param>
/// <param name="is_default">If set, this makes the button the default button in the current view, which means that if the user presses return on a view that does not handle return, it will be treated as if he had clicked on the button</param>
public Button (int x, int y, ustring text, bool is_default)
: base (new Rect (x, y, text.Length + 4 + (is_default ? 2 : 0), 1))
{
CanFocus = true;
Text = text ?? string.Empty;
this.IsDefault = is_default;
}
int SetWidthHeight (ustring text, bool is_default)
{
int w = text.Length + 4 + (is_default ? 2 : 0);
Width = w;
Height = 1;
return w;
}
/// <summary>
/// The text displayed by this <see cref="Button"/>.
/// </summary>
@@ -129,27 +151,6 @@ namespace Terminal.Gui {
SetNeedsDisplay ();
}
/// <summary>
/// Initializes a new instance of <see cref="Button"/> at the given coordinates, based on the given text, and with the specified <see cref="IsDefault"/> value
/// </summary>
/// <remarks>
/// If the value for is_default is true, a special
/// decoration is used, and the enter key on a
/// dialog would implicitly activate this button.
/// </remarks>
/// <param name="x">X position where the button will be shown.</param>
/// <param name="y">Y position where the button will be shown.</param>
/// <param name="text">The button's text</param>
/// <param name="is_default">If set, this makes the button the default button in the current view, which means that if the user presses return on a view that does not handle return, it will be treated as if he had clicked on the button</param>
public Button (int x, int y, ustring text, bool is_default)
: base (new Rect (x, y, text.Length + 4 + (is_default ? 2 : 0), 1))
{
CanFocus = true;
this.IsDefault = is_default;
Text = text;
}
///<inheritdoc cref="Redraw(Rect)"/>
public override void Redraw (Rect region)
{

View File

@@ -137,14 +137,19 @@ namespace Terminal.Gui {
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);
if (Driver.Clip.IsEmpty || Driver.Clip.Contains (contentView.RectToScreen (contentView.Frame))) {
var savedClip = ClipToBounds ();
contentView.Redraw (contentView.Bounds);
Driver.Clip = savedClip;
} else {
contentView.Redraw (contentView.Bounds);
}
ClearNeedsDisplay ();
Driver.SetAttribute (ColorScheme.Normal);
Driver.DrawFrame (scrRect, padding, false);
@@ -152,7 +157,6 @@ namespace Terminal.Gui {
if (HasFocus)
Driver.SetAttribute (ColorScheme.HotNormal);
Driver.DrawWindowTitle (scrRect, Title, padding, padding, padding, padding);
Driver.Clip = savedClip;
Driver.SetAttribute (ColorScheme.Normal);
}
}

View File

@@ -110,13 +110,13 @@ namespace Terminal.Gui {
///<inheritdoc cref="Redraw(Rect)"/>
public override void Redraw (Rect region)
{
base.Redraw (region);
for (int i = 0; i < radioLabels.Length; i++) {
Move (0, i);
Driver.SetAttribute (ColorScheme.Normal);
Driver.AddStr (i == selected ? "(o) " : "( ) ");
DrawHotString (radioLabels [i], HasFocus && i == cursor, ColorScheme);
}
base.Redraw (region);
}
///<inheritdoc cref="PositionCursor"/>

View File

@@ -401,11 +401,14 @@ namespace Terminal.Gui {
public override void Redraw(Rect region)
{
SetViewsNeedsDisplay ();
var oldClip = ClipToBounds ();
Driver.SetAttribute (ColorScheme.Normal);
Clear ();
base.Redraw(region);
Driver.Clip = oldClip;
var savedClip = ClipToBounds ();
contentView.Redraw (contentView.Bounds);
vertical.Redraw (vertical.Bounds);
horizontal.Redraw (vertical.Bounds);
Driver.Clip = savedClip;
Driver.SetAttribute (ColorScheme.Normal);
}