diff --git a/Dock.slnx b/Dock.slnx index 3466da765..de52a95c2 100644 --- a/Dock.slnx +++ b/Dock.slnx @@ -49,6 +49,7 @@ + diff --git a/samples/DockFigmaSample/App.axaml b/samples/DockFigmaSample/App.axaml new file mode 100644 index 000000000..6362c15f2 --- /dev/null +++ b/samples/DockFigmaSample/App.axaml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + diff --git a/samples/DockFigmaSample/App.axaml.cs b/samples/DockFigmaSample/App.axaml.cs new file mode 100644 index 000000000..91c46c21e --- /dev/null +++ b/samples/DockFigmaSample/App.axaml.cs @@ -0,0 +1,61 @@ +using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Markup.Xaml; +using DockFigmaSample.ViewModels; +using DockFigmaSample.ViewModels.Documents; +using DockFigmaSample.ViewModels.Tools; +using DockFigmaSample.Views; +using DockFigmaSample.Views.Documents; +using DockFigmaSample.Views.Tools; +using ReactiveUI; +using Splat; + +namespace DockFigmaSample; + +public class App : Application +{ + public override void Initialize() + { + AvaloniaXamlLoader.Load(this); + RegisterViews(); + } + + private static void RegisterViews() + { + Locator.CurrentMutable.Register>(() => new MainWindow()); + Locator.CurrentMutable.Register>(() => new HomeView()); + Locator.CurrentMutable.Register>(() => new WorkspaceView()); + + Locator.CurrentMutable.Register>(() => new CanvasDocumentView()); + Locator.CurrentMutable.Register>(() => new DesignCanvasView()); + Locator.CurrentMutable.Register>(() => new PrototypeCanvasView()); + Locator.CurrentMutable.Register>(() => new InspectCanvasView()); + + Locator.CurrentMutable.Register>(() => new ToolbarToolView()); + Locator.CurrentMutable.Register>(() => new LayersToolView()); + Locator.CurrentMutable.Register>(() => new AssetsToolView()); + Locator.CurrentMutable.Register>(() => new InspectorToolView()); + Locator.CurrentMutable.Register>(() => new InspectorDesignView()); + Locator.CurrentMutable.Register>(() => new InspectorPrototypeView()); + Locator.CurrentMutable.Register>(() => new InspectorInspectView()); + Locator.CurrentMutable.Register>(() => new CommentsToolView()); + } + + public override void OnFrameworkInitializationCompleted() + { + var vm = new MainWindowViewModel(); + + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + desktop.MainWindow = new MainWindow + { + DataContext = vm + }; + } + + base.OnFrameworkInitializationCompleted(); +#if DEBUG + this.AttachDevTools(); +#endif + } +} diff --git a/samples/DockFigmaSample/Converters/EnumEqualsConverter.cs b/samples/DockFigmaSample/Converters/EnumEqualsConverter.cs new file mode 100644 index 000000000..8c8589889 --- /dev/null +++ b/samples/DockFigmaSample/Converters/EnumEqualsConverter.cs @@ -0,0 +1,29 @@ +using System; +using System.Globalization; +using Avalonia; +using Avalonia.Data.Converters; + +namespace DockFigmaSample.Converters; + +public class EnumEqualsConverter : IValueConverter +{ + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + if (value is null || parameter is null) + { + return false; + } + + return value.Equals(parameter); + } + + public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) + { + if (value is true && parameter is not null) + { + return parameter; + } + + return AvaloniaProperty.UnsetValue; + } +} diff --git a/samples/DockFigmaSample/DockFigmaSample.csproj b/samples/DockFigmaSample/DockFigmaSample.csproj new file mode 100644 index 000000000..624a26718 --- /dev/null +++ b/samples/DockFigmaSample/DockFigmaSample.csproj @@ -0,0 +1,25 @@ + + + net10.0 + WinExe + false + false + enable + OnlyProperties + true + $(BaseIntermediateOutputPath)\GeneratedFiles + + + + + + + + + + + + + + + diff --git a/samples/DockFigmaSample/Models/AssetSwatch.cs b/samples/DockFigmaSample/Models/AssetSwatch.cs new file mode 100644 index 000000000..8686c713a --- /dev/null +++ b/samples/DockFigmaSample/Models/AssetSwatch.cs @@ -0,0 +1,17 @@ +using Avalonia.Media; + +namespace DockFigmaSample.Models; + +public class AssetSwatch +{ + public AssetSwatch(string name, Color color) + { + Name = name; + Brush = new SolidColorBrush(color); + Hex = $"#{color.R:X2}{color.G:X2}{color.B:X2}"; + } + + public string Name { get; } + public IBrush Brush { get; } + public string Hex { get; } +} diff --git a/samples/DockFigmaSample/Models/CommentItem.cs b/samples/DockFigmaSample/Models/CommentItem.cs new file mode 100644 index 000000000..5b3d5f8bb --- /dev/null +++ b/samples/DockFigmaSample/Models/CommentItem.cs @@ -0,0 +1,42 @@ +using System; + +namespace DockFigmaSample.Models; + +public class CommentItem +{ + public CommentItem(string author, string body, string time) + { + Author = author; + Body = body; + Time = time; + Initials = GetInitials(author); + } + + public string Author { get; } + public string Body { get; } + public string Time { get; } + public string Initials { get; } + + private static string GetInitials(string author) + { + if (string.IsNullOrWhiteSpace(author)) + { + return "?"; + } + + var parts = author.Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + if (parts.Length == 0) + { + return "?"; + } + + if (parts.Length == 1) + { + return parts[0].Substring(0, 1).ToUpperInvariant(); + } + + var first = parts[0].Substring(0, 1).ToUpperInvariant(); + var last = parts[^1].Substring(0, 1).ToUpperInvariant(); + return first + last; + } +} diff --git a/samples/DockFigmaSample/Models/ComponentItem.cs b/samples/DockFigmaSample/Models/ComponentItem.cs new file mode 100644 index 000000000..eea11bf96 --- /dev/null +++ b/samples/DockFigmaSample/Models/ComponentItem.cs @@ -0,0 +1,13 @@ +namespace DockFigmaSample.Models; + +public class ComponentItem +{ + public ComponentItem(string name, string description) + { + Name = name; + Description = description; + } + + public string Name { get; } + public string Description { get; } +} diff --git a/samples/DockFigmaSample/Models/InspectSpecItem.cs b/samples/DockFigmaSample/Models/InspectSpecItem.cs new file mode 100644 index 000000000..8bcef148b --- /dev/null +++ b/samples/DockFigmaSample/Models/InspectSpecItem.cs @@ -0,0 +1,13 @@ +namespace DockFigmaSample.Models; + +public class InspectSpecItem +{ + public InspectSpecItem(string label, string value) + { + Label = label; + Value = value; + } + + public string Label { get; } + public string Value { get; } +} diff --git a/samples/DockFigmaSample/Models/LayerItem.cs b/samples/DockFigmaSample/Models/LayerItem.cs new file mode 100644 index 000000000..126f4adf9 --- /dev/null +++ b/samples/DockFigmaSample/Models/LayerItem.cs @@ -0,0 +1,21 @@ +using Avalonia; + +namespace DockFigmaSample.Models; + +public class LayerItem +{ + public LayerItem(string name, string type, int depth, bool isVisible = true, bool isLocked = false) + { + Name = name; + Type = type; + IsVisible = isVisible; + IsLocked = isLocked; + Indent = new Thickness(8 + depth * 16, 2, 8, 2); + } + + public string Name { get; } + public string Type { get; } + public bool IsVisible { get; } + public bool IsLocked { get; } + public Thickness Indent { get; } +} diff --git a/samples/DockFigmaSample/Models/PrototypeFlowItem.cs b/samples/DockFigmaSample/Models/PrototypeFlowItem.cs new file mode 100644 index 000000000..b67556b38 --- /dev/null +++ b/samples/DockFigmaSample/Models/PrototypeFlowItem.cs @@ -0,0 +1,15 @@ +namespace DockFigmaSample.Models; + +public class PrototypeFlowItem +{ + public PrototypeFlowItem(string trigger, string action, string destination) + { + Trigger = trigger; + Action = action; + Destination = destination; + } + + public string Trigger { get; } + public string Action { get; } + public string Destination { get; } +} diff --git a/samples/DockFigmaSample/Models/RecentFileItem.cs b/samples/DockFigmaSample/Models/RecentFileItem.cs new file mode 100644 index 000000000..7b14881d1 --- /dev/null +++ b/samples/DockFigmaSample/Models/RecentFileItem.cs @@ -0,0 +1,19 @@ +using Avalonia.Media; + +namespace DockFigmaSample.Models; + +public class RecentFileItem +{ + public RecentFileItem(string name, string team, string updated, Color accent) + { + Name = name; + Team = team; + Updated = updated; + AccentBrush = new SolidColorBrush(accent); + } + + public string Name { get; } + public string Team { get; } + public string Updated { get; } + public IBrush AccentBrush { get; } +} diff --git a/samples/DockFigmaSample/Models/SampleData.cs b/samples/DockFigmaSample/Models/SampleData.cs new file mode 100644 index 000000000..5d7ec1511 --- /dev/null +++ b/samples/DockFigmaSample/Models/SampleData.cs @@ -0,0 +1,85 @@ +using System.Collections.ObjectModel; +using Avalonia.Media; + +namespace DockFigmaSample.Models; + +public static class SampleData +{ + public static ObservableCollection CreateLayers() => new() + { + new LayerItem("Landing Page", "Frame", 0), + new LayerItem("Hero", "Group", 1), + new LayerItem("Headline", "Text", 2), + new LayerItem("Subhead", "Text", 2), + new LayerItem("CTA", "Group", 2), + new LayerItem("Button Primary", "Rectangle", 3), + new LayerItem("Button Label", "Text", 3), + new LayerItem("Product Cards", "Group", 1), + new LayerItem("Card 01", "Frame", 2), + new LayerItem("Card 02", "Frame", 2), + new LayerItem("Card 03", "Frame", 2), + new LayerItem("Footer", "Group", 1, isLocked: true) + }; + + public static ObservableCollection CreateComponents() => new() + { + new ComponentItem("Button / Primary", "CTA with gradient fill"), + new ComponentItem("Badge / Status", "Rounded pill badge"), + new ComponentItem("Card / Pricing", "Card with shadow"), + new ComponentItem("Input / Search", "Leading icon input"), + new ComponentItem("Nav / Top", "Logo + actions") + }; + + public static ObservableCollection CreateSwatches() => new() + { + new AssetSwatch("Primary", Color.Parse("#3D7EFF")), + new AssetSwatch("Warm", Color.Parse("#FF8A4C")), + new AssetSwatch("Mint", Color.Parse("#22C55E")), + new AssetSwatch("Ink", Color.Parse("#1E2228")), + new AssetSwatch("Canvas", Color.Parse("#ECEFF3")), + new AssetSwatch("Lavender", Color.Parse("#9A8CFF")) + }; + + public static ObservableCollection CreateComments() => new() + { + new CommentItem("Ava Fox", "Can we nudge the hero title up 12px?", "2m"), + new CommentItem("Noah Reed", "The pricing cards feel tight on mobile.", "9m"), + new CommentItem("Iris Park", "Try a softer background behind the CTA.", "1h") + }; + + public static ObservableCollection CreateRecentFiles() => new() + { + new RecentFileItem("Marketing Site", "Nimbus", "Updated 12m ago", Color.Parse("#3D7EFF")), + new RecentFileItem("Onboarding Flow", "Nimbus", "Updated 2h ago", Color.Parse("#FF8A4C")), + new RecentFileItem("Design System", "Atlas", "Updated yesterday", Color.Parse("#22C55E")), + new RecentFileItem("Mobile Kit", "Atlas", "Updated 4d ago", Color.Parse("#F97316")) + }; + + public static ObservableCollection CreatePrototypeFlows() => new() + { + new PrototypeFlowItem("On Click", "Navigate", "Pricing Modal"), + new PrototypeFlowItem("On Hover", "Swap", "Button / Hover"), + new PrototypeFlowItem("After Delay", "Auto Animate", "Hero Alt") + }; + + public static ObservableCollection CreateInspectSpecs() => new() + { + new InspectSpecItem("Spacing", "24 px"), + new InspectSpecItem("Padding", "32 px"), + new InspectSpecItem("Radius", "24 px"), + new InspectSpecItem("Shadow", "0 24 48 0"), + new InspectSpecItem("Font", "Space Grotesk / 28") + }; + + public static ObservableCollection CreateToolbarTools() => new() + { + new ToolItem("Move", "V", true), + new ToolItem("Frame", "F"), + new ToolItem("Section", "S"), + new ToolItem("Rectangle", "R"), + new ToolItem("Ellipse", "O"), + new ToolItem("Pen", "P"), + new ToolItem("Text", "T"), + new ToolItem("Comment", "C") + }; +} diff --git a/samples/DockFigmaSample/Models/ToolItem.cs b/samples/DockFigmaSample/Models/ToolItem.cs new file mode 100644 index 000000000..973610fca --- /dev/null +++ b/samples/DockFigmaSample/Models/ToolItem.cs @@ -0,0 +1,15 @@ +namespace DockFigmaSample.Models; + +public class ToolItem +{ + public ToolItem(string name, string glyph, bool isActive = false) + { + Name = name; + Glyph = glyph; + IsActive = isActive; + } + + public string Name { get; } + public string Glyph { get; } + public bool IsActive { get; } +} diff --git a/samples/DockFigmaSample/Program.cs b/samples/DockFigmaSample/Program.cs new file mode 100644 index 000000000..131566974 --- /dev/null +++ b/samples/DockFigmaSample/Program.cs @@ -0,0 +1,20 @@ +using System; +using Avalonia; +using ReactiveUI.Avalonia; + +namespace DockFigmaSample; + +internal class Program +{ + [STAThread] + private static void Main(string[] args) + { + BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); + } + + public static AppBuilder BuildAvaloniaApp() => + AppBuilder.Configure() + .UsePlatformDetect() + .UseReactiveUI() + .LogToTrace(); +} diff --git a/samples/DockFigmaSample/Styles/FigmaTheme.axaml b/samples/DockFigmaSample/Styles/FigmaTheme.axaml new file mode 100644 index 000000000..cbe3d9242 --- /dev/null +++ b/samples/DockFigmaSample/Styles/FigmaTheme.axaml @@ -0,0 +1,283 @@ + + + Space Grotesk, Segoe UI + JetBrains Mono, Consolas + + #101317 + #5C6673 + #ECEFF3 + #FFFFFF + #E1E6ED + #F4F6FA + #101317 + #1E2330 + #FAFBFC + #3D7EFF + #FF8A4C + #22C55E + #FFFFFFCC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/DockFigmaSample/ViewLocator.cs b/samples/DockFigmaSample/ViewLocator.cs new file mode 100644 index 000000000..299baea05 --- /dev/null +++ b/samples/DockFigmaSample/ViewLocator.cs @@ -0,0 +1,43 @@ +using System; +using Avalonia.Controls; +using Avalonia.Controls.Templates; +using Dock.Model.Core; +using ReactiveUI; +using Splat; + +namespace DockFigmaSample; + +public partial class ViewLocator : IDataTemplate +{ + public Control? Build(object? data) + { + if (data is null) + { + return null; + } + + var viewLocator = Locator.Current.GetService(); + if (viewLocator?.ResolveView(data) is Control control) + { + return control; + } + + throw new Exception($"Unable to create view for type: {data.GetType()}"); + } + + public bool Match(object? data) + { + if (data is null) + { + return false; + } + + if (data is IDockable) + { + return true; + } + + var viewLocator = Locator.Current.GetService(); + return viewLocator?.ResolveView(data) is not null; + } +} diff --git a/samples/DockFigmaSample/ViewModels/Documents/CanvasDocumentViewModel.cs b/samples/DockFigmaSample/ViewModels/Documents/CanvasDocumentViewModel.cs new file mode 100644 index 000000000..01f236e9c --- /dev/null +++ b/samples/DockFigmaSample/ViewModels/Documents/CanvasDocumentViewModel.cs @@ -0,0 +1,39 @@ +using System; +using System.Reactive.Linq; +using Dock.Model.ReactiveUI.Navigation.Controls; +using DockFigmaSample.ViewModels; +using ReactiveUI; + +namespace DockFigmaSample.ViewModels.Documents; + +public class CanvasDocumentViewModel : RoutableDocument +{ + private readonly DesignCanvasViewModel _designView; + private readonly PrototypeCanvasViewModel _prototypeView; + private readonly InspectCanvasViewModel _inspectView; + + public CanvasDocumentViewModel(IScreen host) : base(host, "canvas") + { + _designView = new DesignCanvasViewModel(this); + _prototypeView = new PrototypeCanvasViewModel(this); + _inspectView = new InspectCanvasViewModel(this); + + Router.Navigate.Execute(_designView).Subscribe(_ => { }); + } + + public void SetMode(WorkspaceMode mode) + { + switch (mode) + { + case WorkspaceMode.Design: + Router.Navigate.Execute(_designView).Subscribe(_ => { }); + break; + case WorkspaceMode.Prototype: + Router.Navigate.Execute(_prototypeView).Subscribe(_ => { }); + break; + case WorkspaceMode.Inspect: + Router.Navigate.Execute(_inspectView).Subscribe(_ => { }); + break; + } + } +} diff --git a/samples/DockFigmaSample/ViewModels/Documents/DesignCanvasViewModel.cs b/samples/DockFigmaSample/ViewModels/Documents/DesignCanvasViewModel.cs new file mode 100644 index 000000000..db2c36ad1 --- /dev/null +++ b/samples/DockFigmaSample/ViewModels/Documents/DesignCanvasViewModel.cs @@ -0,0 +1,17 @@ +using ReactiveUI; + +namespace DockFigmaSample.ViewModels.Documents; + +public class DesignCanvasViewModel : ReactiveObject, IRoutableViewModel +{ + public DesignCanvasViewModel(IScreen host) + { + HostScreen = host; + } + + public string UrlPathSegment { get; } = "design-canvas"; + public IScreen HostScreen { get; } + + public string FrameName { get; } = "Landing Page"; + public string FrameSize { get; } = "1440 x 900"; +} diff --git a/samples/DockFigmaSample/ViewModels/Documents/InspectCanvasViewModel.cs b/samples/DockFigmaSample/ViewModels/Documents/InspectCanvasViewModel.cs new file mode 100644 index 000000000..3c376d0d3 --- /dev/null +++ b/samples/DockFigmaSample/ViewModels/Documents/InspectCanvasViewModel.cs @@ -0,0 +1,17 @@ +using ReactiveUI; + +namespace DockFigmaSample.ViewModels.Documents; + +public class InspectCanvasViewModel : ReactiveObject, IRoutableViewModel +{ + public InspectCanvasViewModel(IScreen host) + { + HostScreen = host; + } + + public string UrlPathSegment { get; } = "inspect-canvas"; + public IScreen HostScreen { get; } + + public string SelectedLayer { get; } = "Hero / CTA"; + public string Measurements { get; } = "320 x 56"; +} diff --git a/samples/DockFigmaSample/ViewModels/Documents/PrototypeCanvasViewModel.cs b/samples/DockFigmaSample/ViewModels/Documents/PrototypeCanvasViewModel.cs new file mode 100644 index 000000000..03c29e84b --- /dev/null +++ b/samples/DockFigmaSample/ViewModels/Documents/PrototypeCanvasViewModel.cs @@ -0,0 +1,22 @@ +using System.Collections.ObjectModel; +using ReactiveUI; + +namespace DockFigmaSample.ViewModels.Documents; + +public class PrototypeCanvasViewModel : ReactiveObject, IRoutableViewModel +{ + public PrototypeCanvasViewModel(IScreen host) + { + HostScreen = host; + } + + public string UrlPathSegment { get; } = "prototype-canvas"; + public IScreen HostScreen { get; } + + public ObservableCollection Frames { get; } = new() + { + "Landing Page", + "Pricing Modal", + "Checkout" + }; +} diff --git a/samples/DockFigmaSample/ViewModels/HomeViewModel.cs b/samples/DockFigmaSample/ViewModels/HomeViewModel.cs new file mode 100644 index 000000000..a84e08649 --- /dev/null +++ b/samples/DockFigmaSample/ViewModels/HomeViewModel.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.ObjectModel; +using System.Reactive; +using System.Reactive.Linq; +using DockFigmaSample.Models; +using ReactiveUI; + +namespace DockFigmaSample.ViewModels; + +public class HomeViewModel : ReactiveObject, IRoutableViewModel +{ + public HomeViewModel(IScreen host) + { + HostScreen = host; + OpenWorkspace = ReactiveCommand.Create(() => + HostScreen.Router.Navigate.Execute(new WorkspaceViewModel(host)).Subscribe(_ => { })); + } + + public string UrlPathSegment { get; } = "home"; + public IScreen HostScreen { get; } + + public ObservableCollection RecentFiles { get; } = SampleData.CreateRecentFiles(); + + public ReactiveCommand OpenWorkspace { get; } +} diff --git a/samples/DockFigmaSample/ViewModels/MainWindowViewModel.cs b/samples/DockFigmaSample/ViewModels/MainWindowViewModel.cs new file mode 100644 index 000000000..279246314 --- /dev/null +++ b/samples/DockFigmaSample/ViewModels/MainWindowViewModel.cs @@ -0,0 +1,16 @@ +using System; +using System.Reactive.Linq; +using ReactiveUI; + +namespace DockFigmaSample.ViewModels; + +public class MainWindowViewModel : ReactiveObject, IScreen +{ + public RoutingState Router { get; } = new RoutingState(); + + public MainWindowViewModel() + { + var home = new HomeViewModel(this); + Router.Navigate.Execute(home).Subscribe(_ => { }); + } +} diff --git a/samples/DockFigmaSample/ViewModels/Tools/AssetsToolViewModel.cs b/samples/DockFigmaSample/ViewModels/Tools/AssetsToolViewModel.cs new file mode 100644 index 000000000..a7945ea73 --- /dev/null +++ b/samples/DockFigmaSample/ViewModels/Tools/AssetsToolViewModel.cs @@ -0,0 +1,11 @@ +using System.Collections.ObjectModel; +using Dock.Model.ReactiveUI.Controls; +using DockFigmaSample.Models; + +namespace DockFigmaSample.ViewModels.Tools; + +public class AssetsToolViewModel : Tool +{ + public ObservableCollection Swatches { get; } = SampleData.CreateSwatches(); + public ObservableCollection Components { get; } = SampleData.CreateComponents(); +} diff --git a/samples/DockFigmaSample/ViewModels/Tools/CommentsToolViewModel.cs b/samples/DockFigmaSample/ViewModels/Tools/CommentsToolViewModel.cs new file mode 100644 index 000000000..4a59ad5ff --- /dev/null +++ b/samples/DockFigmaSample/ViewModels/Tools/CommentsToolViewModel.cs @@ -0,0 +1,10 @@ +using System.Collections.ObjectModel; +using Dock.Model.ReactiveUI.Controls; +using DockFigmaSample.Models; + +namespace DockFigmaSample.ViewModels.Tools; + +public class CommentsToolViewModel : Tool +{ + public ObservableCollection Comments { get; } = SampleData.CreateComments(); +} diff --git a/samples/DockFigmaSample/ViewModels/Tools/InspectorDesignViewModel.cs b/samples/DockFigmaSample/ViewModels/Tools/InspectorDesignViewModel.cs new file mode 100644 index 000000000..a8703bac9 --- /dev/null +++ b/samples/DockFigmaSample/ViewModels/Tools/InspectorDesignViewModel.cs @@ -0,0 +1,22 @@ +using ReactiveUI; + +namespace DockFigmaSample.ViewModels.Tools; + +public class InspectorDesignViewModel : ReactiveObject, IRoutableViewModel +{ + public InspectorDesignViewModel(IScreen host) + { + HostScreen = host; + } + + public string UrlPathSegment { get; } = "inspector-design"; + public IScreen HostScreen { get; } + + public string Width { get; } = "1280"; + public string Height { get; } = "720"; + public string Radius { get; } = "24"; + public string Fill { get; } = "#FFFFFF"; + public string Opacity { get; } = "100"; + public string Layout { get; } = "Auto"; + public string Spacing { get; } = "24"; +} diff --git a/samples/DockFigmaSample/ViewModels/Tools/InspectorInspectViewModel.cs b/samples/DockFigmaSample/ViewModels/Tools/InspectorInspectViewModel.cs new file mode 100644 index 000000000..7d0b4fbe3 --- /dev/null +++ b/samples/DockFigmaSample/ViewModels/Tools/InspectorInspectViewModel.cs @@ -0,0 +1,18 @@ +using System.Collections.ObjectModel; +using DockFigmaSample.Models; +using ReactiveUI; + +namespace DockFigmaSample.ViewModels.Tools; + +public class InspectorInspectViewModel : ReactiveObject, IRoutableViewModel +{ + public InspectorInspectViewModel(IScreen host) + { + HostScreen = host; + } + + public string UrlPathSegment { get; } = "inspector-inspect"; + public IScreen HostScreen { get; } + + public ObservableCollection Specs { get; } = SampleData.CreateInspectSpecs(); +} diff --git a/samples/DockFigmaSample/ViewModels/Tools/InspectorPrototypeViewModel.cs b/samples/DockFigmaSample/ViewModels/Tools/InspectorPrototypeViewModel.cs new file mode 100644 index 000000000..c789d8299 --- /dev/null +++ b/samples/DockFigmaSample/ViewModels/Tools/InspectorPrototypeViewModel.cs @@ -0,0 +1,18 @@ +using System.Collections.ObjectModel; +using DockFigmaSample.Models; +using ReactiveUI; + +namespace DockFigmaSample.ViewModels.Tools; + +public class InspectorPrototypeViewModel : ReactiveObject, IRoutableViewModel +{ + public InspectorPrototypeViewModel(IScreen host) + { + HostScreen = host; + } + + public string UrlPathSegment { get; } = "inspector-prototype"; + public IScreen HostScreen { get; } + + public ObservableCollection Flows { get; } = SampleData.CreatePrototypeFlows(); +} diff --git a/samples/DockFigmaSample/ViewModels/Tools/InspectorToolViewModel.cs b/samples/DockFigmaSample/ViewModels/Tools/InspectorToolViewModel.cs new file mode 100644 index 000000000..82fd2fd7a --- /dev/null +++ b/samples/DockFigmaSample/ViewModels/Tools/InspectorToolViewModel.cs @@ -0,0 +1,39 @@ +using System; +using System.Reactive.Linq; +using Dock.Model.ReactiveUI.Navigation.Controls; +using DockFigmaSample.ViewModels; +using ReactiveUI; + +namespace DockFigmaSample.ViewModels.Tools; + +public class InspectorToolViewModel : RoutableTool +{ + private readonly InspectorDesignViewModel _designView; + private readonly InspectorPrototypeViewModel _prototypeView; + private readonly InspectorInspectViewModel _inspectView; + + public InspectorToolViewModel(IScreen host) : base(host, "inspector") + { + _designView = new InspectorDesignViewModel(this); + _prototypeView = new InspectorPrototypeViewModel(this); + _inspectView = new InspectorInspectViewModel(this); + + Router.Navigate.Execute(_designView).Subscribe(_ => { }); + } + + public void SetMode(WorkspaceMode mode) + { + switch (mode) + { + case WorkspaceMode.Design: + Router.Navigate.Execute(_designView).Subscribe(_ => { }); + break; + case WorkspaceMode.Prototype: + Router.Navigate.Execute(_prototypeView).Subscribe(_ => { }); + break; + case WorkspaceMode.Inspect: + Router.Navigate.Execute(_inspectView).Subscribe(_ => { }); + break; + } + } +} diff --git a/samples/DockFigmaSample/ViewModels/Tools/LayersToolViewModel.cs b/samples/DockFigmaSample/ViewModels/Tools/LayersToolViewModel.cs new file mode 100644 index 000000000..2a75a1941 --- /dev/null +++ b/samples/DockFigmaSample/ViewModels/Tools/LayersToolViewModel.cs @@ -0,0 +1,10 @@ +using System.Collections.ObjectModel; +using Dock.Model.ReactiveUI.Controls; +using DockFigmaSample.Models; + +namespace DockFigmaSample.ViewModels.Tools; + +public class LayersToolViewModel : Tool +{ + public ObservableCollection Layers { get; } = SampleData.CreateLayers(); +} diff --git a/samples/DockFigmaSample/ViewModels/Tools/ToolbarToolViewModel.cs b/samples/DockFigmaSample/ViewModels/Tools/ToolbarToolViewModel.cs new file mode 100644 index 000000000..8ff70e720 --- /dev/null +++ b/samples/DockFigmaSample/ViewModels/Tools/ToolbarToolViewModel.cs @@ -0,0 +1,10 @@ +using System.Collections.ObjectModel; +using Dock.Model.ReactiveUI.Controls; +using DockFigmaSample.Models; + +namespace DockFigmaSample.ViewModels.Tools; + +public class ToolbarToolViewModel : Tool +{ + public ObservableCollection Tools { get; } = SampleData.CreateToolbarTools(); +} diff --git a/samples/DockFigmaSample/ViewModels/WorkspaceDockFactory.cs b/samples/DockFigmaSample/ViewModels/WorkspaceDockFactory.cs new file mode 100644 index 000000000..a814ab7d0 --- /dev/null +++ b/samples/DockFigmaSample/ViewModels/WorkspaceDockFactory.cs @@ -0,0 +1,156 @@ +using System; +using System.Collections.Generic; +using Dock.Avalonia.Controls; +using Dock.Model.Controls; +using Dock.Model.Core; +using Dock.Model.ReactiveUI; +using Dock.Model.ReactiveUI.Controls; +using Dock.Model.ReactiveUI.Navigation.Controls; +using Dock.Settings; +using DockFigmaSample.ViewModels.Documents; +using DockFigmaSample.ViewModels.Tools; +using ReactiveUI; + +namespace DockFigmaSample.ViewModels; + +public class WorkspaceDockFactory : Factory +{ + private readonly IScreen _host; + + public WorkspaceDockFactory(IScreen host) + { + _host = host; + } + + public CanvasDocumentViewModel CanvasDocument { get; private set; } = null!; + public InspectorToolViewModel InspectorTool { get; private set; } = null!; + + public override IRootDock CreateLayout() + { + CanvasDocument = new CanvasDocumentViewModel(_host) + { + Id = "Canvas", + Title = "Canvas" + }; + + var toolbarTool = new ToolbarToolViewModel + { + Id = "Toolbar", + Title = "Tools" + }; + + var layersTool = new LayersToolViewModel + { + Id = "Layers", + Title = "Layers" + }; + + var assetsTool = new AssetsToolViewModel + { + Id = "Assets", + Title = "Assets" + }; + + InspectorTool = new InspectorToolViewModel(_host) + { + Id = "Inspector", + Title = "Properties" + }; + + var commentsTool = new CommentsToolViewModel + { + Id = "Comments", + Title = "Comments" + }; + + var documentDock = new DocumentDock + { + Id = "Documents", + VisibleDockables = CreateList(CanvasDocument), + ActiveDockable = CanvasDocument, + CanCreateDocument = false + }; + + var toolbarDock = new ToolDock + { + Id = "ToolbarDock", + Alignment = Alignment.Left, + Proportion = 0.08, + VisibleDockables = CreateList(toolbarTool), + ActiveDockable = toolbarTool + }; + + var leftPanelDock = new ToolDock + { + Id = "LeftPanel", + Alignment = Alignment.Left, + Proportion = 0.22, + VisibleDockables = CreateList(layersTool, assetsTool), + ActiveDockable = layersTool + }; + + var leftGroup = new ProportionalDock + { + Orientation = Orientation.Horizontal, + VisibleDockables = CreateList(toolbarDock, new ProportionalDockSplitter(), leftPanelDock), + ActiveDockable = leftPanelDock + }; + + var bottomDock = new ToolDock + { + Id = "BottomPanel", + Alignment = Alignment.Bottom, + Proportion = 0.25, + VisibleDockables = CreateList(commentsTool), + ActiveDockable = commentsTool + }; + + var centerGroup = new ProportionalDock + { + Orientation = Orientation.Vertical, + VisibleDockables = CreateList(documentDock, new ProportionalDockSplitter(), bottomDock), + ActiveDockable = documentDock + }; + + var rightDock = new ToolDock + { + Id = "RightPanel", + Alignment = Alignment.Right, + Proportion = 0.26, + VisibleDockables = CreateList(InspectorTool), + ActiveDockable = InspectorTool + }; + + var mainLayout = new ProportionalDock + { + Orientation = Orientation.Horizontal, + VisibleDockables = CreateList(leftGroup, new ProportionalDockSplitter(), centerGroup, new ProportionalDockSplitter(), rightDock), + ActiveDockable = centerGroup + }; + + var root = new RoutableRootDock(_host) + { + VisibleDockables = CreateList(mainLayout), + DefaultDockable = mainLayout, + ActiveDockable = mainLayout + }; + + root.LeftPinnedDockables = CreateList(); + root.RightPinnedDockables = CreateList(); + root.TopPinnedDockables = CreateList(); + root.BottomPinnedDockables = CreateList(); + root.PinnedDock = null; + + return root; + } + + public override void InitLayout(IDockable layout) + { + HostWindowLocator = new Dictionary> + { + [nameof(IDockWindow)] = () => DockSettings.UseManagedWindows ? new ManagedHostWindow() : new HostWindow() + }; + + base.InitLayout(layout); + } +} diff --git a/samples/DockFigmaSample/ViewModels/WorkspaceMode.cs b/samples/DockFigmaSample/ViewModels/WorkspaceMode.cs new file mode 100644 index 000000000..5dd077254 --- /dev/null +++ b/samples/DockFigmaSample/ViewModels/WorkspaceMode.cs @@ -0,0 +1,8 @@ +namespace DockFigmaSample.ViewModels; + +public enum WorkspaceMode +{ + Design, + Prototype, + Inspect +} diff --git a/samples/DockFigmaSample/ViewModels/WorkspaceViewModel.cs b/samples/DockFigmaSample/ViewModels/WorkspaceViewModel.cs new file mode 100644 index 000000000..85d414f07 --- /dev/null +++ b/samples/DockFigmaSample/ViewModels/WorkspaceViewModel.cs @@ -0,0 +1,61 @@ +using System.Reactive; +using Dock.Model.Controls; +using DockFigmaSample.ViewModels.Documents; +using DockFigmaSample.ViewModels.Tools; +using ReactiveUI; + +namespace DockFigmaSample.ViewModels; + +public class WorkspaceViewModel : ReactiveObject, IRoutableViewModel +{ + private IRootDock? _layout; + private WorkspaceMode _editorMode; + private readonly CanvasDocumentViewModel _canvasDocument; + private readonly InspectorToolViewModel _inspectorTool; + + public WorkspaceViewModel(IScreen host) + { + HostScreen = host; + + var factory = new WorkspaceDockFactory(host); + Layout = factory.CreateLayout(); + if (Layout is not null) + { + factory.InitLayout(Layout); + } + + _canvasDocument = factory.CanvasDocument; + _inspectorTool = factory.InspectorTool; + + SwitchMode = ReactiveCommand.Create(SetMode); + SetMode(WorkspaceMode.Design); + } + + public string UrlPathSegment { get; } = "workspace"; + public IScreen HostScreen { get; } + + public IRootDock? Layout + { + get => _layout; + set => this.RaiseAndSetIfChanged(ref _layout, value); + } + + public WorkspaceMode EditorMode + { + get => _editorMode; + private set => this.RaiseAndSetIfChanged(ref _editorMode, value); + } + + public string FileName { get; } = "LandingPage.fig"; + public string ProjectName { get; } = "Nimbus"; + public string StatusMessage { get; } = "All changes saved"; + + public ReactiveCommand SwitchMode { get; } + + private void SetMode(WorkspaceMode mode) + { + EditorMode = mode; + _canvasDocument.SetMode(mode); + _inspectorTool.SetMode(mode); + } +} diff --git a/samples/DockFigmaSample/Views/Documents/CanvasDocumentView.axaml b/samples/DockFigmaSample/Views/Documents/CanvasDocumentView.axaml new file mode 100644 index 000000000..9d690ef37 --- /dev/null +++ b/samples/DockFigmaSample/Views/Documents/CanvasDocumentView.axaml @@ -0,0 +1,23 @@ + + + + + + + + + + + diff --git a/samples/DockFigmaSample/Views/Documents/CanvasDocumentView.axaml.cs b/samples/DockFigmaSample/Views/Documents/CanvasDocumentView.axaml.cs new file mode 100644 index 000000000..3b9b4409b --- /dev/null +++ b/samples/DockFigmaSample/Views/Documents/CanvasDocumentView.axaml.cs @@ -0,0 +1,13 @@ +using Avalonia.Markup.Xaml; +using DockFigmaSample.ViewModels.Documents; +using ReactiveUI.Avalonia; + +namespace DockFigmaSample.Views.Documents; + +public partial class CanvasDocumentView : ReactiveUserControl +{ + public CanvasDocumentView() + { + AvaloniaXamlLoader.Load(this); + } +} diff --git a/samples/DockFigmaSample/Views/Documents/DesignCanvasView.axaml b/samples/DockFigmaSample/Views/Documents/DesignCanvasView.axaml new file mode 100644 index 000000000..2402f72a6 --- /dev/null +++ b/samples/DockFigmaSample/Views/Documents/DesignCanvasView.axaml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/DockFigmaSample/Views/Documents/DesignCanvasView.axaml.cs b/samples/DockFigmaSample/Views/Documents/DesignCanvasView.axaml.cs new file mode 100644 index 000000000..026c9067e --- /dev/null +++ b/samples/DockFigmaSample/Views/Documents/DesignCanvasView.axaml.cs @@ -0,0 +1,13 @@ +using Avalonia.Markup.Xaml; +using DockFigmaSample.ViewModels.Documents; +using ReactiveUI.Avalonia; + +namespace DockFigmaSample.Views.Documents; + +public partial class DesignCanvasView : ReactiveUserControl +{ + public DesignCanvasView() + { + AvaloniaXamlLoader.Load(this); + } +} diff --git a/samples/DockFigmaSample/Views/Documents/InspectCanvasView.axaml b/samples/DockFigmaSample/Views/Documents/InspectCanvasView.axaml new file mode 100644 index 000000000..cb506dd9d --- /dev/null +++ b/samples/DockFigmaSample/Views/Documents/InspectCanvasView.axaml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/DockFigmaSample/Views/Documents/InspectCanvasView.axaml.cs b/samples/DockFigmaSample/Views/Documents/InspectCanvasView.axaml.cs new file mode 100644 index 000000000..a61d76257 --- /dev/null +++ b/samples/DockFigmaSample/Views/Documents/InspectCanvasView.axaml.cs @@ -0,0 +1,13 @@ +using Avalonia.Markup.Xaml; +using DockFigmaSample.ViewModels.Documents; +using ReactiveUI.Avalonia; + +namespace DockFigmaSample.Views.Documents; + +public partial class InspectCanvasView : ReactiveUserControl +{ + public InspectCanvasView() + { + AvaloniaXamlLoader.Load(this); + } +} diff --git a/samples/DockFigmaSample/Views/Documents/PrototypeCanvasView.axaml b/samples/DockFigmaSample/Views/Documents/PrototypeCanvasView.axaml new file mode 100644 index 000000000..5ac23229c --- /dev/null +++ b/samples/DockFigmaSample/Views/Documents/PrototypeCanvasView.axaml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/DockFigmaSample/Views/Documents/PrototypeCanvasView.axaml.cs b/samples/DockFigmaSample/Views/Documents/PrototypeCanvasView.axaml.cs new file mode 100644 index 000000000..4c53a5155 --- /dev/null +++ b/samples/DockFigmaSample/Views/Documents/PrototypeCanvasView.axaml.cs @@ -0,0 +1,13 @@ +using Avalonia.Markup.Xaml; +using DockFigmaSample.ViewModels.Documents; +using ReactiveUI.Avalonia; + +namespace DockFigmaSample.Views.Documents; + +public partial class PrototypeCanvasView : ReactiveUserControl +{ + public PrototypeCanvasView() + { + AvaloniaXamlLoader.Load(this); + } +} diff --git a/samples/DockFigmaSample/Views/HomeView.axaml b/samples/DockFigmaSample/Views/HomeView.axaml new file mode 100644 index 000000000..f104e16bf --- /dev/null +++ b/samples/DockFigmaSample/Views/HomeView.axaml @@ -0,0 +1,51 @@ + + + + + + + + + + + +