Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drag/Dropping Window to Maximized Cuts Taskbar (BorderOnly Deco) #11685

Closed
ArchLeaders opened this issue Jun 7, 2023 Discussed in #11222 · 35 comments · Fixed by #16297
Closed

Drag/Dropping Window to Maximized Cuts Taskbar (BorderOnly Deco) #11685

ArchLeaders opened this issue Jun 7, 2023 Discussed in #11222 · 35 comments · Fixed by #16297

Comments

@ArchLeaders
Copy link

Discussed in #11222

Originally posted by ArchLeaders May 3, 2023
I'm setting up a custom window chrome for my application, but I'm getting some weird behavior with the drag/drop to fullscreen action in Windows 10.

Setting WindowState to WindowState.Maximized works just fine, but when I drag the window to the top of the screen and drop it cuts out the task bar and about 7 pixels off the side of the window.

window-behaviour.mp4

Is there a way to intercept this behavior and call whatever Avalonia does to fullscreen to the correct bounds instead? Or just another way to fix this?

  • Avalonia Version: 11.0.0-preview6 and 11.0.0-r1.1
  • OS: Windows 10
@ArchLeaders
Copy link
Author

ArchLeaders commented Jun 7, 2023

I should also mention that the other window decoration properties did not work in my case, the main issue being the window drop shadow. The only solution I could find that would remove the system chrome while retaining the shadow (border), was using BorderOnly with a custom resize border and drag region.

@timunie
Copy link
Contributor

timunie commented Jun 7, 2023

Window has a property called OffScreenMargin. Can you try to apply this to your inner Content?

<Window xmlns="https://github.com/avaloniaui"
        ... other namespaces etc ... >
   <Grid Margin="{Binding $parent[Window].OffScreenMargin}">
     ... Content ...
   </Grid>
</Window>

@ArchLeaders
Copy link
Author

Window has a property called OffScreenMargin. Can you try to apply this to your inner Content?

<Window xmlns="https://github.com/avaloniaui"
        ... other namespaces etc ... >
   <Grid Margin="{Binding $parent[Window].OffScreenMargin}">
     ... Content ...
   </Grid>
</Window>

No luck with that sadly, still cuts the border when drop/dropping to fullscreen (same effect as before)

image

@cyraid
Copy link

cyraid commented Jun 14, 2023

Apparently my bug report got deleted? It was when it's maximized and no chrome it gets cut by 8 pixels or so in a maximized state.

@rprimora-pricer
Copy link

I experience the same issue. I think you are right for the pixels. It should be 8px which is the padding applied to the Window when it's maximized.

It's as if this way of maximizing resizes the Window without padding and then internally changes state to maximized.

@ApaulMyLittleAirport
Copy link

Margin="{Binding $parent[Window].OffScreenMargin} ,This one really works, thank you!!

@rprimora-pricer
Copy link

rprimora-pricer commented Sep 22, 2023

I have looked closer at the issue today and managed to get a working code.

In your constructor add the following code block:

this.GetPropertyChangedObservable(WindowStateProperty).AddClassHandler<Visual>((t, args) =>
{
    /// I am doing this only for Windows.
    if (OperatingSystem.IsWindows())
    {
        /// If the state is maximised we will enter our code block.
        if (args.GetNewValue<WindowState>() == WindowState.Maximized)
        {
            /// Get the screen from the window.
            var screen = Screens.ScreenFromWindow(this);
            /// This is the part where we check if the actual height is more than the working area cos that would
            /// mean the window has gone over the taskbar.
            if (screen.WorkingArea.Height < ClientSize.Height)
            {
                /// We set the client size of our window to be exactly the size of our working area.
                ClientSize = screen.WorkingArea.Size.ToSize(screen.Scaling);
                /// This is the last piece of the puzzle. If we maximize the Window by setting the state
                /// then this position will be correct. But, if the state is set as a result of dragging the window
                /// to the top then the position will most likely be less then 0 ( it will be (-7,-7) in our case).
                if (Position.X < 0 || Position.Y < 0)
                    Position = screen.WorkingArea.Position; // <-- This is needed because you might have your taskbar in a different position (Left | Right | Top | Bottom).
            }
        }
    }
});

I really hope Avalonia solves this issue.

@ArchLeaders
Copy link
Author

I have looked closer at the issue today and managed to get a working code.

In your constructor add the following code block:

this.GetPropertyChangedObservable(WindowStateProperty).AddClassHandler<Visual>((t, args) =>
{
    /// I am doing this only for Windows.
    if (OperatingSystem.IsWindows())
    {
        /// If the state is maximised we will enter our code block.
        if (args.GetNewValue<WindowState>() == WindowState.Maximized)
        {
            /// Get the screen from the window.
            var screen = Screens.ScreenFromWindow(this);
            /// This is the part where we check if the actual height is more than the working area cos that would
            /// mean the window has gone over the taskbar.
            if (screen.WorkingArea.Height < ClientSize.Height)
            {
                /// We set the client size of our window to be exactly the size of our working area.
                ClientSize = screen.WorkingArea.Size.ToSize(screen.Scaling);
                /// This is the last piece of the puzzle. If we maximize the Window by setting the state
                /// then this position will be correct. But, if the state is set as a result of dragging the window
                /// to the top then the position will most likely be less then 0 ( it will be (-7,-7) in our case).
                if (Position.X < 0 || Position.Y < 0)
                    Position = screen.WorkingArea.Position; // <-- This is needed because you might have your taskbar in a different position (Left | Right | Top | Bottom).
            }
        }
    }
});

I really hope Avalonia solves this issue.

Hey, thanks for this! I just tested it and it almost works perfectly.

One thing I noticed; when you check the height it should be multiplied by the screen scale. It didn't work on a 4K monitor because the check failed.

if (screen.WorkingArea.Height < ClientSize.Height * screen.Scaling) {
  // . . .
}

Visual it looks fine, but it seems to restrict actually using the taskbar. I'll mess around with it and see if I can fix that issue.

Thanks again for the research on this!

@ArchLeaders
Copy link
Author

ArchLeaders commented Oct 4, 2023

Visual it looks fine, but it seems to restrict actually using the taskbar. I'll mess around with it and see if I can fix that issue.

The taskbar seems to work fine if the window position is anything greater than 0, 0. I.e. if the taskbar is not docked to the bottom, or the position is hardcoded to 0, 1 or something.

I don't know enough about how windows work to say why this happens, but maybe it will be insightful to someone else.

@rprimora-pricer
Copy link

@ArchLeaders good catch on scaling. I'll check it out.

I am not sure about the usage of taskbar. What do you mean by this?

@ArchLeaders
Copy link
Author

ArchLeaders commented Oct 5, 2023

I am not sure about the usage of taskbar. What do you mean by this?

Might be a condition of my setup, but when the window position (this.Position) is set to PixelPoint(0, 0) I can't click on the taskbar (as if there is a clear panel covering it). There is also a small 1px-wide white line lining the left side of the taskbar:

image

*Edit*

For some reason I thought it worked if I set SystemDecorations="None", but it doesn't. Only the white line disappears.

@timunie
Copy link
Contributor

timunie commented Oct 5, 2023

@ArchLeaders how does latest nightly look for you?

Also probably check what FluentAvalonia does to get AppWindow working on Windows. They did a heavy lifting to get it somewhat work.

@ArchLeaders
Copy link
Author

ArchLeaders commented Oct 5, 2023

@ArchLeaders how does latest nightly look for you?

Can't seem to connect to https://nuget-feed-nightly.avaloniaui.net/api/download/symbols.

*Edit*

Nevermind, just needed to deleted and re-add the source instead of updating.

@ArchLeaders
Copy link
Author

Latest nightly has the same issue as >11.0.1, there's a weird white border around the window and sizing isn't applied.

image

With this hack, sizing is weirdly off when dragging to fullscreen.

image

Clicking the maximize button (with and without the hack) results in it having about 7-8px of padding on the inside of the border.

image

Dragging to fullscreen without the hack seals it to the edge of the screen (without the 7-8px cutoff that it had previously)

(My other monitor is in the screenshot as a reference to the taskbar)

image

@timunie
Copy link
Contributor

timunie commented Oct 5, 2023

is there any minimal sample I can check on my computer?

@ArchLeaders
Copy link
Author

Not currently, but I can put one together pretty quick.

@ArchLeaders
Copy link
Author

@timunie I created a minimal reproduction using the latest nightly, hope this helps:

https://github.com/ArchLeaders/AvaloniaBorderOnlyWindow

Thanks for looking into this :D

@rprimora-pricer
Copy link

rprimora-pricer commented Oct 9, 2023

Latest nightly has the same issue as >11.0.1, there's a weird white border around the window and sizing isn't applied.

image

If you are refering to the white border on top part of the Window, isn't this the ExtendClientAreaTitleBarHeightHint set to something above 0?

@ArchLeaders
Copy link
Author

If zou are refering to the white border on top part of the Window, isn't this the ExtendClientAreaTitleBarHeightHint set to something above 0?

Yes I am refering to that, I don't think that's the case though. Setting it to 0 has the same effect.

image

@rprimora-pricer
Copy link

rprimora-pricer commented Oct 9, 2023

@ArchLeaders Can you try and set it to -1?

@timunie
Copy link
Contributor

timunie commented Oct 9, 2023

<Window x:Class="AvaloniaBorderOnlyWindow.ShellView"
        xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Title="ShellView"
        Width="400"
        Height="400"
        d:DesignHeight="450"
        d:DesignWidth="800"
+       ExtendClientAreaChromeHints="SystemChrome"
+       ExtendClientAreaToDecorationsHint="True"
        SystemDecorations="BorderOnly"
        mc:Ignorable="d">

That works for me at least

@ArchLeaders
Copy link
Author

@ArchLeaders Can you try and set it to -1?

@rprimora-pricer Same effect with ExtendClientAreaTitleBarHeightHint="-1".

@ArchLeaders
Copy link
Author

<Window x:Class="AvaloniaBorderOnlyWindow.ShellView"
        xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Title="ShellView"
        Width="400"
        Height="400"
        d:DesignHeight="450"
        d:DesignWidth="800"
+       ExtendClientAreaChromeHints="SystemChrome"
+       ExtendClientAreaToDecorationsHint="True"
        SystemDecorations="BorderOnly"
        mc:Ignorable="d">

That works for me at least

Oh yeah that does work. I wonder why I never tried that before.

@ArchLeaders
Copy link
Author

Still has an issue with resizing, but at least BorderOnly works.

@rprimora-pricer
Copy link

What issues you have with resizing?

@ArchLeaders
Copy link
Author

Same as here: #11685 (comment)

(Taskbar is not clickable when dragging to fullscreen)

@timunie
Copy link
Contributor

timunie commented Oct 9, 2023

then please update your minimal sample to reproduce the mentioned issue.

@ArchLeaders
Copy link
Author

ArchLeaders commented Oct 9, 2023

Changes should be synced on github.

Now the main button changes the window state from the code behind (which works as expected)

But dragging with window to the fullscreen (or Win + ArrowUp) disallows the use of the taskbar.

@rprimora-pricer
Copy link

Same as here: #11685 (comment)

(Taskbar is not clickable when dragging to fullscreen)

Oh, I just saw what you meant by it. The hack does indeed fix the sizing but for some reason the Taskbar is not functioning. It is curious behavior.

Feels like a fullscreen gone wrong. I say this because normally in Windows to go fullscreen all you have to do is maximize the screen and set its bounds to be the same as the screen size. Windows will then hide the taskbar automatically. In our case, without the hack, when you drag a window for maximize same will happen.

@timunie
Copy link
Contributor

timunie commented Oct 10, 2023

can't reproduce using Windows 11 and your Button, but see the issue using [Win]+[Arrow Up]. Strange issue.

@ArchLeaders
Copy link
Author

can't reproduce using Windows 11 and your Button, but see the issue using [Win]+[Arrow Up]. Strange issue.

Yes, the button works correctly on my end too.

@rprimora-pricer
Copy link

rprimora-pricer commented Oct 11, 2023

I found some more workarounds :)

Try adding this to a static class somewhere.

public static class WindowHelper
{
	[Flags]
	private enum SetWindowPosFlags : uint
	{
		HideWindow = 128,
		ShowWindow = 64
	}

	[DllImport("user32.dll", SetLastError = true)]
	private static extern int SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);

	public static void FixAfterMaximizing(IntPtr hWnd, Screen screen)
	{
		SetWindowPos(hWnd, IntPtr.Zero, screen.WorkingArea.X, screen.WorkingArea.Y, screen.WorkingArea.Width, screen.WorkingArea.Height, (uint)SetWindowPosFlags.ShowWindow);
	}
}

Then in that workaround that I gave you add this:

this.GetPropertyChangedObservable(WindowStateProperty).AddClassHandler<Visual>((t, args) =>
{
    /// I am doing this only for Windows.
    if (OperatingSystem.IsWindows())
    {
        /// If the state is maximised we will enter our code block.
        if (args.GetNewValue<WindowState>() == WindowState.Maximized)
        {
            /// Get the screen from the window.
            var screen = Screens.ScreenFromWindow(this);
            /// This is the part where we check if the actual height is more than the working area cos that would
            /// mean the window has gone over the taskbar.
            if (screen.WorkingArea.Height < ClientSize.Height * screen.Scaling) // <- Part with scaling is your suggestion
            {
                /// We set the client size of our window to be exactly the size of our working area.
                ClientSize = screen.WorkingArea.Size.ToSize(screen.Scaling);
                /// This is the last piece of the puzzle. If we maximize the Window by setting the state
                /// then this position will be correct. But, if the state is set as a result of dragging the window
                /// to the top then the position will most likely be less then 0 ( it will be (-7,-7) in our case).
                if (Position.X < 0 || Position.Y < 0)
                {
                    Position = screen.WorkingArea.Position; // <-- This is needed because you might have your taskbar in a different position (Left | Right | Top | Bottom).
                    WindowHelper.FixAfterMaximizing(TryGetPlatformHandle().Handle, screen); /// <- Make the call here
                }
            }
        }
    }
});

I tried it on many screens and it works. Before it worked only on primary screen.

@timunie
Copy link
Contributor

timunie commented Oct 11, 2023

@rprimora-pricer I think instead of searching for several workarounds, it would be better to fix it in source. So if you want to, do this:

  • Add your sample to src/samples/Sandbox
  • Apply the "fix" in Win32 project
  • File a PR if you found a valid fix

Still up to you, if you want to do a PR. I don't want to force you. Any help is appreciated ❤️

@emmauss
Copy link
Contributor

emmauss commented Oct 12, 2023

could you test with, #13228

@ArchLeaders
Copy link
Author

could you test with, #13228

@emmauss doesn't seem to have any effect.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
8 participants