Skip to content

Commit

Permalink
Merge pull request #137 from reactivemarbles/FixFor135
Browse files Browse the repository at this point in the history
Fix for #135
  • Loading branch information
ChrisPulman authored Jul 28, 2024
2 parents 7a316ad + 7b7bb8c commit 490e8d9
Show file tree
Hide file tree
Showing 25 changed files with 533 additions and 234 deletions.
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
<!-- Include PDB in the built .nupkg -->
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
<IncludePackageReferencesDuringMarkupCompilation>true</IncludePackageReferencesDuringMarkupCompilation>
<AvaloniaVersion>11.1.0</AvaloniaVersion>
<AvaloniaVersion>11.1.1</AvaloniaVersion>
<ReactiveUIVersion>20.1.1</ReactiveUIVersion>
<XamarinReactiveUIVersion>19.6.12</XamarinReactiveUIVersion>
<CrissCrossCoreTargetFrameworks>netstandard2.0;net6.0;net8.0</CrissCrossCoreTargetFrameworks>
Expand Down
2 changes: 1 addition & 1 deletion Version.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json",
"version": "2.0.2",
"version": "2.0.3",
"publicReleaseRefSpec": [
"^refs/heads/master$",
"^refs/heads/main$"
Expand Down
7 changes: 2 additions & 5 deletions src/CrissCross.WPF.UI/Appearance/ApplicationThemeManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,10 @@ public static class ApplicationThemeManager
/// <param name="applicationTheme">Theme to set.</param>
/// <param name="backgroundEffect">Whether the custom background effect should be applied.</param>
/// <param name="updateAccent">Whether the color accents should be changed.</param>
/// <param name="forceBackground">If <see langword="true"/>, bypasses the app's theme compatibility check and tries to force the change of a background effect.</param>
public static void Apply(
ApplicationTheme applicationTheme,
WindowBackdropType backgroundEffect = WindowBackdropType.Mica,
bool updateAccent = true,
bool forceBackground = false)
bool updateAccent = true)
{
if (updateAccent)
{
Expand Down Expand Up @@ -124,8 +122,7 @@ public static void Apply(
WindowBackgroundManager.UpdateBackground(
mainWindow,
applicationTheme,
backgroundEffect,
forceBackground);
backgroundEffect);
}
}

Expand Down
3 changes: 1 addition & 2 deletions src/CrissCross.WPF.UI/Appearance/SystemThemeWatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -198,8 +198,7 @@ private static void UpdateObservedWindow(nint hWnd)
WindowBackgroundManager.UpdateBackground(
observedWindow.RootVisual,
currentApplicationTheme,
observedWindow.Backdrop,
observedWindow.ForceBackgroundReplace);
observedWindow.Backdrop);
}
}
}
19 changes: 12 additions & 7 deletions src/CrissCross.WPF.UI/Appearance/WindowBackgroundManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,10 @@ public static void RemoveDarkThemeFromWindow(Window? window)
/// <param name="window">The window.</param>
/// <param name="applicationTheme">The application theme.</param>
/// <param name="backdrop">The backdrop.</param>
/// <param name="forceBackground">if set to <c>true</c> [force background].</param>
public static void UpdateBackground(
Window? window,
ApplicationTheme applicationTheme,
WindowBackdropType backdrop,
bool forceBackground)
WindowBackdropType backdrop)
{
if (window is null)
{
Expand All @@ -85,10 +83,14 @@ public static void UpdateBackground(
}

// This was required to update the background when moving from a HC theme to light/dark theme. However, this breaks theme proper light/dark theme changing on Windows 10.
// else
// {
// _ = WindowBackdrop.RemoveBackground(window);
// }
// But window backdrop effects are not applied when it has an opaque (or any) background on W11 (so removing this breaks backdrop effects when switching themes), however, for legacy MICA it may not be required
// using existing variable, though the OS build which (officially) supports setting DWM_SYSTEMBACKDROP_TYPE attribute is build 22621
// source: https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwm_systembackdrop_type
if (Win32.Utilities.IsOSWindows11Insider1OrNewer && backdrop is not WindowBackdropType.None)
{
_ = WindowBackdrop.RemoveBackground(window);
}

_ = WindowBackdrop.ApplyBackdrop(window, backdrop);
if (applicationTheme is ApplicationTheme.Dark)
{
Expand All @@ -99,6 +101,7 @@ public static void UpdateBackground(
RemoveDarkThemeFromWindow(window);
}

_ = WindowBackdrop.RemoveTitlebarBackground(window);
foreach (var subWindow in window.OwnedWindows)
{
if (subWindow is Window windowSubWindow)
Expand All @@ -113,6 +116,8 @@ public static void UpdateBackground(
{
RemoveDarkThemeFromWindow(windowSubWindow);
}

_ = WindowBackdrop.RemoveTitlebarBackground(window);
}
}
}
Expand Down
203 changes: 162 additions & 41 deletions src/CrissCross.WPF.UI/Controls/Arc/Arc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,38 +22,35 @@ namespace CrissCross.WPF.UI.Controls;
/// </example>
public class Arc : System.Windows.Shapes.Shape
{
/// <summary>
/// Property for <see cref="StartAngle"/>.
/// </summary>
/// <summary>Identifies the <see cref="StartAngle"/> dependency property.</summary>
public static readonly DependencyProperty StartAngleProperty = DependencyProperty.Register(
nameof(StartAngle),
typeof(double),
typeof(Arc),
new PropertyMetadata(0.0d, PropertyChangedCallback));

/// <summary>
/// Property for <see cref="EndAngle"/>.
/// </summary>
/// <summary>Identifies the <see cref="EndAngle"/> dependency property.</summary>
public static readonly DependencyProperty EndAngleProperty = DependencyProperty.Register(
nameof(EndAngle),
typeof(double),
typeof(Arc),
new PropertyMetadata(0.0d, PropertyChangedCallback));

/// <summary>
/// Initializes static members of the <see cref="Arc"/> class.
/// Overrides default properties.
/// </summary>
static Arc()
{
StrokeStartLineCapProperty.OverrideMetadata(
typeof(Arc),
new FrameworkPropertyMetadata(PenLineCap.Round));
/// <summary>Identifies the <see cref="SweepDirection"/> dependency property.</summary>
public static readonly DependencyProperty SweepDirectionProperty = DependencyProperty.Register(
nameof(SweepDirection),
typeof(SweepDirection),
typeof(Arc),
new PropertyMetadata(SweepDirection.Clockwise, PropertyChangedCallback));

StrokeEndLineCapProperty.OverrideMetadata(
typeof(Arc),
new FrameworkPropertyMetadata(PenLineCap.Round));
}
/// <summary>Identifies the <see cref="StrokeStartLineCap"/> dependency property.</summary>
public static new readonly DependencyProperty StrokeStartLineCapProperty = DependencyProperty.Register(
nameof(StrokeStartLineCap),
typeof(PenLineCap),
typeof(Arc),
new PropertyMetadata(PenLineCap.Round, PropertyChangedCallback));

private System.Windows.Controls.Viewbox? _rootLayout;

/// <summary>
/// Gets or sets the initial angle from which the arc will be drawn.
Expand All @@ -74,12 +71,30 @@ public double EndAngle
}

/// <summary>
/// Gets a value indicating whether if IsLargeArc is <see langword="true"/>, then one of the two larger arc sweeps is chosen; otherwise, if is <see langword="false"/>, one of the smaller arc sweeps is chosen.
/// Gets or sets the direction to where the arc will be drawn.
/// </summary>
public SweepDirection SweepDirection
{
get => (SweepDirection)GetValue(SweepDirectionProperty);
set => SetValue(SweepDirectionProperty, value);
}

/// <summary>
/// Gets or sets a <see cref="T:System.Windows.Media.PenLineCap" /> enumeration value that describes the <see cref="T:System.Windows.Shapes.Shape" /> at the start of a <see cref="P:System.Windows.Shapes.Shape.Stroke" />.
/// </summary>
public new PenLineCap StrokeStartLineCap
{
get => (PenLineCap)GetValue(StrokeStartLineCapProperty);
set => SetValue(StrokeStartLineCapProperty, value);
}

/// <summary>
/// Gets a value indicating whether one of the two larger arc sweeps is chosen; otherwise, if is <see langword="false"/>, one of the smaller arc sweeps is chosen.
/// </summary>
public bool IsLargeArc { get; internal set; }

/// <inheritdoc />
protected override Geometry DefiningGeometry => GetDefiningGeometry();
protected override Geometry DefiningGeometry => DefinedGeometry();

/// <summary>
/// Event triggered when one of the key parameters is changed. Forces the geometry to be redrawn.
Expand All @@ -94,8 +109,6 @@ protected static void PropertyChangedCallback(DependencyObject d, DependencyProp
}

control.IsLargeArc = Math.Abs(control.EndAngle - control.StartAngle) > 180;

// Force complete new layout pass
control.InvalidateVisual();
}

Expand All @@ -104,26 +117,24 @@ protected static void PropertyChangedCallback(DependencyObject d, DependencyProp
/// <para><see href="https://stackoverflow.com/a/36756365/13224348">Based on Mark Feldman implementation.</see></para>
/// </summary>
/// <returns>A Geometry.</returns>
protected Geometry GetDefiningGeometry()
protected Geometry DefinedGeometry()
{
var geometryStream = new StreamGeometry();
var arcSize = new Size(
Math.Max(0, (RenderSize.Width - StrokeThickness) / 2),
Math.Max(0, (RenderSize.Height - StrokeThickness) / 2));

using (var context = geometryStream.Open())
{
context.BeginFigure(PointAtAngle(Math.Min(StartAngle, EndAngle)), false, false);

context.ArcTo(
PointAtAngle(Math.Max(StartAngle, EndAngle)),
arcSize,
0,
IsLargeArc,
SweepDirection.Counterclockwise,
true,
false);
}
using var context = geometryStream.Open();
context.BeginFigure(PointAtAngle(Math.Min(StartAngle, EndAngle)), false, false);

context.ArcTo(
PointAtAngle(Math.Max(StartAngle, EndAngle)),
arcSize,
0,
IsLargeArc,
SweepDirection,
true,
false);

geometryStream.Transform = new TranslateTransform(StrokeThickness / 2, StrokeThickness / 2);

Expand All @@ -138,10 +149,120 @@ protected Geometry GetDefiningGeometry()
/// <returns>A Point.</returns>
protected Point PointAtAngle(double angle)
{
var radAngle = angle * (Math.PI / 180);
var xRadius = (RenderSize.Width - StrokeThickness) / 2;
var yRadius = (RenderSize.Height - StrokeThickness) / 2;
if (SweepDirection == SweepDirection.Counterclockwise)
{
angle += 90;
angle %= 360;
if (angle < 0)
{
angle += 360;
}

var radAngle = angle * (Math.PI / 180);
var xRadius = (RenderSize.Width - StrokeThickness) / 2;
var yRadius = (RenderSize.Height - StrokeThickness) / 2;

return new Point(
xRadius + (xRadius * Math.Cos(radAngle)),
yRadius - (yRadius * Math.Sin(radAngle)));
}
else
{
angle -= 90;
angle %= 360;
if (angle < 0)
{
angle += 360;
}

var radAngle = angle * (Math.PI / 180);
var xRadius = (RenderSize.Width - StrokeThickness) / 2;
var yRadius = (RenderSize.Height - StrokeThickness) / 2;

return new Point(
xRadius + (xRadius * Math.Cos(-radAngle)),
yRadius - (yRadius * Math.Sin(-radAngle)));
}
}

/// <summary>
/// Overrides <see cref="M:System.Windows.Media.Visual.GetVisualChild(System.Int32)" />, and returns a child at the specified index from a collection of child elements.
/// </summary>
/// <param name="index">The zero-based index of the requested child element in the collection.</param>
/// <returns>
/// The requested child element. This should not return null; if the provided index is out of range, an exception is thrown.
/// </returns>
/// <exception cref="System.ArgumentOutOfRangeException">index - Arc should have only 1 child.</exception>
protected override Visual? GetVisualChild(int index)
{
if (index != 0)
{
throw new ArgumentOutOfRangeException(nameof(index), "Arc should have only 1 child");
}

EnsureRootLayout();

return _rootLayout;
}

/// <summary>
/// When overridden in a derived class, measures the size in layout required for child elements and determines a size for the <see cref="T:System.Windows.FrameworkElement" />-derived class.
/// </summary>
/// <param name="availableSize">The available size that this element can give to child elements. Infinity can be specified as a value to indicate that the element will size to whatever content is available.</param>
/// <returns>
/// The size that this element determines it needs during layout, based on its calculations of child element sizes.
/// </returns>
protected override Size MeasureOverride(Size availableSize)
{
EnsureRootLayout();

_rootLayout!.Measure(availableSize);
return _rootLayout.DesiredSize;
}

/// <summary>
/// Arranges a <see cref="T:System.Windows.Shapes.Shape" /> by evaluating its <see cref="P:System.Windows.Shapes.Shape.RenderedGeometry" /> and <see cref="P:System.Windows.Shapes.Shape.Stretch" /> properties.
/// </summary>
/// <param name="finalSize">The final evaluated size of the <see cref="T:System.Windows.Shapes.Shape" />.</param>
/// <returns>
/// The final size of the arranged <see cref="T:System.Windows.Shapes.Shape" /> element.
/// </returns>
protected override Size ArrangeOverride(Size finalSize)
{
EnsureRootLayout();

_rootLayout!.Arrange(new Rect(default, finalSize));
return finalSize;
}

/// <summary>Overrides the default OnRender method to draw the <see cref="Arc" /> element.</summary>
/// <param name="drawingContext">A <see cref="DrawingContext" /> object that is drawn during the rendering pass of this <see cref="System.Windows.Shapes.Shape" />.</param>
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
if (drawingContext == null)
{
throw new ArgumentNullException(nameof(drawingContext));
}

Pen pen =
new(Stroke, StrokeThickness)
{
StartLineCap = StrokeStartLineCap,
EndLineCap = StrokeStartLineCap
};

drawingContext.DrawGeometry(Stroke, pen, DefinedGeometry());
}

private void EnsureRootLayout()
{
if (_rootLayout != null)
{
return;
}

return new Point(xRadius + (xRadius * Math.Cos(radAngle)), yRadius - (yRadius * Math.Sin(radAngle)));
_rootLayout = new System.Windows.Controls.Viewbox { SnapsToDevicePixels = true };
AddVisualChild(_rootLayout);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ public override void OnApplyTemplate()
}

/// <inheritdoc cref="UIElement.Focus" />
public new void Focus() => TextBox?.Focus();
public new bool Focus() => TextBox?.Focus() == true;

/// <summary>
/// Gets the template child.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<Setter Property="Padding" Value="0" />
<Setter Property="VerticalScrollBarVisibility" Value="Auto" />
<Setter Property="HorizontalScrollBarVisibility" Value="Disabled" />
<Setter Property="PanningMode" Value="Both" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Template">
Expand Down
5 changes: 3 additions & 2 deletions src/CrissCross.WPF.UI/Controls/FluentWindow/FluentWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ protected virtual void OnBackdropTypeChanged(WindowBackdropType oldValue, Window

if (newValue == WindowBackdropType.None)
{
WindowBackdrop.RemoveBackdrop(this);
_ = WindowBackdrop.RemoveBackdrop(this);
return;
}

Expand All @@ -146,7 +146,8 @@ protected virtual void OnBackdropTypeChanged(WindowBackdropType oldValue, Window

if (WindowBackdrop.IsSupported(newValue) && WindowBackdrop.RemoveBackground(this))
{
WindowBackdrop.ApplyBackdrop(this, newValue);
_ = WindowBackdrop.ApplyBackdrop(this, newValue);
_ = WindowBackdrop.RemoveTitlebarBackground(this);
}
}

Expand Down
Loading

0 comments on commit 490e8d9

Please sign in to comment.