Browse Source

Merge branch 'master' into feature/windowstate-fullscreen

pull/3849/head
danwalmsley 7 years ago
committed by GitHub
parent
commit
acf18cf134
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 46
      src/Avalonia.Controls/Presenters/TextPresenter.cs
  2. 6
      src/Avalonia.Visuals/Media/FormattedText.cs
  3. 26
      src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
  4. 9
      src/Avalonia.Visuals/Rendering/RendererBase.cs
  5. 12
      src/Skia/Avalonia.Skia/FormattedTextImpl.cs
  6. 2
      tests/Avalonia.RenderTests/TestBase.cs

46
src/Avalonia.Controls/Presenters/TextPresenter.cs

@ -74,16 +74,15 @@ namespace Avalonia.Controls.Presenters
static TextPresenter()
{
AffectsRender<TextPresenter>(PasswordCharProperty,
SelectionBrushProperty, SelectionForegroundBrushProperty,
SelectionStartProperty, SelectionEndProperty);
AffectsRender<TextPresenter>(SelectionBrushProperty);
Observable.Merge(
TextProperty.Changed,
SelectionStartProperty.Changed,
SelectionEndProperty.Changed,
PasswordCharProperty.Changed
).AddClassHandler<TextPresenter>((x,_) => x.InvalidateFormattedText());
Observable.Merge(TextProperty.Changed, TextBlock.ForegroundProperty.Changed,
TextAlignmentProperty.Changed, TextWrappingProperty.Changed,
TextBlock.FontSizeProperty.Changed, TextBlock.FontStyleProperty.Changed,
TextBlock.FontWeightProperty.Changed, TextBlock.FontFamilyProperty.Changed,
SelectionStartProperty.Changed, SelectionEndProperty.Changed,
SelectionForegroundBrushProperty.Changed, PasswordCharProperty.Changed
).AddClassHandler<TextPresenter>((x, _) => x.InvalidateFormattedText());
CaretIndexProperty.Changed.AddClassHandler<TextPresenter>((x, e) => x.CaretIndexChanged((int)e.NewValue));
}
@ -184,7 +183,7 @@ namespace Avalonia.Controls.Presenters
{
get
{
return _formattedText ?? (_formattedText = CreateFormattedText(Bounds.Size, Text));
return _formattedText ?? (_formattedText = CreateFormattedText());
}
}
@ -219,7 +218,7 @@ namespace Avalonia.Controls.Presenters
get => GetValue(SelectionForegroundBrushProperty);
set => SetValue(SelectionForegroundBrushProperty, value);
}
public IBrush CaretBrush
{
get => GetValue(CaretBrushProperty);
@ -284,13 +283,9 @@ namespace Avalonia.Controls.Presenters
/// </summary>
protected void InvalidateFormattedText()
{
if (_formattedText != null)
{
_constraint = _formattedText.Constraint;
_formattedText = null;
}
_formattedText = null;
InvalidateVisual();
InvalidateMeasure();
}
/// <summary>
@ -307,6 +302,7 @@ namespace Avalonia.Controls.Presenters
}
FormattedText.Constraint = Bounds.Size;
context.DrawText(Foreground, new Point(), FormattedText);
}
@ -424,20 +420,20 @@ namespace Avalonia.Controls.Presenters
/// <summary>
/// Creates the <see cref="FormattedText"/> used to render the text.
/// </summary>
/// <param name="constraint">The constraint of the text.</param>
/// <param name="text">The text to generated the <see cref="FormattedText"/> for.</param>
/// <returns>A <see cref="FormattedText"/> object.</returns>
protected virtual FormattedText CreateFormattedText(Size constraint, string text)
protected virtual FormattedText CreateFormattedText()
{
FormattedText result = null;
var text = Text;
if (PasswordChar != default(char))
{
result = CreateFormattedTextInternal(constraint, new string(PasswordChar, text?.Length ?? 0));
result = CreateFormattedTextInternal(_constraint, new string(PasswordChar, text?.Length ?? 0));
}
else
{
result = CreateFormattedTextInternal(constraint, text);
result = CreateFormattedTextInternal(_constraint, text);
}
var selectionStart = SelectionStart;
@ -467,13 +463,15 @@ namespace Avalonia.Controls.Presenters
{
if (TextWrapping == TextWrapping.Wrap)
{
FormattedText.Constraint = new Size(availableSize.Width, double.PositiveInfinity);
_constraint = new Size(availableSize.Width, double.PositiveInfinity);
}
else
{
FormattedText.Constraint = Size.Infinity;
_constraint = Size.Infinity;
}
_formattedText = null;
return FormattedText.Bounds.Size;
}

6
src/Avalonia.Visuals/Media/FormattedText.cs

@ -200,7 +200,13 @@ namespace Avalonia.Media
private void Set<T>(ref T field, T value)
{
if (field != null && field.Equals(value))
{
return;
}
field = value;
_platformImpl = null;
}
}

26
src/Avalonia.Visuals/Rendering/DeferredRenderer.cs

@ -35,6 +35,7 @@ namespace Avalonia.Rendering
private IRef<IDrawOperation> _currentDraw;
private readonly IDeferredRendererLock _lock;
private readonly object _sceneLock = new object();
private readonly Action _updateSceneIfNeededDelegate;
/// <summary>
/// Initializes a new instance of the <see cref="DeferredRenderer"/> class.
@ -49,7 +50,7 @@ namespace Avalonia.Rendering
IRenderLoop renderLoop,
ISceneBuilder sceneBuilder = null,
IDispatcher dispatcher = null,
IDeferredRendererLock rendererLock = null)
IDeferredRendererLock rendererLock = null) : base(true)
{
Contract.Requires<ArgumentNullException>(root != null);
@ -59,6 +60,7 @@ namespace Avalonia.Rendering
Layers = new RenderLayers();
_renderLoop = renderLoop;
_lock = rendererLock ?? new ManagedDeferredRendererLock();
_updateSceneIfNeededDelegate = UpdateSceneIfNeeded;
}
/// <summary>
@ -73,7 +75,7 @@ namespace Avalonia.Rendering
public DeferredRenderer(
IVisual root,
IRenderTarget renderTarget,
ISceneBuilder sceneBuilder = null)
ISceneBuilder sceneBuilder = null) : base(true)
{
Contract.Requires<ArgumentNullException>(root != null);
Contract.Requires<ArgumentNullException>(renderTarget != null);
@ -83,6 +85,7 @@ namespace Avalonia.Rendering
_sceneBuilder = sceneBuilder ?? new SceneBuilder();
Layers = new RenderLayers();
_lock = new ManagedDeferredRendererLock();
_updateSceneIfNeededDelegate = UpdateSceneIfNeeded;
}
/// <inheritdoc/>
@ -261,7 +264,8 @@ namespace Avalonia.Rendering
try
{
var (scene, updated) = UpdateRenderLayersAndConsumeSceneIfNeeded(ref context);
if (updated)
FpsTick();
using (scene)
{
if (scene?.Item != null)
@ -318,17 +322,25 @@ namespace Avalonia.Rendering
_lastSceneId = scene.Generation;
var isUiThread = Dispatcher.UIThread.CheckAccess();
// We have consumed the previously available scene, but there might be some dirty
// rects since the last update. *If* we are on UI thread, we can force immediate scene
// rebuild before rendering anything on-screen
// We are calling the same method recursively here
if (!recursiveCall && Dispatcher.UIThread.CheckAccess() && NeedsUpdate)
if (!recursiveCall && isUiThread && NeedsUpdate)
{
UpdateScene();
var (rs, _) = UpdateRenderLayersAndConsumeSceneIfNeeded(ref context, true);
return (rs, true);
}
// We are rendering a new scene version, so it's highly likely
// that there is already a pending update for animations
// So we are scheduling an update call so UI thread could prepare a scene before
// the next render timer tick
if (!recursiveCall && !isUiThread)
Dispatcher.UIThread.Post(_updateSceneIfNeededDelegate, DispatcherPriority.Render);
// Indicate that we have updated the layers
return (sceneRef.Clone(), true);
}
@ -534,6 +546,12 @@ namespace Avalonia.Rendering
context = RenderTarget.CreateDrawingContext(this);
}
private void UpdateSceneIfNeeded()
{
if(NeedsUpdate)
UpdateScene();
}
private void UpdateScene()
{
Dispatcher.UIThread.VerifyAccess();

9
src/Avalonia.Visuals/Rendering/RendererBase.cs

@ -7,6 +7,7 @@ namespace Avalonia.Rendering
{
public class RendererBase
{
private readonly bool _useManualFpsCounting;
private static int s_fontSize = 18;
private readonly Stopwatch _stopwatch = Stopwatch.StartNew();
private int _framesThisSecond;
@ -14,8 +15,9 @@ namespace Avalonia.Rendering
private FormattedText _fpsText;
private TimeSpan _lastFpsUpdate;
public RendererBase()
public RendererBase(bool useManualFpsCounting = false)
{
_useManualFpsCounting = useManualFpsCounting;
_fpsText = new FormattedText
{
Typeface = FontManager.Current?.GetOrAddTypeface(FontFamily.Default),
@ -23,12 +25,15 @@ namespace Avalonia.Rendering
};
}
protected void FpsTick() => _framesThisSecond++;
protected void RenderFps(IDrawingContextImpl context, Rect clientRect, int? layerCount)
{
var now = _stopwatch.Elapsed;
var elapsed = now - _lastFpsUpdate;
++_framesThisSecond;
if (!_useManualFpsCounting)
++_framesThisSecond;
if (elapsed.TotalSeconds > 1)
{

12
src/Skia/Avalonia.Skia/FormattedTextImpl.cs

@ -149,7 +149,17 @@ namespace Avalonia.Skia
if (index >= Text.Length || index < 0)
{
var r = rects.LastOrDefault();
return new Rect(r.X + r.Width, r.Y, 0, _lineHeight);
var c = Text[Text.Length - 1];
switch (c)
{
case '\n':
case '\r':
return new Rect(r.X, r.Y, 0, _lineHeight);
default:
return new Rect(r.X + r.Width, r.Y, 0, _lineHeight);
}
}
return rects[index];
}

2
tests/Avalonia.RenderTests/TestBase.cs

@ -184,7 +184,7 @@ namespace Avalonia.Direct2D1.RenderTests
public void Signal(DispatcherPriority prio)
{
throw new NotImplementedException();
// No-op
}
public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick)

Loading…
Cancel
Save