A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

164 lines
6.8 KiB

using System.Diagnostics;
using Avalonia.Media;
using Avalonia.Platform;
namespace Avalonia.Rendering.Composition.Server;
partial class ServerCompositionVisual
{
// Dirty flags, handled by RecomputeOwnProperties
private bool _combinedTransformDirty;
private bool _clipSizeDirty;
private bool _ownBoundsDirty;
private bool _compositionFieldsDirty;
private bool _contentChanged;
private bool _delayPropagateNeedsBoundsUpdate;
private bool _delayPropagateIsDirtyForRender;
private bool _delayPropagateHasExtraDirtyRects;
// Dirty rect, re-render flags, set by PropagateFlags
private bool _needsBoundingBoxUpdate;
private bool _isDirtyForRender;
private bool _isDirtyForRenderInSubgraph;
// Transform that accounts for offset, RenderTransform and other properties of _this_ visual that is
// used to transform to parent's coordinate space
// Updated by RecomputeOwnProperties pass
private Matrix? _ownTransform;
public Matrix? OwnTransform => _ownTransform;
// The bounds of this visual's own content, excluding children
// Coordinate space: local
// Updated by RecomputeOwnProperties pass
private LtrbRect? _ownContentBounds;
// The bounds of this visual and its subtree
// Coordinate space: local
// Updated by: PreSubgraph, PostSubraph (recursive)
private LtrbRect? _subTreeBounds;
public LtrbRect? SubTreeBounds => _subTreeBounds;
// The bounds of this visual and its subtree
// Coordinate space: parent
// Updated by: PostSubgraph
private LtrbRect? _transformedSubTreeBounds;
// Visual's own clip area
// Coordinate space: local
private LtrbRect? _ownClipRect;
private bool _needsToAddExtraDirtyRectToDirtyRegion;
private LtrbRect _extraDirtyRect;
public virtual LtrbRect? ComputeOwnContentBounds() => null;
public Matrix CombinedTransformMatrix { get; private set; } = Matrix.Identity;
// WPF's cheatsheet
//-----------------------------------------------------------------------------
// Node Operation | NeedsToBe | NeedsBBoxUpdate | HasNodeThat | Visit
// | AddedToDirty | (parent chain) | NeedsToBeAdded | child
// | Region | | ToDirtyRegion |
// | | | (parent chain) |
//=============================================================================
// Set transform | Y | Y | Y(N)
// -----------------+---------------+-----------------+-----------------------
// Set opacity | Y | N | Y(N)
// -----------------+---------------+-----------------+-----------------------
// Set clip | Y | Y | Y(N)
// -----------------+---------------+-----------------+-----------------------
// AttachRenderData | Y | Y | Y(N)
// -----------------+---------------+-----------------+-----------------------
// FreeRenderData | Y | Y | Y(N)
// -----------------+---------------+-----------------+-----------------------
// InsertChild | N | Y | Y
// | Y(child) | N | Y(N)
// -----------------+---------------+-----------------+-----------------------
// InsertChildAt | N | Y | Y
// | Y(child) | N | Y(N)
// -----------------+---------------+-----------------+-----------------------
// ZOrderChild | N | N | Y
// | Y(child) | N | Y(N)
// -----------------+---------------+-----------------+-----------------------
// ReplaceChild | Y | Y | Y(N)
// -----------------+---------------+-----------------+-----------------------
// RemoveChild | Y | Y | Y(N)
private void PropagateFlags(bool fNeedsBoundingBoxUpdate, bool fDirtyForRender, bool fAdditionalDirtyRegion = false)
{
Root?.RequestUpdate();
var parent = Parent;
var setIsDirtyForRenderInSubgraph = fAdditionalDirtyRegion || fDirtyForRender;
while (parent != null &&
((fNeedsBoundingBoxUpdate && !parent._needsBoundingBoxUpdate) ||
(setIsDirtyForRenderInSubgraph && !parent._isDirtyForRenderInSubgraph)))
{
parent._needsBoundingBoxUpdate |= fNeedsBoundingBoxUpdate;
parent._isDirtyForRenderInSubgraph |= setIsDirtyForRenderInSubgraph;
parent = parent.Parent;
}
_needsBoundingBoxUpdate |= fNeedsBoundingBoxUpdate;
_isDirtyForRender |= fDirtyForRender;
// If node itself is dirty for render, we don't need to keep track of extra dirty rects
_needsToAddExtraDirtyRectToDirtyRegion = !fDirtyForRender && (_needsToAddExtraDirtyRectToDirtyRegion || fAdditionalDirtyRegion);
}
public void RecomputeOwnProperties()
{
var setDirtyBounds = _contentChanged || _delayPropagateNeedsBoundsUpdate;
var setDirtyForRender = _contentChanged || _delayPropagateIsDirtyForRender;
var setHasExtraDirtyRect = _delayPropagateHasExtraDirtyRects;
_delayPropagateIsDirtyForRender =
_delayPropagateHasExtraDirtyRects =
_delayPropagateIsDirtyForRender = false;
_enqueuedForOwnPropertiesRecompute = false;
if (_ownBoundsDirty)
{
_ownContentBounds = ComputeOwnContentBounds()?.NullIfZeroSize();
setDirtyForRender = setDirtyBounds = true;
}
if (_clipSizeDirty)
{
LtrbRect? clip = null;
if (Clip != null)
clip = new(Clip.Bounds);
if (ClipToBounds)
{
var bounds = new LtrbRect(0, 0, Size.X, Size.Y);
clip = clip?.IntersectOrEmpty(bounds) ?? bounds;
}
if (_ownClipRect != clip)
{
_ownClipRect = clip;
setDirtyForRender = setDirtyBounds = true;
}
}
if (_combinedTransformDirty)
{
_ownTransform = MatrixUtils.ComputeTransform(Size, AnchorPoint, CenterPoint, TransformMatrix, Scale,
RotationAngle, Orientation, Offset);
setDirtyForRender = setDirtyBounds = true;
ActHelper_CombinedTransformChanged();
}
setDirtyForRender |= _compositionFieldsDirty;
_ownBoundsDirty = _clipSizeDirty = _combinedTransformDirty = _compositionFieldsDirty = false;
PropagateFlags(setDirtyBounds, setDirtyForRender, setHasExtraDirtyRect);
}
}