using Avalonia; using Avalonia.Controls; using Avalonia.Input; namespace DodoSSH.Client.App.Views; /// /// Tells a drawn titlebar's double-click from a press that starts a drag. /// /// /// /// Both titlebars used to answer a double-click through DoubleTapped and start a drag on every press. /// The two do not mix: BeginMoveDrag hands the press to the operating system's own move loop, which /// eats the release — and a double tap is a gesture Avalonia assembles from presses and releases, /// so the second click often never completed one and the window did not maximise. The convention it was /// imitating is one nobody thinks about until it does not work. /// /// /// So the press decides, before the drag begins: a second press inside the platform's double-click time and /// distance is the maximise, and does not start a drag at all. The click count is honoured where the platform /// reports one, and the time and distance are measured here for where the move loop kept it from counting. /// /// internal sealed class CaptionPresses { private ulong lastAt; private Point lastWhere; private bool waitingForASecond; /// Whether this press is the second of a double-click on . internal bool IsSecond(PointerPressedEventArgs e, Visual bar) { var settings = Application.Current?.PlatformSettings; var time = settings?.GetDoubleTapTime(e.Pointer.Type) ?? TimeSpan.FromMilliseconds(500); var size = settings?.GetDoubleTapSize(e.Pointer.Type) ?? new Size(4, 4); var where = e.GetPosition(bar); var second = e.ClickCount == 2 || (waitingForASecond && e.Timestamp - lastAt <= (ulong)time.TotalMilliseconds && Math.Abs(where.X - lastWhere.X) <= size.Width && Math.Abs(where.Y - lastWhere.Y) <= size.Height); // A double-click spends both presses: a third that follows quickly starts again rather than toggling // the window straight back. waitingForASecond = !second; lastAt = e.Timestamp; lastWhere = where; return second; } }