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