diff --git a/src/ClaudeDo.Ui/Views/Islands/DetailsIslandView.axaml.cs b/src/ClaudeDo.Ui/Views/Islands/DetailsIslandView.axaml.cs index cd7dc617..07aa7445 100644 --- a/src/ClaudeDo.Ui/Views/Islands/DetailsIslandView.axaml.cs +++ b/src/ClaudeDo.Ui/Views/Islands/DetailsIslandView.axaml.cs @@ -161,7 +161,8 @@ public partial class DetailsIslandView : UserControl var window = new DiffViewerView { DataContext = diffVm }; window.Closed += (_, _) => _diffViewerWindow = null; _diffViewerWindow = window; - window.Show(owner); + window.CenterOn(owner); + window.Show(); // no owner: an owned window would be pinned above the main window return System.Threading.Tasks.Task.CompletedTask; }; diff --git a/src/ClaudeDo.Ui/Views/Modals/DiffViewerView.axaml b/src/ClaudeDo.Ui/Views/Modals/DiffViewerView.axaml index a6e7e137..8cc145ef 100644 --- a/src/ClaudeDo.Ui/Views/Modals/DiffViewerView.axaml +++ b/src/ClaudeDo.Ui/Views/Modals/DiffViewerView.axaml @@ -11,7 +11,6 @@ WindowDecorations="BorderOnly" ExtendClientAreaToDecorationsHint="True" ExtendClientAreaTitleBarHeightHint="-1" - WindowStartupLocation="CenterOwner" Background="{DynamicResource SurfaceBrush}"> diff --git a/src/ClaudeDo.Ui/Views/Modals/MergeModalView.axaml b/src/ClaudeDo.Ui/Views/Modals/MergeModalView.axaml index e90f6e3f..1cea2654 100644 --- a/src/ClaudeDo.Ui/Views/Modals/MergeModalView.axaml +++ b/src/ClaudeDo.Ui/Views/Modals/MergeModalView.axaml @@ -11,7 +11,6 @@ WindowDecorations="BorderOnly" ExtendClientAreaToDecorationsHint="True" ExtendClientAreaTitleBarHeightHint="-1" - WindowStartupLocation="CenterOwner" Background="{DynamicResource SurfaceBrush}"> diff --git a/src/ClaudeDo.Ui/Views/Modals/MergeModalView.axaml.cs b/src/ClaudeDo.Ui/Views/Modals/MergeModalView.axaml.cs index 8999811d..bd1b5a87 100644 --- a/src/ClaudeDo.Ui/Views/Modals/MergeModalView.axaml.cs +++ b/src/ClaudeDo.Ui/Views/Modals/MergeModalView.axaml.cs @@ -41,7 +41,9 @@ public partial class MergeModalView : Window OpenWindows.Remove(vm.TaskId); window._closed.TrySetResult(); }; - window.Show(owner); + // Independent top-level (no owner): an owned window is pinned above its owner by the OS. + window.CenterOn(owner); + window.Show(); return window._closed.Task; } } diff --git a/src/ClaudeDo.Ui/Views/WindowDialogService.cs b/src/ClaudeDo.Ui/Views/WindowDialogService.cs index 60503a3f..2798cacd 100644 --- a/src/ClaudeDo.Ui/Views/WindowDialogService.cs +++ b/src/ClaudeDo.Ui/Views/WindowDialogService.cs @@ -143,7 +143,8 @@ public sealed class WindowDialogService : IDialogService diffVm.CloseAction = () => diffDlg.Close(); diffDlg.Closed += (_, _) => _diffViewerWindow = null; _diffViewerWindow = diffDlg; - diffDlg.Show(_owner); + diffDlg.CenterOn(_owner); + diffDlg.Show(); // no owner: an owned window would be pinned above the main window }; vm.ConfirmAction = message => ConfirmAsync(message); if (Shell is { } shell) diff --git a/src/ClaudeDo.Ui/Views/WindowPlacement.cs b/src/ClaudeDo.Ui/Views/WindowPlacement.cs new file mode 100644 index 00000000..5a7d293f --- /dev/null +++ b/src/ClaudeDo.Ui/Views/WindowPlacement.cs @@ -0,0 +1,28 @@ +using Avalonia; +using Avalonia.Controls; + +namespace ClaudeDo.Ui.Views; + +internal static class WindowPlacement +{ + /// + /// Centres a not-yet-shown window on , clamped to the owner's screen. + /// Replaces CenterOwner for windows shown without an owner (which the OS would pin above it). + /// + public static void CenterOn(this Window window, Window owner) + { + var scale = owner.DesktopScaling; + var ownerSize = owner.FrameSize ?? owner.ClientSize; + var x = owner.Position.X + (int)((ownerSize.Width - window.Width) * scale / 2); + var y = owner.Position.Y + (int)((ownerSize.Height - window.Height) * scale / 2); + + if (owner.Screens.ScreenFromWindow(owner)?.WorkingArea is { } area) + { + x = Math.Max(area.X, Math.Min(x, area.Right - (int)(window.Width * scale))); + y = Math.Max(area.Y, Math.Min(y, area.Bottom - (int)(window.Height * scale))); + } + + window.WindowStartupLocation = WindowStartupLocation.Manual; + window.Position = new PixelPoint(x, y); + } +}