Skip to content

Commit

Permalink
Refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
VoidXH committed May 5, 2024
1 parent 42a5191 commit 6043702
Show file tree
Hide file tree
Showing 5 changed files with 208 additions and 134 deletions.
115 changes: 115 additions & 0 deletions CavernSamples/FilterStudio/Graphs/ManipulatableGraph.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
using Microsoft.Msagl.Drawing;
using Microsoft.Msagl.WpfGraphControl;
using System;
using System.Linq;
using System.Windows.Controls;
using System.Windows.Input;

namespace FilterStudio.Graphs {
/// <summary>
/// A <see cref="Graph"/>-displaying control with a wide range of manipulation features for the user.
/// </summary>
public class ManipulatableGraph : ScrollViewer {
/// <summary>
/// Called when the user left-clicks anywhere in the graph control bounds, passes the clicked edge or node.
/// If the click doesn't hit any graph element, the <see cref="object"/> will be null.
/// </summary>
/// <remarks>The clicked node is not necessarily selected. To check that, use <see cref="SelectedNode"/>.</remarks>
public event Action<object> OnLeftClick;

/// <summary>
/// Called when the user right-clicks anywhere in the graph control bounds, passes the clicked edge or node.
/// If the click doesn't hit any graph element, the <see cref="object"/> will be null.
/// </summary>
public event Action<object> OnRightClick;

/// <summary>
/// The currently selected node is determined by border line thickness.
/// </summary>
public StyledNode SelectedNode => (StyledNode)viewer.Graph?.Nodes.FirstOrDefault(x => x.Attr.LineWidth > 1);

/// <summary>
/// Handle to MSAGL.
/// </summary>
public Graph Graph {
get => viewer.Graph;
set {
viewer.Graph = value;
OnLeftClick?.Invoke(null); // Says nothing is selected now, nothing has a thick border on redraw
}
}

/// <summary>
/// An inner panel that acts as a window for the graph. The <see cref="ScrollViewer"/> is the curtain,
/// it keeps the graph in the bounds of the control wherever the user moves it.
/// </summary>
readonly DockPanel panel;

/// <summary>
/// Handles displaying and manipulating the graph.
/// </summary>
readonly GraphViewer viewer;

/// <summary>
/// A <see cref="Graph"/>-displaying control with a wide range of manipulation features for the user.
/// </summary>
public ManipulatableGraph() {
VerticalScrollBarVisibility = ScrollBarVisibility.Hidden;
panel = new DockPanel();
AddChild(panel);
viewer = new GraphViewer();
viewer.BindToPanel(panel);
}

/// <summary>
/// Force select a node on the graph by <paramref name="uid"/>.
/// </summary>
public void SelectNode(string uid) {
Node node = viewer.Graph.FindNode(uid);
node.Attr.LineWidth = 2;
Dispatcher.BeginInvoke(() => { // Call after the graph was redrawn
OnLeftClick(node);
});
}

/// <summary>
/// Context menu options pass the selected node in their <paramref name="sender"/> parameter. Use this function to get the actually
/// selected node, not the one that was last left-clicked. For edges, the source node will be selected (new nodes are intuitively
/// then can be added after that to be between the edge's endpoints).
/// </summary>
public StyledNode GetSelectedNode(object sender) {
if (sender is StyledNode hoverNode) { // Context menu, node = parallel
return hoverNode;
} else if (sender is Edge edge) { // Context menu, edge = inline
return (StyledNode)viewer.Graph.FindNode(edge.Source);
} else { // Window
return SelectedNode;
}
}

/// <summary>
/// Hack to provide a Click event for MSAGL's WPF library.
/// </summary>
protected override void OnPreviewMouseUp(MouseButtonEventArgs e) {
IViewerObject element = viewer.ObjectUnderMouseCursor;
object param = null;
if (element is IViewerNode vnode) {
param = vnode.Node;
} else if (element is IViewerEdge edge) {
param = edge.Edge;
}

if (e.ChangedButton == MouseButton.Left) {
StyledNode node = SelectedNode;
if (node != null) {
node.Attr.LineWidth = 1; // Nodes selected with SelectNode are not actually selected, just were widened
}
Dispatcher.BeginInvoke(() => { // Call after the graph has handled it
OnLeftClick?.Invoke(param);
});
} else {
OnRightClick?.Invoke(param);
}
}
}
}
28 changes: 7 additions & 21 deletions CavernSamples/FilterStudio/MainWindow.AddFilters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,25 +44,11 @@ void AddBiquad(object sender, RoutedEventArgs e) {
}
}

/// <summary>
/// Context menu options pass the selected node in their <paramref name="sender"/> parameter. Use this function to get the actually
/// selected node, not the one that was last left-clicked.
/// </summary>
StyledNode GetSelectedNode(object sender) {
if (sender is IViewerNode hoverNode) { // Context menu, node = parallel
return (StyledNode)hoverNode.Node;
} else if (sender is IViewerEdge edge) { // Context menu, edge = inline
return (StyledNode)viewer.Graph.FindNode(edge.Edge.Source);
} else { // Window
return SelectedFilter;
}
}

/// <summary>
/// Checks to perform before a filter can be added. If an error happens, returns its message, otherwise null.
/// </summary>
string PreFilterAddingChecks(object sender) {
StyledNode node = GetSelectedNode(sender);
StyledNode node = graph.GetSelectedNode(sender);
if (node == null) {
return (string)language["NNode"];
} else if (node.Filter.Filter is OutputChannel) {
Expand All @@ -87,14 +73,14 @@ void AddFilter(object sender, Filter filter) {
/// When the <see cref="PreFilterAddingChecks"/> have passed, and the filter has been determined, add it to the graph.
/// </summary>
void FinalizeFilter(object sender, Filter filter) {
if (sender is IViewerNode node) { // Context menu, node = parallel
((StyledNode)node.Node).Filter.AddChild(filter);
} else if (sender is IViewerEdge edge) { // Context menu, edge = inline
((StyledNode)viewer.Graph.FindNode(edge.Edge.Source)).Filter.AddBeforeChildren(filter);
if (sender is StyledNode node) { // Context menu, node = parallel
node.Filter.AddChild(filter);
} else if (sender is Edge edge) { // Context menu, edge = inline
((StyledNode)graph.Graph.FindNode(edge.Source)).Filter.AddBeforeChildren(filter);
} else if (filterShift.IsChecked || Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)) {
SelectedFilter.Filter.AddChild(filter);
graph.SelectedNode.Filter.AddChild(filter);
} else {
SelectedFilter.Filter.AddBeforeChildren(filter);
graph.SelectedNode.Filter.AddBeforeChildren(filter);
}
ReloadGraph();
}
Expand Down
55 changes: 55 additions & 0 deletions CavernSamples/FilterStudio/MainWindow.Graph.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using Microsoft.Msagl.Drawing;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
using System;

using VoidX.WPF;

using FilterStudio.Graphs;

namespace FilterStudio {
// Handlers of the filter graph control
partial class MainWindow {
/// <summary>
/// When selecting a node, open it for modification.
/// </summary>
void GraphLeftClick(object element) {
StyledNode node = graph.SelectedNode;
if (node == null || node.Filter == null) {
selectedNode.Text = (string)language["NNode"];
properties.ItemsSource = Array.Empty<object>();
return;
}

selectedNode.Text = node.LabelText;
properties.ItemsSource = new ObjectToDataGrid(node.Filter.Filter, FilterPropertyChanged, e => Error(e.Message));
}

/// <summary>
/// Display the context menu when the graph is right clicked.
/// </summary>
void GraphRightClick(object element) {
List<(string, Action<object, RoutedEventArgs>)> menuItems = [
((string)language["FLabe"], (_, e) => AddLabel(element, e)),
((string)language["FGain"], (_, e) => AddGain(element, e)),
((string)language["FDela"], (_, e) => AddDelay(element, e)),
((string)language["FBiqu"], (_, e) => AddBiquad(element, e)),
];
if (element is Node) {
menuItems.Add((null, null));
menuItems.Add(((string)language["CoDel"], (_, e) => DeleteNode(element, e)));
}
QuickContextMenu.Show(menuItems);
}

/// <summary>
/// Updates the graph based on the <see cref="rootNodes"/>.
/// </summary>
void ReloadGraph() {
if (rootNodes != null) {
graph.Graph = Parsing.ParseConfigurationFile(rootNodes, Parsing.ParseBackground((SolidColorBrush)Background));
}
}
}
}
5 changes: 2 additions & 3 deletions CavernSamples/FilterStudio/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:fs="clr-namespace:FilterStudio.Graphs"
mc:Ignorable="d"
Title="Cavern Filter Studio" Width="1024" Height="768" Background="#696969">
<Window.Resources>
Expand Down Expand Up @@ -41,9 +42,7 @@
<DataGrid Name="properties" CanUserReorderColumns="False" CanUserResizeRows="False" CanUserSortColumns="False" ColumnWidth="*"/>
</DockPanel>
<Grid>
<ScrollViewer VerticalScrollBarVisibility="Disabled">
<DockPanel Name="graph" PreviewMouseUp="GraphClick"/>
</ScrollViewer>
<fs:ManipulatableGraph x:Name="graph"/>
<TextBlock Name="help1" Margin="10,0,0,30" HorizontalAlignment="Left" VerticalAlignment="Bottom" Text="{StaticResource Help1}"/>
<TextBlock Name="help2" Margin="10,0,0,10" HorizontalAlignment="Left" VerticalAlignment="Bottom" Text="{StaticResource Help2}"/>
</Grid>
Expand Down
Loading

0 comments on commit 6043702

Please sign in to comment.