refactoring treeview

This commit is contained in:
Lars Brubaker 2023-03-24 17:23:05 -07:00
parent dfefb936ef
commit ca2843e5be
7 changed files with 224 additions and 284 deletions

View file

@ -44,7 +44,6 @@ using MatterHackers.DataConverters3D;
using MatterHackers.ImageProcessing;
using MatterHackers.Localizations;
using MatterHackers.MatterControl.CustomWidgets;
using MatterHackers.MatterControl.DataStorage;
using MatterHackers.MatterControl.DesignTools.EditableTypes;
using MatterHackers.MatterControl.DesignTools.Operations;
using MatterHackers.MatterControl.Library.Widgets;

View file

@ -66,14 +66,14 @@ namespace MatterHackers.MatterControl.Library.Widgets
horizontalSplitter.Panel2.Padding = theme.DefaultContainerPadding;
treeView.AfterSelect += this.TreeView_AfterSelect;
TreeView.AfterSelect += this.TreeView_AfterSelect;
treeView.NodeMouseDoubleClick += (s, e) =>
TreeView.NodeMouseDoubleClick += (s, e) =>
{
if (e is MouseEventArgs mouseEvent
&& mouseEvent.Button == MouseButtons.Left
&& mouseEvent.Clicks == 2
&& treeView?.SelectedNode is TreeNode treeNode)
&& TreeView?.SelectedNode is TreeNode treeNode)
{
nextButton.InvokeClick();
}
@ -85,7 +85,7 @@ namespace MatterHackers.MatterControl.Library.Widgets
{
var rootNode = this.CreateTreeNode(rootDirectory);
rootNode.Expandable = true;
rootNode.TreeView = treeView;
rootNode.TreeView = TreeView;
contentPanel.AddChild(rootNode);
}
@ -120,43 +120,10 @@ namespace MatterHackers.MatterControl.Library.Widgets
horizontalSplitter.Panel2.AddChild(panel2Column);
}
protected override bool FilterTree(TreeNode context, string filter, bool parentVisible, List<TreeNode> matches)
protected override bool NodeMatchesFilter(TreeNode context, string filter)
{
// Filter against make/model for printers or make for top level nodes
string itemText = context.Text;
bool hasFilterText = itemText.IndexOf(filter, StringComparison.OrdinalIgnoreCase) != -1;
context.Visible = hasFilterText || parentVisible;
if (context.Visible
&& context.NodeParent != null)
{
context.NodeParent.Visible = true;
context.NodeParent.Expanded = true;
context.Expanded = true;
}
if (context.NodeParent != null
&& hasFilterText)
{
matches.Add(context);
}
bool childMatched = false;
foreach (var child in context.Nodes)
{
childMatched |= FilterTree(child, filter, hasFilterText || parentVisible, matches);
}
bool hasMatch = childMatched || hasFilterText;
if (hasMatch)
{
context.Visible = context.Expanded = true;
}
return hasMatch;
// check if the node matches the filter
return context.Text.IndexOf(filter, StringComparison.OrdinalIgnoreCase) != -1;
}
private static void SetImage(TreeNode node, ImageBuffer image)
@ -216,15 +183,15 @@ namespace MatterHackers.MatterControl.Library.Widgets
private void TreeView_AfterSelect(object sender, TreeNode e)
{
if (treeView.SelectedNode?.Tag != null)
if (TreeView.SelectedNode?.Tag != null)
{
UiThread.RunOnIdle(() =>
{
if (treeView.SelectedNode != null)
if (TreeView.SelectedNode != null)
{
string printerName = treeView.SelectedNode.Tag.ToString();
string printerName = TreeView.SelectedNode.Tag.ToString();
this.SelectedMaterial = treeView.SelectedNode.Tag as MaterialInfo;
this.SelectedMaterial = TreeView.SelectedNode.Tag as MaterialInfo;
materialInfo.CloseChildren();
var printerDetails = new PrinterDetails(
@ -266,7 +233,7 @@ namespace MatterHackers.MatterControl.Library.Widgets
printerDetails.ProductDataContainer.AddChild(settingsBackground);
};
nextButtonEnabled(treeView.SelectedNode != null);
nextButtonEnabled(TreeView.SelectedNode != null);
}
});
}

View file

@ -59,14 +59,14 @@ namespace MatterHackers.MatterControl.Library.Widgets
horizontalSplitter.Panel2.Padding = theme.DefaultContainerPadding;
treeView.AfterSelect += this.TreeView_AfterSelect;
TreeView.AfterSelect += this.TreeView_AfterSelect;
treeView.NodeMouseDoubleClick += (s, e) =>
TreeView.NodeMouseDoubleClick += (s, e) =>
{
if (e is MouseEventArgs mouseEvent
&& mouseEvent.Button == MouseButtons.Left
&& mouseEvent.Clicks == 2
&& treeView?.SelectedNode is TreeNode treeNode)
&& TreeView?.SelectedNode is TreeNode treeNode)
{
nextButton.InvokeClick();
}
@ -84,7 +84,7 @@ namespace MatterHackers.MatterControl.Library.Widgets
var rootNode = this.CreateTreeNode(oem);
rootNode.Expandable = true;
rootNode.TreeView = treeView;
rootNode.TreeView = TreeView;
rootNode.Load += (s, e) =>
{
var image = OemSettings.Instance.GetIcon(oem.Key, theme);
@ -226,45 +226,13 @@ namespace MatterHackers.MatterControl.Library.Widgets
this.PrinterNameError.Visible = true;
}
protected override bool FilterTree(TreeNode context, string filter, bool parentVisible, List<TreeNode> matches)
{
// Filter against make/model for printers or make for top level nodes
string itemText = (context.Tag as MakeModelInfo)?.ToString() ?? context.Text;
bool hasFilterText = itemText.IndexOf(filter, StringComparison.OrdinalIgnoreCase) != -1;
context.Visible = hasFilterText || parentVisible;
if (context.Visible
&& context.NodeParent != null)
{
context.NodeParent.Visible = true;
context.NodeParent.Expanded = true;
context.Expanded = true;
}
if (context.NodeParent != null
&& hasFilterText)
{
matches.Add(context);
}
bool childMatched = false;
foreach (var child in context.Nodes)
{
childMatched |= FilterTree(child, filter, hasFilterText || parentVisible, matches);
}
bool hasMatch = childMatched || hasFilterText;
if (hasMatch)
{
context.Visible = context.Expanded = true;
}
return hasMatch;
}
protected override bool NodeMatchesFilter(TreeNode context, string filter)
{
// Filter against make/model for printers or make for top level nodes
string itemText = (context.Tag as MakeModelInfo)?.ToString() ?? context.Text;
return itemText.IndexOf(filter, StringComparison.OrdinalIgnoreCase) != -1;
}
private void ClearError()
{
@ -314,24 +282,24 @@ namespace MatterHackers.MatterControl.Library.Widgets
private void TreeView_AfterSelect(object sender, TreeNode e)
{
nameSection.Enabled = treeView.SelectedNode != null;
nameSection.Enabled = TreeView.SelectedNode != null;
this.ClearError();
this.PrinterNameError.Visible = false;
if (nameSection.Enabled
&& treeView.SelectedNode.Tag != null)
&& TreeView.SelectedNode.Tag != null)
{
UiThread.RunOnIdle(() =>
{
if (usingDefaultName
&& treeView.SelectedNode != null)
&& TreeView.SelectedNode != null)
{
string printerName = treeView.SelectedNode.Tag.ToString();
string printerName = TreeView.SelectedNode.Tag.ToString();
printerNameInput.Text = Util.GetNonCollidingName(printerName, this.ExistingPrinterNames);
this.SelectedPrinter = treeView.SelectedNode.Tag as MakeModelInfo;
this.SelectedPrinter = TreeView.SelectedNode.Tag as MakeModelInfo;
printerInfo.CloseChildren();
@ -356,7 +324,7 @@ namespace MatterHackers.MatterControl.Library.Widgets
});
}
nextButtonEnabled(treeView.SelectedNode != null
nextButtonEnabled(TreeView.SelectedNode != null
&& !string.IsNullOrWhiteSpace(printerNameInput.Text));
}
});

View file

@ -27,177 +27,213 @@ of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using MatterHackers.Agg;
using MatterHackers.Agg.Platform;
using MatterHackers.Agg.UI;
using MatterHackers.ImageProcessing;
using MatterHackers.MatterControl.CustomWidgets;
using System;
using System.Collections.Generic;
using System.Linq;
namespace MatterHackers.MatterControl.Library.Widgets
{
public abstract class SearchableTreePanel : FlowLayoutWidget
{
protected TextEditWithInlineCancel searchBox;
protected TreeView treeView;
protected Splitter horizontalSplitter;
protected ThemeConfig theme;
protected FlowLayoutWidget contentPanel;
public abstract class SearchableTreePanel : FlowLayoutWidget
{
protected FlowLayoutWidget contentPanel;
protected Splitter horizontalSplitter;
protected TextEditWithInlineCancel searchBox;
protected ThemeConfig theme;
public SearchableTreePanel(ThemeConfig theme)
: base(FlowDirection.TopToBottom)
{
this.theme = theme;
this.TreeLoaded = false;
public SearchableTreePanel(ThemeConfig theme)
: base(FlowDirection.TopToBottom)
{
this.theme = theme;
this.TreeLoaded = false;
var searchIcon = StaticData.Instance.LoadIcon("icon_search_24x24.png", 16, 16).SetToColor(theme.TextColor).AjustAlpha(0.3);
var searchIcon = StaticData.Instance.LoadIcon("icon_search_24x24.png", 16, 16).SetToColor(theme.TextColor).AjustAlpha(0.3);
searchBox = new TextEditWithInlineCancel(theme)
{
Name = "Search",
HAnchor = HAnchor.Stretch,
Margin = new BorderDouble(6),
};
searchBox = new TextEditWithInlineCancel(theme)
{
Name = "Search",
HAnchor = HAnchor.Stretch,
Margin = new BorderDouble(6),
};
searchBox.ResetButton.Visible = false;
searchBox.ResetButton.Visible = false;
var searchInput = searchBox.TextEditWidget;
var searchInput = searchBox.TextEditWidget;
searchInput.BeforeDraw += (s, e) =>
{
if (!searchBox.ResetButton.Visible)
{
e.Graphics2D.Render(
searchIcon,
searchInput.Width - searchIcon.Width - 5,
searchInput.LocalBounds.Bottom + searchInput.Height / 2 - searchIcon.Height / 2);
}
};
searchInput.BeforeDraw += (s, e) =>
{
if (!searchBox.ResetButton.Visible)
{
e.Graphics2D.Render(
searchIcon,
searchInput.Width - searchIcon.Width - 5,
searchInput.LocalBounds.Bottom + searchInput.Height / 2 - searchIcon.Height / 2);
}
};
searchBox.ResetButton.Click += (s, e) =>
{
this.ClearSearch();
};
searchBox.ResetButton.Click += (s, e) =>
{
this.ClearSearch();
};
searchBox.KeyDown += (s, e) =>
{
if (e.KeyCode == Keys.Escape)
{
this.ClearSearch();
e.Handled = true;
}
};
searchBox.KeyDown += (s, e) =>
{
if (e.KeyCode == Keys.Escape)
{
this.ClearSearch();
e.Handled = true;
}
};
searchBox.TextEditWidget.ActualTextEditWidget.TextChanged += (s, e) =>
{
if (string.IsNullOrWhiteSpace(searchBox.Text))
{
this.ClearSearch();
}
else
{
this.PerformSearch(searchBox.Text);
}
};
searchBox.TextEditWidget.ActualTextEditWidget.TextChanged += (s, e) =>
{
if (string.IsNullOrWhiteSpace(searchBox.Text))
{
this.ClearSearch();
}
else
{
this.PerformSearch(searchBox.Text);
}
};
horizontalSplitter = new Splitter()
{
SplitterDistance = Math.Max(UserSettings.Instance.LibraryViewWidth, 20),
SplitterSize = theme.SplitterWidth,
SplitterBackground = theme.SplitterBackground
};
horizontalSplitter.AnchorAll();
horizontalSplitter = new Splitter()
{
SplitterDistance = Math.Max(UserSettings.Instance.LibraryViewWidth, 20),
SplitterSize = theme.SplitterWidth,
SplitterBackground = theme.SplitterBackground
};
horizontalSplitter.AnchorAll();
horizontalSplitter.DistanceChanged += (s, e) =>
{
UserSettings.Instance.LibraryViewWidth = Math.Max(horizontalSplitter.SplitterDistance, 20);
};
horizontalSplitter.DistanceChanged += (s, e) =>
{
UserSettings.Instance.LibraryViewWidth = Math.Max(horizontalSplitter.SplitterDistance, 20);
};
this.AddChild(horizontalSplitter);
this.AddChild(horizontalSplitter);
var leftPanel = new FlowLayoutWidget(FlowDirection.TopToBottom)
{
HAnchor = HAnchor.Stretch,
VAnchor = VAnchor.Stretch
};
var leftPanel = new FlowLayoutWidget(FlowDirection.TopToBottom)
{
HAnchor = HAnchor.Stretch,
VAnchor = VAnchor.Stretch
};
leftPanel.AddChild(searchBox);
leftPanel.AddChild(searchBox);
treeView = new TreeView(theme)
{
HAnchor = HAnchor.Stretch,
VAnchor = VAnchor.Stretch,
};
leftPanel.AddChild(treeView);
TreeView = new TreeView(theme)
{
HAnchor = HAnchor.Stretch,
VAnchor = VAnchor.Stretch,
};
leftPanel.AddChild(TreeView);
horizontalSplitter.Panel1.AddChild(leftPanel);
horizontalSplitter.Panel1.AddChild(leftPanel);
contentPanel = new FlowLayoutWidget(FlowDirection.TopToBottom)
{
HAnchor = HAnchor.Fit,
VAnchor = VAnchor.Fit,
Margin = new BorderDouble(left: 2)
};
treeView.AddChild(contentPanel);
}
contentPanel = new FlowLayoutWidget(FlowDirection.TopToBottom)
{
HAnchor = HAnchor.Fit,
VAnchor = VAnchor.Fit,
Margin = new BorderDouble(left: 2)
};
TreeView.AddChild(contentPanel);
}
protected virtual void PerformSearch(string filter)
{
var matches = new List<TreeNode>();
public bool TreeLoaded { get; protected set; }
public TreeView TreeView { get; set; }
Console.WriteLine("Filter for: " + filter);
protected abstract bool NodeMatchesFilter(TreeNode context, string filter);
foreach (var rootNode in contentPanel.Children.OfType<TreeNode>())
{
FilterTree(rootNode, filter, false, matches);
}
protected virtual void OnClearSearch()
{
}
if (matches.Count == 1)
{
treeView.SelectedNode = matches.First();
}
else
{
treeView.SelectedNode = null;
}
protected virtual void PerformSearch(string filter)
{
var matches = new List<TreeNode>();
searchBox.ResetButton.Visible = true;
}
Console.WriteLine("Filter for: " + filter);
private void ClearSearch()
{
foreach (var rootNode in contentPanel.Children.OfType<TreeNode>())
{
ResetTree(rootNode);
}
foreach (var rootNode in contentPanel.Children.OfType<TreeNode>())
{
FilterTree(rootNode, filter, false, matches);
}
searchBox.Text = "";
searchBox.ResetButton.Visible = false;
treeView.SelectedNode = null;
if (matches.Count == 1)
{
TreeView.SelectedNode = matches.First();
}
else
{
TreeView.SelectedNode = null;
}
this.OnClearSearch();
}
searchBox.ResetButton.Visible = true;
}
protected abstract bool FilterTree(TreeNode context, string filter, bool parentVisible, List<TreeNode> matches);
private void ClearSearch()
{
foreach (var rootNode in contentPanel.Children.OfType<TreeNode>())
{
ResetTree(rootNode);
}
private void ResetTree(TreeNode context)
{
context.Visible = true;
context.Expanded = false;
searchBox.Text = "";
searchBox.ResetButton.Visible = false;
TreeView.SelectedNode = null;
foreach (var child in context.Nodes)
{
ResetTree(child);
}
}
this.OnClearSearch();
}
protected virtual void OnClearSearch()
{
}
private bool FilterTree(TreeNode context, string filter, bool parentVisible, List<TreeNode> matches)
{
var hasFilterText = NodeMatchesFilter(context, filter);
public bool TreeLoaded { get; protected set; }
}
}
context.Visible = hasFilterText || parentVisible;
if (context.Visible
&& context.NodeParent != null)
{
context.NodeParent.Visible = true;
context.NodeParent.Expanded = true;
context.Expanded = true;
}
if (context.NodeParent != null
&& hasFilterText)
{
matches.Add(context);
}
bool childMatched = false;
foreach (var child in context.Nodes)
{
childMatched |= FilterTree(child, filter, hasFilterText || parentVisible, matches);
}
bool hasMatch = childMatched || hasFilterText;
if (hasMatch)
{
context.Visible = context.Expanded = true;
}
return hasMatch;
}
private void ResetTree(TreeNode context)
{
context.Visible = true;
context.Expanded = false;
foreach (var child in context.Nodes)
{
ResetTree(child);
}
}
}
}

View file

@ -82,45 +82,12 @@ namespace MatterHackers.MatterControl
base.PerformSearch(filter);
}
protected override bool FilterTree(TreeNode context, string filter, bool parentVisible, List<TreeNode> matches)
{
// Filter against make/model for printers or make for top level nodes
string path = (context as HelpArticleTreeNode)?.HelpArticle.Path;
protected override bool NodeMatchesFilter(TreeNode context, string filter)
{
string path = (context as HelpArticleTreeNode)?.HelpArticle.Path;
bool isSearchMatch = searchHits.Contains(path);
context.Visible = isSearchMatch || parentVisible;
if (context.Visible
&& context.NodeParent != null)
{
context.NodeParent.Visible = true;
context.NodeParent.Expanded = true;
context.Expanded = true;
}
if (context.NodeParent != null
&& isSearchMatch)
{
matches.Add(context);
}
bool childMatched = false;
foreach (var child in context.Nodes)
{
childMatched |= FilterTree(child, filter, isSearchMatch || parentVisible, matches);
}
bool hasMatch = childMatched || isSearchMatch;
if (hasMatch)
{
context.Visible = context.Expanded = true;
}
return hasMatch;
}
return searchHits.Contains(path);
}
protected override void OnClearSearch()
{
@ -268,7 +235,7 @@ namespace MatterHackers.MatterControl
Padding = new BorderDouble(left: theme.DefaultContainerPadding / 2)
};
treeView.AfterSelect += (s, e) =>
TreeView.AfterSelect += (s, e) =>
{
// Hide all sibling content controls
foreach (var child in horizontalSplitter.Panel2.Children)
@ -276,7 +243,7 @@ namespace MatterHackers.MatterControl
child.Visible = false;
}
if (treeView.SelectedNode?.Tag is HelpArticle article)
if (TreeView.SelectedNode?.Tag is HelpArticle article)
{
markdownWidget.MatchingText = this.MatchingText;
@ -296,34 +263,34 @@ namespace MatterHackers.MatterControl
// Show Markdown help page
markdownWidget.Visible = true;
}
else if (treeView.SelectedNode?.Tag is GuiWidget widget)
else if (TreeView.SelectedNode?.Tag is GuiWidget widget)
{
// Show non-markdown page
widget.Visible = true;
}
};
treeView.Load += (s, e) =>
TreeView.Load += (s, e) =>
{
rootNode.Expanded = true;
if (treeView.SelectedNode == null)
if (TreeView.SelectedNode == null)
{
if (string.IsNullOrEmpty(guideKey))
{
treeView.SelectedNode = rootNode.Nodes.FirstOrDefault();
TreeView.SelectedNode = rootNode.Nodes.FirstOrDefault();
}
else
{
if (initialSelection != null)
{
treeView.SelectedNode = initialSelection;
TreeView.SelectedNode = initialSelection;
}
// TODO: Implement or revise .Expanded
if (treeView.SelectedNode != null)
if (TreeView.SelectedNode != null)
{
foreach (var ancestor in treeView.SelectedNode.Parents<TreeNode>())
foreach (var ancestor in TreeView.SelectedNode.Parents<TreeNode>())
{
ancestor.Expanded = true;
}
@ -331,9 +298,9 @@ namespace MatterHackers.MatterControl
}
}
if (treeView.SelectedNode == null)
if (TreeView.SelectedNode == null)
{
treeView.SelectedNode = rootNode;
TreeView.SelectedNode = rootNode;
}
};
@ -348,7 +315,7 @@ namespace MatterHackers.MatterControl
rootNode = ProcessTree(ApplicationController.Instance.HelpArticles);
rootNode.Text = "Help";
rootNode.TreeView = treeView;
rootNode.TreeView = TreeView;
contentPanel.AddChild(rootNode);
@ -402,12 +369,12 @@ namespace MatterHackers.MatterControl
public string ActiveNodePath
{
get => treeView.SelectedNode?.Tag as string;
get => TreeView.SelectedNode?.Tag as string;
set
{
if (nodesByPath.TryGetValue(value, out HelpArticleTreeNode treeNode))
{
treeView.SelectedNode = treeNode;
TreeView.SelectedNode = treeNode;
}
}
}