Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ version it publishes under "What's Changed" in the GitHub release notes, above t
Add a `## <version>` section before tagging a release. Write what someone using Filee notices: what's new, where to
find it, what it does and doesn't do.

## 1.7.2

### Fixes

- When the computer was busy (a screen recorder running, a slow graphics card), slices of the donut could stay lit
after the pointer left them, pile up in the accent colour and fly apart as the donut closed. A late frame moved the
highlight further than it should, and every following frame made it worse. The animations now stay steady however
late the frames come. Turning on "Reduce animations" was the workaround until now.

## 1.7.1

### Fixes
Expand Down
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
parameters of a member is fine (CS1573). -->
<NoWarn>$(NoWarn);CS1591;CS1573</NoWarn>

<Version>1.7.1</Version>
<Version>1.7.2</Version>
<Product>Filee</Product>
<Authors>Filee contributors</Authors>
<Copyright>Copyright (c) Filee contributors</Copyright>
Expand Down
17 changes: 14 additions & 3 deletions src/Filee.App/Controls/DonutMenu.cs
Original file line number Diff line number Diff line change
Expand Up @@ -478,11 +478,22 @@ private struct Spring
public double Value;
public double Velocity;

/// <summary>
/// Longest time one integration step covers. With these stiffnesses a single step of a late frame (50 ms)
/// overshoots more each frame instead of settling: slices stayed lit and flew apart on a busy machine.
/// </summary>
private const double MaxStep = 1 / 120.0;

public bool Step(double target, double dt, double stiffness = 320, double damping = 20)
{
var force = stiffness * (target - Value) - damping * Velocity;
Velocity += force * dt;
Value += Velocity * dt;
var steps = Math.Max(1, (int)Math.Ceiling(dt / MaxStep));
var h = dt / steps;
for (var i = 0; i < steps; i++)
{
var force = stiffness * (target - Value) - damping * Velocity;
Velocity += force * h;
Value += Velocity * h;
}
var settled = Math.Abs(target - Value) < 0.001 && Math.Abs(Velocity) < 0.01;
if (settled)
{
Expand Down
28 changes: 28 additions & 0 deletions tests/Filee.App.Tests/DonutHighlightTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,34 @@ public void A_highlight_settles_without_dipping_below_zero()
Assert.Equal(0, donut.LitAmount(1), 3);
}

[AvaloniaFact]
public void Highlights_settle_when_frames_come_late()
{
TestServices.EnsureInitialized("ko");
Filee.App.Services.Motion.Configure(false, () => false);
var donut = new DonutMenu
{
Items = new[] { "PNG", "JPG", "WEBP", "PDF" }.Select(l => new DonutItem { Id = l, Label = l }).ToList(),
};
// Every frame as late as the loop allows (a busy GPU, a screen recorder): the pointer crosses the slices.
const double late = 0.05;
for (var slice = 0; slice < 4; slice++)
{
donut.SetExternalHighlight(slice);
for (var i = 0; i < 6; i++)
donut.Advance(late);
}
for (var i = 0; i < 60; i++)
{
donut.Advance(late);
for (var slice = 0; slice < 3; slice++)
Assert.InRange(donut.LitAmount(slice), 0, 1);
}

Assert.All(Enumerable.Range(0, 3), slice => Assert.Equal(0, donut.LitAmount(slice), 3));
Assert.Equal(1, donut.LitAmount(3), 3);
}

[AvaloniaFact] // switching the setting restyles the open windows, which belong to the UI thread
public void Following_the_system_reads_it_again_when_a_window_opens()
{
Expand Down
Loading