dotnet / maui

.NET MAUI is the .NET Multi-platform App UI, a framework for building native device applications spanning mobile, tablet, and desktop.
https://dot.net/maui
MIT License
22.24k stars 1.76k forks source link

Android: Border offset from its region, and increases with DPI #15339

Closed dotMorten closed 1 year ago

dotMorten commented 1 year ago

Description

If I render a border on Android, there's a 1px outline (ie margin) around the border. However if I increase the border thickness, that margin increases. It's more pronounced on high-dpi displays and seem then increase with the border thickness. It's easiest reproducible on the Windows Subsystem for Android. If you have multiple monitors with different DPI you can move the window across and see the margin increase (it seems to stay at about 1px regardless of border thickness on 100% scale factors, but 200% you see huge increases).

Steps to Reproduce

  1. Create a new .NET MAUI app.
  2. Replace the ScrollView with the following XAML (and remove the button click stuff in code-behind too):
    <Grid ColumnDefinitions="*,*" RowDefinitions="*,*">
        <Border BackgroundColor="Red" Stroke="Black" StrokeThickness="1" />
    </Grid>
  3. Launch on WSA.
  4. Notice the faint white outline next to the black stroke (along edge and below purple):
image
  1. Change the stroke to 10px and try again. The higher-dpi monitor you have, the larger the offset. Here at 150%:
image

At 200% it's almost as much as the border thickness.

Link to public reproduction project repository

N/A

Version with bug

7.0.81

Last version that worked well

Unknown/Other

Affected platforms

Android

Affected platform versions

WSA

Did you find any workaround?

No response

Relevant log output

No response

jstedfast commented 1 year ago

Test case: MauiBorderOffsetBug.zip

Managed to repro. Increasing the StrokeThickness increases the white padding around the black border.

jstedfast commented 1 year ago

Modified my above example a bit to pack 4 borders into the grid. In the below screenshot, you can see the results with a strokethickness of 10. Dropping that to 1 will produce a different result where the white padding between each border rectangle in the grid becomes more like 1px spacing rather than what looks like 4-5px in this screenshot. Likewise, increasing the strokethickness to 20 increases that white padding between the cells.

Screenshot 2023-06-01 131852

jstedfast commented 1 year ago

This doesn't seem to be a Border drawing bug. With the above test case, if I change the StokeThickness of any of the borders within the grid to something bigger (or smaller), the spacing between each of the grid children increases (or decreases).

For example:

WTF???

https://github.com/dotnet/maui/assets/338984/7369dd07-986d-4a3b-ac97-3e4a65528bb4

jstedfast commented 1 year ago

When running this same app on Windows, I get this:

Screenshot 2023-06-01 163053

After changing the red border's StrokeThickness to 40:

Screenshot 2023-06-01 163327

It is obvious that the border's stroke is not square at the corners.

dotMorten commented 1 year ago

I debugged the WinUI portion a bit. It seems like it is using a Path instead of a Border to render a border 🤷 . For whatever reason it adds Bezier curves to the corners which explains the rounded corners. This is a separate issue from this issue though, and you should probably log that separately.

image

jstedfast commented 1 year ago

There was some logic that was insetting the bounds to compensate for the stroke thickness which then passed it off to another method which again inset the bounds to compensate for the stroke thickness.

Unfortunately, even after fixing that, the problem persists...

Based on that and the fact that changing the stroke thickness of 1 Border in the Grid changes the white padding around EVERY Border in the Grid, there must be some broken logic for measuring the children of a Grid?

I thought maybe the * calculations were the most likely issue, so to verify that I set absolute width/height values in the RowDefinitions and ColumnDefinitions for the Grid, but even with that, the problem persists.

<Grid ColumnDefinitions="180,180" RowDefinitions="360,360" RowSpacing="0" ColumnSpacing="0">
    <Border Grid.Row="0" Grid.Column="0" BackgroundColor="Red" Stroke="Black" StrokeThickness="10" />
    <Border Grid.Row="0" Grid.Column="1" BackgroundColor="Orange" Stroke="Black" StrokeThickness="10" />
    <Border Grid.Row="1" Grid.Column="0" BackgroundColor="Green" Stroke="Black" StrokeThickness="10" />
    <Border Grid.Row="1" Grid.Column="1" BackgroundColor="Blue" Stroke="Black" StrokeThickness="10" />
</Grid>

Aaarrgghh!!!

WTF??

https://github.com/dotnet/maui/assets/338984/2571ba2e-461f-4ff6-a685-662bacbcd533

jstedfast commented 1 year ago

Okay, so... I discovered that MauiDrawable.Android.cs's OnDraw() method seems to be what does the drawing code for Border (at least on Android, other platforms seem to use BorderDrawable.cs?).

Anyway, in the OnDraw() method, it has:

var bounds = new Graphics.Rect(0, 0, _width, _height);
var clipPath = _shape?.ToPlatform(bounds, _strokeThickness);

If I modify the code to use a hard-coded 0 instead of _strokeThickness, then it draws correctly the first time (IOW no gaps between the Grid cells). But if I then modify any of the StrokeThickness values of any of the Border elements, then all goes to hell again.

jstedfast commented 1 year ago

Okay, so the good news is the bug that I described about changing the StrokeThickness of 1 Border in a Grid changing the layout of the Grid is no longer happening with the latest maui:main branch. Yay!