forked from unoplatform/uno
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Port RootScale and related classes
- Loading branch information
1 parent
983501b
commit 30ab0b4
Showing
6 changed files
with
429 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Runtime.CompilerServices; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
namespace Uno.UI.Xaml.Internal; | ||
|
||
internal static class Inlined | ||
{ | ||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
internal static bool IsCloseReal(float a, float b) => Math.Abs(a - b) < float.Epsilon; // TODO Uno: The original logic is more complex, but might not be necessary in Uno. | ||
} |
This file was deleted.
Oops, something went wrong.
54 changes: 54 additions & 0 deletions
54
src/Uno.UI/UI/Xaml/Internal/Scaling/CoreWindowRootScale.mux.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
namespace Uno.UI.Xaml.Core.Scaling; | ||
|
||
internal class CoreWindowRootScale : RootScale | ||
{ | ||
public CoreWindowRootScale(RootScaleConfig config, CoreServices coreServices, VisualTree visualTree) : | ||
base(config, coreServices, visualTree) | ||
{ | ||
} | ||
|
||
protected override void ApplyScaleProtected(bool scaleChanged) | ||
{ | ||
//var mainRootVisual = _coreServices.MainRootVisual; | ||
//if (mainRootVisual is not null) | ||
//{ | ||
// float scale = GetRootVisualScale(); | ||
// // The composition subsystem has installed a scale transform on top of our tree, update our tree to be aware of it | ||
// // This ensures that Xaml will know about the physical DPI when needed, e.g. rendering crisp text. | ||
// mainRootVisual.RasterizationScale = scale; | ||
|
||
// //const auto connectedAnimationRoot = m_pCoreServices->GetConnectedAnimationRoot(); | ||
// //if (connectedAnimationRoot) | ||
// //{ | ||
// // // Plateau scale has been applied on the island, and we need to cancel it here, | ||
// // // because the snapshots are created using the pixel size including the plateau scale, | ||
// // // and we don't want double scaling. | ||
// // CValue inverseScaleTransform; | ||
// // IFC_RETURN(CreateReverseTransform(&inverseScaleTransform)); | ||
// // IFC_RETURN(connectedAnimationRoot->SetValueByKnownIndex(KnownPropertyIndex::UIElement_RenderTransform, inverseScaleTransform)); | ||
// //} | ||
//} | ||
|
||
//if (scaleChanged) | ||
//{ | ||
// // We need to force re-layout and re-render only if the scale has actually changed. | ||
// m_pCoreServices->MarkRootScaleTransformDirty(); | ||
|
||
// // Warning: This will not work correctly in the Context of AppWindows | ||
// // Task 18843113: Do not use global MRT resource manager, instead add a new MRT Resource instance on each CRootVisualInstanc | ||
// { | ||
// const unsigned int scalePercentage = XcpRound(GetEffectiveRasterizationScale() * 100.0f); | ||
// // Update the scale factor on the resource manager. | ||
// xref_ptr<IPALResourceManager> resourceManager; | ||
// IFC_RETURN(m_pCoreServices->GetResourceManager(resourceManager.ReleaseAndGetAddressOf())); | ||
// IFC_RETURN(resourceManager->SetScaleFactor(scalePercentage)); | ||
|
||
// // Flush the XAML parser cache. If the application to reload XAML after a scale change, we want to | ||
// // re-query MRT for the XAML resource, potentially picking up a new resource for the new scale. | ||
// std::shared_ptr<XamlNodeStreamCacheManager> spXamlNodeStreamCacheManager; | ||
// IFC_RETURN(m_pCoreServices->GetXamlNodeStreamCacheManager(spXamlNodeStreamCacheManager)); | ||
// spXamlNodeStreamCacheManager->Flush(); | ||
// } | ||
//} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. See LICENSE in the project root for license information. | ||
// MUX Reference dxaml\xcp\components\scaling\inc\RootScale.h, tag winui3/release/1.5.1 | ||
|
||
#nullable enable | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using Microsoft.UI.Content; | ||
using Microsoft.UI.Xaml; | ||
|
||
namespace Uno.UI.Xaml.Core.Scaling; | ||
|
||
internal enum RootScaleConfig | ||
{ | ||
// Parent scale is identity or it expects the root visual tree to apply system DPI scale itself. | ||
ParentInvert, | ||
// Parent scale already applies the system DPI scale, so need to apply in the internal root visual tree. | ||
ParentApply, | ||
} | ||
|
||
partial class RootScale | ||
{ | ||
internal bool IsInitialized => _initialized; | ||
|
||
internal static float GetRasterizationScaleForContentRoot(ContentRoot? coreContextRoot) | ||
{ | ||
if (GetRootScaleForElement(coreContextRoot) is { } rootScale) | ||
{ | ||
return rootScale.GetEffectiveRasterizationScale(); | ||
} | ||
return 1.0f; | ||
} | ||
|
||
internal static float GetRasterizationScaleForElement(DependencyObject pDO) | ||
{ | ||
if (GetRootScaleForElement(pDO) is { } rootScale) | ||
{ | ||
return rootScale.GetEffectiveRasterizationScale(); | ||
} | ||
return 1.0f; | ||
} | ||
|
||
internal static float GetRasterizationScaleForElementWithFallback(DependencyObject pDO) | ||
{ | ||
var rootScale = GetRootScaleForElementWithFallback(pDO); | ||
if (rootScale is not null) | ||
{ | ||
return rootScale.GetEffectiveRasterizationScale(); | ||
} | ||
return 1.0f; | ||
} | ||
|
||
protected abstract void ApplyScaleProtected(bool scaleChanged); | ||
|
||
private protected VisualTree? VisualTree => _visualTree; | ||
|
||
internal enum ScaleKind | ||
{ | ||
System, | ||
Test, | ||
} | ||
|
||
private readonly RootScaleConfig _config; | ||
// The system DPI, this is accumulated scale, | ||
// tipically this is control by the Display Settings app. | ||
private float _systemScale = 1.0f; | ||
// Used only for testing, it replaces the system DPI scale with a value | ||
// This can only be used when config is RootScaleConfig::ParentInvert | ||
private float _testOverrideScale; | ||
private readonly List<DisplayListener> _displayListeners = new(); | ||
private readonly VisualTree _visualTree; | ||
private bool _initialized; | ||
private bool _updating; | ||
private ImageReloadManager? _imageReloadManager; | ||
private ContentIsland? _content; // IExpCompositionContent | ||
|
||
protected readonly CoreServices _coreServices; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. See LICENSE in the project root for license information. | ||
|
||
#nullable enable | ||
|
||
using Microsoft.UI.Content; | ||
using Microsoft.UI.Xaml; | ||
using Uno.Disposables; | ||
using static Uno.UI.Xaml.Internal.Inlined; | ||
|
||
namespace Uno.UI.Xaml.Core.Scaling; | ||
|
||
internal partial class RootScale | ||
{ | ||
public RootScale(RootScaleConfig config, CoreServices coreServices, VisualTree visualTree) | ||
{ | ||
_config = config; | ||
_visualTree = visualTree; | ||
_coreServices = coreServices; | ||
} | ||
|
||
// TODO Uno: Implement | ||
//~RootScale() | ||
//{ | ||
// // It's OK to still have some displayListeners at this point. When a test is shutting down XAML, we'll | ||
// // destroy this object, but we may still have a CLoadedImageSurface object in this list. Since we're shutting | ||
// // down, there won't be new scale changes anyway. | ||
// _displayListeners.Clear(); | ||
// _imageReloadManager.ClearImages(); | ||
//} | ||
|
||
private float GetSystemScale() | ||
{ | ||
float rasterizationScale = 1.0f; | ||
if (_content is not null) | ||
{ | ||
// For CoreWindow scenarios, the CompositionContent is also listening for the CoreWindow's closed event. | ||
// CompositionContent will get the notification first and close the entire visual tree, then Xaml will | ||
// exit its message loop and tear down the tree. Since CompositionContent already closed everything, | ||
// Xaml will get lots of RO_E_CLOSED errors. These are all safe to ignore. So tolerate RO_E_CLOSED if | ||
// we're also in the middle of tearing down the tree. | ||
rasterizationScale = _content.RasterizationScale; | ||
} | ||
|
||
return rasterizationScale; | ||
} | ||
|
||
internal void SetContentIsland(ContentIsland? content) => _content = content; | ||
|
||
private void UpdateSystemScale() | ||
{ | ||
// Remove SuspendFailFastOnStowedException | ||
// Bug 19696972: QueryScaleFactor silently fails at statup | ||
// SuspendFailFastOnStowedException raiiSuspender; | ||
var systemScale = GetSystemScale(); | ||
if (systemScale != 0.0f) | ||
{ | ||
SetSystemScale(systemScale); | ||
} | ||
} | ||
|
||
private float GetEffectiveRasterizationScale() | ||
{ | ||
if (!IsInitialized && !_updating) | ||
{ | ||
UpdateSystemScale(); | ||
} | ||
|
||
// A testOverrideScale of 0 means there's no override; just use the systemScale | ||
float effectiveScale = _testOverrideScale == 0.0f ? _systemScale : _testOverrideScale; | ||
return effectiveScale; | ||
} | ||
|
||
protected float GetRootVisualScale() | ||
{ | ||
// In XamlOneCoreTransforms mode, there is no need to do a RenderTransform on the root, because the scale has already been | ||
// applied for us by the CompositionIsland. However, due to legacy reasons, our DComp tests has a dependency that, even when the scale is 1, | ||
// a RenderTransform is still applied on the root (Identity). To support these tests, we will always apply a scale transform on the root | ||
// in XamlOneCoreTransforms mode. When we've enabled XamlOneCoreTransforms mode by default, we can break this dependency and | ||
// update the tests to not expect an Identity RenderTransform set on the root. | ||
// In OneCoreTransforms mode, there's already a scale applied to XAML visuals matching the systemScale, so we factor that scale | ||
// out on the XAML content. | ||
float newRootVisualScale = 0.0f; | ||
float effectiveScale = GetEffectiveRasterizationScale(); | ||
if (_config == RootScaleConfig.ParentApply) | ||
{ | ||
// This is the case where we're pushing a non-identity scale into the root visual | ||
newRootVisualScale = effectiveScale / _systemScale; | ||
} | ||
else | ||
{ | ||
newRootVisualScale = effectiveScale; | ||
} | ||
return newRootVisualScale; | ||
} | ||
|
||
private void SetTestOverride(float scale) => SetScale(scale, ScaleKind.Test); | ||
|
||
private void SetSystemScale(float scale) => SetScale(scale, ScaleKind.System); | ||
|
||
private void SetScale(float scale, RootScale.ScaleKind kind) | ||
{ | ||
_updating = true; | ||
using var cleanup = Disposable.Create(() => | ||
{ | ||
_updating = false; | ||
}); | ||
|
||
float oldScale = GetEffectiveRasterizationScale(); | ||
bool scaleIsValid = scale != 0.0f; | ||
switch (kind) | ||
{ | ||
case RootScale.ScaleKind.System: | ||
if (scaleIsValid) | ||
{ | ||
_systemScale = scale; | ||
} | ||
break; | ||
case RootScale.ScaleKind.Test: | ||
_testOverrideScale = scale; | ||
break; | ||
} | ||
float newScale = GetEffectiveRasterizationScale(); | ||
bool scaleChanged = !IsCloseReal(oldScale, newScale); | ||
ApplyScale(scaleChanged); | ||
_initialized = true; | ||
} | ||
|
||
private void ApplyScale() => ApplyScale(false); | ||
|
||
private void ApplyScale(bool scaleChanged) | ||
{ | ||
ApplyScaleProtected(scaleChanged); | ||
|
||
if (scaleChanged) | ||
{ | ||
foreach (var displayListener in _displayListeners) | ||
{ | ||
displayListener.OnScaleChanged(); | ||
} | ||
|
||
// TODO Uno: Reload images on scale change! | ||
//if (IsInitialized()) | ||
//{ | ||
// // Reload images. | ||
// m_imageReloadManager.ReloadImages(ResourceInvalidationReason.ScaleChanged); | ||
//} | ||
|
||
VisualTree.ContentRoot.AddPendingXamlRootChangedEvent(ContentRoot.ChangeType.RasterizationScale); | ||
} | ||
} | ||
|
||
private void AddDisplayListener(DisplayListener displayListener) | ||
{ | ||
MUX_ASSERT(!_displayListeners.Contains(displayListener)); | ||
_displayListeners.Add(displayListener); | ||
} | ||
|
||
private void RemoveDisplayListener(DisplayListener displayListener) | ||
{ | ||
MUX_ASSERT(_displayListeners.Count(d => d == displayListener) == 1); | ||
_displayListeners.Remove(displayListener); | ||
} | ||
|
||
//CImageReloadManager& RootScale::GetImageReloadManager() | ||
//{ | ||
// return m_imageReloadManager; | ||
//} | ||
|
||
private RootScale? GetRootScaleForElement(DependencyObject pDO) | ||
{ | ||
if (VisualTree.GetContentRootForElement(pDO) is { } contentRoot) | ||
{ | ||
return GetRootScaleForContentRoot(contentRoot); | ||
} | ||
|
||
return null; | ||
} | ||
|
||
private RootScale? GetRootScaleForContentRoot(ContentRoot contentRoot) | ||
{ | ||
if (contentRoot is not null) | ||
{ | ||
if (contentRoot.VisualTree is { } visualTree) | ||
{ | ||
return visualTree.RootScale; | ||
} | ||
} | ||
|
||
return null; | ||
} | ||
|
||
private RootScale? GetRootScaleForElementWithFallback(DependencyObject? pDO) | ||
{ | ||
RootScale? result = null; | ||
if (pDO is not null) | ||
{ | ||
result = GetRootScaleForElement(pDO); | ||
} | ||
|
||
if (result is null) | ||
{ | ||
var coreServices = CoreServices.Instance; // TODO Uno: This should be DXamlServices::GetHandle() | ||
var contentRootCoordinator = coreServices.ContentRootCoordinator; | ||
if (contentRootCoordinator.CoreWindowContentRoot is { } root) | ||
{ | ||
result = GetRootScaleForContentRoot(root); | ||
} | ||
} | ||
|
||
return result; | ||
} | ||
} |
Oops, something went wrong.