2018-05-21 13:30:06 -07:00
|
|
|
|
/*
|
2018-05-22 07:00:00 -07:00
|
|
|
|
Copyright (c) 2018, Lars Brubaker, John Lewin
|
2018-05-21 13:30:06 -07:00
|
|
|
|
All rights reserved.
|
|
|
|
|
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
|
|
modification, are permitted provided that the following conditions are met:
|
|
|
|
|
|
|
|
|
|
|
|
1. Redistributions of source code must retain the above copyright notice, this
|
|
|
|
|
|
list of conditions and the following disclaimer.
|
|
|
|
|
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
|
|
|
|
this list of conditions and the following disclaimer in the documentation
|
|
|
|
|
|
and/or other materials provided with the distribution.
|
|
|
|
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
|
|
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
|
|
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
|
|
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
|
|
|
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
|
|
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
|
|
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
|
|
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
|
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
|
|
|
|
The views and conclusions contained in the software and documentation are those
|
|
|
|
|
|
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.ObjectModel;
|
|
|
|
|
|
using System.Linq;
|
2018-05-22 07:00:00 -07:00
|
|
|
|
using MatterHackers.Agg;
|
|
|
|
|
|
using MatterHackers.Agg.Font;
|
|
|
|
|
|
using MatterHackers.Agg.Image;
|
2018-05-22 13:14:23 -07:00
|
|
|
|
using MatterHackers.Agg.Platform;
|
2018-05-22 07:00:00 -07:00
|
|
|
|
using MatterHackers.Agg.UI;
|
2018-06-23 07:12:41 -07:00
|
|
|
|
using MatterHackers.ImageProcessing;
|
2018-05-22 13:14:23 -07:00
|
|
|
|
using MatterHackers.VectorMath;
|
2018-05-21 13:30:06 -07:00
|
|
|
|
|
2018-06-05 08:35:38 -07:00
|
|
|
|
namespace MatterHackers.MatterControl.CustomWidgets
|
2018-05-21 13:30:06 -07:00
|
|
|
|
{
|
|
|
|
|
|
public class TreeNode : FlowLayoutWidget, ICheckbox
|
|
|
|
|
|
{
|
|
|
|
|
|
private GuiWidget content;
|
2018-06-07 16:14:24 -07:00
|
|
|
|
private TreeView _treeView;
|
|
|
|
|
|
private ImageBuffer _image = null;
|
|
|
|
|
|
private TextWidget textWidget;
|
|
|
|
|
|
private TreeExpandWidget expandWidget;
|
|
|
|
|
|
private ImageWidget imageWidget;
|
2018-06-07 16:19:51 -07:00
|
|
|
|
private bool isDirty;
|
2018-05-21 13:30:06 -07:00
|
|
|
|
|
2018-07-12 09:22:28 -07:00
|
|
|
|
public TreeNode(ThemeConfig theme, bool useIcon = true)
|
2018-05-21 13:30:06 -07:00
|
|
|
|
: base(FlowDirection.TopToBottom)
|
|
|
|
|
|
{
|
2018-05-30 16:08:46 -07:00
|
|
|
|
this.HAnchor = HAnchor.Fit | HAnchor.Left;
|
|
|
|
|
|
this.VAnchor = VAnchor.Fit;
|
2018-05-21 13:30:06 -07:00
|
|
|
|
|
2018-05-30 16:08:46 -07:00
|
|
|
|
this.TitleBar = new FlowLayoutWidget();
|
|
|
|
|
|
this.TitleBar.Click += (s, e) =>
|
2018-05-21 13:30:06 -07:00
|
|
|
|
{
|
|
|
|
|
|
if (TreeView != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
TreeView.SelectedNode = this;
|
|
|
|
|
|
}
|
2018-08-07 16:42:40 -07:00
|
|
|
|
|
2018-08-07 17:41:41 -07:00
|
|
|
|
this.TreeView.NotifyItemClicked(this.TitleBar, e);
|
2018-05-21 13:30:06 -07:00
|
|
|
|
};
|
2018-06-07 16:14:24 -07:00
|
|
|
|
this.AddChild(this.TitleBar);
|
2018-05-30 16:08:46 -07:00
|
|
|
|
|
|
|
|
|
|
// add a check box
|
|
|
|
|
|
expandWidget = new TreeExpandWidget(theme)
|
|
|
|
|
|
{
|
|
|
|
|
|
Expandable = GetNodeCount(false) != 0,
|
|
|
|
|
|
VAnchor = VAnchor.Fit,
|
|
|
|
|
|
Height = 16,
|
|
|
|
|
|
Width = 16
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
var expandCheckBox = new CheckBox(expandWidget)
|
|
|
|
|
|
{
|
|
|
|
|
|
Checked = this.Expanded,
|
|
|
|
|
|
VAnchor = VAnchor.Center,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2018-06-06 09:05:59 -07:00
|
|
|
|
this.ExpandedChanged += (s, e) =>
|
2018-05-30 16:08:46 -07:00
|
|
|
|
{
|
|
|
|
|
|
expandWidget.Expanded = this.Expanded;
|
|
|
|
|
|
expandCheckBox.Checked = this.Expanded;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2018-06-06 09:05:59 -07:00
|
|
|
|
expandCheckBox.CheckedStateChanged += (s, e) =>
|
2018-05-30 16:08:46 -07:00
|
|
|
|
{
|
|
|
|
|
|
Expanded = expandCheckBox.Checked;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
this.TitleBar.AddChild(expandCheckBox);
|
|
|
|
|
|
|
2018-06-06 09:05:59 -07:00
|
|
|
|
this.HighlightRegion = new FlowLayoutWidget()
|
2018-06-01 08:54:15 -07:00
|
|
|
|
{
|
|
|
|
|
|
VAnchor = VAnchor.Fit,
|
|
|
|
|
|
HAnchor = HAnchor.Fit,
|
2018-06-06 09:05:59 -07:00
|
|
|
|
Padding = useIcon ? 0 : new BorderDouble(4, 2),
|
2018-06-01 08:54:15 -07:00
|
|
|
|
Selectable = false
|
|
|
|
|
|
};
|
2018-06-06 09:05:59 -07:00
|
|
|
|
this.TitleBar.AddChild(this.HighlightRegion);
|
2018-06-01 08:54:15 -07:00
|
|
|
|
|
2018-05-30 16:08:46 -07:00
|
|
|
|
// add a check box
|
2018-06-06 09:05:59 -07:00
|
|
|
|
if (useIcon)
|
2018-05-30 16:08:46 -07:00
|
|
|
|
{
|
2018-06-06 09:05:59 -07:00
|
|
|
|
_image = new ImageBuffer(16, 16);
|
|
|
|
|
|
|
|
|
|
|
|
this.HighlightRegion.AddChild(imageWidget = new ImageWidget(this.Image)
|
2018-05-30 16:08:46 -07:00
|
|
|
|
{
|
|
|
|
|
|
VAnchor = VAnchor.Center,
|
|
|
|
|
|
BackgroundColor = new Color(theme.Colors.PrimaryTextColor, 12),
|
|
|
|
|
|
Margin = 2,
|
|
|
|
|
|
Selectable = false
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
2018-06-01 08:54:15 -07:00
|
|
|
|
|
2018-06-06 09:05:59 -07:00
|
|
|
|
this.HighlightRegion.AddChild(textWidget = new TextWidget(this.Text, pointSize: theme.DefaultFontSize, textColor: theme.Colors.PrimaryTextColor)
|
2018-05-30 16:08:46 -07:00
|
|
|
|
{
|
|
|
|
|
|
Selectable = false,
|
|
|
|
|
|
AutoExpandBoundsToText = true,
|
|
|
|
|
|
VAnchor = VAnchor.Center
|
|
|
|
|
|
});
|
2018-05-21 13:30:06 -07:00
|
|
|
|
|
|
|
|
|
|
content = new FlowLayoutWidget(FlowDirection.TopToBottom)
|
|
|
|
|
|
{
|
|
|
|
|
|
HAnchor = HAnchor.Fit | HAnchor.Left,
|
|
|
|
|
|
Visible = false, // content starts out not visible
|
|
|
|
|
|
Name = "content",
|
2018-06-01 09:03:32 -07:00
|
|
|
|
Margin = new BorderDouble(12, 3),
|
2018-05-21 13:30:06 -07:00
|
|
|
|
};
|
|
|
|
|
|
AddChild(content);
|
|
|
|
|
|
|
2018-06-07 16:19:51 -07:00
|
|
|
|
Nodes.CollectionChanged += (s, e) => isDirty = true;
|
2018-05-21 13:30:06 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public FlowLayoutWidget TitleBar { get; }
|
|
|
|
|
|
|
2018-06-06 09:05:59 -07:00
|
|
|
|
public FlowLayoutWidget HighlightRegion { get; }
|
2018-06-01 08:54:15 -07:00
|
|
|
|
|
2018-06-07 16:14:24 -07:00
|
|
|
|
// **** Not implemented ****
|
|
|
|
|
|
public void BeginEdit() => throw new NotImplementedException();
|
|
|
|
|
|
public void Collapse(bool collapseChildren) => throw new NotImplementedException();
|
|
|
|
|
|
public void Collapse() => throw new NotImplementedException();
|
|
|
|
|
|
public void EndEdit(bool cancel) => throw new NotImplementedException();
|
|
|
|
|
|
public void EnsureVisible() => throw new NotImplementedException();
|
|
|
|
|
|
public void ExpandAll() => throw new NotImplementedException();
|
|
|
|
|
|
public void Remove() => throw new NotImplementedException();
|
2018-05-21 13:30:06 -07:00
|
|
|
|
|
|
|
|
|
|
public int GetNodeCount(bool includeSubTrees)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (includeSubTrees)
|
|
|
|
|
|
{
|
|
|
|
|
|
return this.Descendants<TreeNode>().Count();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-05-22 13:14:23 -07:00
|
|
|
|
return content?.Children.Where((c) => c is TreeNode).Count() ?? 0;
|
2018-05-21 13:30:06 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-06-23 07:12:41 -07:00
|
|
|
|
public bool AlwaysExpandable
|
|
|
|
|
|
{
|
|
|
|
|
|
get => expandWidget.AlwaysExpandable;
|
|
|
|
|
|
set => expandWidget.AlwaysExpandable = value;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-06-07 16:19:51 -07:00
|
|
|
|
public override void OnDraw(Graphics2D graphics2D)
|
|
|
|
|
|
{
|
2018-06-22 14:10:51 -07:00
|
|
|
|
if (isDirty)
|
2018-06-07 16:19:51 -07:00
|
|
|
|
{
|
|
|
|
|
|
RebuildContentSection();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
base.OnDraw(graphics2D);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-05-21 13:30:06 -07:00
|
|
|
|
public override void OnTextChanged(EventArgs e)
|
|
|
|
|
|
{
|
2018-05-22 13:14:23 -07:00
|
|
|
|
if (textWidget != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
textWidget.Text = this.Text;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-05-21 13:30:06 -07:00
|
|
|
|
base.OnTextChanged(e);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void Toggle()
|
|
|
|
|
|
{
|
|
|
|
|
|
content.Visible = !content.Visible;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void RebuildContentSection()
|
|
|
|
|
|
{
|
|
|
|
|
|
// Remove but don't close all the current nodes
|
|
|
|
|
|
content.RemoveAllChildren();
|
|
|
|
|
|
|
|
|
|
|
|
// Then add them back in (after the change)
|
|
|
|
|
|
foreach (var node in Nodes)
|
|
|
|
|
|
{
|
|
|
|
|
|
node.NodeParent = this;
|
|
|
|
|
|
node.ClearRemovedFlag();
|
|
|
|
|
|
content.AddChild(node);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// If the node count is ending at 0 we removed content and need to rebuild the title bar so it will net have a + in it
|
2018-05-22 13:14:23 -07:00
|
|
|
|
expandWidget.Expandable = GetNodeCount(false) != 0;
|
2018-06-07 16:19:51 -07:00
|
|
|
|
|
|
|
|
|
|
isDirty = false;
|
|
|
|
|
|
|
2018-05-21 13:30:06 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#region Properties
|
|
|
|
|
|
|
|
|
|
|
|
public bool Checked { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
public bool Editing { get; }
|
|
|
|
|
|
|
|
|
|
|
|
public bool Expanded
|
|
|
|
|
|
{
|
2018-05-22 13:14:23 -07:00
|
|
|
|
get => content?.Visible == true;
|
2018-05-21 13:30:06 -07:00
|
|
|
|
set
|
|
|
|
|
|
{
|
|
|
|
|
|
if (content.Visible != value)
|
|
|
|
|
|
{
|
|
|
|
|
|
content.Visible = value;
|
|
|
|
|
|
ExpandedChanged?.Invoke(this, null);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public TreeNode FirstNode { get; }
|
|
|
|
|
|
|
|
|
|
|
|
public ImageBuffer Image
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
return _image;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
set
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_image != value)
|
|
|
|
|
|
{
|
|
|
|
|
|
_image = value;
|
2018-05-22 13:14:23 -07:00
|
|
|
|
|
|
|
|
|
|
if(imageWidget != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
imageWidget.Image = _image;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-05-21 13:30:06 -07:00
|
|
|
|
OnImageChanged(null);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public TreeNode LastNode { get; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets the zero-based depth of the tree node in the TreeView control.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public int Level { get; }
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
// Summary:
|
|
|
|
|
|
// Gets the next sibling tree node.
|
|
|
|
|
|
//
|
|
|
|
|
|
// Returns:
|
|
|
|
|
|
// A TreeNode that represents the next sibling tree node.
|
|
|
|
|
|
public TreeNode NextNode { get; }
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
// Summary:
|
|
|
|
|
|
// Gets the next visible tree node.
|
|
|
|
|
|
//
|
|
|
|
|
|
// Returns:
|
|
|
|
|
|
// A TreeNode that represents the next visible tree node.
|
|
|
|
|
|
public TreeNode NextVisibleNode { get; }
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
// Summary:
|
|
|
|
|
|
// Gets or sets the font that is used to display the text on the tree node label.
|
|
|
|
|
|
//
|
|
|
|
|
|
// Returns:
|
|
|
|
|
|
// The StyledTypeFace that is used to display the text on the tree node label.
|
|
|
|
|
|
public StyledTypeFace NodeFont { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
// Summary:
|
|
|
|
|
|
// Gets the parent tree node of the current tree node.
|
|
|
|
|
|
//
|
|
|
|
|
|
// Returns:
|
|
|
|
|
|
// A TreeNode that represents the parent of the current tree
|
|
|
|
|
|
// node.
|
|
|
|
|
|
public TreeNode NodeParent { get; protected set; }
|
|
|
|
|
|
|
|
|
|
|
|
public ObservableCollection<TreeNode> Nodes { get; } = new ObservableCollection<TreeNode>();
|
|
|
|
|
|
|
|
|
|
|
|
public int PointSize { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
// Summary:
|
|
|
|
|
|
// Gets the previous sibling tree node.
|
|
|
|
|
|
//
|
|
|
|
|
|
// Returns:
|
|
|
|
|
|
// A TreeNode that represents the previous sibling tree node.
|
|
|
|
|
|
public TreeNode PrevNode { get; }
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
// Summary:
|
|
|
|
|
|
// Gets the previous visible tree node.
|
|
|
|
|
|
//
|
|
|
|
|
|
// Returns:
|
|
|
|
|
|
// A TreeNode that represents the previous visible tree node.
|
|
|
|
|
|
public TreeNode PrevVisibleNode { get; }
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
// Summary:
|
|
|
|
|
|
// Gets a value indicating whether the tree node is in the selected state.
|
|
|
|
|
|
//
|
|
|
|
|
|
// Returns:
|
|
|
|
|
|
// true if the tree node is in the selected state; otherwise, false.
|
|
|
|
|
|
public bool Selected
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
if (TreeView != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return TreeView.SelectedNode == this;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
// Summary:
|
|
|
|
|
|
// Gets or sets the image list index value of the image that is displayed when the
|
|
|
|
|
|
// tree node is in the selected state.
|
|
|
|
|
|
//
|
|
|
|
|
|
// Returns:
|
|
|
|
|
|
// A zero-based index value that represents the image position in an ImageList.
|
|
|
|
|
|
public ImageBuffer SelectedImage { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
// Summary:
|
|
|
|
|
|
// Gets or sets the index of the image that is used to indicate the state of the
|
|
|
|
|
|
// TreeNode when the parent TreeView has
|
|
|
|
|
|
// its TreeView.CheckBoxes property set to false.
|
|
|
|
|
|
//
|
|
|
|
|
|
// Returns:
|
|
|
|
|
|
// The index of the image that is used to indicate the state of the TreeNode.
|
|
|
|
|
|
//
|
|
|
|
|
|
// Exceptions:
|
|
|
|
|
|
// T:System.ArgumentOutOfRangeException:
|
|
|
|
|
|
// The specified index is less than -1 or greater than 14.
|
|
|
|
|
|
public ImageBuffer StateImage { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
// Summary:
|
|
|
|
|
|
// Gets or sets the object that contains data about the tree node.
|
|
|
|
|
|
//
|
|
|
|
|
|
// Returns:
|
|
|
|
|
|
// An System.Object that contains data about the tree node. The default is null.
|
|
|
|
|
|
public object Tag { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
public Color TextColor { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
// Summary:
|
|
|
|
|
|
// Gets the parent tree view that the tree node is assigned to.
|
|
|
|
|
|
//
|
|
|
|
|
|
// Returns:
|
|
|
|
|
|
// A TreeView that represents the parent tree view that the
|
|
|
|
|
|
// tree node is assigned to, or null if the node has not been assigned to a tree
|
|
|
|
|
|
// view.
|
2018-06-01 08:49:02 -07:00
|
|
|
|
|
|
|
|
|
|
public virtual TreeView TreeView
|
|
|
|
|
|
{
|
|
|
|
|
|
get => _treeView ?? NodeParent.TreeView;
|
|
|
|
|
|
set => _treeView = value;
|
|
|
|
|
|
}
|
2018-05-21 13:30:06 -07:00
|
|
|
|
|
|
|
|
|
|
private void OnImageChanged(EventArgs args)
|
|
|
|
|
|
{
|
|
|
|
|
|
ImageChanged?.Invoke(this, null);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion Properties
|
|
|
|
|
|
|
|
|
|
|
|
#region Events
|
|
|
|
|
|
|
|
|
|
|
|
public event EventHandler CheckedStateChanged;
|
|
|
|
|
|
|
|
|
|
|
|
public event EventHandler ExpandedChanged;
|
|
|
|
|
|
|
|
|
|
|
|
public event EventHandler ImageChanged;
|
|
|
|
|
|
|
|
|
|
|
|
#endregion Events
|
2018-05-22 13:14:23 -07:00
|
|
|
|
|
|
|
|
|
|
private class TreeExpandWidget : FlowLayoutWidget
|
|
|
|
|
|
{
|
|
|
|
|
|
private ImageBuffer arrowRight;
|
|
|
|
|
|
private ImageBuffer arrowDown;
|
|
|
|
|
|
private ImageBuffer placeholder;
|
|
|
|
|
|
private IconButton imageButton = null;
|
|
|
|
|
|
|
|
|
|
|
|
public TreeExpandWidget(ThemeConfig theme)
|
|
|
|
|
|
{
|
|
|
|
|
|
arrowRight = AggContext.StaticData.LoadIcon("fa-angle-right_12.png", theme.InvertIcons);
|
|
|
|
|
|
arrowDown = AggContext.StaticData.LoadIcon("fa-angle-down_12.png", theme.InvertIcons);
|
|
|
|
|
|
placeholder = new ImageBuffer(16, 16);
|
|
|
|
|
|
|
|
|
|
|
|
//this.VAnchor = VAnchor.Center;
|
|
|
|
|
|
this.Margin = new BorderDouble(right: 5);
|
|
|
|
|
|
|
|
|
|
|
|
imageButton = new IconButton(placeholder, theme)
|
|
|
|
|
|
{
|
|
|
|
|
|
MinimumSize = new Vector2(16, 16),
|
|
|
|
|
|
VAnchor = VAnchor.Center,
|
|
|
|
|
|
Selectable = false,
|
|
|
|
|
|
Width = 16,
|
|
|
|
|
|
Height = 16
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
this.AddChild(imageButton);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-06-23 07:12:41 -07:00
|
|
|
|
private bool _alwaysExpandable;
|
|
|
|
|
|
public bool AlwaysExpandable
|
|
|
|
|
|
{
|
|
|
|
|
|
get => _alwaysExpandable;
|
|
|
|
|
|
set
|
|
|
|
|
|
{
|
|
|
|
|
|
imageButton.SetIcon((_expanded) ? arrowDown : arrowRight);
|
|
|
|
|
|
_alwaysExpandable = value;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-05-22 13:14:23 -07:00
|
|
|
|
private bool? _expandable = null;
|
|
|
|
|
|
public bool Expandable
|
|
|
|
|
|
{
|
2018-06-23 07:12:41 -07:00
|
|
|
|
get => _expandable == true || this.AlwaysExpandable;
|
2018-05-22 13:14:23 -07:00
|
|
|
|
set
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_expandable != value)
|
|
|
|
|
|
{
|
|
|
|
|
|
_expandable = value;
|
|
|
|
|
|
|
|
|
|
|
|
if (!value)
|
|
|
|
|
|
{
|
|
|
|
|
|
imageButton.SetIcon(placeholder);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
imageButton.SetIcon((this.Expanded) ? arrowDown : arrowRight);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private bool _expanded;
|
|
|
|
|
|
public bool Expanded
|
|
|
|
|
|
{
|
|
|
|
|
|
get => _expanded;
|
|
|
|
|
|
set
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_expanded != value)
|
|
|
|
|
|
{
|
|
|
|
|
|
_expanded = value;
|
|
|
|
|
|
|
|
|
|
|
|
if (!this.Expandable)
|
|
|
|
|
|
{
|
|
|
|
|
|
imageButton.SetIcon(placeholder);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
imageButton.SetIcon((_expanded) ? arrowDown : arrowRight);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2018-05-21 13:30:06 -07:00
|
|
|
|
}
|
|
|
|
|
|
}
|