From b5799c3dedbe85cbdd40e0f52eb1a0ca438b06f0 Mon Sep 17 00:00:00 2001 From: Thomas Nind <31306100+tznind@users.noreply.github.com> Date: Mon, 25 Oct 2021 20:30:39 +0100 Subject: [PATCH] Fixed label positions to begin at Margins not just bottom left of screen (#1488) * Fixed label positions to begin at Margins not just bottom left of screen * Tidied up Axis GetLabels and added margin control to GraphViewExample * Added tests for very large margins --- Terminal.Gui/Core/Graphs/Axis.cs | 57 +++++----- Terminal.Gui/Views/GraphView.cs | 14 ++- UICatalog/Scenarios/GraphViewExample.cs | 15 +++ UnitTests/GraphViewTests.cs | 132 ++++++++++++++++++++++++ 4 files changed, 185 insertions(+), 33 deletions(-) diff --git a/Terminal.Gui/Core/Graphs/Axis.cs b/Terminal.Gui/Core/Graphs/Axis.cs index 5c6d3223d..bb64d35cb 100644 --- a/Terminal.Gui/Core/Graphs/Axis.cs +++ b/Terminal.Gui/Core/Graphs/Axis.cs @@ -256,7 +256,7 @@ namespace Terminal.Gui.Graphs { int labels = 0; int y = GetAxisYPosition (graph); - var start = graph.ScreenToGraphSpace (0, y); + var start = graph.ScreenToGraphSpace ((int)graph.MarginLeft, y); var end = graph.ScreenToGraphSpace (bounds.Width, y); // don't draw labels below the minimum @@ -270,24 +270,22 @@ namespace Terminal.Gui.Graphs { int screenX = graph.GraphSpaceToScreen (new PointF (current.X, current.Y)).X; - // Ensure the axis point does not draw into the margin - if (screenX >= graph.MarginLeft) { - // The increment we will render (normally a top T unicode symbol) - var toRender = new AxisIncrementToRender (Orientation, screenX, current.X); + // The increment we will render (normally a top T unicode symbol) + var toRender = new AxisIncrementToRender (Orientation, screenX, current.X); - // Not every increment has to have a label - if (ShowLabelsEvery != 0) { + // Not every increment has to have a label + if (ShowLabelsEvery != 0) { - // if this increment does also needs a label - if (labels++ % ShowLabelsEvery == 0) { - toRender.Text = LabelGetter (toRender); - }; - } - - // Label or no label definetly render it - yield return toRender; + // if this increment does also needs a label + if (labels++ % ShowLabelsEvery == 0) { + toRender.Text = LabelGetter (toRender); + }; } + // Label or no label definetly render it + yield return toRender; + + current.X += Increment; } } @@ -426,7 +424,7 @@ namespace Terminal.Gui.Graphs { // remember screen space is top down so the lowest graph // space value is at the bottom of the screen - var start = graph.ScreenToGraphSpace (x, bounds.Height - 1); + var start = graph.ScreenToGraphSpace (x, bounds.Height - (1 + (int)graph.MarginBottom)); var end = graph.ScreenToGraphSpace (x, 0); // don't draw labels below the minimum @@ -435,30 +433,27 @@ namespace Terminal.Gui.Graphs { } var current = start; - var dontDrawBelowScreenY = bounds.Height - graph.MarginBottom; while (current.Y < end.Y) { int screenY = graph.GraphSpaceToScreen (new PointF (current.X, current.Y)).Y; - // if the axis label is above the bottom margin (screen y starts at 0 at the top) - if (screenY < dontDrawBelowScreenY) { - // Create the axis symbol - var toRender = new AxisIncrementToRender (Orientation, screenY, current.Y); + // Create the axis symbol + var toRender = new AxisIncrementToRender (Orientation, screenY, current.Y); - // and the label (if we are due one) - if (ShowLabelsEvery != 0) { + // and the label (if we are due one) + if (ShowLabelsEvery != 0) { - // if this increment also needs a label - if (labels++ % ShowLabelsEvery == 0) { - toRender.Text = LabelGetter (toRender); - }; - } - - // draw the axis symbol (and label if it has one) - yield return toRender; + // if this increment also needs a label + if (labels++ % ShowLabelsEvery == 0) { + toRender.Text = LabelGetter (toRender); + }; } + // draw the axis symbol (and label if it has one) + yield return toRender; + + current.Y += Increment; } } diff --git a/Terminal.Gui/Views/GraphView.cs b/Terminal.Gui/Views/GraphView.cs index 8a38c0e5f..f192da841 100644 --- a/Terminal.Gui/Views/GraphView.cs +++ b/Terminal.Gui/Views/GraphView.cs @@ -114,6 +114,15 @@ namespace Terminal.Gui { return; } + // The drawable area of the graph (anything that isn't in the margins) + var graphScreenWidth = Bounds.Width - ((int)MarginLeft); + var graphScreenHeight = Bounds.Height - (int)MarginBottom; + + // if the margins take up the full draw bounds don't render + if (graphScreenWidth < 0 || graphScreenHeight < 0) { + return; + } + // Draw 'before' annotations foreach (var a in Annotations.ToArray().Where (a => a.BeforeSeries)) { a.Render (this); @@ -137,8 +146,9 @@ namespace Terminal.Gui { SetDriverColorToGraphColor (); - // The drawable area of the graph (anything that isn't in the margins) - Rect drawBounds = new Rect((int)MarginLeft,0, Bounds.Width - ((int)MarginLeft), Bounds.Height - (int)MarginBottom); + + Rect drawBounds = new Rect((int)MarginLeft,0, graphScreenWidth, graphScreenHeight); + RectangleF graphSpace = ScreenToGraphSpace (drawBounds); foreach (var s in Series.ToArray ()) { diff --git a/UICatalog/Scenarios/GraphViewExample.cs b/UICatalog/Scenarios/GraphViewExample.cs index b98caacf3..c92faaeb0 100644 --- a/UICatalog/Scenarios/GraphViewExample.cs +++ b/UICatalog/Scenarios/GraphViewExample.cs @@ -52,6 +52,10 @@ namespace UICatalog.Scenarios { new MenuBarItem ("_View", new MenuItem [] { new MenuItem ("Zoom _In", "", () => Zoom(0.5f)), new MenuItem ("Zoom _Out", "", () => Zoom(2f)), + new MenuItem ("MarginLeft++", "", () => Margin(true,true)), + new MenuItem ("MarginLeft--", "", () => Margin(true,false)), + new MenuItem ("MarginBottom++", "", () => Margin(false,true)), + new MenuItem ("MarginBottom--", "", () => Margin(false,false)), }), }); @@ -676,6 +680,17 @@ namespace UICatalog.Scenarios { graphView.SetNeedsDisplay (); } + private void Margin (bool left, bool increase) + { + if (left) { + graphView.MarginLeft = (uint)Math.Max(0,graphView.MarginLeft + (increase ? 1 : -1)); + } + else { + graphView.MarginBottom = (uint)Math.Max (0, graphView.MarginBottom + (increase ? 1 : -1)); + } + + graphView.SetNeedsDisplay (); + } private void Quit () { diff --git a/UnitTests/GraphViewTests.cs b/UnitTests/GraphViewTests.cs index 774e295c5..d91933e78 100644 --- a/UnitTests/GraphViewTests.cs +++ b/UnitTests/GraphViewTests.cs @@ -1398,6 +1398,138 @@ namespace Terminal.Gui.Views { Application.Shutdown (); } + [Fact] + public void YAxisLabels_With_MarginBottom () + { + GraphViewTests.InitFakeDriver (); + var gv = new GraphView { + ColorScheme = new ColorScheme (), + Bounds = new Rect (0, 0, 10, 7) + }; + + gv.CellSize = new PointF (1, 0.5f); + gv.AxisY.Increment = 1; + gv.AxisY.ShowLabelsEvery = 1; + + gv.Series.Add (new ScatterSeries { + Points = { new PointF (1, 1), new PointF (5, 0) } + }); + + // reserve 3 cells of the console for the margin + gv.MarginBottom = 3; + gv.MarginLeft = 1; + + gv.Redraw (gv.Bounds); + + var expected = +@" + │ +1┤x + │ +0┼┬┬┬┬x┬┬┬ + 0 5 + + "; + GraphViewTests.AssertDriverContentsAre (expected, output); + + + // Shutdown must be called to safely clean up Application if Init has been called + Application.Shutdown (); + } + + [Fact] + public void XAxisLabels_With_MarginLeft() + { + GraphViewTests.InitFakeDriver (); + var gv = new GraphView { + ColorScheme = new ColorScheme (), + Bounds = new Rect (0, 0, 10, 7) + }; + + gv.CellSize = new PointF (1, 0.5f); + gv.AxisY.Increment = 1; + gv.AxisY.ShowLabelsEvery = 1; + + gv.Series.Add (new ScatterSeries { + Points = { new PointF (1, 1), new PointF (5, 0) } + }); + + // reserve 3 cells of the left for the margin + gv.MarginLeft = 3; + gv.MarginBottom = 1; + + gv.Redraw (gv.Bounds); + + var expected = + @" + │ + 2┤ + │ + 1┤x + │ + 0┼┬┬┬┬x┬ + 0 5 + + "; + GraphViewTests.AssertDriverContentsAre (expected, output); + + + // Shutdown must be called to safely clean up Application if Init has been called + Application.Shutdown (); + } + + + [Fact] + public void MarginBottom_BiggerThanHeight_ExpectBlankGraph () + { + var gv = GraphViewTests.GetGraph (); + gv.Height = 10; + gv.MarginBottom = 20; + + gv.Series.Add (new ScatterSeries { + Points = { new PointF (1, 1), new PointF (5, 0) } + }); + + + gv.Redraw (gv.Bounds); + + var expected = + @" + + + "; + GraphViewTests.AssertDriverContentsAre (expected, output); + + + // Shutdown must be called to safely clean up Application if Init has been called + Application.Shutdown (); + } + [Fact] + public void MarginLeft_BiggerThanWidth_ExpectBlankGraph () + { + var gv = GraphViewTests.GetGraph (); + gv.Width = 10; + gv.MarginLeft = 20; + + gv.Series.Add (new ScatterSeries { + Points = { new PointF (1, 1), new PointF (5, 0) } + }); + + + gv.Redraw (gv.Bounds); + + var expected = + @" + + + "; + GraphViewTests.AssertDriverContentsAre (expected, output); + + + // Shutdown must be called to safely clean up Application if Init has been called + Application.Shutdown (); + } + [Fact] public void PathAnnotation_Diamond () {