Skip to content

Commit 073c3ee

Browse files
committed
refactor: renaming and utilities
1 parent de64b33 commit 073c3ee

18 files changed

+236
-216
lines changed

src/Everywhere.Windows/Services/Win32NativeHelper.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ public void SetWindowHitTestInvisible(Window window)
149149
PInvoke.SetLayeredWindowAttributes(hWnd, new COLORREF(), 255, LAYERED_WINDOW_ATTRIBUTES_FLAGS.LWA_ALPHA);
150150
}
151151

152-
private readonly Dictionary<nint, CompositionContext> compositionContexts = [];
152+
private readonly Dictionary<nint, CompositionContext> _compositionContexts = [];
153153

154154
// Modify from WPF source code
155155
// https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Shell/WindowChromeWorker.cs,de42dddb12b0ad8f
@@ -163,7 +163,7 @@ public void SetWindowCornerRadius(Window window, CornerRadius cornerRadius)
163163

164164
Compositor compositor;
165165
Visual rootVisual;
166-
if (!compositionContexts.TryGetValue(hWnd, out var compositionContext))
166+
if (!_compositionContexts.TryGetValue(hWnd, out var compositionContext))
167167
{
168168
// we will need lots of hacks, let's go
169169
if (window.PlatformImpl?.GetType().GetField("_glSurface", BindingFlags.Instance | BindingFlags.NonPublic) is not { } glSurfaceField) return;
@@ -179,7 +179,7 @@ public void SetWindowCornerRadius(Window window, CornerRadius cornerRadius)
179179

180180
compositor = Compositor.FromAbi(avaloniaCompositor.NativePointer);
181181
var target = DesktopWindowTarget.FromAbi(avaloniaTarget.NativePointer);
182-
compositionContexts[hWnd] = compositionContext = new CompositionContext(compositor, rootVisual = target.Root);
182+
_compositionContexts[hWnd] = compositionContext = new CompositionContext(compositor, rootVisual = target.Root);
183183

184184
window.ScalingChanged += delegate
185185
{
@@ -194,7 +194,7 @@ public void SetWindowCornerRadius(Window window, CornerRadius cornerRadius)
194194
window.Closed += delegate
195195
{
196196
compositionContext.Clip?.Dispose();
197-
compositionContexts.Remove(hWnd);
197+
_compositionContexts.Remove(hWnd);
198198
};
199199
}
200200
else
@@ -272,11 +272,11 @@ public unsafe void HideWindowWithoutAnimation(Window window)
272272
}
273273
}
274274

275-
private readonly Lock clipboardLock = new();
275+
private readonly Lock _clipboardLock = new();
276276

277277
public unsafe Task<WriteableBitmap?> GetClipboardBitmapAsync() => Task.Run(() =>
278278
{
279-
using var _ = clipboardLock.EnterScope();
279+
using var _ = _clipboardLock.EnterScope();
280280

281281
if (!PInvoke.OpenClipboard(HWND.Null)) return null;
282282

src/Everywhere/App.axaml.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
using Avalonia.Media.Imaging;
77
using Everywhere.Enums;
88
using Everywhere.Models;
9-
using Everywhere.Utils;
9+
using Everywhere.Utilities;
1010
using Everywhere.Views;
1111
using Everywhere.Views.Pages;
1212
using Microsoft.Extensions.DependencyInjection;

src/Everywhere/Entrance.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#if !DEBUG
2-
using Everywhere.Utils;
2+
using Everywhere.Utilities;
33
#endif
44

55
using Serilog;

src/Everywhere/I18N/DynamicResourceKey.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using System.Diagnostics.CodeAnalysis;
22
using Avalonia.Controls;
33
using Avalonia.Reactive;
4-
using Everywhere.Utils;
4+
using Everywhere.Utilities;
55
using MessagePack;
66

77
namespace Everywhere.I18N;

src/Everywhere/Models/ObjectObserver.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
using System.ComponentModel;
44
using System.Reflection;
55
using System.Text.Json.Serialization;
6-
using Everywhere.Utils;
6+
using Everywhere.Utilities;
77
using ZLinq;
88

99
namespace Everywhere.Models;

src/Everywhere/Models/Settings.cs

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
using CommunityToolkit.Mvvm.ComponentModel;
88
using Everywhere.Attributes;
99
using Everywhere.Enums;
10-
using Everywhere.Utils;
10+
using Everywhere.Utilities;
1111
using Microsoft.Extensions.Configuration;
1212
using Microsoft.Extensions.DependencyInjection;
1313
using WritableJsonConfiguration;
@@ -29,11 +29,24 @@ public class Settings : ObservableObject
2929
[HiddenSettingsItem]
3030
public InternalSettings Internal { get; } = new();
3131

32-
private readonly DebounceHelper saveDebounceHelper = new(TimeSpan.FromSeconds(0.5));
33-
private readonly Dictionary<string, object?> saveBuffer = new();
32+
private readonly Dictionary<string, object?> _saveBuffer = new();
33+
private readonly DebounceExecutor<Dictionary<string, object?>> _saveDebounceExecutor;
3434

3535
public Settings(IConfiguration configuration)
3636
{
37+
_saveDebounceExecutor = new DebounceExecutor<Dictionary<string, object?>>(
38+
() => _saveBuffer,
39+
saveBuffer =>
40+
{
41+
lock (saveBuffer)
42+
{
43+
if (saveBuffer.Count == 0) return;
44+
foreach (var (key, value) in saveBuffer) configuration.Set(key, value);
45+
saveBuffer.Clear();
46+
}
47+
},
48+
TimeSpan.FromSeconds(0.5));
49+
3750
new ObjectObserver(HandleSettingsChanges)
3851
.Observe(Common, nameof(Common))
3952
.Observe(Behavior, nameof(Behavior))
@@ -42,17 +55,8 @@ public Settings(IConfiguration configuration)
4255

4356
void HandleSettingsChanges(in ObjectObserverChangedEventArgs e)
4457
{
45-
lock (saveBuffer) saveBuffer[e.Path] = e.Value;
46-
47-
saveDebounceHelper.Execute(() =>
48-
{
49-
lock (saveBuffer)
50-
{
51-
if (saveBuffer.Count == 0) return;
52-
foreach (var (key, value) in saveBuffer) configuration.Set(key, value);
53-
saveBuffer.Clear();
54-
}
55-
});
58+
lock (_saveBuffer) _saveBuffer[e.Path] = e.Value;
59+
_saveDebounceExecutor.Trigger();
5660
}
5761
}
5862
}

src/Everywhere/Utils/AnonymousDisposable.cs renamed to src/Everywhere/Utilities/AnonymousDisposable.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using System.ComponentModel;
22

3-
namespace Everywhere.Utils;
3+
namespace Everywhere.Utilities;
44

55
public class AnonymousDisposable(Action disposeAction) : IDisposable
66
{
@@ -10,7 +10,7 @@ public void Dispose()
1010
disposeAction();
1111
}
1212

13-
public static AnonymousDisposable FromNotifyPropertyCHanged(INotifyPropertyChanged source, PropertyChangedEventHandler handler)
13+
public static AnonymousDisposable FromNotifyPropertyChanged(INotifyPropertyChanged source, PropertyChangedEventHandler handler)
1414
{
1515
source.PropertyChanged += handler;
1616
return new AnonymousDisposable(() => source.PropertyChanged -= handler);
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
namespace Everywhere.Utilities;
2+
3+
/// <summary>
4+
/// A high-performance, low-allocation debounced executor.
5+
/// It debounces calls to a parameterless method and, when the delay has passed,
6+
/// it invokes a value provider (Func{T}) and passes the result to an action (Action{T}).
7+
/// </summary>
8+
/// <typeparam name="T">The type of the value to be processed.</typeparam>
9+
public sealed class DebounceExecutor<T> : IDisposable
10+
{
11+
private readonly Timer _timer;
12+
private readonly Func<T> _valueProvider;
13+
private readonly Action<T> _action;
14+
private readonly TimeSpan _delay;
15+
16+
private volatile bool _isDisposed;
17+
18+
/// <summary>
19+
/// Initializes a new instance of the <see cref="DebounceExecutor{T}"/> class.
20+
/// </summary>
21+
/// <param name="valueProvider">The function to call to get the value when the action is to be executed.</param>
22+
/// <param name="action">The action to execute with the value from the provider.</param>
23+
/// <param name="delay">The debounce delay time.</param>
24+
public DebounceExecutor(Func<T> valueProvider, Action<T> action, TimeSpan delay)
25+
{
26+
_valueProvider = valueProvider;
27+
_action = action;
28+
_delay = delay;
29+
// The state object is 'this' instance, passed to the callback.
30+
_timer = new Timer(TimerCallback, this, Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan);
31+
}
32+
33+
/// <summary>
34+
/// Triggers the execution of the action after the debounce delay.
35+
/// If called again before the delay has passed, the timer is reset.
36+
/// I've renamed Execute to Trigger, as it's a more fitting name for a parameterless method that starts a process.
37+
/// </summary>
38+
public void Trigger()
39+
{
40+
if (_isDisposed)
41+
{
42+
return;
43+
}
44+
45+
// This is thread-safe. It will reset the timer to the specified delay.
46+
_timer.Change(_delay, Timeout.InfiniteTimeSpan);
47+
}
48+
49+
private static void TimerCallback(object? state)
50+
{
51+
// The callback runs on a ThreadPool thread.
52+
var instance = (DebounceExecutor<T>)state!;
53+
if (instance._isDisposed)
54+
{
55+
return;
56+
}
57+
58+
try
59+
{
60+
// Get the value and execute the action.
61+
var value = instance._valueProvider();
62+
instance._action(value);
63+
}
64+
catch
65+
{
66+
// Depending on requirements, you might want to log exceptions here.
67+
// By default, we suppress exceptions from the provider or action to prevent the timer from crashing.
68+
}
69+
}
70+
71+
/// <summary>
72+
/// Disposes the executor, stopping any pending operations.
73+
/// </summary>
74+
public void Dispose()
75+
{
76+
if (_isDisposed)
77+
{
78+
return;
79+
}
80+
_isDisposed = true;
81+
82+
// Dispose the timer, which prevents any further callbacks.
83+
// The WaitHandle ensures that we wait for any currently executing callback to complete.
84+
using (var waitHandle = new ManualResetEvent(false))
85+
{
86+
if (_timer.Dispose(waitHandle))
87+
{
88+
waitHandle.WaitOne();
89+
}
90+
}
91+
}
92+
}

src/Everywhere/Utils/DisposeCollector.cs renamed to src/Everywhere/Utilities/DisposeCollector.cs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
using System.Diagnostics.CodeAnalysis;
22

3-
namespace Everywhere.Utils;
3+
namespace Everywhere.Utilities;
44

55
public class DisposeCollector<T>(bool enableDisposeOnFinalize = false) : IReadOnlyList<T>, IDisposable where T : IDisposable
66
{
77
public bool IsDisposed { get; private set; }
88

99
public bool EnableDisposeOnFinalize { get; set; } = enableDisposeOnFinalize;
1010

11-
protected readonly List<T> disposables = [];
11+
protected readonly List<T> _disposables = [];
1212

1313
~DisposeCollector()
1414
{
@@ -18,15 +18,15 @@ public class DisposeCollector<T>(bool enableDisposeOnFinalize = false) : IReadOn
1818
public T Add(T disposable)
1919
{
2020
if (IsDisposed) throw new ObjectDisposedException(nameof(DisposeCollector<>));
21-
disposables.Add(disposable);
21+
_disposables.Add(disposable);
2222
return disposable;
2323
}
2424

2525
public T Add(Func<T> factory)
2626
{
2727
if (IsDisposed) throw new ObjectDisposedException(nameof(DisposeCollector<>));
2828
var disposable = factory();
29-
disposables.Add(disposable);
29+
_disposables.Add(disposable);
3030
return disposable;
3131
}
3232

@@ -35,7 +35,7 @@ public void RemoveAndDispose(ref T? disposable)
3535
if (IsDisposed) throw new ObjectDisposedException(nameof(DisposeCollector<>));
3636
if (disposable == null) return;
3737
disposable.Dispose();
38-
if (!disposables.Remove(disposable)) return;
38+
if (!_disposables.Remove(disposable)) return;
3939
disposable = default;
4040
}
4141

@@ -45,8 +45,8 @@ public void RemoveAndDispose(ref T? disposable)
4545
public void DisposeAndClear()
4646
{
4747
// 逆序释放,防止释放后的对象被再次使用
48-
foreach (var disposable in disposables.Reversed()) disposable.Dispose();
49-
disposables.Clear();
48+
foreach (var disposable in _disposables.Reversed()) disposable.Dispose();
49+
_disposables.Clear();
5050
}
5151

5252
public void Dispose()
@@ -68,16 +68,16 @@ public static void DisposeToDefault(ref T? disposable)
6868

6969
public IEnumerator<T> GetEnumerator()
7070
{
71-
return disposables.GetEnumerator();
71+
return _disposables.GetEnumerator();
7272
}
7373

7474
IEnumerator IEnumerable.GetEnumerator()
7575
{
76-
return ((IEnumerable)disposables).GetEnumerator();
76+
return ((IEnumerable)_disposables).GetEnumerator();
7777
}
7878

79-
public int Count => disposables.Count;
80-
public T this[int index] => disposables[index];
79+
public int Count => _disposables.Count;
80+
public T this[int index] => _disposables[index];
8181
}
8282

8383
public class DisposeCollector(bool enableDisposeOnFinalize = false) : DisposeCollector<IDisposable>(enableDisposeOnFinalize)
@@ -94,7 +94,7 @@ public void RemoveAndDispose<T>(ref T? disposable) where T : IDisposable
9494
{
9595
if (IsDisposed) throw new ObjectDisposedException(nameof(DisposeCollector<>));
9696
if (disposable == null) return;
97-
disposables.Remove(disposable);
97+
_disposables.Remove(disposable);
9898
disposable.Dispose();
9999
disposable = default;
100100
}
@@ -105,12 +105,12 @@ public void Replace<T>([NotNullIfNotNull(nameof(newDisposable))] ref T? oldDispo
105105
if (oldDisposable != null)
106106
{
107107
oldDisposable.Dispose();
108-
disposables.Remove(oldDisposable);
108+
_disposables.Remove(oldDisposable);
109109
}
110110

111111
oldDisposable = newDisposable;
112112
if (oldDisposable == null) return;
113-
disposables.Add(oldDisposable);
113+
_disposables.Add(oldDisposable);
114114
}
115115

116116
[MethodImpl(MethodImplOptions.AggressiveInlining)]

0 commit comments

Comments
 (0)