Skip to content

Commit

Permalink
Add UI elements for textual filtering of tests
Browse files Browse the repository at this point in the history
  • Loading branch information
rowo360 committed Dec 19, 2024
1 parent 6344094 commit c84d775
Show file tree
Hide file tree
Showing 7 changed files with 218 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// ***********************************************************************
// Copyright (c) Charlie Poole and TestCentric contributors.
// Licensed under the MIT License. See LICENSE file in root directory.
// ***********************************************************************

using System.Drawing;
using System.Windows.Forms;
using System;

namespace TestCentric.Gui.Controls
{
/// <summary>
/// This class is required to stretch a ToolStripTextBox control within a ToolStrip to fill the available space and to resize when the control resizes.
/// The implementation is from the Microsoft Windows Forms documentation, but simplified to the current use case.
/// "How to: Stretch a ToolStripTextBox to Fill the Remaining Width of a ToolStrip"
/// https://learn.microsoft.com/en-us/dotnet/desktop/winforms/controls/stretch-a-toolstriptextbox-to-fill-the-remaining-width-of-a-toolstrip-wf?view=netframeworkdesktop-4.8
/// </summary>
internal class StretchToolStripTextBox : ToolStripTextBox
{
public override Size GetPreferredSize(Size constrainingSize)
{
// Get width of the owning ToolStrip
int textBoxMargin = 2;
Int32 width = Owner.DisplayRectangle.Width - textBoxMargin;

// If the available width is less than the default width, use the default width
if (width < DefaultSize.Width) width = DefaultSize.Width;

// Retrieve the preferred size from the base class, but change the width to the calculated width.
Size size = base.GetPreferredSize(constrainingSize);
size.Width = width;
return size;
}
}
}
135 changes: 135 additions & 0 deletions src/TestCentric/testcentric.gui/Elements/TextBoxElement.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// ***********************************************************************
// Copyright (c) Charlie Poole and TestCentric contributors.
// Licensed under the MIT License. See LICENSE file in root directory.
// ***********************************************************************


using System;
using System.Windows.Forms;

namespace TestCentric.Gui.Elements
{
/// <summary>
/// This class implements the ISelection interface for a TextBox control. It provides this additional functionality:
/// - show a PlaceHoder text if there's no text input
/// - Invoke the SelectionChanged event as soon as no further input is made within a short period of time.
/// </summary>
public class TextBoxElement : ISelection
{
private Timer _typingTimer;

public event CommandHandler SelectionChanged;

public TextBoxElement(Control textBox, string placeHolderText)
{
TextBox = textBox;
PlaceHolderText = placeHolderText;
TextBox.TextChanged += OnTextChanged;

TextBox.LostFocus += OnTextBoxLostFocus;
TextBox.GotFocus += OnTextBoxGotFocus;

// Call LostFocus to set initial text and color
OnTextBoxLostFocus(null, EventArgs.Empty);
}

public string SelectedItem
{
get => TextBox.Text;
set => TextBox.Text = value;
}

public int SelectedIndex
{
get => 0;
set => throw new NotImplementedException();
}

public bool Enabled
{
get => TextBox.Enabled;
set => TextBox.Enabled = value;
}

public bool Visible
{
get => TextBox.Visible;
set => TextBox.Visible = value;
}

public string Text
{
get => TextBox.Text;
set => TextBox.Text = value;
}

private string PlaceHolderText { get; set; }

private Control TextBox { get; }

private bool IsPlaceHolderTextShown { get; set; }

public void InvokeIfRequired(MethodInvoker _delegate)
{
throw new NotImplementedException();
}

public void Refresh()
{
throw new NotImplementedException();
}

private void OnTextBoxGotFocus(object sender, EventArgs e)
{
// If the PlaceHolderText is shown, replace it with an empty text
if (IsPlaceHolderTextShown)
{
TextBox.Text = "";
TextBox.ForeColor = System.Drawing.Color.Black;
IsPlaceHolderTextShown = false;
}
}

private void OnTextBoxLostFocus(object sender, EventArgs e)
{
// If there's no text input, show the PlaceHolderText instead
string searchText = TextBox.Text;
if (string.IsNullOrEmpty(searchText) && !string.IsNullOrEmpty(PlaceHolderText))
{
IsPlaceHolderTextShown = true;
TextBox.Text = PlaceHolderText;
TextBox.ForeColor = System.Drawing.Color.LightGray;
}
}

private void OnTextChanged(object sender, EventArgs e)
{
if (IsPlaceHolderTextShown)
return;

if (_typingTimer == null)
{
_typingTimer = new Timer();
_typingTimer.Interval = 600;
_typingTimer.Tick += TypingTimerTimeout;
}

_typingTimer.Stop();
_typingTimer.Start();
}

private void TypingTimerTimeout(object sender, EventArgs e)
{
var timer = sender as Timer;
if (timer == null)
return;

// The timer must be stopped!
timer.Stop();
if (SelectionChanged != null)
SelectionChanged();

TextBox.Focus();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,12 @@ private void WireUpEvents()
_model.TestCentricTestFilter.OutcomeFilter = filter;
};

_view.TextFilter.SelectionChanged += () =>
{
var text = _view.TextFilter.Text;
_model.TestCentricTestFilter.TextFilter = text;
};

// Node selected in tree
//_treeView.SelectedNodesChanged += (nodes) =>
//{
Expand Down
2 changes: 2 additions & 0 deletions src/TestCentric/testcentric.gui/Views/ITestTreeView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ public interface ITestTreeView : IView
// Test Filter related properties / methods
IMultiSelection OutcomeFilter { get; }

ISelection TextFilter { get; }

void SetTestFilterVisibility(bool visible);

// Tree-related Methods
Expand Down
23 changes: 23 additions & 0 deletions src/TestCentric/testcentric.gui/Views/TestTreeView.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions src/TestCentric/testcentric.gui/Views/TestTreeView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public TestTreeView()
TestPropertiesCommand = new CommandMenuElement(testPropertiesMenuItem);
ViewAsXmlCommand = new CommandMenuElement(viewAsXmlMenuItem);
OutcomeFilter = new MultiCheckedToolStripButtonGroup(new[] { filterOutcomePassedButton, filterOutcomeFailedButton, filterOutcomeWarningButton, filterOutcomeNotRunButton });
TextFilter = new TextBoxElement(filterTextBox.Control, "Filter...");
TreeView = treeView;

// NOTE: We use MouseDown here rather than MouseUp because
Expand Down Expand Up @@ -127,6 +128,8 @@ public bool CheckBoxes

public IMultiSelection OutcomeFilter { get; private set; }

public ISelection TextFilter { get; private set; }

public TreeNode ContextNode { get; private set; }
public ContextMenuStrip TreeContextMenu => TreeView.ContextMenuStrip;

Expand Down Expand Up @@ -205,6 +208,7 @@ public void InvokeIfRequired(MethodInvoker _delegate)
public void SetTestFilterVisibility(bool isVisible)
{
filterToolStrip.Visible = isVisible;
filterTextToolStrip.Visible = isVisible;
}

public void LoadAlternateImages(string imageSet)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,19 @@ public void OutcomeFilterChanged_ApplyFilter()
_model.TestCentricTestFilter.Received().OutcomeFilter = selectedItems;
}

[Test]
public void TextFilterChanged_ApplyFilter()
{
// 1. Arrange
_view.TextFilter.Text.Returns("TestA");

// 2. Act
_view.TextFilter.SelectionChanged += Raise.Event<CommandHandler>();

// 3. Assert
_model.TestCentricTestFilter.Received().TextFilter = "TestA";
}

// TODO: Version 1 Test - Make it work if needed.
//[Test]
//public void WhenContextNodeIsNotNull_RunCommandExecutesThatTest()
Expand Down

0 comments on commit c84d775

Please sign in to comment.