diff --git a/Calame.DocumentContexts/Calame.DocumentContexts.csproj b/Calame.DocumentContexts/Calame.DocumentContexts.csproj new file mode 100644 index 0000000..ac1a732 --- /dev/null +++ b/Calame.DocumentContexts/Calame.DocumentContexts.csproj @@ -0,0 +1,11 @@ + + + $(TargetFramework) + True + + + + + + + \ No newline at end of file diff --git a/Calame.DocumentContexts/IRootComponentsContext.cs b/Calame.DocumentContexts/IRootComponentsContext.cs new file mode 100644 index 0000000..08afa36 --- /dev/null +++ b/Calame.DocumentContexts/IRootComponentsContext.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using Glyph.Composition; + +namespace Calame.DocumentContexts +{ + public interface IRootComponentsContext + { + IEnumerable RootComponents { get; } + } +} \ No newline at end of file diff --git a/Calame.DocumentContexts/IRootInteractivesContext.cs b/Calame.DocumentContexts/IRootInteractivesContext.cs new file mode 100644 index 0000000..229222f --- /dev/null +++ b/Calame.DocumentContexts/IRootInteractivesContext.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using Fingear.Interactives; + +namespace Calame.DocumentContexts +{ + public interface IRootInteractivesContext + { + IEnumerable RootInteractives { get; } + } +} \ No newline at end of file diff --git a/Calame.DocumentContexts/IRootScenesContext.cs b/Calame.DocumentContexts/IRootScenesContext.cs new file mode 100644 index 0000000..ba0c4cc --- /dev/null +++ b/Calame.DocumentContexts/IRootScenesContext.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using Glyph; + +namespace Calame.DocumentContexts +{ + public interface IRootScenesContext + { + IEnumerable RootScenes { get; } + } +} \ No newline at end of file diff --git a/Calame.DocumentContexts/IRootsContext.cs b/Calame.DocumentContexts/IRootsContext.cs new file mode 100644 index 0000000..876b80c --- /dev/null +++ b/Calame.DocumentContexts/IRootsContext.cs @@ -0,0 +1,9 @@ +using System.Collections; + +namespace Calame.DocumentContexts +{ + public interface IRootsContext + { + IEnumerable Roots { get; } + } +} \ No newline at end of file diff --git a/Calame.DocumentContexts/ISelectionCommandContext.cs b/Calame.DocumentContexts/ISelectionCommandContext.cs new file mode 100644 index 0000000..b934e9e --- /dev/null +++ b/Calame.DocumentContexts/ISelectionCommandContext.cs @@ -0,0 +1,20 @@ +using System; +using System.Threading.Tasks; +using System.Windows.Input; + +namespace Calame.DocumentContexts +{ + public interface ISelectionCommandContext + { + ICommand SelectCommand { get; } + event EventHandler CanSelectChanged; + bool CanSelect(object instance); + Task SelectAsync(object instance); + } + + public interface ISelectionCommandContext : ISelectionCommandContext + { + bool CanSelect(T instance); + Task SelectAsync(T instance); + } +} \ No newline at end of file diff --git a/Calame.Icons/Providers/CalameIconProvider.cs b/Calame.Icons/Providers/CalameIconProvider.cs index fbd9195..136c39f 100644 --- a/Calame.Icons/Providers/CalameIconProvider.cs +++ b/Calame.Icons/Providers/CalameIconProvider.cs @@ -24,6 +24,7 @@ protected override PackIconMaterialKind GetTargetKey(CalameIconKey key) case CalameIconKey.Pause: return PackIconMaterialKind.Pause; case CalameIconKey.Stop: return PackIconMaterialKind.Stop; case CalameIconKey.NextFrame: return PackIconMaterialKind.SkipNext; + case CalameIconKey.ViewerDebugMode: return PackIconMaterialKind.ApplicationCog; case CalameIconKey.ResetSession: return PackIconMaterialKind.RotateLeft; case CalameIconKey.DefaultCamera: return PackIconMaterialKind.AppleAirplay; diff --git a/Calame.Viewer/Calame.Viewer.csproj b/Calame.Viewer/Calame.Viewer.csproj index 7d01a41..7c52c2e 100644 --- a/Calame.Viewer/Calame.Viewer.csproj +++ b/Calame.Viewer/Calame.Viewer.csproj @@ -4,6 +4,7 @@ True + diff --git a/Calame.Viewer/Commands/ViewerDebugModeCommand.cs b/Calame.Viewer/Commands/ViewerDebugModeCommand.cs new file mode 100644 index 0000000..24bf526 --- /dev/null +++ b/Calame.Viewer/Commands/ViewerDebugModeCommand.cs @@ -0,0 +1,31 @@ +using System.Threading.Tasks; +using Calame.Commands.Base; +using Calame.Icons; +using Calame.Viewer.Commands.Base; +using Gemini.Framework.Commands; + +namespace Calame.Viewer.Commands +{ + [CommandDefinition] + public class ViewerDebugModeCommand : CalameCommandDefinitionBase + { + public override string Text => "Viewer _Debug Mode"; + public override object IconKey => CalameIconKey.ViewerDebugMode; + + [CommandHandler] + public class CommandHandler : ViewerDocumentCommandHandlerBase + { + protected override void UpdateStatus(Command command, IViewerDocument document) + { + base.UpdateStatus(command, document); + command.Checked = document?.DebugMode ?? false; + } + + protected override Task RunAsync(Command command, IViewerDocument document) + { + document.DebugMode = !document.DebugMode; + return Task.CompletedTask; + } + } + } +} \ No newline at end of file diff --git a/Calame.Viewer/DebuggableViewerContexts.cs b/Calame.Viewer/DebuggableViewerContexts.cs new file mode 100644 index 0000000..b7b9fd5 --- /dev/null +++ b/Calame.Viewer/DebuggableViewerContexts.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Input; +using Calame.DocumentContexts; +using Calame.Viewer.ViewModels; +using Caliburn.Micro; +using Fingear.Interactives; +using Glyph; +using Glyph.Composition; +using Glyph.Engine; +using Stave; + +namespace Calame.Viewer +{ + public class DebuggableViewerContexts : PropertyChangedBase, IRootsContext, IRootComponentsContext, IRootScenesContext, IRootInteractivesContext + { + private ViewerViewModel _viewer; + public ViewerViewModel Viewer + { + get => _viewer; + private set => Set(ref _viewer, value); + } + + private IEnumerable _roots; + public IEnumerable Roots + { + get => _roots; + private set => Set(ref _roots, value); + } + + private IEnumerable _rootComponents; + public IEnumerable RootComponents + { + get => _rootComponents; + private set => Set(ref _rootComponents, value); + } + + private IEnumerable _rootInteractives; + public IEnumerable RootInteractives + { + get => _rootInteractives; + private set => Set(ref _rootInteractives, value); + } + + private bool _debugMode; + public bool DebugMode + { + get => _debugMode; + set + { + if (Set(ref _debugMode, value)) + RefreshContexts(); + } + } + + public GlyphEngine Engine => _viewer.Runner.Engine; + public IEnumerable RootScenes => _viewer.Runner.Engine.ProjectionManager.SceneRoots; + + private IGlyphComponent _userParentComponent; + + public IGlyphComponent UserParentComponent + { + get => _userParentComponent; + set + { + if (_userParentComponent == value) + return; + + _userParentComponent = value; + + if (!DebugMode) + RefreshContexts(); + } + } + + public DebuggableViewerContexts(ViewerViewModel viewer, ISelectionCommandContext selectionCommandContext) + { + Viewer = viewer; + SelectCommand = new SelectionCommand(selectionCommandContext); + } + + public void RefreshContexts() + { + if (DebugMode) + { + RootComponents = new IGlyphComponent[] { _viewer.Runner.Engine.Root }; + RootInteractives = new IInteractive[] { _viewer.Runner.Engine.InteractionManager.Root }; + } + else + { + RootComponents = (UserParentComponent ?? _viewer.UserRoot).Components; + RootInteractives = _viewer.InteractiveModes.Where(x => x.IsUserMode).Select(x => x.Interactive); + } + + Roots = RootComponents; + CanSelectChanged?.Invoke(this, EventArgs.Empty); + } + + public ICommand SelectCommand { get; } + public event EventHandler CanSelectChanged; + + public bool CanSelect(IGlyphComponent component) + { + if (DebugMode) + return CanSelectInDebugMode(component); + + return CanSelectInUserMode(component); + } + + private bool CanSelectInDebugMode(IGlyphComponent component) + { + return CanSelectBase(component) + && !component.AndAllParents().Any(_viewer.NotSelectableComponents.Contains); + } + + private bool CanSelectInUserMode(IGlyphComponent component) + { + return CanSelectBase(component) + && component.AllParents().Contains(UserParentComponent ?? _viewer.UserRoot); + } + + private bool CanSelectBase(IGlyphComponent component) + { + return component != null && !component.GetType().IsValueType; + } + + private class SelectionCommand : ICommand + { + private readonly ISelectionCommandContext _context; + + public SelectionCommand(ISelectionCommandContext context) + { + _context = context; + } + + public event EventHandler CanExecuteChanged + { + add => _context.CanSelectChanged += value; + remove => _context.CanSelectChanged -= value; + } + + public bool CanExecute(object parameter) => _context.CanSelect(parameter); + public void Execute(object parameter) => _context.SelectAsync(parameter).Wait(); + } + } +} \ No newline at end of file diff --git a/Calame.Viewer/IViewerDocument.cs b/Calame.Viewer/IViewerDocument.cs index 1c461d8..9746ba3 100644 --- a/Calame.Viewer/IViewerDocument.cs +++ b/Calame.Viewer/IViewerDocument.cs @@ -1,12 +1,24 @@ -using Calame.Viewer.ViewModels; +using Calame.DocumentContexts; +using Calame.Viewer.ViewModels; using Gemini.Framework; +using Glyph.Composition; using Glyph.Engine; namespace Calame.Viewer { - public interface IViewerDocument : IDocument, IViewerViewModelOwner, IDocumentContext, IDocumentContext, IDocumentContext + public interface IViewerDocument : IDocument, IViewerViewModelOwner, + IDocumentContext, + IDocumentContext, + IDocumentContext, + IDocumentContext, + IDocumentContext, + IDocumentContext, + IDocumentContext, + IDocumentContext>, + ISelectionCommandContext { ViewerViewModel Viewer { get; } + bool DebugMode { get; set; } void EnableFreeCamera(); } } \ No newline at end of file diff --git a/Calame.Viewer/IViewerInteractiveMode.cs b/Calame.Viewer/IViewerInteractiveMode.cs index 6efb7ec..6b23f85 100644 --- a/Calame.Viewer/IViewerInteractiveMode.cs +++ b/Calame.Viewer/IViewerInteractiveMode.cs @@ -10,5 +10,6 @@ public interface IViewerInteractiveMode IInteractive Interactive { get; } Cursor Cursor { get; } bool UseFreeCamera { get; } + bool IsUserMode { get; } } } \ No newline at end of file diff --git a/Calame.Viewer/Modules/BoxedComponentSelectorModule.cs b/Calame.Viewer/Modules/BoxedComponentSelectorModule.cs index e4bdfb8..c9165f4 100644 --- a/Calame.Viewer/Modules/BoxedComponentSelectorModule.cs +++ b/Calame.Viewer/Modules/BoxedComponentSelectorModule.cs @@ -1,6 +1,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Calame.DocumentContexts; using Calame.Viewer.Modules.Base; using Calame.Viewer.ViewModels; using Caliburn.Micro; @@ -19,8 +20,7 @@ namespace Calame.Viewer.Modules public class BoxedComponentSelectorModule : ViewerModuleBase, IHandle> { private readonly IEventAggregator _eventAggregator; - private IDocumentContext _currentDocument; - private IDocumentContext _filteringContext; + private ISelectionCommandContext _selectionCommandContext; private GlyphObject _root; private ShapedObjectSelector _shapedObjectSelector; @@ -68,7 +68,7 @@ protected override void DisconnectRunner() private bool ComponentFilter(IGlyphComponent glyphComponent) { - return _filteringContext?.Context.Filter(glyphComponent) ?? true; + return _selectionCommandContext?.CanSelect(glyphComponent) ?? true; } private async void OnShapedObjectSelectorSelectionChanged(object sender, IBoxedComponent boxedComponent) @@ -77,13 +77,14 @@ private async void OnShapedObjectSelectorSelectionChanged(object sender, IBoxedC return; SelectedComponent = boxedComponent?.AllParents().OfType().First(x => x.Components.AnyOfType()); - await _eventAggregator.PublishAsync(new SelectionRequest(_currentDocument, SelectedComponent)); + + if (_selectionCommandContext != null) + await _selectionCommandContext.SelectAsync(SelectedComponent); } Task IHandle>.HandleAsync(IDocumentContext message, CancellationToken cancellationToken) { - _currentDocument = message; - _filteringContext = message as IDocumentContext; + _selectionCommandContext = (message as IDocumentContext)?.Context; return Task.CompletedTask; } diff --git a/Calame.Viewer/Modules/SelectionRendererModule.cs b/Calame.Viewer/Modules/SelectionRendererModule.cs index ef6194c..de293e1 100644 --- a/Calame.Viewer/Modules/SelectionRendererModule.cs +++ b/Calame.Viewer/Modules/SelectionRendererModule.cs @@ -41,7 +41,7 @@ protected override void HandleComponent(IGlyphComponent selection) if (!(selection is IBoxedComponent boxedSelection)) return; - _root = Model.EditorModeRoot.Add(Model.ComponentsFilter.ExcludedRoots.Add); + _root = Model.EditorModeRoot.Add(beforeAdding: Model.NotSelectableComponents.Add); _root.Add(); var lineMesh = new LineMesh(Color.Purple); diff --git a/Calame.Viewer/Modules/TransformationEditorModule.cs b/Calame.Viewer/Modules/TransformationEditorModule.cs index f2c17fc..c4838c1 100644 --- a/Calame.Viewer/Modules/TransformationEditorModule.cs +++ b/Calame.Viewer/Modules/TransformationEditorModule.cs @@ -38,7 +38,7 @@ protected override void HandleComponent(IGlyphComponent selection) if (sceneNode == null) return; - var transformationEditor = Model.EditorModeRoot.Add(beforeAdding: Model.ComponentsFilter.ExcludedRoots.Add); + var transformationEditor = Model.EditorModeRoot.Add(beforeAdding: Model.NotSelectableComponents.Add); transformationEditor.EditedObject = new MultiModeTransformationController(sceneNode); transformationEditor.RaycastClient = Model.Client; @@ -51,7 +51,7 @@ protected override void HandleData(IGlyphData selection) if (controller.Anchor == null) return; - var transformationEditor = Model.EditorModeRoot.Add(beforeAdding: Model.ComponentsFilter.ExcludedRoots.Add); + var transformationEditor = Model.EditorModeRoot.Add(beforeAdding: Model.NotSelectableComponents.Add); transformationEditor.EditedObject = controller; transformationEditor.RaycastClient = Model.Client; diff --git a/Calame.Viewer/ViewModels/ViewerViewModel.cs b/Calame.Viewer/ViewModels/ViewerViewModel.cs index 1fadb57..6075d52 100644 --- a/Calame.Viewer/ViewModels/ViewerViewModel.cs +++ b/Calame.Viewer/ViewModels/ViewerViewModel.cs @@ -17,6 +17,7 @@ using Fingear.Interactives.Containers; using Fingear.Interactives.Interfaces; using Glyph; +using Glyph.Composition; using Glyph.Core; using Glyph.Core.Inputs; using Glyph.Engine; @@ -49,8 +50,8 @@ public class ViewerViewModel : PropertyChangedBase, IHandle Modules { get; } public ReadOnlyObservableList InteractiveModes { get; } - - public ComponentFilter ComponentsFilter { get; } + + public ObservableCollection NotSelectableComponents { get; } public ISelectionSpread LastSelection { get; set; } private IViewerInteractiveMode _selectedMode; @@ -80,9 +81,9 @@ public GlyphWpfRunner Runner foreach (IViewerModule module in Modules) module.Disconnect(); - - ComponentsFilter.ExcludedRoots.Clear(); + NotSelectableComponents.Clear(); + engine.InteractionManager.Root.Remove(_viewerModeToggle); engine.InteractionManager.Root.Remove(_editorInteractive); @@ -160,8 +161,8 @@ public ViewerViewModel(IViewerViewModelOwner owner, IEventAggregator eventAggreg module.Model = this; Modules = new ReadOnlyList(modules); - - ComponentsFilter = new ComponentFilter(); + + NotSelectableComponents = new ObservableCollection(); _owner.Activated += OnActivated; _owner.Deactivated += OnDeactivated; @@ -268,6 +269,7 @@ public class EditorModeModule : ViewerModuleBase, IViewerInteractiveMode public object IconKey => CalameIconKey.EditorMode; public Cursor Cursor => Cursors.Cross; public bool UseFreeCamera => true; + bool IViewerInteractiveMode.IsUserMode => false; public GlyphObject Root { get; private set; } diff --git a/Calame.Viewer/ViewerCommandDefinitions.cs b/Calame.Viewer/ViewerCommandDefinitions.cs index 1a9e3d7..62dad3e 100644 --- a/Calame.Viewer/ViewerCommandDefinitions.cs +++ b/Calame.Viewer/ViewerCommandDefinitions.cs @@ -17,6 +17,18 @@ public class ViewerToolBar static public ToolBarItemGroupDefinition ModesGroup = new ToolBarItemGroupDefinition(Definition, 0); [Export] static public ToolBarItemDefinition EditorMode = new CommandToolBarItemDefinition(ModesGroup, 0); + + [Export] + static public ToolBarItemGroupDefinition DebugGroup = new ToolBarItemGroupDefinition(Definition, 100); + [Export] + static public ToolBarItemDefinition ViewerDebugMode = new CommandToolBarItemDefinition(DebugGroup, 0); + + [Export] + static public ToolBarItemGroupDefinition CameraActionGroup = new ToolBarItemGroupDefinition(Definition, 200); + [Export] + static public ToolBarItemDefinition ResetCamera = new CommandToolBarItemDefinition(CameraActionGroup, 0); + [Export] + static public ToolBarItemDefinition FocusCamera = new CommandToolBarItemDefinition(CameraActionGroup, 10); } public class ViewerMenu @@ -28,11 +40,31 @@ public class ViewerMenu static public MenuItemGroupDefinition ModesGroup = new MenuItemGroupDefinition(Definition, 0); [Export] static public MenuItemDefinition EditorMode = new CommandMenuItemDefinition(ModesGroup, 0); + + [Export] + static public MenuItemGroupDefinition DebugGroup = new MenuItemGroupDefinition(Definition, 100); + [Export] + static public MenuItemDefinition ViewerDebugMode = new CommandMenuItemDefinition(DebugGroup, 0); + + [Export] + static public MenuItemGroupDefinition CameraActionGroup = new MenuItemGroupDefinition(Definition, 200); + [Export] + static public MenuItemDefinition ResetCamera = new CommandMenuItemDefinition(CameraActionGroup, 0); + [Export] + static public MenuItemDefinition FocusCamera = new CommandMenuItemDefinition(CameraActionGroup, 10); } static public class SessionShortcuts { [Export] static public CommandKeyboardShortcut EditorMode = new CommandKeyboardShortcut(new KeyGesture(Key.F1)); + + [Export] + static public CommandKeyboardShortcut ViewerDebugMode = new CommandKeyboardShortcut(new KeyGesture(Key.D, ModifierKeys.Control | ModifierKeys.Alt | ModifierKeys.Shift)); + + [Export] + static public CommandKeyboardShortcut ResetCamera = new CommandKeyboardShortcut(new KeyGesture(Key.F, ModifierKeys.Control | ModifierKeys.Alt)); + [Export] + static public CommandKeyboardShortcut FocusCamera = new CommandKeyboardShortcut(new KeyGesture(Key.F, ModifierKeys.Control)); } } \ No newline at end of file diff --git a/Calame.sln b/Calame.sln index efc723a..575f50b 100644 --- a/Calame.sln +++ b/Calame.sln @@ -117,6 +117,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Glyph.Pipeline", "External\ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Gemini", "External\gemini\src\Gemini\Gemini.csproj", "{659541B0-C2A8-4F33-984F-EEB3729F8969}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Calame.DocumentContexts", "Calame.DocumentContexts\Calame.DocumentContexts.csproj", "{4494C77C-503F-4519-906F-92D2E3119C4F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -335,6 +337,10 @@ Global {659541B0-C2A8-4F33-984F-EEB3729F8969}.Debug|Any CPU.Build.0 = Debug|Any CPU {659541B0-C2A8-4F33-984F-EEB3729F8969}.Release|Any CPU.ActiveCfg = Release|Any CPU {659541B0-C2A8-4F33-984F-EEB3729F8969}.Release|Any CPU.Build.0 = Release|Any CPU + {4494C77C-503F-4519-906F-92D2E3119C4F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4494C77C-503F-4519-906F-92D2E3119C4F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4494C77C-503F-4519-906F-92D2E3119C4F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4494C77C-503F-4519-906F-92D2E3119C4F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Calame/CalameTool.cs b/Calame/CalameTool.cs index 3c0bdad..b9e00a4 100644 --- a/Calame/CalameTool.cs +++ b/Calame/CalameTool.cs @@ -35,6 +35,12 @@ protected CalameTool(IShell shell, IEventAggregator eventAggregator, IIconProvid _iconProvider = iconProvider; _iconDescriptor = iconDescriptorManager.GetDescriptor(); + if (shell.ActiveItem is THandledDocument document) + { + CurrentDocument = document; + OnDocumentActivated(CurrentDocument); + } + Shell.ActiveDocumentChanged += ShellOnActiveDocumentChanged; EventAggregator.SubscribeOnUI(this); @@ -68,8 +74,8 @@ private void ShellOnActiveDocumentChanged(object sender, EventArgs e) if (Shell.ActiveItem != null) return; - CurrentDocument = null; OnDocumentsCleaned(); + CurrentDocument = null; } public async Task HandleAsync(IDocumentContext message, CancellationToken cancellationToken) diff --git a/Calame/Commands/Base/OpenLayoutCommandBase.cs b/Calame/Commands/Base/OpenLayoutCommandBase.cs new file mode 100644 index 0000000..342d12f --- /dev/null +++ b/Calame/Commands/Base/OpenLayoutCommandBase.cs @@ -0,0 +1,30 @@ +using System.Threading.Tasks; +using Caliburn.Micro; +using Gemini.Framework; +using Gemini.Framework.Commands; +using Gemini.Framework.Services; + +namespace Calame.Commands.Base +{ + public abstract class OpenLayoutCommandBase : CalameCommandHandlerBase + where TDefinition : CommandDefinition + { + private readonly IShell _shell; + + public OpenLayoutCommandBase() + { + _shell = IoC.Get(); + } + + protected override Task RunAsync(Command command) + { + foreach (ITool tool in _shell.Tools) + tool.IsVisible = false; + + OpenLayout(_shell); + return Task.CompletedTask; + } + + protected abstract void OpenLayout(IShell shell); + } +} \ No newline at end of file diff --git a/Calame/ComponentFilter.cs b/Calame/ComponentFilter.cs deleted file mode 100644 index 8174e15..0000000 --- a/Calame/ComponentFilter.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Collections.ObjectModel; -using System.Linq; -using Glyph.Composition; -using Stave; - -namespace Calame -{ - public class ComponentFilter : IComponentFilter - { - public ObservableCollection ExcludedRoots { get; } = new ObservableCollection(); - - public bool Filter(IGlyphComponent component) - { - return !ExcludedRoots.Any(x => x.AndAllChildren().Contains(component)); - } - } -} \ No newline at end of file diff --git a/Calame/IComponentFilter.cs b/Calame/IComponentFilter.cs deleted file mode 100644 index 513db94..0000000 --- a/Calame/IComponentFilter.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Glyph.Composition; - -namespace Calame -{ - public interface IComponentFilter - { - bool Filter(IGlyphComponent component); - } -} \ No newline at end of file diff --git a/Calame/Icons/CalameIconKey.cs b/Calame/Icons/CalameIconKey.cs index 62410ae..d2ac4ef 100644 --- a/Calame/Icons/CalameIconKey.cs +++ b/Calame/Icons/CalameIconKey.cs @@ -16,6 +16,7 @@ public enum CalameIconKey Pause, Stop, NextFrame, + ViewerDebugMode, ResetSession, DefaultCamera, FreeCamera, diff --git a/Calame/UserControls/CalameTreeView.xaml b/Calame/UserControls/CalameTreeView.xaml index e504e96..49c3e73 100644 --- a/Calame/UserControls/CalameTreeView.xaml +++ b/Calame/UserControls/CalameTreeView.xaml @@ -118,9 +118,16 @@ - + + + + + + + + diff --git a/Calame/UserControls/CalameTreeView.xaml.cs b/Calame/UserControls/CalameTreeView.xaml.cs index 187459b..389114b 100644 --- a/Calame/UserControls/CalameTreeView.xaml.cs +++ b/Calame/UserControls/CalameTreeView.xaml.cs @@ -1,6 +1,5 @@ using System; using System.Collections; -using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.Linq; @@ -18,6 +17,8 @@ namespace Calame.UserControls { public interface ITreeContext { + bool DisableChildrenIfParentDisabled { get; } + event EventHandler BaseFilterChanged; ITreeViewItemModel CreateTreeItemModel(object data, ICollectionSynchronizerConfiguration synchronizerConfiguration); bool IsMatchingBaseFilter(object data); } @@ -32,22 +33,12 @@ public IEnumerable ItemsSource set => SetValue(ItemsSourceProperty, value); } - static private readonly Dictionary> Synchronizers = new Dictionary>(); - static private void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var treeView = (CalameTreeView)d; var itemsSource = (IEnumerable)e.NewValue; - if (!Synchronizers.TryGetValue(treeView, out ObservableListSynchronizer synchronizer)) - { - synchronizer = new ObservableListSynchronizer(treeView); - - synchronizer.Subscribe(treeView._treeItems); - Synchronizers.Add(treeView, synchronizer); - } - - synchronizer.Reference = itemsSource != null ? new EnumerableReadOnlyObservableList(itemsSource) : null; + treeView._synchronizer.Reference = itemsSource != null ? new EnumerableReadOnlyObservableList(itemsSource) : null; treeView.UpdateFilter(forceExpand: true); } @@ -63,7 +54,7 @@ void ICollectionSynchronizerConfiguration.DisposeIte => collectedItem.Dispose(); static public readonly DependencyProperty TreeContextProperty - = DependencyProperty.Register(nameof(TreeContext), typeof(ITreeContext), typeof(CalameTreeView), new PropertyMetadata(default(ITreeContext))); + = DependencyProperty.Register(nameof(TreeContext), typeof(ITreeContext), typeof(CalameTreeView), new PropertyMetadata(default(ITreeContext), OnTreeContextChanged)); public ITreeContext TreeContext { @@ -71,6 +62,28 @@ public ITreeContext TreeContext set => SetValue(TreeContextProperty, value); } + static private void OnTreeContextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var treeView = (CalameTreeView)d; + var oldValue = (ITreeContext)e.OldValue; + var newValue = (ITreeContext)e.NewValue; + + if (oldValue != null) + { + oldValue.BaseFilterChanged -= treeView.OnBaseFilterChanged; + + treeView._synchronizer.Unsubscribe(treeView._treeItems); + } + + if (newValue != null) + { + treeView._synchronizer.Subscribe(treeView._treeItems); + treeView.UpdateFilter(forceExpand: true); + + newValue.BaseFilterChanged += treeView.OnBaseFilterChanged; + } + } + static public readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register(nameof(SelectedItem), typeof(object), typeof(CalameTreeView), new PropertyMetadata(default(object), OnSelectedItemChanged)); @@ -105,8 +118,9 @@ public IIconDescriptor IconDescriptor get => (IIconDescriptor)GetValue(IconDescriptorProperty); set => SetValue(IconDescriptorProperty, value); } - - private readonly ObservableList _treeItems; + + private readonly ObservableListSynchronizer _synchronizer; + private ObservableList _treeItems; public IReadOnlyObservableList TreeItems { get; } private ITreeViewItemModel _selectedTreeItem; @@ -154,6 +168,8 @@ public CalameTreeView() _treeItems = new ObservableList(); TreeItems = new ReadOnlyObservableList(_treeItems); + _synchronizer = new ObservableListSynchronizer(this); + CollapseAllCommand = new RelayCommand(OnCollapseAll); ExpandAllCommand = new RelayCommand(OnExpandAll); FocusSelectionCommand = new RelayCommand(OnFocusSelection, CanFocusSelection); @@ -238,6 +254,7 @@ private void OnItemChanged(object sender, NotifyCollectionChangedEventArgs e) } } + private void OnBaseFilterChanged(object sender, EventArgs eventArgs) => UpdateFilter(forceExpand: true); private void UpdateFilter(bool forceExpand) { // If the selected tree item is filtered, it will be unselect diff --git a/Calame/Utils/ObservableHelpers.cs b/Calame/Utils/ObservableHelpers.cs index 94aa533..d23ff86 100644 --- a/Calame/Utils/ObservableHelpers.cs +++ b/Calame/Utils/ObservableHelpers.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Reactive; using System.Reactive.Linq; +using System.Windows.Input; namespace Calame.Utils { @@ -24,5 +25,12 @@ static public IObservable> OnPropertyChan { return OnPropertyChanged(notifyPropertyChanged).Where(x => propertyNames.Contains(x.EventArgs.PropertyName)); } + + static public IObservable> OnCanExecuteChanged(ICommand command) + { + return Observable.FromEventPattern( + x => command.CanExecuteChanged += x, + x => command.CanExecuteChanged -= x); + } } } \ No newline at end of file diff --git a/Calame/Utils/TreeViewItemModelBuilder.cs b/Calame/Utils/TreeViewItemModelBuilder.cs index 68a370b..8e5590d 100644 --- a/Calame/Utils/TreeViewItemModelBuilder.cs +++ b/Calame/Utils/TreeViewItemModelBuilder.cs @@ -3,6 +3,7 @@ using System.ComponentModel; using System.Linq; using System.Reactive.Linq; +using System.Windows.Input; using Calame.Icons; using Diese.Collections.Observables; using Diese.Collections.Observables.ReadOnly; @@ -29,7 +30,27 @@ static private IObservable OnPropertyChanged(T data, string propertyName { return ObservableHelpers.OnPropertyChanged(notifier?.Invoke(data) ?? data as INotifyPropertyChanged, propertyName); } - + + public TreeViewItemModelBuilder IsEnabled(Func commandGetter, Func commandParameterGetter = null, bool valueByDefault = true) + => IsEnabled(x => CanExecuteGetter(x, commandGetter, commandParameterGetter, valueByDefault), x => OnCanExecuteChanged(x, commandGetter)); + public TreeViewItemModelBuilder IsTriggered(Func commandGetter, Func commandParameterGetter = null, bool valueByDefault = false) + => IsTriggered(x => CanExecuteGetter(x, commandGetter, commandParameterGetter, valueByDefault), x => OnCanExecuteChanged(x, commandGetter)); + + static private bool CanExecuteGetter(T data, Func commandGetter, Func commandParameterGetter, bool valueByDefault) + { + bool result = commandGetter(data)?.CanExecute(commandParameterGetter?.Invoke(data) ?? data) ?? valueByDefault; + return result; + } + + static private IObservable OnCanExecuteChanged(T data, Func commandGetter) + { + ICommand command = commandGetter(data); + if (command == null) + return Observable.Empty(); + + return ObservableHelpers.OnCanExecuteChanged(command); + } + public TreeViewItemModelBuilder DisplayName(Func getter, Func> observableFunc = null) { AddBinding(getter, observableFunc, (v, x) => v.DisplayName = x); diff --git a/Modules/Calame.BrushPanel/Calame.BrushPanel.csproj b/Modules/Calame.BrushPanel/Calame.BrushPanel.csproj index 791196e..00e2ba9 100644 --- a/Modules/Calame.BrushPanel/Calame.BrushPanel.csproj +++ b/Modules/Calame.BrushPanel/Calame.BrushPanel.csproj @@ -4,6 +4,7 @@ True + diff --git a/Modules/Calame.BrushPanel/ViewModels/BrushPanelViewModel.cs b/Modules/Calame.BrushPanel/ViewModels/BrushPanelViewModel.cs index 9b6889e..183f4ef 100644 --- a/Modules/Calame.BrushPanel/ViewModels/BrushPanelViewModel.cs +++ b/Modules/Calame.BrushPanel/ViewModels/BrushPanelViewModel.cs @@ -1,11 +1,11 @@ using System; -using System.Collections; using System.Collections.Generic; using System.ComponentModel.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Windows.Input; +using Calame.DocumentContexts; using Calame.Icons; using Calame.UserControls; using Calame.Utils; @@ -19,7 +19,6 @@ using Gemini.Framework.Services; using Glyph.Composition; using Glyph.Composition.Modelization; -using Glyph.Engine; namespace Calame.BrushPanel.ViewModels { @@ -32,23 +31,25 @@ public sealed class BrushPanelViewModel : CalameTool _dataTreeItemBuilder; private readonly TreeViewItemModelBuilder _componentTreeItemBuilder; - - private GlyphEngine _engine; - private IBrushViewerModule _viewerModule; - private IDocumentContext _filteringContext; + + public bool DisableChildrenIfParentDisabled => true; + public event EventHandler BaseFilterChanged; private readonly IEngineBrushViewModel[] _allEngineBrushes; private readonly IDataBrushViewModel[] _allDataBrushes; private readonly ObservableCollection _brushes; public IReadOnlyObservableCollection Brushes { get; } - private IEnumerable _items; - public IEnumerable Items + private IRootsContext _rootsContext; + public IRootsContext RootsContext { - get => _items; - private set => SetValue(ref _items, value); + get => _rootsContext; + private set => SetValue(ref _rootsContext, value); } private object _selectedCanvas; @@ -57,44 +58,12 @@ public object SelectedCanvas get => _selectedCanvas; set { - object previousCanvas = _selectedCanvas; - if (!SetValue(ref _selectedCanvas, value)) return; OnCanvasChanged(); - ISelectionRequest selectionRequest; - if (_selectedCanvas != null) - { - switch (_selectedCanvas) - { - case IGlyphData data: - selectionRequest = new SelectionRequest(CurrentDocument, data); - break; - case IGlyphComponent component: - selectionRequest = new SelectionRequest(CurrentDocument, component); - break; - default: - throw new NotSupportedException(); - } - } - else - { - switch (previousCanvas) - { - case IGlyphData _: - selectionRequest = new SelectionRequest(CurrentDocument, (IGlyphData)null); - break; - case IGlyphComponent _: - selectionRequest = new SelectionRequest(CurrentDocument, (IGlyphComponent)null); - break; - default: - throw new NotSupportedException(); - } - } - - EventAggregator.PublishAsync(selectionRequest).Wait(); + _selectionCommandContext?.SelectAsync(_selectedCanvas).Wait(); SwitchToBrushModeAsync().Wait(); } } @@ -155,22 +124,16 @@ public BrushPanelViewModel(IShell shell, IEventAggregator eventAggregator, IIcon _allDataBrushes = allDataBrushes; _brushes = new ObservableCollection(); Brushes = new ReadOnlyObservableCollection(_brushes); - - if (shell.ActiveItem is IDocumentContext documentContext) - _viewerModule = documentContext.Context.Modules.FirstOfTypeOrDefault(); } protected override Task OnDocumentActivated(IDocumentContext activeDocument) { - _engine = activeDocument.Context.Runner.Engine; - _viewerModule = activeDocument.Context.Modules.FirstOfTypeOrDefault(); - _filteringContext = activeDocument as IDocumentContext; - - if (activeDocument is IDocumentContext dataContext) - Items = new[] { dataContext.Context }; - else - Items = _engine.Root.Components; - + ViewerViewModel viewer = activeDocument.Context; + + _selectionCommandContext = (activeDocument as IDocumentContext)?.Context; + _viewerModule = viewer.Modules.FirstOfTypeOrDefault(); + RootsContext = ((IDocumentContext)activeDocument).Context; + //IBrush previousBrush = _viewerModule.Brush; //IPaint previousPaint = _viewerModule.Paint; @@ -181,6 +144,9 @@ protected override Task OnDocumentActivated(IDocumentContext ac //SelectedBrush = _brushes.FirstOrDefault(x => x == previousBrush); //SelectedPaint = SelectedBrush?.Paints.FirstOrDefault(x => x.Paint == previousPaint); + if (_selectionCommandContext != null) + _selectionCommandContext.CanSelectChanged += OnCanSelectChanged; + _viewerModule.ApplyEnded += OnBrushApplyEnded; return Task.CompletedTask; @@ -188,12 +154,16 @@ protected override Task OnDocumentActivated(IDocumentContext ac protected override Task OnDocumentsCleaned() { + _viewerModule.ApplyEnded -= OnBrushApplyEnded; + + if (_selectionCommandContext != null) + _selectionCommandContext.CanSelectChanged -= OnCanSelectChanged; + HandleSelection(null); - Items = null; - _engine = null; + RootsContext = null; + _selectionCommandContext = null; _viewerModule = null; - _filteringContext = null; return Task.CompletedTask; } @@ -218,21 +188,13 @@ ITreeViewItemModel ITreeContext.CreateTreeItemModel(object model, ICollectionSyn bool ITreeContext.IsMatchingBaseFilter(object model) { - IGlyphComponent componentToFilter; - switch (model) - { - case IGlyphData data: - componentToFilter = data.BindedObject; - break; - case IGlyphComponent component: - componentToFilter = component; - break; - default: - throw new NotSupportedException(); - } - return GetAllBrushesForType(model).Any(brush => brush.IsValidForCanvas(model)) - && _filteringContext.Context.Filter(componentToFilter); + && (_selectionCommandContext?.CanSelect(model) ?? true); + } + + private void OnCanSelectChanged(object sender, EventArgs e) + { + BaseFilterChanged?.Invoke(this, EventArgs.Empty); } private IEnumerable GetAllBrushesForType(object canvas) @@ -250,12 +212,18 @@ private IEnumerable GetAllBrushesForType(object canvas) Task IHandle>.HandleAsync(ISelectionSpread message, CancellationToken cancellationToken) { + if (message.DocumentContext != CurrentDocument) + return Task.CompletedTask; + HandleSelection(message.Item); return Task.CompletedTask; } Task IHandle>.HandleAsync(ISelectionSpread message, CancellationToken cancellationToken) { + if (message.DocumentContext != CurrentDocument) + return Task.CompletedTask; + HandleSelection(message.Item); return Task.CompletedTask; } diff --git a/Modules/Calame.BrushPanel/ViewerModule.cs b/Modules/Calame.BrushPanel/ViewerModule.cs index d2fff12..710bbfa 100644 --- a/Modules/Calame.BrushPanel/ViewerModule.cs +++ b/Modules/Calame.BrushPanel/ViewerModule.cs @@ -111,6 +111,7 @@ public IInteractive Interactive public object IconKey => CalameIconKey.BrushMode; Cursor IViewerInteractiveMode.Cursor => Cursors.Pen; bool IViewerInteractiveMode.UseFreeCamera => true; + bool IViewerInteractiveMode.IsUserMode => false; protected override void ConnectModel() => Model.AddInteractiveMode(this); protected override void DisconnectModel() => Model.RemoveInteractiveMode(this); diff --git a/Modules/Calame.BrushPanel/Views/BrushPanelView.xaml b/Modules/Calame.BrushPanel/Views/BrushPanelView.xaml index bf309db..19974ef 100644 --- a/Modules/Calame.BrushPanel/Views/BrushPanelView.xaml +++ b/Modules/Calame.BrushPanel/Views/BrushPanelView.xaml @@ -21,7 +21,7 @@ diff --git a/Modules/Calame.CompositionGraph/Calame.CompositionGraph.csproj b/Modules/Calame.CompositionGraph/Calame.CompositionGraph.csproj index a1d896a..6d97998 100644 --- a/Modules/Calame.CompositionGraph/Calame.CompositionGraph.csproj +++ b/Modules/Calame.CompositionGraph/Calame.CompositionGraph.csproj @@ -4,6 +4,7 @@ True + diff --git a/Modules/Calame.CompositionGraph/ViewModels/CompositionGraphViewModel.cs b/Modules/Calame.CompositionGraph/ViewModels/CompositionGraphViewModel.cs index 5c35b97..8515030 100644 --- a/Modules/Calame.CompositionGraph/ViewModels/CompositionGraphViewModel.cs +++ b/Modules/Calame.CompositionGraph/ViewModels/CompositionGraphViewModel.cs @@ -1,6 +1,8 @@ -using System.ComponentModel.Composition; +using System; +using System.ComponentModel.Composition; using System.Threading; using System.Threading.Tasks; +using Calame.DocumentContexts; using Calame.Icons; using Calame.UserControls; using Calame.Utils; @@ -10,26 +12,27 @@ using Gemini.Framework.Services; using Glyph.Composition; using Glyph.Composition.Modelization; -using Glyph.Engine; namespace Calame.CompositionGraph.ViewModels { [Export(typeof(CompositionGraphViewModel))] - public sealed class CompositionGraphViewModel : CalameTool>, IHandle>, IHandle>, ITreeContext + public sealed class CompositionGraphViewModel : CalameTool>, ITreeContext, + IHandle>, + IHandle> { public override PaneLocation PreferredLocation => PaneLocation.Left; public IIconProvider IconProvider { get; } public IIconDescriptor IconDescriptor { get; } - + private readonly TreeViewItemModelBuilder _treeItemBuilder; - private IDocumentContext _filteringContext; + private ISelectionCommandContext _selectionCommandContext; - private IGlyphComponent _root; - public IGlyphComponent Root + private IRootComponentsContext _rootComponentsContext; + public IRootComponentsContext RootComponentsContext { - get => _root; - private set => SetValue(ref _root, value); + get => _rootComponentsContext; + private set => SetValue(ref _rootComponentsContext, value); } private IGlyphComponent _selection; @@ -41,8 +44,7 @@ public IGlyphComponent Selection if (!SetValue(ref _selection, value)) return; - var selectionRequest = new SelectionRequest(CurrentDocument, _selection); - EventAggregator.PublishAsync(selectionRequest).Wait(); + _selectionCommandContext?.SelectAsync(_selection).Wait(); } } @@ -63,15 +65,15 @@ public CompositionGraphViewModel(IShell shell, IEventAggregator eventAggregator, .DisplayName(x => x.Name, nameof(IGlyphComponent.Name)) .ChildrenSource(x => new EnumerableReadOnlyObservableList(x.Components), nameof(IGlyphComponent.Components)) .IconDescription(x => iconDescriptor.GetIcon(x)) - .IsEnabled(x => _filteringContext?.Context.Filter(x) ?? true); + .IsEnabled(_ => _selectionCommandContext?.SelectCommand); } - protected override Task OnDocumentActivated(IDocumentContext activeDocument) + protected override Task OnDocumentActivated(IDocumentContext activeDocument) { _selection = null; - Root = activeDocument.Context.Root; - _filteringContext = activeDocument as IDocumentContext; + _selectionCommandContext = (activeDocument as IDocumentContext)?.Context; + RootComponentsContext = activeDocument.Context; return Task.CompletedTask; } @@ -80,8 +82,8 @@ protected override Task OnDocumentsCleaned() { _selection = null; - Root = null; - _filteringContext = null; + RootComponentsContext = null; + _selectionCommandContext = null; return Task.CompletedTask; } @@ -105,6 +107,8 @@ ITreeViewItemModel ITreeContext.CreateTreeItemModel(object data, ICollectionSync return _treeItemBuilder.Build((IGlyphComponent)data, synchronizerConfiguration); } + public bool DisableChildrenIfParentDisabled => true; + event EventHandler ITreeContext.BaseFilterChanged { add { } remove { } } bool ITreeContext.IsMatchingBaseFilter(object data) => true; } } \ No newline at end of file diff --git a/Modules/Calame.CompositionGraph/Views/CompositionGraphView.xaml b/Modules/Calame.CompositionGraph/Views/CompositionGraphView.xaml index 2f9d3d4..f3aef15 100644 --- a/Modules/Calame.CompositionGraph/Views/CompositionGraphView.xaml +++ b/Modules/Calame.CompositionGraph/Views/CompositionGraphView.xaml @@ -10,7 +10,7 @@ d:DesignHeight="300" d:DesignWidth="300"> diff --git a/Modules/Calame.DataModelTree/Calame.DataModelTree.csproj b/Modules/Calame.DataModelTree/Calame.DataModelTree.csproj index 9e90903..7db1028 100644 --- a/Modules/Calame.DataModelTree/Calame.DataModelTree.csproj +++ b/Modules/Calame.DataModelTree/Calame.DataModelTree.csproj @@ -4,6 +4,7 @@ True + diff --git a/Modules/Calame.DataModelTree/ViewModels/DataModelTreeViewModel.cs b/Modules/Calame.DataModelTree/ViewModels/DataModelTreeViewModel.cs index 8621508..3bae670 100644 --- a/Modules/Calame.DataModelTree/ViewModels/DataModelTreeViewModel.cs +++ b/Modules/Calame.DataModelTree/ViewModels/DataModelTreeViewModel.cs @@ -1,4 +1,5 @@ -using System.ComponentModel.Composition; +using System; +using System.ComponentModel.Composition; using System.Threading; using System.Threading.Tasks; using Calame.Icons; @@ -89,6 +90,8 @@ ITreeViewItemModel ITreeContext.CreateTreeItemModel(object data, ICollectionSync return _treeItemBuilder.Build((IGlyphCreator)data, synchronizerConfiguration); } + public bool DisableChildrenIfParentDisabled => false; + event EventHandler ITreeContext.BaseFilterChanged { add { } remove { } } bool ITreeContext.IsMatchingBaseFilter(object data) => true; } } \ No newline at end of file diff --git a/Modules/Calame.DataModelViewer/Base/ViewerEditorBase.cs b/Modules/Calame.DataModelViewer/Base/ViewerEditorBase.cs index ea04eb2..d94627a 100644 --- a/Modules/Calame.DataModelViewer/Base/ViewerEditorBase.cs +++ b/Modules/Calame.DataModelViewer/Base/ViewerEditorBase.cs @@ -60,12 +60,17 @@ public virtual void PrepareEditor(GlyphEngine engine, GlyphObject editorRoot) renderScheduler.Plan(drawer => drawer.SpriteBatchStack.Current.Draw(pixel, drawer.DisplayedRectangle.BoundingBox.ToIntegers(), Color.CornflowerBlue)) .Before(renderScheduler.RenderViewTask); - var dataRoot = editorRoot.Add(); - dataRoot.Name = "Data Root"; - dataRoot.Add(); + //var dataRoot = editorRoot.Add(); + //dataRoot.Name = "Data Root"; + //dataRoot.Add(); + + //Data.Instantiate(); + //dataRoot.Add(Data.BindedObject); + + editorRoot.Add(); Data.Instantiate(); - dataRoot.Add(Data.BindedObject); + editorRoot.Add(Data.BindedObject); } public virtual void OnDragOver(DragEventArgs dragEventArgs) diff --git a/Modules/Calame.DataModelViewer/ViewModels/DataModelViewerViewModel.cs b/Modules/Calame.DataModelViewer/ViewModels/DataModelViewerViewModel.cs index d355cd0..365ef2f 100644 --- a/Modules/Calame.DataModelViewer/ViewModels/DataModelViewerViewModel.cs +++ b/Modules/Calame.DataModelViewer/ViewModels/DataModelViewerViewModel.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.ComponentModel.Composition; using System.IO; @@ -7,6 +8,7 @@ using System.Threading.Tasks; using System.Windows; using System.Windows.Input; +using Calame.DocumentContexts; using Calame.Icons; using Calame.Viewer; using Calame.Viewer.Messages; @@ -27,7 +29,10 @@ namespace Calame.DataModelViewer.ViewModels { [Export(typeof(DataModelViewerViewModel))] [PartCreationPolicy(CreationPolicy.NonShared)] - public class DataModelViewerViewModel : CalamePersistedDocumentBase, IViewerDocument, IRunnableDocument, IDocumentContext, IHandle>, IHandle> + public class DataModelViewerViewModel : CalamePersistedDocumentBase, IViewerDocument, IRunnableDocument, + IDocumentContext, IDocumentContext>, ISelectionCommandContext, IRootsContext, + IHandle>, + IHandle> { private readonly IImportedTypeProvider _importedTypeProvider; private readonly SelectionHistoryManager _selectionHistoryManager; @@ -46,15 +51,9 @@ public IEditor Editor RefreshIcon(); } } - - IDocument IDocumentContext.Document => this; - GlyphEngine IDocumentContext.Context => Viewer.Runner?.Engine; - ViewerViewModel IDocumentContext.Context => Viewer; - IComponentFilter IDocumentContext.Context => Viewer.ComponentsFilter; - IGlyphData IDocumentContext.Context => Editor.Data; - void IViewerDocument.EnableFreeCamera() {} Type IRunnableDocument.RunCommandDefinitionType => Editor?.RunCommandDefinitionType; + void IViewerDocument.EnableFreeCamera() { } public ICommand DragOverCommand { get; } public ICommand DropCommand { get; } @@ -73,6 +72,8 @@ public DataModelViewerViewModel(IEventAggregator eventAggregator, ILoggerProvide Viewer = new ViewerViewModel(this, eventAggregator, viewerModuleSources); + _debuggableViewerContexts = new DebuggableViewerContexts(Viewer, this); + DragOverCommand = new RelayCommand(x => Editor.OnDragOver((DragEventArgs)x)); DropCommand = new RelayCommand(x => Editor.OnDrop((DragEventArgs)x)); } @@ -114,6 +115,8 @@ private async Task InitializeEngineAsync() Editor.RegisterDependencies(_engine.Registry); Editor.PrepareEditor(Viewer.Runner.Engine, Viewer.UserRoot); + _debuggableViewerContexts.RefreshContexts(); + _engine.Initialize(); await _engine.LoadContentAsync(); @@ -173,5 +176,79 @@ async Task IHandle>.HandleAsync(ISelectionReq Viewer.LastSelection = selection; await EventAggregator.PublishAsync(selection, cancellationToken); } + + private readonly DebuggableViewerContexts _debuggableViewerContexts; + + bool IViewerDocument.DebugMode + { + get => _debuggableViewerContexts.DebugMode; + set => _debuggableViewerContexts.DebugMode = value; + } + + IDocument IDocumentContext.Document => this; + GlyphEngine IDocumentContext.Context => _debuggableViewerContexts.Engine; + ViewerViewModel IDocumentContext.Context => _debuggableViewerContexts.Viewer; + IRootComponentsContext IDocumentContext.Context => _debuggableViewerContexts; + IRootScenesContext IDocumentContext.Context => _debuggableViewerContexts; + IRootInteractivesContext IDocumentContext.Context => _debuggableViewerContexts; + + ISelectionCommandContext IDocumentContext.Context => this; + ISelectionCommandContext IDocumentContext>.Context => this; + ISelectionCommandContext IDocumentContext>.Context => this; + + IRootsContext IDocumentContext.Context => this; + public IEnumerable Roots => new []{ Editor.Data }; + IGlyphData IDocumentContext.Context => Editor.Data; + + public ICommand SelectCommand => _debuggableViewerContexts.SelectCommand; + + event EventHandler ISelectionCommandContext.CanSelectChanged + { + add => _debuggableViewerContexts.CanSelectChanged += value; + remove => _debuggableViewerContexts.CanSelectChanged -= value; + } + + public bool CanSelect(object instance) + { + switch (instance) + { + case IGlyphData data: + return CanSelect(data); + case IGlyphComponent component: + return CanSelect(component); + } + + return false; + } + + public bool CanSelect(IGlyphData data) => CanSelect(data.BindedObject); + public bool CanSelect(IGlyphComponent component) => _debuggableViewerContexts.CanSelect(component); + + public Task SelectAsync(object instance) + { + switch (instance) + { + case IGlyphData data: + return SelectAsync(data); + case IGlyphComponent component: + return SelectAsync(component); + } + + return Task.CompletedTask; + } + + public Task SelectAsync(IGlyphData data) + { + if (CanSelect(data)) + return EventAggregator.PublishAsync(new SelectionRequest(this, data)); + return Task.CompletedTask; + } + + public Task SelectAsync(IGlyphComponent component) + { + if (CanSelect(component)) + return EventAggregator.PublishAsync(new SelectionRequest(this, component)); + return Task.CompletedTask; + } } } \ No newline at end of file diff --git a/Modules/Calame.InteractionTree/Calame.InteractionTree.csproj b/Modules/Calame.InteractionTree/Calame.InteractionTree.csproj index a89e9b9..8d79767 100644 --- a/Modules/Calame.InteractionTree/Calame.InteractionTree.csproj +++ b/Modules/Calame.InteractionTree/Calame.InteractionTree.csproj @@ -4,6 +4,7 @@ True + diff --git a/Modules/Calame.InteractionTree/ViewModels/InteractionTreeViewModel.cs b/Modules/Calame.InteractionTree/ViewModels/InteractionTreeViewModel.cs index fb8eb0c..20e4ed6 100644 --- a/Modules/Calame.InteractionTree/ViewModels/InteractionTreeViewModel.cs +++ b/Modules/Calame.InteractionTree/ViewModels/InteractionTreeViewModel.cs @@ -2,6 +2,7 @@ using System.ComponentModel; using System.ComponentModel.Composition; using System.Threading.Tasks; +using Calame.DocumentContexts; using Calame.Icons; using Calame.UserControls; using Calame.Utils; @@ -11,26 +12,25 @@ using Fingear.Controls; using Fingear.Interactives; using Gemini.Framework.Services; -using Glyph.Engine; namespace Calame.InteractionTree.ViewModels { [Export(typeof(InteractionTreeViewModel))] - public sealed class InteractionTreeViewModel : CalameTool>, ITreeContext + public sealed class InteractionTreeViewModel : CalameTool>, ITreeContext { public override PaneLocation PreferredLocation => PaneLocation.Left; public IIconProvider IconProvider { get; } public IIconDescriptor IconDescriptor { get; } - private GlyphEngine _engine; private readonly TreeViewItemModelBuilder _interactiveTreeItemBuilder; private readonly TreeViewItemModelBuilder _controlTreeItemBuilder; - public GlyphEngine Engine + private IRootInteractivesContext _rootInteractivesContext; + public IRootInteractivesContext RootInteractivesContext { - get => _engine; - private set => SetValue(ref _engine, value); + get => _rootInteractivesContext; + private set => SetValue(ref _rootInteractivesContext, value); } protected override object IconKey => CalameIconKey.InteractionTree; @@ -64,15 +64,15 @@ public InteractionTreeViewModel(IShell shell, IEventAggregator eventAggregator, .IsTriggered(x => x.IsActive, nameof(IControl.IsActive)); } - protected override Task OnDocumentActivated(IDocumentContext activeDocument) + protected override Task OnDocumentActivated(IDocumentContext activeDocument) { - Engine = activeDocument.Context; + RootInteractivesContext = activeDocument.Context; return Task.CompletedTask; } protected override Task OnDocumentsCleaned() { - Engine = null; + RootInteractivesContext = null; return Task.CompletedTask; } @@ -89,6 +89,8 @@ public ITreeViewItemModel CreateTreeItemModel(object data, ICollectionSynchroniz } } + public bool DisableChildrenIfParentDisabled => true; + event EventHandler ITreeContext.BaseFilterChanged { add { } remove { } } bool ITreeContext.IsMatchingBaseFilter(object data) => true; } } \ No newline at end of file diff --git a/Modules/Calame.InteractionTree/Views/InteractionTreeView.xaml b/Modules/Calame.InteractionTree/Views/InteractionTreeView.xaml index 65a4a4a..801f755 100644 --- a/Modules/Calame.InteractionTree/Views/InteractionTreeView.xaml +++ b/Modules/Calame.InteractionTree/Views/InteractionTreeView.xaml @@ -4,18 +4,14 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:viewModels="clr-namespace:Calame.InteractionTree.ViewModels" - xmlns:converters="clr-namespace:Calame.Converters;assembly=Calame" xmlns:userControls="clr-namespace:Calame.UserControls;assembly=Calame" mc:Ignorable="d" d:DataContext="{d:DesignInstance viewModels:InteractionTreeViewModel, IsDesignTimeCreatable=True}" d:DesignHeight="300" d:DesignWidth="300"> - - - diff --git a/Modules/Calame.PropertyGrid/Calame.PropertyGrid.csproj b/Modules/Calame.PropertyGrid/Calame.PropertyGrid.csproj index 5974b69..706de23 100644 --- a/Modules/Calame.PropertyGrid/Calame.PropertyGrid.csproj +++ b/Modules/Calame.PropertyGrid/Calame.PropertyGrid.csproj @@ -4,6 +4,7 @@ True + diff --git a/Modules/Calame.PropertyGrid/Controls/InlineObjectControl.xaml.cs b/Modules/Calame.PropertyGrid/Controls/InlineObjectControl.xaml.cs index 4c19290..5914f2d 100644 --- a/Modules/Calame.PropertyGrid/Controls/InlineObjectControl.xaml.cs +++ b/Modules/Calame.PropertyGrid/Controls/InlineObjectControl.xaml.cs @@ -1,5 +1,6 @@ using System; using System.Windows; +using System.Windows.Input; using Calame.Icons; namespace Calame.PropertyGrid.Controls @@ -114,8 +115,16 @@ static private void OnIsReadOnlyValueChanged(DependencyObject d, DependencyPrope static private void OnSelectItemCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var control = (InlineObjectControl)d; + var oldCommand = (ICommand)e.OldValue; + var newCommand = (ICommand)e.NewValue; + if (oldCommand != null) + oldCommand.CanExecuteChanged -= control.OnSelectItemCommandChanged; + control.RefreshAccessIcon(); + + if (newCommand != null) + newCommand.CanExecuteChanged += control.OnSelectItemCommandChanged; } protected override Type GetNewItemType() => NewItemBaseType ?? GetItemType(); @@ -143,7 +152,7 @@ private void RefreshCanAddItem() bool value = !IsReadOnlyValue && Value == null; SetCanAddItem(value); } - + private void RefreshCanRemoveItem() { bool ComputeValue() @@ -163,6 +172,7 @@ bool ComputeValue() SetCanRemoveItem(ComputeValue()); } + private void OnSelectItemCommandChanged(object sender, EventArgs e) => RefreshAccessIcon(); private void RefreshAccessIcon() { if (SelectItemCommand != null && SelectItemCommand.CanExecute(Value)) diff --git a/Modules/Calame.PropertyGrid/Controls/PropertyGridPopupOwnerBase.cs b/Modules/Calame.PropertyGrid/Controls/PropertyGridPopupOwnerBase.cs index 4fcfd88..d5cd7db 100644 --- a/Modules/Calame.PropertyGrid/Controls/PropertyGridPopupOwnerBase.cs +++ b/Modules/Calame.PropertyGrid/Controls/PropertyGridPopupOwnerBase.cs @@ -360,9 +360,6 @@ protected void OnExpandObject(object control) popup.SelectItemCommand = selectItemCommand; popup.CanSelectItem = selectItemCommand.CanExecute(propertyGrid.SelectedObject); - popup.Removed += OnRemoved; - propertyGrid.PropertyValueChanged += OnPropertyValueChanged; - bool CanSelectItem(object item) => SelectItemCommand?.CanExecute(item) ?? false; void OnSelectItem(object item) { @@ -370,6 +367,9 @@ void OnSelectItem(object item) SelectItemCommand?.Execute(item); } + popup.Removed += OnRemoved; + propertyGrid.PropertyValueChanged += OnPropertyValueChanged; + void OnRemoved(object sender, EventArgs e) => OnItemRemoved(popup, popupOwner); void OnPropertyValueChanged(object sender, PropertyValueChangedEventArgs e) => OnPopupPropertyValueChanged(propertyGrid, popupOwner, e); diff --git a/Modules/Calame.PropertyGrid/ViewModels/PropertyGridViewModel.cs b/Modules/Calame.PropertyGrid/ViewModels/PropertyGridViewModel.cs index 956120c..9a7e3d8 100644 --- a/Modules/Calame.PropertyGrid/ViewModels/PropertyGridViewModel.cs +++ b/Modules/Calame.PropertyGrid/ViewModels/PropertyGridViewModel.cs @@ -7,14 +7,12 @@ using System.Threading.Tasks; using Calame.Commands; using Calame.ContentFileTypes; +using Calame.DocumentContexts; using Calame.Icons; -using Calame.PropertyGrid.Controls; using Caliburn.Micro; using Gemini.Framework; using Gemini.Framework.Commands; using Gemini.Framework.Services; -using Glyph.Composition; -using Glyph.Composition.Modelization; using Glyph.Engine; using Glyph.Pipeline; @@ -40,14 +38,21 @@ public object SelectedObject public string WorkingDirectory { get => _workingDirectory; - set => SetValue(ref _workingDirectory, value); + private set => SetValue(ref _workingDirectory, value); } private IRawContentLibrary _rawContentLibrary; public IRawContentLibrary RawContentLibrary { get => _rawContentLibrary; - set => SetValue(ref _rawContentLibrary, value); + private set => SetValue(ref _rawContentLibrary, value); + } + + private ISelectionCommandContext _selectionCommandContext; + public ISelectionCommandContext SelectionCommandContext + { + get => _selectionCommandContext; + private set => SetValue(ref _selectionCommandContext, value); } public IContentFileTypeResolver ContentFileTypeResolver { get; } @@ -58,7 +63,6 @@ public IRawContentLibrary RawContentLibrary public RelayCommand OpenFileCommand { get; } public RelayCommand OpenFolderCommand { get; } public AsyncCommand DirtyDocumentCommand { get; } - public RelayCommand SelectItemCommand { get; } protected override object IconKey => CalameIconKey.PropertyGrid; @@ -83,7 +87,6 @@ public PropertyGridViewModel(IShell shell, IEventAggregator eventAggregator, IIm OpenFileCommand = new RelayCommand(OnOpenFile, CanOpenFile); DirtyDocumentCommand = new AsyncCommand(OnDirtyDocument); - SelectItemCommand = new RelayCommand(OnSelectItem, CanSelectItem); } private bool CanOpenFolder(object path) => !string.IsNullOrWhiteSpace((string)path); @@ -103,39 +106,13 @@ private Task OnDirtyDocument() return EventAggregator.PublishAsync(new DirtyMessage(CurrentDocument, SelectedObject)); } - private bool CanSelectItem(object item) - { - switch (item) - { - case IGlyphData _: - case IGlyphComponent _: - return !item.GetType().IsValueType; - default: - return false; - } - } - - private void OnSelectItem(object item) - { - ISelectionRequest selectionRequest; - switch (item) - { - case IGlyphData data: - selectionRequest = new SelectionRequest(CurrentDocument, data); - break; - case IGlyphComponent component: - selectionRequest = new SelectionRequest(CurrentDocument, component); - break; - default: throw new NotSupportedException(); - } - EventAggregator.PublishAsync(selectionRequest).Wait(); - } - protected override Task OnDocumentActivated(IDocumentContext activeDocument) { SelectedObject = null; + WorkingDirectory = activeDocument.WorkingDirectory; RawContentLibrary = (activeDocument as IDocumentContext)?.Context.ContentLibrary as IRawContentLibrary; + SelectionCommandContext = (activeDocument as IDocumentContext)?.Context; return Task.CompletedTask; } @@ -143,8 +120,10 @@ protected override Task OnDocumentActivated(IDocumentContext activeDocument) protected override Task OnDocumentsCleaned() { SelectedObject = null; + WorkingDirectory = null; RawContentLibrary = null; + SelectionCommandContext = null; return Task.CompletedTask; } diff --git a/Modules/Calame.PropertyGrid/Views/PropertyGridView.xaml b/Modules/Calame.PropertyGrid/Views/PropertyGridView.xaml index e586c59..5fd9da6 100644 --- a/Modules/Calame.PropertyGrid/Views/PropertyGridView.xaml +++ b/Modules/Calame.PropertyGrid/Views/PropertyGridView.xaml @@ -25,7 +25,7 @@ NextCommand="{Binding NextCommand}" OpenFileCommand="{Binding OpenFileCommand}" OpenFolderCommand="{Binding OpenFolderCommand}" - SelectItemCommand="{Binding SelectItemCommand}"> + SelectItemCommand="{Binding SelectionCommandContext.SelectCommand}"> diff --git a/Modules/Calame.SceneGraph/Calame.SceneGraph.csproj b/Modules/Calame.SceneGraph/Calame.SceneGraph.csproj index cc1e790..e2ffda7 100644 --- a/Modules/Calame.SceneGraph/Calame.SceneGraph.csproj +++ b/Modules/Calame.SceneGraph/Calame.SceneGraph.csproj @@ -4,6 +4,7 @@ True + diff --git a/Modules/Calame.SceneGraph/ViewModels/SceneGraphViewModel.cs b/Modules/Calame.SceneGraph/ViewModels/SceneGraphViewModel.cs index aaa1fdc..487e062 100644 --- a/Modules/Calame.SceneGraph/ViewModels/SceneGraphViewModel.cs +++ b/Modules/Calame.SceneGraph/ViewModels/SceneGraphViewModel.cs @@ -3,6 +3,7 @@ using System.ComponentModel.Composition; using System.Threading; using System.Threading.Tasks; +using Calame.DocumentContexts; using Calame.Icons; using Calame.UserControls; using Calame.Utils; @@ -13,30 +14,29 @@ using Glyph.Composition; using Glyph.Composition.Modelization; using Glyph.Core; -using Glyph.Engine; namespace Calame.SceneGraph.ViewModels { [Export(typeof(SceneGraphViewModel))] - public sealed class SceneGraphViewModel : CalameTool>, IHandle>, IHandle>, ITreeContext + public sealed class SceneGraphViewModel : CalameTool>, ITreeContext, + IHandle>, + IHandle> { public override PaneLocation PreferredLocation => PaneLocation.Left; public IIconProvider IconProvider { get; } public IIconDescriptor IconDescriptor { get; } - private GlyphEngine _engine; - private IDocumentContext _filteringContext; - private IGlyphComponent _selection; - private SceneNode _selectionNode; - private readonly TreeViewItemModelBuilder _treeItemBuilder; - - public GlyphEngine Engine + private IRootScenesContext _rootScenesContext; + public IRootScenesContext RootScenesContext { - get => _engine; - private set => SetValue(ref _engine, value); + get => _rootScenesContext; + set => Set(ref _rootScenesContext, value); } + private ISelectionCommandContext _selectionCommandContext; + + private IGlyphComponent _selection; public IGlyphComponent Selection { get => _selection; @@ -48,11 +48,11 @@ public IGlyphComponent Selection _selectionNode = _selection?.GetSceneNode(); NotifyOfPropertyChange(nameof(SelectionNode)); - var selectionRequest = new SelectionRequest(CurrentDocument, _selection); - EventAggregator.PublishAsync(selectionRequest).Wait(); + _selectionCommandContext?.SelectAsync(_selection).Wait(); } } + private SceneNode _selectionNode; public SceneNode SelectionNode { get => _selectionNode; @@ -63,12 +63,13 @@ public SceneNode SelectionNode _selection = _selectionNode?.Parent; NotifyOfPropertyChange(nameof(Selection)); - - var selectionRequest = new SelectionRequest(CurrentDocument, _selection); - EventAggregator.PublishAsync(selectionRequest).Wait(); + + _selectionCommandContext?.SelectAsync(_selection).Wait(); } } + private readonly TreeViewItemModelBuilder _treeItemBuilder; + protected override object IconKey => CalameIconKey.SceneGraph; [ImportingConstructor] @@ -86,30 +87,33 @@ public SceneGraphViewModel(IShell shell, IEventAggregator eventAggregator, IIcon .DisplayName(x => (x as IGlyphComponent)?.Parent?.Name ?? x.ToString(), nameof(IGlyphComponent.Name), x => (x as IGlyphComponent)?.Parent as INotifyPropertyChanged) .ChildrenSource(x => x.Children, nameof(ISceneNode.Children)) .IconDescription(x => iconDescriptor.GetIcon((x as IGlyphComponent)?.Parent ?? x as IGlyphComponent)) - .IsEnabled(x => _filteringContext?.Context.Filter(x as IGlyphComponent) ?? true); - - if (shell.ActiveItem is IDocumentContext documentContext) - Engine = documentContext.Context; + .IsEnabled(x => _selectionCommandContext?.SelectCommand, x => (x as IGlyphComponent)?.Parent); } - protected override Task OnDocumentActivated(IDocumentContext activeDocument) + protected override Task OnDocumentActivated(IDocumentContext activeDocument) { _selection = null; _selectionNode = null; - Engine = activeDocument.Context; - _filteringContext = activeDocument as IDocumentContext; + _selectionCommandContext = (activeDocument as IDocumentContext)?.Context; + RootScenesContext = activeDocument.Context; + + if (_selectionCommandContext != null) + _selectionCommandContext.CanSelectChanged += OnCanSelectChanged; return Task.CompletedTask; } protected override Task OnDocumentsCleaned() { + if (_selectionCommandContext != null) + _selectionCommandContext.CanSelectChanged -= OnCanSelectChanged; + _selection = null; _selectionNode = null; - Engine = null; - _filteringContext = null; + RootScenesContext = null; + _selectionCommandContext = null; return Task.CompletedTask; } @@ -132,11 +136,28 @@ private void HandleSelection(IGlyphComponent component) SetValue(ref _selectionNode, component?.GetSceneNode(), nameof(SelectionNode)); } + public bool DisableChildrenIfParentDisabled => false; + + bool ITreeContext.IsMatchingBaseFilter(object data) + { + return _selectionCommandContext == null || _selectionCommandContext.CanSelect((data as IGlyphComponent)?.Parent); + } + + private event EventHandler BaseFilterChanged; + event EventHandler ITreeContext.BaseFilterChanged + { + add => BaseFilterChanged += value; + remove => BaseFilterChanged -= value; + } + + private void OnCanSelectChanged(object sender, EventArgs e) + { + BaseFilterChanged?.Invoke(this, EventArgs.Empty); + } + ITreeViewItemModel ITreeContext.CreateTreeItemModel(object data, ICollectionSynchronizerConfiguration synchronizerConfiguration) { return _treeItemBuilder.Build((ISceneNode)data, synchronizerConfiguration); } - - bool ITreeContext.IsMatchingBaseFilter(object data) => true; } } \ No newline at end of file diff --git a/Modules/Calame.SceneGraph/Views/SceneGraphView.xaml b/Modules/Calame.SceneGraph/Views/SceneGraphView.xaml index b8518a4..7fc7af2 100644 --- a/Modules/Calame.SceneGraph/Views/SceneGraphView.xaml +++ b/Modules/Calame.SceneGraph/Views/SceneGraphView.xaml @@ -10,7 +10,7 @@ d:DesignHeight="300" d:DesignWidth="300"> diff --git a/Modules/Calame.SceneViewer/SessionCommandDefinitions.cs b/Modules/Calame.SceneViewer/SessionCommandDefinitions.cs index 3580a05..6ad41a5 100644 --- a/Modules/Calame.SceneViewer/SessionCommandDefinitions.cs +++ b/Modules/Calame.SceneViewer/SessionCommandDefinitions.cs @@ -15,28 +15,18 @@ static public class SessionToolBar static public ToolBarItemDefinition SessionMode = new CommandToolBarItemDefinition(ViewerToolBar.ModesGroup, 20); [Export] - static public ToolBarDefinition Definition = new ToolBarDefinition(200, "Session"); - - [Export] - static public ToolBarItemGroupDefinition EngineGroup = new ToolBarItemGroupDefinition(Definition, 100); + static public ToolBarItemGroupDefinition EngineGroup = new ToolBarItemGroupDefinition(ViewerToolBar.Definition, 150); [Export] static public ToolBarItemDefinition EnginePause = new CommandToolBarItemDefinition(EngineGroup, 0); [Export] static public ToolBarItemDefinition EngineNextFrame = new CommandToolBarItemDefinition(EngineGroup, 10); [Export] - static public ToolBarItemGroupDefinition CameraModeGroup = new ToolBarItemGroupDefinition(Definition, 110); + static public ToolBarItemGroupDefinition CameraModeGroup = new ToolBarItemGroupDefinition(ViewerToolBar.Definition, 160); [Export] static public ToolBarItemDefinition FreeCamera = new CommandToolBarItemDefinition(CameraModeGroup, 0); [Export] static public ToolBarItemDefinition DefaultCamera = new CommandToolBarItemDefinition(CameraModeGroup, 10); - - [Export] - static public ToolBarItemGroupDefinition CameraActionGroup = new ToolBarItemGroupDefinition(Definition, 120); - [Export] - static public ToolBarItemDefinition ResetCamera = new CommandToolBarItemDefinition(CameraActionGroup, 0); - [Export] - static public ToolBarItemDefinition FocusCamera = new CommandToolBarItemDefinition(CameraActionGroup, 10); } static public class SessionMenu @@ -45,25 +35,18 @@ static public class SessionMenu static public MenuItemDefinition SessionMode = new CommandMenuItemDefinition(ViewerMenu.ModesGroup, 20); [Export] - static public MenuItemGroupDefinition EngineGroup = new MenuItemGroupDefinition(ViewerMenu.Definition, 100); + static public MenuItemGroupDefinition EngineGroup = new MenuItemGroupDefinition(ViewerMenu.Definition, 150); [Export] static public MenuItemDefinition EnginePause = new CommandMenuItemDefinition(EngineGroup, 0); [Export] static public MenuItemDefinition EngineNextFrame = new CommandMenuItemDefinition(EngineGroup, 10); [Export] - static public MenuItemGroupDefinition CameraModeGroup = new MenuItemGroupDefinition(ViewerMenu.Definition, 110); + static public MenuItemGroupDefinition CameraModeGroup = new MenuItemGroupDefinition(ViewerMenu.Definition, 160); [Export] static public MenuItemDefinition FreeCamera = new CommandMenuItemDefinition(CameraModeGroup, 0); [Export] static public MenuItemDefinition DefaultCamera = new CommandMenuItemDefinition(CameraModeGroup, 10); - - [Export] - static public MenuItemGroupDefinition CameraActionGroup = new MenuItemGroupDefinition(ViewerMenu.Definition, 110); - [Export] - static public MenuItemDefinition ResetCamera = new CommandMenuItemDefinition(CameraActionGroup, 0); - [Export] - static public MenuItemDefinition FocusCamera = new CommandMenuItemDefinition(CameraActionGroup, 10); } static public class SessionShortcuts @@ -80,10 +63,5 @@ static public class SessionShortcuts static public CommandKeyboardShortcut FreeCamera = new CommandKeyboardShortcut(new KeyGesture(Key.F, ModifierKeys.Alt)); [Export] static public CommandKeyboardShortcut DefaultCamera = new CommandKeyboardShortcut(new KeyGesture(Key.D, ModifierKeys.Alt)); - - [Export] - static public CommandKeyboardShortcut ResetCamera = new CommandKeyboardShortcut(new KeyGesture(Key.F, ModifierKeys.Control | ModifierKeys.Alt)); - [Export] - static public CommandKeyboardShortcut FocusCamera = new CommandKeyboardShortcut(new KeyGesture(Key.F, ModifierKeys.Control)); } } \ No newline at end of file diff --git a/Modules/Calame.SceneViewer/ViewModels/SceneViewerViewModel.cs b/Modules/Calame.SceneViewer/ViewModels/SceneViewerViewModel.cs index 6fd34d6..b7f6f78 100644 --- a/Modules/Calame.SceneViewer/ViewModels/SceneViewerViewModel.cs +++ b/Modules/Calame.SceneViewer/ViewModels/SceneViewerViewModel.cs @@ -5,6 +5,7 @@ using System.Threading; using System.Threading.Tasks; using System.Windows.Input; +using Calame.DocumentContexts; using Calame.Icons; using Calame.SceneViewer.Commands; using Calame.Viewer; @@ -55,11 +56,6 @@ public ISession Session public ViewerViewModel Viewer { get; } public SessionModeModule SessionMode { get; } - IDocument IDocumentContext.Document => this; - GlyphEngine IDocumentContext.Context => Viewer.Runner?.Engine; - ViewerViewModel IDocumentContext.Context => Viewer; - IComponentFilter IDocumentContext.Context => Viewer.ComponentsFilter; - public bool FreeCameraEnabled { get; private set; } public Type RunCommandDefinitionType { get; } = typeof(ResetSessionCommand); @@ -82,6 +78,8 @@ public SceneViewerViewModel(IShell shell, IEventAggregator eventAggregator, Viewer = new ViewerViewModel(this, eventAggregator, viewerModuleSources); Viewer.RunnerChanged += ViewerViewModelOnRunnerChanged; + _debuggableViewerContexts = new DebuggableViewerContexts(Viewer, this); + SessionMode = new SessionModeModule(); Viewer.InsertInteractiveMode(0, SessionMode); @@ -112,6 +110,8 @@ public async Task InitializeSession() await Session.PrepareSessionAsync(_sessionContext); await Session.ResetSessionAsync(_sessionContext); + _debuggableViewerContexts.RefreshContexts(); + _engine.Initialize(); await _engine.LoadContentAsync(); @@ -130,7 +130,8 @@ public async Task InitializeSession() public async Task ResetSession() { await EventAggregator.PublishAsync(SelectionRequest.Empty(this)); - Viewer.ComponentsFilter.ExcludedRoots.Clear(); + + Viewer.NotSelectableComponents.Clear(); _selectionHistoryManager.GetHistory(this).Clear(); await Session.ResetSessionAsync(_sessionContext); @@ -243,11 +244,55 @@ public class SessionModeModule : ViewerModuleBase, IViewerInteractiveMode public Cursor Cursor => Cursors.None; public bool UseFreeCamera => false; + bool IViewerInteractiveMode.IsUserMode => true; protected override void ConnectModel() => Model.AddInteractiveMode(this); protected override void DisconnectModel() => Model.RemoveInteractiveMode(this); protected override void ConnectRunner() {} protected override void DisconnectRunner() {} } + + private readonly DebuggableViewerContexts _debuggableViewerContexts; + + bool IViewerDocument.DebugMode + { + get => _debuggableViewerContexts.DebugMode; + set => _debuggableViewerContexts.DebugMode = value; + } + + IDocument IDocumentContext.Document => this; + GlyphEngine IDocumentContext.Context => _debuggableViewerContexts.Engine; + ViewerViewModel IDocumentContext.Context => _debuggableViewerContexts.Viewer; + IRootsContext IDocumentContext.Context => _debuggableViewerContexts; + IRootComponentsContext IDocumentContext.Context => _debuggableViewerContexts; + IRootScenesContext IDocumentContext.Context => _debuggableViewerContexts; + IRootInteractivesContext IDocumentContext.Context => _debuggableViewerContexts; + ISelectionCommandContext IDocumentContext.Context => this; + ISelectionCommandContext IDocumentContext>.Context => this; + + public ICommand SelectCommand => _debuggableViewerContexts.SelectCommand; + + event EventHandler ISelectionCommandContext.CanSelectChanged + { + add => _debuggableViewerContexts.CanSelectChanged += value; + remove => _debuggableViewerContexts.CanSelectChanged -= value; + } + + public bool CanSelect(object instance) => instance is IGlyphComponent component && CanSelect(component); + public bool CanSelect(IGlyphComponent component) => _debuggableViewerContexts.CanSelect(component); + + public Task SelectAsync(object instance) + { + if (instance is IGlyphComponent component) + return SelectAsync(component); + return Task.CompletedTask; + } + + public Task SelectAsync(IGlyphComponent component) + { + if (CanSelect(component)) + return EventAggregator.PublishAsync(new SelectionRequest(this, component)); + return Task.CompletedTask; + } } } \ No newline at end of file