Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
using System;
using System.Collections.Generic;
using System.Linq;
using Godot;
using GodotExt;
using JetBrains.Annotations;
using OpenScadGraphEditor.Utils;
namespace OpenScadGraphEditor.Widgets.ProjectTree
{
[UsedImplicitly]
public class ProjectTree : Tree
{
/// <summary>
/// The mapping of ids to nodes.
/// </summary>
private Dictionary<string, ProjectTreeEntry> _entries = new Dictionary<string, ProjectTreeEntry>();
/// <summary>
/// The expanded state of the nodes.
/// </summary>
private Dictionary<string, bool> _expanded = new Dictionary<string, bool>();
// event called when an item in the list is double-clicked
public event Action<ProjectTreeEntry> ItemActivated;
// event called when an item in the tree is right-clicked
public event Action<ProjectTreeEntry, Vector2> ItemContextMenuRequested;
public override void _Ready()
{
AllowRmbSelect = true;
HideRoot = true;
this.Connect("item_activated")
.To(this, nameof(OnItemActivated));
this.Connect("item_rmb_selected")
.To(this, nameof(OnItemRmbSelected));
this.Connect("item_collapsed")
.To(this, nameof(OnItemCollapsed));
}
public void Setup(List<ProjectTreeEntry> entries)
{
Clear();
_entries.Clear();
var root = GetRoot() ?? CreateItem();
ApplyChildren(root, entries);
// remove all entries from the expansion state that are no longer in the tree
_expanded.Keys
.Except(_entries.Keys)
.ToList() // avoid concurrent modification
.ForAll(id => _expanded.Remove(id));
}
private void ApplyChildren(TreeItem root, List<ProjectTreeEntry> entries)
{
// kill all items.
var item = root.GetChildren();
while (item != null)
{
var newItem = item.GetNext();
item.Free();
item = newItem;
}
// now add the new items.
foreach (var entry in entries)
{
_entries[entry.Id] = entry;
var newItem = CreateItem(root);
newItem.DisableFolding = !entry.CanBeCollapsed;
newItem.SetMetadata(0, entry.Id);
newItem.SetText(0, entry.Title);
newItem.SetIcon(0, entry.Icon);
newItem.SetIconMaxWidth(0, 32);
if (_expanded.TryGetValue(entry.Id, out var expanded))
{
newItem.Collapsed = !expanded && entry.CanBeCollapsed;
}
else
{
newItem.Collapsed = entry.CanBeCollapsed;
}
// recurse
ApplyChildren(newItem, entry.Children);
}
}
public ProjectTreeEntry GetEntry(string index)
{
return _entries[index];
}
[CanBeNull]
private ProjectTreeEntry GetSelectedEntry()
{
var selectedItem = GetSelected();
if (selectedItem == null)
{
return null;
}
var id = selectedItem.GetMetadata(0) as string;
if (id == null)
{
return null;
}
return _entries.TryGetValue(id, out var result) ? result : null;
}
private void OnItemCollapsed(TreeItem item)
{
if (!(item.GetMetadata(0) is string id))
{
return;
}
_expanded[id] = !item.Collapsed;
}
public override object GetDragData(Vector2 position)
{
var entry = GetSelectedEntry();
if (entry == null || !entry.TryGetDragData(out var data))
{
return null;
}
var panel = new PanelContainer();
var label = new Label();
panel.AddChild(label);
label.Text = entry.Title;
SetDragPreview(panel);
return new DragData(data);
}
private void OnItemActivated()
{
var selectedEntry = GetSelectedEntry();
if (selectedEntry == null)
{
return;
}
ItemActivated?.Invoke(selectedEntry);
}
private void OnItemRmbSelected([UsedImplicitly] Vector2 _)
{
var selectedEntry = GetSelectedEntry();
if (selectedEntry == null)
{
return;
}
ItemContextMenuRequested?.Invoke(selectedEntry, GetGlobalMousePosition());
}
}
}7192f5216This file is pinned to the selected release. Download the original to open it in your design tool.