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

feat: Add WindowCornerHintsProperty #17354

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

BAndysc
Copy link
Contributor

@BAndysc BAndysc commented Oct 26, 2024

What does the pull request do?

This PR adds a new StyledProperty to the Window class: WindowCornerHintsProperty, which provides hints for window corner appearance. The hint is only used on Windows, as only this platform supports controlling corner appearance.

This is required for Classic.Avalonia theme to look good.

What is the current behavior?

Currently, there is no way to control window corners; it is up to Avalonia to decide which corner style to use.

What is the updated/expected behavior with this PR?

When WindowCornerHintsProperty is set to default platform, round, or do not round, the appropriate corner preference is used. However, by default, WindowCornerHintsProperty has the value NoHint, which indicates that it is up to Avalonia to decide which corner style should be used. This way, the default behavior is the same as it is now.

How was the solution implemented (if it's not obvious)?

This PR follows the implementation of ExtendClientAreaChromeHintsProperty.

Checklist

Breaking changes

Obsoletions / Deprecations

Fixed issues

Fixes #17294

@avaloniaui-bot
Copy link

You can test this PR using the following package version. 11.3.999-cibuild0052796-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@maxkatz6
Copy link
Member

This API makes sense to me as part of Win32Properties class: https://github.com/AvaloniaUI/Avalonia/blob/master/src/Avalonia.Controls/Platform/Win32Properties.cs#L17. Not xplat.

@BAndysc
Copy link
Contributor Author

BAndysc commented Oct 28, 2024

This API makes sense to me as part of Win32Properties class: https://github.com/AvaloniaUI/Avalonia/blob/master/src/Avalonia.Controls/Platform/Win32Properties.cs#L17. Not xplat.

Oh, I didn't know about Win32/Mac/X11 Properties classes. I have updated the PR, now it adds a new attached property in Win32Properties, it makes much more sense

@avaloniaui-bot
Copy link

You can test this PR using the following package version. 11.3.999-cibuild0052846-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

{
public static readonly AttachedProperty<Win32WindowCornerHints> WindowCornerHintProperty =
AvaloniaProperty.RegisterAttached<Win32Properties, Window, Win32WindowCornerHints>("WindowCornerHint");

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't we just make this a 3-state bool value? This property is ONLY controlling a window corner radius. This means we need to control whether it is ON or OFF. Then it's probably useful to have a "default" condition as well.

bool? could communicate:

  • null : Platform default
  • true : Round corners
  • false : Don't round corners

Will there ever be a reason to have more values?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's a windows specific property, as it is now, it should mirror windows API.
If we assume it shouldn't be windows specific one day, it can either be made a 3-state bool later. But it's unlikely to be supported on wider range of platforms.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's a windows specific property, as it is now, it should mirror windows API.

Good point. That's the right way to look at it I think so your probably right.

I also notice there are more values supported: https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwm_window_corner_preference. Namely RoundSmall. So an enum is the way to go.

/// <summary>
/// Prevents corners from being rounded.
/// </summary>
DoNotRound
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a fan of this name. I think the whole enum should go away but "NotRounded" is probably better.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's right, changed it for consistency, thanks!

Comment on lines +14 to +19
NoHint,

/// <summary>
/// The platform's default corner style.
/// </summary>
PlatformDefault,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NoHint is exactly the same as PlatformDefault. We shouldn't have two values here. These should be combined into a Default value. Default means no hint is given so the platform defaults will be used.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the current behavior:

if (_isClientAreaExtended && WindowState != WindowState.FullScreen)
{
var margins = UpdateExtendMargins();
DwmExtendFrameIntoClientArea(_hwnd, ref margins);
unsafe
{
int cornerPreference = (int)DwmWindowCornerPreference.DWMWCP_ROUND;
DwmSetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_WINDOW_CORNER_PREFERENCE, &cornerPreference, sizeof(int));
}
}
else
{
var margins = new MARGINS();
DwmExtendFrameIntoClientArea(_hwnd, ref margins);
_offScreenMargin = new Thickness();
_extendedMargins = new Thickness();
unsafe
{
int cornerPreference = (int)DwmWindowCornerPreference.DWMWCP_DEFAULT;
DwmSetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_WINDOW_CORNER_PREFERENCE, &cornerPreference, sizeof(int));
}
}

So it is DWMWCP_ROUND when _isClientAreaExtended is true and window state is not full screen. Otherwise it uses DWMWCP_DEFAULT.

This is because DWMWCP_DEFAULT with client area extended results in not rounded corners.

This is also why I've split the enum into NoHint (aka avalonia default) and Default (aka platform default). However, I could agree than maybe Default aka "Platform Default" is not useful in practice. Avalonia default is good for 99.9% programmers and for the small percentage the choice between "rounded" and "not rounded" it probably enough.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reading, I think we need the following values: Default, Rounded, RoundedSmall, NotRounded (Alternative: Round, RoundSmall, DoNotRound following upstream API)

@avaloniaui-bot
Copy link

You can test this PR using the following package version. 11.3.999-cibuild0052883-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

Comment on lines +8 to +9
[Flags]
public enum Win32WindowCornerHints
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this flags? And plural naming?

Reading the docs I don't think multiple values can be set at once. So it's isn't treated as flags.

https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute
https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwm_window_corner_preference

Perhaps the name should be Win32WindowCornerPreference as well. Although, I think it's supported only on Windows 11+ so calling it "Hints" might be appropriate as its ignored on platforms that don't support it.

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

Successfully merging this pull request may close these issues.

Add an ability to control Windows rounded corners behavior
4 participants