| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007 |
- ๏ปฟusing System.Text;
- using UnitTests;
- using Xunit.Abstractions;
- namespace ViewBaseTests.Drawing;
- public class ViewDrawingClippingTests (ITestOutputHelper output) : FakeDriverBase
- {
- #region GetClip / SetClip Tests
- [Fact]
- public void GetClip_ReturnsDriverClip ()
- {
- IDriver driver = CreateFakeDriver ();
- var region = new Region (new (10, 10, 20, 20));
- driver.Clip = region;
- View view = new () { Driver = driver };
- Region? result = view.GetClip ();
- Assert.NotNull (result);
- Assert.Equal (region, result);
- }
- [Fact]
- public void SetClip_NullRegion_DoesNothing ()
- {
- IDriver driver = CreateFakeDriver ();
- var original = new Region (new (5, 5, 10, 10));
- driver.Clip = original;
- View view = new () { Driver = driver };
- view.SetClip (null);
- Assert.Equal (original, driver.Clip);
- }
- [Fact]
- public void SetClip_ValidRegion_SetsDriverClip ()
- {
- IDriver driver = CreateFakeDriver ();
- var region = new Region (new (10, 10, 30, 30));
- View view = new () { Driver = driver };
- view.SetClip (region);
- Assert.Equal (region, driver.Clip);
- }
- #endregion
- #region SetClipToScreen Tests
- [Fact]
- public void SetClipToScreen_ReturnsPreviousClip ()
- {
- IDriver driver = CreateFakeDriver ();
- var original = new Region (new (5, 5, 10, 10));
- driver.Clip = original;
- View view = new () { Driver = driver };
- Region? previous = view.SetClipToScreen ();
- Assert.Equal (original, previous);
- Assert.NotEqual (original, driver.Clip);
- }
- [Fact]
- public void SetClipToScreen_SetsClipToScreen ()
- {
- IDriver driver = CreateFakeDriver ();
- View view = new () { Driver = driver };
- view.SetClipToScreen ();
- Assert.NotNull (driver.Clip);
- Assert.Equal (driver.Screen, driver.Clip.GetBounds ());
- }
- #endregion
- #region ExcludeFromClip Tests
- [Fact]
- public void ExcludeFromClip_Rectangle_NullDriver_DoesNotThrow ()
- {
- View view = new () { Driver = null };
- Exception? exception = Record.Exception (() => view.ExcludeFromClip (new Rectangle (5, 5, 10, 10)));
- Assert.Null (exception);
- }
- [Fact]
- public void ExcludeFromClip_Rectangle_ExcludesArea ()
- {
- IDriver driver = CreateFakeDriver ();
- driver.Clip = new (new (0, 0, 80, 25));
- View view = new () { Driver = driver };
- var toExclude = new Rectangle (10, 10, 20, 20);
- view.ExcludeFromClip (toExclude);
- // Verify the region was excluded
- Assert.NotNull (driver.Clip);
- Assert.False (driver.Clip.Contains (15, 15));
- }
- [Fact]
- public void ExcludeFromClip_Region_NullDriver_DoesNotThrow ()
- {
- View view = new () { Driver = null };
- Exception? exception = Record.Exception (() => view.ExcludeFromClip (new Region (new (5, 5, 10, 10))));
- Assert.Null (exception);
- }
- [Fact]
- public void ExcludeFromClip_Region_ExcludesArea ()
- {
- IDriver driver = CreateFakeDriver ();
- driver.Clip = new (new (0, 0, 80, 25));
- View view = new () { Driver = driver };
- var toExclude = new Region (new (10, 10, 20, 20));
- view.ExcludeFromClip (toExclude);
- // Verify the region was excluded
- Assert.NotNull (driver.Clip);
- Assert.False (driver.Clip.Contains (15, 15));
- }
- #endregion
- #region AddFrameToClip Tests
- [Fact]
- public void AddFrameToClip_NullDriver_ReturnsNull ()
- {
- var view = new View { X = 0, Y = 0, Width = 10, Height = 10 };
- view.BeginInit ();
- view.EndInit ();
- Region? result = view.AddFrameToClip ();
- Assert.Null (result);
- }
- [Fact]
- public void AddFrameToClip_IntersectsWithFrame ()
- {
- IDriver driver = CreateFakeDriver ();
- driver.Clip = new (driver.Screen);
- var view = new View
- {
- X = 1,
- Y = 1,
- Width = 20,
- Height = 20,
- Driver = driver
- };
- view.BeginInit ();
- view.EndInit ();
- view.LayoutSubViews ();
- Region? previous = view.AddFrameToClip ();
- Assert.NotNull (previous);
- Assert.NotNull (driver.Clip);
- // The clip should now be the intersection of the screen and the view's frame
- var expectedBounds = new Rectangle (1, 1, 20, 20);
- Assert.Equal (expectedBounds, driver.Clip.GetBounds ());
- }
- #endregion
- #region AddViewportToClip Tests
- [Fact]
- public void AddViewportToClip_NullDriver_ReturnsNull ()
- {
- var view = new View { X = 0, Y = 0, Width = 10, Height = 10 };
- view.BeginInit ();
- view.EndInit ();
- Region? result = view.AddViewportToClip ();
- Assert.Null (result);
- }
- [Fact]
- public void AddViewportToClip_IntersectsWithViewport ()
- {
- IDriver driver = CreateFakeDriver ();
- driver.Clip = new (driver.Screen);
- var view = new View
- {
- X = 1,
- Y = 1,
- Width = 20,
- Height = 20,
- Driver = driver
- };
- view.BeginInit ();
- view.EndInit ();
- view.LayoutSubViews ();
- Region? previous = view.AddViewportToClip ();
- Assert.NotNull (previous);
- Assert.NotNull (driver.Clip);
- // The clip should be the viewport area
- Rectangle viewportScreen = view.ViewportToScreen (new Rectangle (Point.Empty, view.Viewport.Size));
- Assert.Equal (viewportScreen, driver.Clip.GetBounds ());
- }
- [Fact]
- public void AddViewportToClip_WithClipContentOnly_LimitsToVisibleContent ()
- {
- IDriver driver = CreateFakeDriver ();
- driver.Clip = new (driver.Screen);
- var view = new View
- {
- X = 1,
- Y = 1,
- Width = 20,
- Height = 20,
- Driver = driver
- };
- view.SetContentSize (new Size (100, 100));
- view.ViewportSettings = ViewportSettingsFlags.ClipContentOnly;
- view.BeginInit ();
- view.EndInit ();
- view.LayoutSubViews ();
- Region? previous = view.AddViewportToClip ();
- Assert.NotNull (previous);
- Assert.NotNull (driver.Clip);
- // The clip should be limited to visible content
- Rectangle visibleContent = view.ViewportToScreen (new Rectangle (new (-view.Viewport.X, -view.Viewport.Y), view.GetContentSize ()));
- Rectangle viewport = view.ViewportToScreen (new Rectangle (Point.Empty, view.Viewport.Size));
- Rectangle expected = Rectangle.Intersect (viewport, visibleContent);
- Assert.Equal (expected, driver.Clip.GetBounds ());
- }
- #endregion
- #region Clip Interaction Tests
- [Fact]
- public void ClipRegions_StackCorrectly_WithNestedViews ()
- {
- IDriver driver = CreateFakeDriver (100, 100);
- driver.Clip = new (driver.Screen);
- var superView = new View
- {
- X = 1,
- Y = 1,
- Width = 50,
- Height = 50,
- Driver = driver
- };
- superView.BeginInit ();
- superView.EndInit ();
- var view = new View
- {
- X = 5,
- Y = 5,
- Width = 30,
- Height = 30
- };
- superView.Add (view);
- superView.LayoutSubViews ();
- // Set clip to superView's frame
- Region? superViewClip = superView.AddFrameToClip ();
- Rectangle superViewBounds = driver.Clip.GetBounds ();
- // Now set clip to view's frame
- Region? viewClip = view.AddFrameToClip ();
- Rectangle viewBounds = driver.Clip.GetBounds ();
- // Child clip should be within superView clip
- Assert.True (superViewBounds.Contains (viewBounds.Location));
- // Restore superView clip
- view.SetClip (superViewClip);
- // Assert.Equal (superViewBounds, driver.Clip.GetBounds ());
- }
- [Fact]
- public void ClipRegions_RespectPreviousClip ()
- {
- IDriver driver = CreateFakeDriver ();
- var initialClip = new Region (new (20, 20, 40, 40));
- driver.Clip = initialClip;
- var view = new View
- {
- X = 1,
- Y = 1,
- Width = 60,
- Height = 60,
- Driver = driver
- };
- view.BeginInit ();
- view.EndInit ();
- view.LayoutSubViews ();
- Region? previous = view.AddFrameToClip ();
- // The new clip should be the intersection of the initial clip and the view's frame
- Rectangle expected = Rectangle.Intersect (
- initialClip.GetBounds (),
- view.FrameToScreen ()
- );
- Assert.Equal (expected, driver.Clip.GetBounds ());
- // Restore should give us back the original
- view.SetClip (previous);
- Assert.Equal (initialClip.GetBounds (), driver.Clip.GetBounds ());
- }
- #endregion
- #region Edge Cases
- [Fact]
- public void AddFrameToClip_EmptyFrame_WorksCorrectly ()
- {
- IDriver driver = CreateFakeDriver ();
- driver.Clip = new (driver.Screen);
- var view = new View
- {
- X = 1,
- Y = 1,
- Width = 0,
- Height = 0,
- Driver = driver
- };
- view.BeginInit ();
- view.EndInit ();
- view.LayoutSubViews ();
- Region? previous = view.AddFrameToClip ();
- Assert.NotNull (previous);
- Assert.NotNull (driver.Clip);
- }
- [Fact]
- public void AddViewportToClip_EmptyViewport_WorksCorrectly ()
- {
- IDriver driver = CreateFakeDriver ();
- driver.Clip = new (driver.Screen);
- var view = new View
- {
- X = 1,
- Y = 1,
- Width = 1, // Minimal size to have adornments
- Height = 1,
- Driver = driver
- };
- view.Border!.Thickness = new (1);
- view.BeginInit ();
- view.EndInit ();
- view.LayoutSubViews ();
- // With border thickness of 1, the viewport should be empty
- Assert.True (view.Viewport.Size.Width == 0 || view.Viewport.Size.Height == 0);
- Region? previous = view.AddViewportToClip ();
- Assert.NotNull (previous);
- }
- [Fact]
- public void ClipRegions_OutOfBounds_HandledCorrectly ()
- {
- IDriver driver = CreateFakeDriver ();
- driver.Clip = new (driver.Screen);
- var view = new View
- {
- X = 100, // Outside screen bounds
- Y = 100,
- Width = 20,
- Height = 20,
- Driver = driver
- };
- view.BeginInit ();
- view.EndInit ();
- view.LayoutSubViews ();
- Region? previous = view.AddFrameToClip ();
- Assert.NotNull (previous);
- // The clip should be empty since the view is outside the screen
- Assert.True (driver.Clip.IsEmpty () || !driver.Clip.Contains (100, 100));
- }
- #endregion
- #region Drawing Tests
- [Fact]
- public void Clip_Set_BeforeDraw_ClipsDrawing ()
- {
- IDriver driver = CreateFakeDriver ();
- var clip = new Region (new (10, 10, 10, 10));
- driver.Clip = clip;
- var view = new View
- {
- X = 0,
- Y = 0,
- Width = 50,
- Height = 50,
- Driver = driver
- };
- view.BeginInit ();
- view.EndInit ();
- view.LayoutSubViews ();
- view.Draw ();
- // Verify clip was used
- Assert.NotNull (driver.Clip);
- }
- [Fact]
- public void Draw_UpdatesDriverClip ()
- {
- IDriver driver = CreateFakeDriver ();
- driver.Clip = new (driver.Screen);
- var view = new View
- {
- X = 1,
- Y = 1,
- Width = 20,
- Height = 20,
- Driver = driver
- };
- view.BeginInit ();
- view.EndInit ();
- view.LayoutSubViews ();
- view.Draw ();
- // Clip should be updated to exclude the drawn view
- Assert.NotNull (driver.Clip);
- // Assert.False (driver.Clip.Contains (15, 15)); // Point inside the view should be excluded
- }
- [Fact]
- public void Draw_WithSubViews_ClipsCorrectly ()
- {
- IDriver driver = CreateFakeDriver ();
- driver.Clip = new (driver.Screen);
- var superView = new View
- {
- X = 1,
- Y = 1,
- Width = 50,
- Height = 50,
- Driver = driver
- };
- var view = new View { X = 5, Y = 5, Width = 20, Height = 20 };
- superView.Add (view);
- superView.BeginInit ();
- superView.EndInit ();
- superView.LayoutSubViews ();
- superView.Draw ();
- // Both superView and view should be excluded from clip
- Assert.NotNull (driver.Clip);
- // Assert.False (driver.Clip.Contains (15, 15)); // Point in superView should be excluded
- }
- /// <summary>
- /// Tests that wide glyphs (๐) are correctly clipped when overlapped by bordered subviews
- /// at different column alignments (even vs odd). Demonstrates:
- /// 1. Full clipping at even columns (X=0, X=2)
- /// 2. Partial clipping at odd columns (X=1) resulting in half-glyphs (๏ฟฝ)
- /// 3. The recursive draw flow and clip exclusion mechanism
- ///
- /// For detailed draw flow documentation, see ViewDrawingClippingTests.DrawFlow.md
- /// </summary>
- [Fact]
- public void Draw_WithBorderSubView_DrawsCorrectly ()
- {
- IApplication app = Application.Create ();
- app.Init ("fake");
- IDriver driver = app!.Driver!;
- driver.SetScreenSize (30, 20);
- driver!.Clip = new (driver.Screen);
- var superView = new Runnable ()
- {
- X = 0,
- Y = 0,
- Width = Dim.Auto () + 4,
- Height = Dim.Auto () + 1,
- Driver = driver
- };
- Rune codepoint = Glyphs.Apple;
- superView.DrawingContent += (s, e) =>
- {
- var view = s as View;
- for (var r = 0; r < view!.Viewport.Height; r++)
- {
- for (var c = 0; c < view.Viewport.Width; c += 2)
- {
- if (codepoint != default (Rune))
- {
- view.AddRune (c, r, codepoint);
- }
- }
- }
- e.DrawContext?.AddDrawnRectangle (view.Viewport);
- e.Cancel = true;
- };
- var viewWithBorderAtX0 = new View
- {
- Text = "viewWithBorderAtX0",
- BorderStyle = LineStyle.Dashed,
- X = 0,
- Y = 1,
- Width = Dim.Auto (),
- Height = 3
- };
- var viewWithBorderAtX1 = new View
- {
- Text = "viewWithBorderAtX1",
- BorderStyle = LineStyle.Dashed,
- X = 1,
- Y = Pos.Bottom (viewWithBorderAtX0) + 1,
- Width = Dim.Auto (),
- Height = 3
- };
- var viewWithBorderAtX2 = new View
- {
- Text = "viewWithBorderAtX2",
- BorderStyle = LineStyle.Dashed,
- X = 2,
- Y = Pos.Bottom (viewWithBorderAtX1) + 1,
- Width = Dim.Auto (),
- Height = 3
- };
- superView.Add (viewWithBorderAtX0, viewWithBorderAtX1, viewWithBorderAtX2);
- driver.GetOutputBuffer ().SetWideGlyphReplacement ((Rune)'โ ');
- app.Begin (superView);
- // Begin calls LayoutAndDraw, so no need to call it again here
- // app.LayoutAndDraw();
- DriverAssert.AssertDriverContentsAre (
- """
- ๐๐๐๐๐๐๐๐๐๐๐๐๐
- โโโโโโโโโโโโโโโโโโโโ๐๐๐
- โviewWithBorderAtX0โ๐๐๐
- โโโโโโโโโโโโโโโโโโโโ๐๐๐
- ๐๐๐๐๐๐๐๐๐๐๐๐๐
- โ โโโโโโโโโโโโโโโโโโโโ ๐๐
- โ โviewWithBorderAtX1โ ๐๐
- โ โโโโโโโโโโโโโโโโโโโโ ๐๐
- ๐๐๐๐๐๐๐๐๐๐๐๐๐
- ๐โโโโโโโโโโโโโโโโโโโโ๐๐
- ๐โviewWithBorderAtX2โ๐๐
- ๐โโโโโโโโโโโโโโโโโโโโ๐๐
- ๐๐๐๐๐๐๐๐๐๐๐๐๐
- """,
- output,
- driver);
- DriverAssert.AssertDriverOutputIs (@"\x1b[38;2;95;158;160m\x1b[48;2;54;69;79m๐๐๐๐๐๐๐๐๐๐๐๐๐\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m \x1b[38;2;95;158;160m\x1b[48;2;54;69;79mโโโโโโโโโโโโโโโโโโโโ๐๐๐\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m \x1b[38;2;95;158;160m\x1b[48;2;54;69;79mโviewWithBorderAtX0โ๐๐๐\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m \x1b[38;2;95;158;160m\x1b[48;2;54;69;79mโโโโโโโโโโโโโโโโโโโโ๐๐๐\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m \x1b[38;2;95;158;160m\x1b[48;2;54;69;79m๐๐๐๐๐๐๐๐๐๐๐๐๐\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m \x1b[38;2;95;158;160m\x1b[48;2;54;69;79mโ โโโโโโโโโโโโโโโโโโโโ ๐๐\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m \x1b[38;2;95;158;160m\x1b[48;2;54;69;79mโ โviewWithBorderAtX1โ ๐๐\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m \x1b[38;2;95;158;160m\x1b[48;2;54;69;79mโ โโโโโโโโโโโโโโโโโโโโ ๐๐\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m \x1b[38;2;95;158;160m\x1b[48;2;54;69;79m๐๐๐๐๐๐๐๐๐๐๐๐๐\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m \x1b[38;2;95;158;160m\x1b[48;2;54;69;79m๐โโโโโโโโโโโโโโโโโโโโ๐๐\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m \x1b[38;2;95;158;160m\x1b[48;2;54;69;79m๐โviewWithBorderAtX2โ๐๐\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m \x1b[38;2;95;158;160m\x1b[48;2;54;69;79m๐โโโโโโโโโโโโโโโโโโโโ๐๐\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m \x1b[38;2;95;158;160m\x1b[48;2;54;69;79m๐๐๐๐๐๐๐๐๐๐๐๐๐\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m",
- output, driver);
- DriverImpl? driverImpl = driver as DriverImpl;
- FakeOutput? fakeOutput = driverImpl!.GetOutput () as FakeOutput;
- output.WriteLine ("Driver Output After Redraw:\n" + driver.GetOutput().GetLastOutput());
- // BUGBUG: Border.set_LineStyle does not call SetNeedsDraw
- viewWithBorderAtX1!.Border!.LineStyle = LineStyle.Single;
- viewWithBorderAtX1.Border!.SetNeedsDraw ();
- app.LayoutAndDraw ();
- DriverAssert.AssertDriverContentsAre (
- """
- ๐๐๐๐๐๐๐๐๐๐๐๐๐
- โโโโโโโโโโโโโโโโโโโโ๐๐๐
- โviewWithBorderAtX0โ๐๐๐
- โโโโโโโโโโโโโโโโโโโโ๐๐๐
- ๐๐๐๐๐๐๐๐๐๐๐๐๐
- โ โโโโโโโโโโโโโโโโโโโโ ๐๐
- โ โviewWithBorderAtX1โ ๐๐
- โ โโโโโโโโโโโโโโโโโโโโ ๐๐
- ๐๐๐๐๐๐๐๐๐๐๐๐๐
- ๐โโโโโโโโโโโโโโโโโโโโ๐๐
- ๐โviewWithBorderAtX2โ๐๐
- ๐โโโโโโโโโโโโโโโโโโโโ๐๐
- ๐๐๐๐๐๐๐๐๐๐๐๐๐
- """,
- output,
- driver);
- // After a full redraw, all cells should be clean
- foreach (Cell cell in driver.Contents!)
- {
- Assert.False (cell.IsDirty);
- }
- }
- [Fact]
- public void Draw_WithBorderSubView_At_Col1_In_WideGlyph_DrawsCorrectly ()
- {
- IApplication app = Application.Create ();
- app.Init ("fake");
- IDriver driver = app!.Driver!;
- driver.SetScreenSize (6, 3); // Minimal: 6 cols wide (3 for content + 2 for border + 1), 3 rows high (1 for content + 2 for border)
- driver!.Clip = new (driver.Screen);
- var superView = new Runnable ()
- {
- X = 0,
- Y = 0,
- Width = Dim.Fill (),
- Height = Dim.Fill (),
- Driver = driver
- };
- Rune codepoint = Glyphs.Apple;
- superView.DrawingContent += (s, e) =>
- {
- View? view = s as View;
- view?.AddStr (0, 0, "๐๐๐๐");
- view?.AddStr (0, 1, "๐๐๐๐");
- view?.AddStr (0, 2, "๐๐๐๐");
- e.DrawContext?.AddDrawnRectangle (view!.Viewport);
- e.Cancel = true;
- };
- // Minimal border at X=1 (odd column), Width=3, Height=3 (includes border)
- var viewWithBorder = new View
- {
- Text = "X",
- BorderStyle = LineStyle.Single,
- X = 1,
- Y = 0,
- Width = 3,
- Height = 3
- };
- superView.Add (viewWithBorder);
- driver.GetOutputBuffer ().SetWideGlyphReplacement ((Rune)'โ ');
- app.Begin (superView);
- DriverAssert.AssertDriverContentsAre (
- """
- โ โโโ๐
- โ โXโ๐
- โ โโโ๐
- """,
- output,
- driver);
- DriverAssert.AssertDriverOutputIs (@"\x1b[38;2;95;158;160m\x1b[48;2;54;69;79mโ โโโ๐โ โXโ๐โ โโโ๐",
- output, driver);
- DriverImpl? driverImpl = driver as DriverImpl;
- FakeOutput? fakeOutput = driverImpl!.GetOutput () as FakeOutput;
- output.WriteLine ("Driver Output:\n" + fakeOutput!.GetLastOutput ());
- }
- [Fact]
- public void Draw_WithBorderSubView_At_Col3_In_WideGlyph_DrawsCorrectly ()
- {
- IApplication app = Application.Create ();
- app.Init ("fake");
- IDriver driver = app!.Driver!;
- driver.SetScreenSize (6, 3); // Screen: 6 cols wide, 3 rows high; enough for 3x3 border subview at col 3 plus content on the left
- driver!.Clip = new (driver.Screen);
- var superView = new Runnable ()
- {
- X = 0,
- Y = 0,
- Width = Dim.Fill (),
- Height = Dim.Fill (),
- Driver = driver
- };
- Rune codepoint = Glyphs.Apple;
- superView.DrawingContent += (s, e) =>
- {
- View? view = s as View;
- view?.AddStr (0, 0, "๐๐๐๐");
- view?.AddStr (0, 1, "๐๐๐๐");
- view?.AddStr (0, 2, "๐๐๐๐");
- e.DrawContext?.AddDrawnRectangle (view!.Viewport);
- e.Cancel = true;
- };
- // Minimal border at X=3 (odd column), Width=3, Height=3 (includes border)
- var viewWithBorder = new View
- {
- Text = "X",
- BorderStyle = LineStyle.Single,
- X = 3,
- Y = 0,
- Width = 3,
- Height = 3
- };
- driver.GetOutputBuffer ().SetWideGlyphReplacement ((Rune)'โ ');
- superView.Add (viewWithBorder);
- app.Begin (superView);
- DriverAssert.AssertDriverContentsAre (
- """
- ๐โ โโโ
- ๐โ โXโ
- ๐โ โโโ
- """,
- output,
- driver);
- DriverAssert.AssertDriverOutputIs (@"\x1b[38;2;95;158;160m\x1b[48;2;54;69;79m๐โ โโโ๐โ โXโ๐โ โโโ",
- output, driver);
- DriverImpl? driverImpl = driver as DriverImpl;
- FakeOutput? fakeOutput = driverImpl!.GetOutput () as FakeOutput;
- output.WriteLine ("Driver Output:\n" + fakeOutput!.GetLastOutput ());
- }
- [Fact]
- public void Draw_NonVisibleView_DoesNotUpdateClip ()
- {
- IDriver driver = CreateFakeDriver ();
- var originalClip = new Region (driver.Screen);
- driver.Clip = originalClip.Clone ();
- var view = new View
- {
- X = 10,
- Y = 10,
- Width = 20,
- Height = 20,
- Visible = false,
- Driver = driver
- };
- view.BeginInit ();
- view.EndInit ();
- view.Draw ();
- // Clip should not be modified for invisible views
- Assert.True (driver.Clip.Equals (originalClip));
- }
- [Fact]
- public void ExcludeFromClip_ExcludesRegion ()
- {
- IDriver driver = CreateFakeDriver ();
- driver.Clip = new (driver.Screen);
- var view = new View
- {
- X = 10,
- Y = 10,
- Width = 20,
- Height = 20,
- Driver = driver
- };
- view.BeginInit ();
- view.EndInit ();
- view.LayoutSubViews ();
- var excludeRect = new Rectangle (15, 15, 10, 10);
- view.ExcludeFromClip (excludeRect);
- Assert.NotNull (driver.Clip);
- Assert.False (driver.Clip.Contains (20, 20)); // Point inside excluded rect should not be in clip
- }
- [Fact]
- public void ExcludeFromClip_WithNullClip_DoesNotThrow ()
- {
- IDriver driver = CreateFakeDriver ();
- driver.Clip = null!;
- var view = new View
- {
- X = 10,
- Y = 10,
- Width = 20,
- Height = 20,
- Driver = driver
- };
- Exception? exception = Record.Exception (() => view.ExcludeFromClip (new Rectangle (15, 15, 10, 10)));
- Assert.Null (exception);
- }
- #endregion
- #region Misc Tests
- [Fact]
- public void SetClip_SetsDriverClip ()
- {
- IDriver driver = CreateFakeDriver ();
- var view = new View
- {
- X = 10,
- Y = 10,
- Width = 20,
- Height = 20,
- Driver = driver
- };
- var newClip = new Region (new (5, 5, 30, 30));
- view.SetClip (newClip);
- Assert.Equal (newClip, driver.Clip);
- }
- [Fact (Skip = "See BUGBUG in SetClip")]
- public void SetClip_WithNullClip_ClearsClip ()
- {
- IDriver driver = CreateFakeDriver ();
- driver.Clip = new (new (10, 10, 20, 20));
- var view = new View
- {
- X = 10,
- Y = 10,
- Width = 20,
- Height = 20,
- Driver = driver
- };
- view.SetClip (null);
- Assert.Null (driver.Clip);
- }
- [Fact]
- public void Draw_Excludes_View_From_Clip ()
- {
- IDriver driver = CreateFakeDriver ();
- var originalClip = new Region (driver.Screen);
- driver.Clip = originalClip.Clone ();
- var view = new View
- {
- X = 10,
- Y = 10,
- Width = 20,
- Height = 20,
- Driver = driver
- };
- view.BeginInit ();
- view.EndInit ();
- view.LayoutSubViews ();
- Region clipWithViewExcluded = originalClip.Clone ();
- clipWithViewExcluded.Exclude (view.Frame);
- view.Draw ();
- Assert.Equal (clipWithViewExcluded, driver.Clip);
- Assert.NotNull (driver.Clip);
- }
- [Fact]
- public void Draw_EmptyViewport_DoesNotCrash ()
- {
- IDriver driver = CreateFakeDriver ();
- driver.Clip = new (driver.Screen);
- var view = new View
- {
- X = 10,
- Y = 10,
- Width = 1,
- Height = 1,
- Driver = driver
- };
- view.Border!.Thickness = new (1);
- view.BeginInit ();
- view.EndInit ();
- view.LayoutSubViews ();
- // With border of 1, viewport should be empty (0x0 or negative)
- Exception? exception = Record.Exception (() => view.Draw ());
- Assert.Null (exception);
- }
- [Fact]
- public void Draw_VeryLargeView_HandlesClippingCorrectly ()
- {
- IDriver driver = CreateFakeDriver ();
- driver.Clip = new (driver.Screen);
- var view = new View
- {
- X = 0,
- Y = 0,
- Width = 1000,
- Height = 1000,
- Driver = driver
- };
- view.BeginInit ();
- view.EndInit ();
- view.LayoutSubViews ();
- Exception? exception = Record.Exception (() => view.Draw ());
- Assert.Null (exception);
- }
- [Fact]
- public void Draw_NegativeCoordinates_HandlesClippingCorrectly ()
- {
- IDriver driver = CreateFakeDriver ();
- driver.Clip = new (driver.Screen);
- var view = new View
- {
- X = -10,
- Y = -10,
- Width = 50,
- Height = 50,
- Driver = driver
- };
- view.BeginInit ();
- view.EndInit ();
- view.LayoutSubViews ();
- Exception? exception = Record.Exception (() => view.Draw ());
- Assert.Null (exception);
- }
- [Fact]
- public void Draw_OutOfScreenBounds_HandlesClippingCorrectly ()
- {
- IDriver driver = CreateFakeDriver ();
- driver.Clip = new (driver.Screen);
- var view = new View
- {
- X = 100,
- Y = 100,
- Width = 50,
- Height = 50,
- Driver = driver
- };
- view.BeginInit ();
- view.EndInit ();
- view.LayoutSubViews ();
- Exception? exception = Record.Exception (() => view.Draw ());
- Assert.Null (exception);
- }
- #endregion
- }
|