Skip to content

Commit 7b9e0ea

Browse files
authored
Merge pull request #1447 from b-editor/feat/optimize-operation-observer
Optimize property tracking with CorePropertyOperationObserver
1 parent 634e0d2 commit 7b9e0ea

File tree

3 files changed

+157
-126
lines changed

3 files changed

+157
-126
lines changed

src/Beutl.Editor/Observers/CoreObjectOperationObserver.cs

Lines changed: 32 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ public sealed class CoreObjectOperationObserver : IOperationObserver
1414
{
1515
private readonly ICoreObject _object;
1616
private readonly string _propertyPath;
17-
private readonly Dictionary<int, CoreObjectOperationObserver> _childPublishers = new();
18-
private readonly Dictionary<int, IOperationObserver> _collectionPublishers = new();
17+
private readonly Dictionary<int, IOperationObserver> _corePropertyPublishers = new();
1918
private readonly Dictionary<string, IOperationObserver> _enginePropertyPublishers = new();
2019
private SplineEasingOperationObserver? _splineEasingPublisher;
2120
private NodeItemOperationObserver? _nodeItemPublisher;
@@ -47,7 +46,12 @@ public CoreObjectOperationObserver(
4746
.Where(i => !string.IsNullOrEmpty(i))
4847
.ToHashSet();
4948
InitializeChildPublishers();
50-
obj.PropertyChanged += OnPropertyChanged;
49+
50+
// Easingの変更を監視
51+
if (obj is KeyFrame)
52+
{
53+
obj.PropertyChanged += OnPropertyChanged;
54+
}
5155

5256
if (obj is EngineObject engineObject)
5357
{
@@ -61,12 +65,7 @@ public void Dispose()
6165
{
6266
_object.PropertyChanged -= OnPropertyChanged;
6367

64-
foreach (CoreObjectOperationObserver child in _childPublishers.Values)
65-
{
66-
child.Dispose();
67-
}
68-
69-
foreach (IOperationObserver collection in _collectionPublishers.Values)
68+
foreach (IOperationObserver collection in _corePropertyPublishers.Values)
7069
{
7170
collection.Dispose();
7271
}
@@ -92,7 +91,8 @@ private string BuildPropertyPath(string propertyName)
9291

9392
private void InitializeChildPublishers()
9493
{
95-
foreach (CoreProperty property in PropertyRegistry.GetRegistered(_object.GetType()))
94+
var objectType = _object.GetType();
95+
foreach (CoreProperty property in PropertyRegistry.GetRegistered(objectType))
9696
{
9797
if (Hierarchical.HierarchicalParentProperty.Id == property.Id) continue;
9898
if (_propertiesToTrack != null && !_propertiesToTrack.Contains(property.Name))
@@ -101,45 +101,37 @@ private void InitializeChildPublishers()
101101
}
102102

103103
// Check if property is excluded from tracking
104-
if (property.TryGetMetadata<CorePropertyMetadata>(_object.GetType(), out var metadata)
104+
if (property.TryGetMetadata<CorePropertyMetadata>(objectType, out var metadata)
105105
&& !metadata.Tracked)
106106
{
107107
continue;
108108
}
109109

110-
object? value = _object.GetValue(property);
111110
string childPath = BuildPropertyPath(property.Name);
112111

113-
if (value is IList list)
114-
{
115-
var elementType = ArrayTypeHelpers.GetElementType(list.GetType());
116-
if (elementType == null) throw new InvalidOperationException("Could not determine the element type of the list.");
117-
var observerType = typeof(CollectionOperationObserver<>).MakeGenericType(elementType);
112+
var observerType = typeof(CorePropertyOperationObserver<>).MakeGenericType(property.PropertyType);
113+
var propertyPublisher = (IOperationObserver)Activator.CreateInstance(
114+
observerType,
115+
_operations,
116+
_object,
117+
property,
118+
_sequenceNumberGenerator,
119+
childPath,
120+
_propertyPathsToTrack)!;
118121

119-
var collectionPublisher = Activator.CreateInstance(observerType,
120-
_operations, list, _object,
121-
childPath, _sequenceNumberGenerator, _propertyPathsToTrack)!;
122-
_collectionPublishers.Add(property.Id, (IOperationObserver)collectionPublisher);
123-
}
124-
else if (value is ICoreObject child)
125-
{
126-
var childPublisher = new CoreObjectOperationObserver(
127-
_operations,
128-
child,
129-
_sequenceNumberGenerator,
130-
childPath,
131-
_propertyPathsToTrack);
132-
_childPublishers.Add(property.Id, childPublisher);
133-
}
122+
_corePropertyPublishers.Add(property.Id, propertyPublisher);
134123
}
135124

136-
InitializeSplineEasingPublisher();
125+
RecreateSplineEasingPublisher();
137126
InitializeNodeItemPublisher();
138127
}
139128

140-
private void InitializeSplineEasingPublisher()
129+
private void RecreateSplineEasingPublisher()
141130
{
142-
if (_object is KeyFrame keyFrame && keyFrame.Easing is SplineEasing splineEasing)
131+
_splineEasingPublisher?.Dispose();
132+
_splineEasingPublisher = null;
133+
134+
if (_object is KeyFrame { Easing: SplineEasing splineEasing } keyFrame)
143135
{
144136
string easingPath = BuildPropertyPath(nameof(KeyFrame.Easing));
145137
_splineEasingPublisher = new SplineEasingOperationObserver(
@@ -191,89 +183,18 @@ private void InitializeEnginePropertyPublishers(EngineObject engineObject)
191183

192184
private void OnPropertyChanged(object? sender, PropertyChangedEventArgs e)
193185
{
194-
// Suppress publishing during remote operation application to prevent echo-back
195-
if (PublishingSuppression.IsSuppressed)
196-
{
186+
if (PublishingSuppression.IsSuppressed) return;
187+
188+
if (e is not CorePropertyChangedEventArgs<Easing> args || sender is not CoreObject source)
197189
return;
198-
}
199190

200-
if (e is not CorePropertyChangedEventArgs args ||
201-
sender is not ICoreObject source ||
202-
args.Property.Id == Hierarchical.HierarchicalParentProperty.Id)
203-
{
191+
if (args.Property.Id != KeyFrame.EasingProperty.Id)
204192
return;
205-
}
206193

207194
if (_propertiesToTrack != null &&
208195
!_propertiesToTrack.Contains(args.Property.Name))
209-
{
210196
return;
211-
}
212-
213-
// Check if property is excluded from tracking
214-
if (args.Property.TryGetMetadata<CorePropertyMetadata>(_object.GetType(), out var metadata)
215-
&& !metadata.Tracked)
216-
{
217-
return;
218-
}
219-
220-
if (_childPublishers.Remove(args.Property.Id, out var childPublisher))
221-
{
222-
childPublisher.Dispose();
223-
}
224-
225-
if (_collectionPublishers.Remove(args.Property.Id, out var collectionPublisher))
226-
{
227-
collectionPublisher.Dispose();
228-
}
229-
230-
string childPath = BuildPropertyPath(args.Property.Name);
231-
232-
if (args.NewValue is IList list)
233-
{
234-
var elementType = ArrayTypeHelpers.GetElementType(list.GetType());
235-
if (elementType == null) throw new InvalidOperationException("Could not determine the element type of the list.");
236-
var observerType = typeof(CollectionOperationObserver<>).MakeGenericType(elementType);
237-
238-
var newPublisher = Activator.CreateInstance(observerType,
239-
_operations, list, source,
240-
childPath, _sequenceNumberGenerator, _propertyPathsToTrack)!;
241-
_collectionPublishers.Add(args.Property.Id, (IOperationObserver)newPublisher);
242-
}
243-
else if (args.NewValue is ICoreObject newChild)
244-
{
245-
var newPublisher = new CoreObjectOperationObserver(
246-
_operations,
247-
newChild,
248-
_sequenceNumberGenerator,
249-
childPath,
250-
_propertyPathsToTrack);
251-
_childPublishers.Add(args.Property.Id, newPublisher);
252-
}
253-
254-
// Handle SplineEasing changes for KeyFrame
255-
if (_object is KeyFrame keyFrame && args.Property.Id == KeyFrame.EasingProperty.Id)
256-
{
257-
_splineEasingPublisher?.Dispose();
258-
_splineEasingPublisher = null;
259-
260-
if (args.NewValue is SplineEasing newSplineEasing)
261-
{
262-
_splineEasingPublisher = new SplineEasingOperationObserver(
263-
_operations,
264-
newSplineEasing,
265-
_sequenceNumberGenerator,
266-
keyFrame,
267-
childPath,
268-
_propertyPathsToTrack);
269-
}
270-
}
271197

272-
var operationType = typeof(UpdatePropertyValueOperation<>).MakeGenericType(args.Property.PropertyType);
273-
var operation = (ChangeOperation)Activator.CreateInstance(
274-
operationType,
275-
source, childPath, args.NewValue, args.OldValue)!;
276-
operation.SequenceNumber = _sequenceNumberGenerator.GetNext();
277-
_operations.OnNext(operation);
198+
RecreateSplineEasingPublisher();
278199
}
279200
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
using System.Collections;
2+
using System.ComponentModel;
3+
using System.Reactive.Subjects;
4+
using Beutl.Editor.Operations;
5+
using Beutl.Serialization;
6+
7+
namespace Beutl.Editor.Observers;
8+
9+
public sealed class CorePropertyOperationObserver<T> : IOperationObserver
10+
{
11+
private CoreObjectOperationObserver? _childPublisher;
12+
private IOperationObserver? _collectionPublisher;
13+
private readonly CoreProperty<T> _property;
14+
private readonly ICoreObject _object;
15+
private readonly Subject<ChangeOperation> _operations = new();
16+
private readonly HashSet<string>? _propertyPathsToTrack;
17+
private readonly string _propertyPath;
18+
private readonly OperationSequenceGenerator _sequenceNumberGenerator;
19+
private readonly IDisposable? _subscription;
20+
21+
public CorePropertyOperationObserver(
22+
IObserver<ChangeOperation>? observer,
23+
ICoreObject obj,
24+
CoreProperty<T> property,
25+
OperationSequenceGenerator sequenceNumberGenerator,
26+
string propertyPath,
27+
HashSet<string>? propertyPathsToTrack = null)
28+
{
29+
_object = obj;
30+
_property = property;
31+
_propertyPath = propertyPath;
32+
_sequenceNumberGenerator = sequenceNumberGenerator;
33+
_propertyPathsToTrack = propertyPathsToTrack;
34+
35+
if (observer != null)
36+
{
37+
_subscription = _operations.Subscribe(observer);
38+
}
39+
40+
RecreateChildPublisher();
41+
obj.PropertyChanged += OnPropertyChanged;
42+
}
43+
44+
public IObservable<ChangeOperation> Operations => _operations;
45+
46+
public void Dispose()
47+
{
48+
_object.PropertyChanged -= OnPropertyChanged;
49+
50+
_childPublisher?.Dispose();
51+
_childPublisher = null;
52+
53+
_collectionPublisher?.Dispose();
54+
_collectionPublisher = null;
55+
56+
_subscription?.Dispose();
57+
_operations.OnCompleted();
58+
}
59+
60+
private void RecreateChildPublisher()
61+
{
62+
_childPublisher?.Dispose();
63+
_childPublisher = null;
64+
65+
_collectionPublisher?.Dispose();
66+
_collectionPublisher = null;
67+
68+
T value = _object.GetValue(_property);
69+
70+
switch (value)
71+
{
72+
case IList list:
73+
var elementType = ArrayTypeHelpers.GetElementType(list.GetType());
74+
if (elementType == null) throw new InvalidOperationException("Could not determine the element type of the list.");
75+
var observerType = typeof(CollectionOperationObserver<>).MakeGenericType(elementType);
76+
77+
_collectionPublisher = (IOperationObserver)Activator.CreateInstance(observerType,
78+
_operations, list, _object,
79+
_propertyPath, _sequenceNumberGenerator, _propertyPathsToTrack)!;
80+
break;
81+
case ICoreObject child:
82+
_childPublisher = new CoreObjectOperationObserver(
83+
_operations,
84+
child,
85+
_sequenceNumberGenerator,
86+
_propertyPath,
87+
_propertyPathsToTrack);
88+
break;
89+
}
90+
}
91+
92+
public void OnPropertyChanged(object? sender, PropertyChangedEventArgs e)
93+
{
94+
if (PublishingSuppression.IsSuppressed) return;
95+
if (e is not CorePropertyChangedEventArgs<T> args) return;
96+
if (sender is not ICoreObject source) return;
97+
98+
// プロパティが同じか
99+
if (args.Property.Id != _property.Id) return;
100+
101+
RecreateChildPublisher();
102+
103+
var operation = new UpdatePropertyValueOperation<T?>(
104+
(CoreObject)source, _propertyPath, args.NewValue, args.OldValue)
105+
{
106+
SequenceNumber = _sequenceNumberGenerator.GetNext()
107+
};
108+
_operations.OnNext(operation);
109+
}
110+
}

src/Beutl.Editor/Observers/EnginePropertyOperationObserver.cs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -129,11 +129,11 @@ private void OnExpressionChanged(IExpression<T>? obj)
129129
return;
130130
}
131131

132-
var operationType = typeof(UpdatePropertyValueOperation<>).MakeGenericType(typeof(IExpression<T>));
133-
var operation = (ChangeOperation)Activator.CreateInstance(
134-
operationType,
135-
_object, $"{_propertyPath}.Expression", obj, _expression)!;
136-
operation.SequenceNumber = _sequenceNumberGenerator.GetNext();
132+
var operation = new UpdatePropertyValueOperation<IExpression<T>?>(
133+
(CoreObject)_object, $"{_propertyPath}.Expression", obj, _expression)
134+
{
135+
SequenceNumber = _sequenceNumberGenerator.GetNext()
136+
};
137137
_operations.OnNext(operation);
138138
_expression = obj;
139139
}
@@ -161,11 +161,11 @@ private void OnAnimationChanged(IAnimation<T>? newAnimation)
161161
_propertyPathsToTrack);
162162
}
163163

164-
var operationType = typeof(UpdatePropertyValueOperation<>).MakeGenericType(typeof(IAnimation<T>));
165-
var operation = (ChangeOperation)Activator.CreateInstance(
166-
operationType,
167-
_object, animationPath, newAnimation, _animation)!;
168-
operation.SequenceNumber = _sequenceNumberGenerator.GetNext();
164+
var operation = new UpdatePropertyValueOperation<IAnimation<T>?>(
165+
(CoreObject)_object, animationPath, newAnimation, _animation)
166+
{
167+
SequenceNumber = _sequenceNumberGenerator.GetNext()
168+
};
169169
_operations.OnNext(operation);
170170
_animation = newAnimation;
171171
}
@@ -183,11 +183,11 @@ private void OnValueChanged(object? sender, PropertyValueChangedEventArgs<T> e)
183183

184184
InitializePublishers(e.NewValue);
185185

186-
var operationType = typeof(UpdatePropertyValueOperation<>).MakeGenericType(typeof(T));
187-
var operation = (ChangeOperation)Activator.CreateInstance(
188-
operationType,
189-
_object, _propertyPath, e.NewValue, e.OldValue)!;
190-
operation.SequenceNumber = _sequenceNumberGenerator.GetNext();
186+
var operation = new UpdatePropertyValueOperation<T>(
187+
(CoreObject)_object, _propertyPath, e.NewValue, e.OldValue)
188+
{
189+
SequenceNumber = _sequenceNumberGenerator.GetNext()
190+
};
191191
_operations.OnNext(operation);
192192
}
193193
}

0 commit comments

Comments
 (0)