Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Example] Add shapes drawing example application #115

Merged
merged 19 commits into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Examples/Nodify.Playground/PlaygroundViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public PlaygroundViewModel()
Settings.PropertyChanged += OnSettingsChanged;
}

private void OnSettingsChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
private void OnSettingsChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(PlaygroundSettings.ShouldConnectNodes))
OnPropertyChanged(nameof(ConnectNodesText));
Expand Down
226 changes: 226 additions & 0 deletions Examples/Nodify.Shapes/App.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
<Application x:Class="Nodify.Shapes.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<Style x:Key="IconButton"
TargetType="Button"
BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Background"
Value="Transparent" />
<Setter Property="BorderBrush"
Value="Transparent" />
<Setter Property="Padding"
Value="2" />
<Setter Property="Cursor"
Value="Hand" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="PART_Border"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
Padding="{TemplateBinding Padding}"
CornerRadius="3"
SnapsToDevicePixels="True">
<ContentPresenter x:Name="contentPresenter"
Focusable="False"
Margin="{TemplateBinding Padding}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsEnabled"
Value="False">
<Setter Property="Opacity"
Value="0.4" />
</Trigger>
</Style.Triggers>
</Style>

<!--ICONS-->

<Viewbox x:Key="MinusIcon"
Stretch="Fill">
<Grid>
<Path StrokeLineJoin="Round"
StrokeStartLineCap="Round"
StrokeEndLineCap="Round"
Data="M0 0h24v24H0z" />
<Path Stroke="White"
StrokeLineJoin="Round"
StrokeStartLineCap="Round"
StrokeEndLineCap="Round"
Data="M5 12h14" />
</Grid>
</Viewbox>
miroiu marked this conversation as resolved.
Show resolved Hide resolved

<Viewbox x:Key="PlusIcon"
Stretch="Fill">
<Grid>
<Path StrokeLineJoin="Round"
StrokeStartLineCap="Round"
StrokeEndLineCap="Round"
Data="M0 0h24v24H0z" />
<Path Stroke="White"
StrokeLineJoin="Round"
StrokeStartLineCap="Round"
StrokeEndLineCap="Round"
Data="M12 5v14M5 12h14" />
</Grid>
</Viewbox>

<Viewbox x:Key="MaximizeIcon"
Stretch="Fill">
<Grid>
<Path StrokeLineJoin="Round"
StrokeStartLineCap="Round"
StrokeEndLineCap="Round"
Data="M0 0h24v24H0z" />
<Path Stroke="White"
StrokeLineJoin="Round"
StrokeStartLineCap="Round"
StrokeEndLineCap="Round"
Data="M4 8V6a2 2 0 0 1 2-2h2M4 16v2a2 2 0 0 0 2 2h2M16 4h2a2 2 0 0 1 2 2v2M16 20h2a2 2 0 0 0 2-2v-2" />
</Grid>
</Viewbox>

<Viewbox x:Key="CursorIcon"
Stretch="Fill"
Margin="2">
<Path Stroke="White"
StrokeThickness="1"
StrokeLineJoin="Round"
StrokeStartLineCap="Round"
StrokeEndLineCap="Round"
Data="M.256.255a.874.874 0 0 0-.18.974l4.753 17.114a.875.875 0 0 0 1.603-.012L10 10l8.334-3.57a.875.875 0 0 0 .01-1.601L1.23.075a.874.874 0 0 0-.974.18Z" />
</Viewbox>

<Viewbox x:Key="CircleIcon"
Stretch="Fill">
<Grid>
<Path StrokeLineJoin="Round"
StrokeStartLineCap="Round"
StrokeEndLineCap="Round"
Data="M0 0h24v24H0z" />
<Path Stroke="White"
StrokeLineJoin="Round"
StrokeStartLineCap="Round"
StrokeEndLineCap="Round"
Data="M3 12a9 9 0 1 0 18 0 9 9 0 1 0-18 0" />
</Grid>
</Viewbox>

<Viewbox x:Key="SquareIcon" Stretch="Fill">
<Grid>
<Path StrokeLineJoin="Round"
StrokeStartLineCap="Round"
StrokeEndLineCap="Round"
Data="M0 0h24v24H0z" />
<Path Stroke="White"
StrokeLineJoin="Round"
StrokeStartLineCap="Round"
StrokeEndLineCap="Round"
Data="M3 5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
</Grid>
</Viewbox>

<Viewbox x:Key="TriangleIcon" Stretch="Fill">
<Grid>
<Path StrokeLineJoin="Round"
StrokeStartLineCap="Round"
StrokeEndLineCap="Round"
Data="M0 0h24v24H0z" />
<Path Stroke="White"
StrokeLineJoin="Round"
StrokeStartLineCap="Round"
StrokeEndLineCap="Round"
Data="M10.363 3.591 2.257 17.125a1.914 1.914 0 0 0 1.636 2.871h16.214a1.914 1.914 0 0 0 1.636-2.87L13.637 3.59a1.914 1.914 0 0 0-3.274 0z" />
</Grid>
</Viewbox>

<Viewbox x:Key="ArrowBackIcon" Stretch="Fill">
<Grid>
<Path StrokeLineJoin="Round"
StrokeStartLineCap="Round"
StrokeEndLineCap="Round"
Data="M0 0h24v24H0z" />
<Path Stroke="White"
StrokeLineJoin="Round"
StrokeStartLineCap="Round"
StrokeEndLineCap="Round"
Data="m9 14-4-4 4-4" />
<Path Stroke="White"
StrokeLineJoin="Round"
StrokeStartLineCap="Round"
StrokeEndLineCap="Round"
Data="M5 10h11a4 4 0 1 1 0 8h-1" />
</Grid>
</Viewbox>

<Viewbox x:Key="ArrowForwardIcon" Stretch="Fill">
<Grid>
<Path StrokeLineJoin="Round"
StrokeStartLineCap="Round"
StrokeEndLineCap="Round"
Data="M0 0h24v24H0z" />
<Path Stroke="White"
StrokeLineJoin="Round"
StrokeStartLineCap="Round"
StrokeEndLineCap="Round"
Data="m15 14 4-4-4-4" />
<Path Stroke="White"
StrokeLineJoin="Round"
StrokeStartLineCap="Round"
StrokeEndLineCap="Round"
Data="M19 10H8a4 4 0 1 0 0 8h1" />
</Grid>
</Viewbox>

<Viewbox x:Key="LockIcon"
Stretch="Fill">
<Grid>
<Path StrokeLineJoin="Round"
StrokeStartLineCap="Round"
StrokeEndLineCap="Round"
Data="M0 0h24v24H0z" />
<Path Stroke="White"
StrokeLineJoin="Round"
StrokeStartLineCap="Round"
StrokeEndLineCap="Round"
Data="M5 13a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2v-6z" />
<Path Stroke="White"
StrokeLineJoin="Round"
StrokeStartLineCap="Round"
StrokeEndLineCap="Round"
Data="M11 16a1 1 0 1 0 2 0 1 1 0 0 0-2 0M8 11V7a4 4 0 1 1 8 0v4" />
</Grid>
</Viewbox>

<Viewbox x:Key="LockOpenIcon"
Stretch="Fill">
<Grid>
<Path StrokeLineJoin="Round"
StrokeStartLineCap="Round"
StrokeEndLineCap="Round"
Data="M0 0h24v24H0z" />
<Path Stroke="White"
StrokeLineJoin="Round"
StrokeStartLineCap="Round"
StrokeEndLineCap="Round"
Data="M5 13a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2z" />
<Path Stroke="White"
StrokeLineJoin="Round"
StrokeStartLineCap="Round"
StrokeEndLineCap="Round"
Data="M11 16a1 1 0 1 0 2 0 1 1 0 1 0-2 0M8 11V6a4 4 0 0 1 8 0" />
</Grid>
</Viewbox>
</ResourceDictionary>
</Application.Resources>
</Application>
16 changes: 16 additions & 0 deletions Examples/Nodify.Shapes/App.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System.Windows;
using System.Windows.Input;

namespace Nodify.Shapes
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
public App()
{
EditorGestures.Mappings.Connection.Disconnect.Value = new AnyGesture(new MouseGesture(MouseAction.LeftClick, ModifierKeys.Alt), new MouseGesture(MouseAction.RightClick));
}
}
}
9 changes: 9 additions & 0 deletions Examples/Nodify.Shapes/AppShellViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Nodify.Shapes.Canvas;

namespace Nodify.Shapes
{
public class AppShellViewModel : ObservableObject
{
public CanvasViewModel Canvas { get; } = new CanvasViewModel();
}
}
10 changes: 10 additions & 0 deletions Examples/Nodify.Shapes/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System.Windows;

[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
93 changes: 93 additions & 0 deletions Examples/Nodify.Shapes/Canvas/CanvasToolbarViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
using System;
using System.Linq;
using System.Windows;
using System.Windows.Input;

namespace Nodify.Shapes.Canvas
{
public enum CanvasTool
{
None,
Ellipse,
Rectangle,
Triangle
}

public class CanvasToolbarViewModel : ObservableObject
{
public static readonly CanvasTool[] AvailableTools = Enum.GetValues(typeof(CanvasTool)).Cast<CanvasTool>().ToArray();

internal static readonly EditorGestures EditorGestures = new EditorGestures();

private bool _locked;
public bool Locked
{
get => _locked;
set
{
if (SetProperty(ref _locked, value))
{
var newMappings = _locked ? LockedGestureMappings.Instance : EditorGestures;
EditorGestures.Mappings.Apply(newMappings);

if (_locked)
{
_selectedTool = CanvasTool.None;
OnPropertyChanged(nameof(SelectedTool));
}
}
}
}

public ICommand ToggleLockCommand { get; set; }

private CanvasTool _selectedTool = CanvasTool.None;
public CanvasTool SelectedTool
{
get => _selectedTool;
set
{
if (SetProperty(ref _selectedTool, Locked ? CanvasTool.None : value))
{
var newMappings = _selectedTool == CanvasTool.None ? EditorGestures : DrawingGesturesMappings.Instance;
EditorGestures.Mappings.Apply(newMappings);
}
}
}

public CanvasViewModel Canvas { get; }

public CanvasToolbarViewModel(CanvasViewModel canvas)
{
// copy any user modifications
EditorGestures.Apply(EditorGestures.Mappings);

ToggleLockCommand = new DelegateCommand(() => Locked = !Locked);
Canvas = canvas;
}

public ShapeViewModel CreateShapeAtLocation(Point location)
{
using (Canvas.UndoRedo.Batch("Create shape"))
{
ShapeViewModel shape = SelectedTool switch
{
CanvasTool.Ellipse => new EllipseViewModel(),
CanvasTool.Rectangle => new RectangleViewModel(),
CanvasTool.Triangle => new TriangleViewModel(),
CanvasTool.None => throw new InvalidOperationException("Cannot draw in this state"),
_ => throw new NotImplementedException(nameof(CanvasTool)),
};

shape.Location = location;
shape.Text = "Double click to edit";

Canvas.AddShape(shape);
Canvas.SelectedShapes.Clear();
Canvas.SelectedShapes.Add(shape);

return shape;
}
}
}
}
Loading
Loading