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

[Windows] Fix Border content position and clipping #6472

Merged
merged 2 commits into from
Apr 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
</Grid.RowDefinitions>

<Border x:Name="BorderView" Margin="5">
<Label Text="Just a Label" FontSize="20"/>
<Label x:Name="BorderContent" Text="Just a Label" FontSize="20"/>
</Border>

<ScrollView
Expand Down Expand Up @@ -61,6 +61,20 @@
Text="#0083B0"
Placeholder="Background End Color Hex"
TextChanged="OnBackgroundChanged"/>
<Label
Text="Content Background"
Style="{StaticResource Headline}"/>
<StackLayout
Orientation="Horizontal">
<CheckBox
x:Name="ContentBackgroundCheckBox"
WidthRequest="48"
VerticalOptions="Center"
CheckedChanged="OnContentBackgroundCheckBoxChanged"/>
<Label
Text="Show Content Background"
VerticalOptions="Center"/>
</StackLayout>
<Label
Text="Border"
Style="{StaticResource Headline}"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public BordersPage()
BorderLineCapPicker.SelectedIndex = 0;

UpdateBackground();
UpdateContentBackground();
UpdateBorder();
UpdateCornerRadius();
}
Expand Down Expand Up @@ -66,6 +67,11 @@ void OnCornerRadiusChanged(object sender, ValueChangedEventArgs e)
UpdateCornerRadius();
}

void OnContentBackgroundCheckBoxChanged(object sender, CheckedChangedEventArgs e)
{
UpdateContentBackground();
}

void UpdateBorderShape()
{
CornerRadiusLayout.IsVisible = BorderShapePicker.SelectedIndex == 1;
Expand Down Expand Up @@ -93,6 +99,11 @@ void UpdateBackground()
};
}

void UpdateContentBackground()
{
BorderContent.BackgroundColor = ContentBackgroundCheckBox.IsChecked ? Color.FromArgb("#99FF0000") : Colors.Transparent;
}

void UpdateBorder()
{
var startColor = GetColorFromString(BorderStartColor.Text);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ protected override IEnumerable<SectionModel> CreateItems() => new[]
{
new SectionModel(typeof(ActivityIndicatorPage), "ActivityIndicator",
"Displays an animation to show that the application is engaged in a lengthy activity."),

new SectionModel(typeof(BordersPage), "Border",
"Draws a border around a View."),

new SectionModel(typeof(BoxViewPage), "BoxView",
"BoxView renders a simple rectangle of a specified width, height, and color. You can use BoxView for decoration, rudimentary graphics, and for interaction with the user through touch."),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@ protected override IEnumerable<SectionModel> CreateItems() => new[]
new SectionModel(typeof(AppThemeBindingPage), "AppThemeBindings",
"Devices typically include light and dark themes, which each refer to a broad set of appearance preferences that can be set at the operating system level. Applications should respect these system themes, and respond immediately when the system theme changes."),

new SectionModel(typeof(BordersPage), "Borders",
"Draws a border around a View, Layout, or Control."),

new SectionModel(typeof(BrushesPage), "Brushes",
"A brush enables you to paint an area, such as the background of a control, using different approaches."),

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,6 @@ protected override IEnumerable<SectionModel> CreateItems() => new[]

new SectionModel(typeof(OthersPage), "Others Concepts",
"Other options like Graphics."),

new SectionModel(typeof(HitTestingPage), "Hit Testing",
"Demonstrates VisualTreeElementExtensions hit testing methods"),
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,21 @@ protected override IEnumerable<SectionModel> CreateItems() => new[]
{
new SectionModel(typeof(GraphicsViewPage), "GraphicsView",
"Allow to draw directly in a Canvas. You can combine a canvas and native Views on the same page."),

new SectionModel(typeof(LargeTitlesPageiOS), "Large Titles - iOS",
"This iOS platform-specific is used to display the page title as a large title on the navigation bar of a NavigationPage, for devices that use iOS 11 or greater."),

new SectionModel(typeof(StyleSheetsPage), "StyleSheets",
"Demonstrates the usage of CSS in XAML."),

new SectionModel(typeof(TwoPaneViewPage), "Foldable",
"Demonstrates the usage of TwoPaneView and hinge sensor."),

new SectionModel(typeof(RenderViewPage), "Render Views",
"Demonstrates rendering views as images.")
"Demonstrates rendering views as images."),

new SectionModel(typeof(HitTestingPage), "Hit Testing",
"Demonstrates VisualTreeElementExtensions hit testing methods"),
};
}
}
11 changes: 11 additions & 0 deletions src/Controls/src/Core/Border.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#nullable enable
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Runtime.CompilerServices;
using Microsoft.Maui.Controls.Shapes;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Layouts;
Expand Down Expand Up @@ -191,5 +192,15 @@ public Thickness PaddingDefaultValueCreator()
{
return Thickness.Zero;
}

protected override void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
base.OnPropertyChanged(propertyName);

if (propertyName == HeightProperty.PropertyName ||
propertyName == WidthProperty.PropertyName ||
propertyName == StrokeShapeProperty.PropertyName)
Handler?.UpdateValue(nameof(IBorderStroke.Shape));
}
}
}
11 changes: 10 additions & 1 deletion src/Controls/src/Core/HandlerImpl/RoundRectangle.Impl.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
using Microsoft.Maui.Graphics;
using System.Runtime.CompilerServices;
using Microsoft.Maui.Graphics;

namespace Microsoft.Maui.Controls.Shapes
{
public partial class RoundRectangle : IShape
{
protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
base.OnPropertyChanged(propertyName);

if (propertyName == CornerRadiusProperty.PropertyName)
Handler?.UpdateValue(nameof(IShapeView.Shape));
}

public override PathF GetPath()
{
var path = new PathF();
Expand Down
58 changes: 29 additions & 29 deletions src/Core/src/Handlers/Border/BorderHandler.Windows.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,46 +3,46 @@
namespace Microsoft.Maui.Handlers
{
public partial class BorderHandler : ViewHandler<IBorderView, ContentPanel>
{
public override void SetVirtualView(IView view)
{
base.SetVirtualView(view);
{
public override void SetVirtualView(IView view)
{
base.SetVirtualView(view);

_ = PlatformView ?? throw new InvalidOperationException($"{nameof(PlatformView)} should have been set by base class.");
_ = VirtualView ?? throw new InvalidOperationException($"{nameof(VirtualView)} should have been set by base class.");
_ = PlatformView ?? throw new InvalidOperationException($"{nameof(PlatformView)} should have been set by base class.");
_ = VirtualView ?? throw new InvalidOperationException($"{nameof(VirtualView)} should have been set by base class.");

PlatformView.CrossPlatformMeasure = VirtualView.CrossPlatformMeasure;
PlatformView.CrossPlatformArrange = VirtualView.CrossPlatformArrange;
}
PlatformView.CrossPlatformMeasure = VirtualView.CrossPlatformMeasure;
PlatformView.CrossPlatformArrange = VirtualView.CrossPlatformArrange;
}

static void UpdateContent(IBorderHandler handler)
{
_ = handler.PlatformView ?? throw new InvalidOperationException($"{nameof(PlatformView)} should have been set by base class.");
_ = handler.VirtualView ?? throw new InvalidOperationException($"{nameof(VirtualView)} should have been set by base class.");
_ = handler.MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} should have been set by base class.");
static void UpdateContent(IBorderHandler handler)
{
_ = handler.PlatformView ?? throw new InvalidOperationException($"{nameof(PlatformView)} should have been set by base class.");
_ = handler.VirtualView ?? throw new InvalidOperationException($"{nameof(VirtualView)} should have been set by base class.");
_ = handler.MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} should have been set by base class.");

handler.PlatformView.Children.Clear();
handler.PlatformView.EnsureBorderPath();

if (handler.VirtualView.PresentedContent is IView view)
handler.PlatformView.Children.Add(view.ToPlatform(handler.MauiContext));
}
if (handler.VirtualView.PresentedContent is IView view)
handler.PlatformView.Content = view.ToPlatform(handler.MauiContext);
}

protected override ContentPanel CreatePlatformView()
{
if (VirtualView == null)
{
throw new InvalidOperationException($"{nameof(VirtualView)} must be set to create a LayoutView");
}
protected override ContentPanel CreatePlatformView()
{
if (VirtualView == null)
{
throw new InvalidOperationException($"{nameof(VirtualView)} must be set to create a LayoutView");
}

var view = new ContentPanel
var view = new ContentPanel
{
CrossPlatformMeasure = VirtualView.CrossPlatformMeasure,
CrossPlatformArrange = VirtualView.CrossPlatformArrange
};
CrossPlatformMeasure = VirtualView.CrossPlatformMeasure,
CrossPlatformArrange = VirtualView.CrossPlatformArrange
};

return view;
}
return view;
}

public static void MapContent(IBorderHandler handler, IBorderView border)
{
Expand Down
74 changes: 73 additions & 1 deletion src/Core/src/Platform/Windows/ContentPanel.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
#nullable enable
using System;
using Microsoft.Graphics.Canvas;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Graphics.Win2D;
using Microsoft.UI.Composition;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Hosting;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Shapes;

namespace Microsoft.Maui.Platform
Expand All @@ -10,8 +16,20 @@ public class ContentPanel : Panel
{
readonly Path? _borderPath;
IShape? _borderShape;
FrameworkElement? _content;

internal Path? BorderPath => _borderPath;

internal FrameworkElement? Content
{
get => _content;
set
{
_content = value;
AddContent(_content);
}
}

internal Func<double, double, Size>? CrossPlatformMeasure { get; set; }
internal Func<Graphics.Rect, Size>? CrossPlatformArrange { get; set; }

Expand Down Expand Up @@ -52,12 +70,14 @@ public ContentPanel()
SizeChanged += ContentPanelSizeChanged;
}

private void ContentPanelSizeChanged(object sender, UI.Xaml.SizeChangedEventArgs e)
void ContentPanelSizeChanged(object sender, UI.Xaml.SizeChangedEventArgs e)
{
if (_borderPath == null)
return;

_borderPath.UpdatePath(_borderShape, ActualWidth, ActualHeight);
UpdateContent();
UpdateClip(_borderShape);
}

internal void EnsureBorderPath()
Expand All @@ -84,6 +104,58 @@ public void UpdateBorderShape(IShape borderShape)
return;

_borderPath.UpdateBorderShape(_borderShape, ActualWidth, ActualHeight);
UpdateContent();
UpdateClip(_borderShape);
}

void AddContent(FrameworkElement? content)
{
if (content == null)
return;

if (!Children.Contains(_content))
Children.Add(_content);
}

void UpdateContent()
{
if (Content == null || _borderPath == null)
return;

var strokeThickness = _borderPath.StrokeThickness;

Content.RenderTransform = new TranslateTransform() { X = -strokeThickness, Y = -strokeThickness };
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 main change to fix the wrong positioning.

}

void UpdateClip(IShape? borderShape)
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 main change for Clipping.

{
if (Content == null)
return;

var clipGeometry = borderShape;

if (clipGeometry == null)
return;

double width = Content.ActualWidth;
double height = Content.ActualHeight;

if (height <= 0 && width <= 0)
return;

var visual = ElementCompositionPreview.GetElementVisual(Content);
var compositor = visual.Compositor;

var pathSize = new Graphics.Rect(0, 0, width, height);
var clipPath = clipGeometry.PathForBounds(pathSize);
var device = CanvasDevice.GetSharedDevice();
var geometry = clipPath.AsPath(device);

var path = new CompositionPath(geometry);
var pathGeometry = compositor.CreatePathGeometry(path);
var geometricClip = compositor.CreateGeometricClip(pathGeometry);

visual.Clip = geometricClip;
}
}
}