Browse Source

Merge branch 'master' into clipboard-image

pull/12246/head
viordash 3 years ago
committed by GitHub
parent
commit
b9a357a5ae
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      build/ExternalConsumers.props
  2. 8
      native/Avalonia.Native/src/OSX/WindowBaseImpl.mm
  3. 31
      samples/ControlCatalog/Models/GDPdLengthConverter.cs
  4. 21
      samples/ControlCatalog/Pages/DataGridPage.xaml
  5. 69
      src/Android/Avalonia.Android/AndroidInputMethod.cs
  6. 108
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  7. 4
      src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
  8. 122
      src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs
  9. 165
      src/Avalonia.Base/Visual.Composition.cs
  10. 28
      src/Avalonia.Base/Visual.cs
  11. 16
      src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
  12. 159
      src/Avalonia.Controls.DataGrid/DataGridColumn.cs
  13. 4
      src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs
  14. 17
      src/Avalonia.Controls.DataGrid/DataGridColumns.cs
  15. 1
      src/Avalonia.Controls.DataGrid/DataGridLength.cs
  16. 5
      src/Avalonia.Controls.DataGrid/DataGridRows.cs
  17. 20
      src/Avalonia.X11/X11Info.cs
  18. 10
      src/Avalonia.X11/X11Platform.cs
  19. 15
      src/Avalonia.X11/X11Window.Ime.cs
  20. 2
      src/Avalonia.X11/XLib.cs
  21. 2
      src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs
  22. 10
      src/Windows/Avalonia.Win32/WindowImpl.cs
  23. 1
      src/tools/Avalonia.Analyzers/Avalonia.Analyzers.csproj
  24. 1
      src/tools/Avalonia.Generators/Avalonia.Generators.csproj
  25. 6
      src/tools/Avalonia.Generators/Common/XamlXNameResolver.cs
  26. 3
      tests/Avalonia.Generators.Tests/Views/NamedControl.xml
  27. 3
      tests/Avalonia.Generators.Tests/Views/xNamedControl.xml
  28. 20
      tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs

1
build/ExternalConsumers.props

@ -1,6 +1,7 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<InternalsVisibleTo Include="AvaloniaUI.Xpf.WinApiShim, PublicKey=0024000004800000940000000602000000240000525341310004000001000100a7f4b8b7db0bfb8d74992dc94ecafae031019197ff4263d87ac0a5835fab101c973ccab6fa6e7d90e8f987374f7c6de18dd0b5cd7d6c41e574a8bc66b64836b7c7e707e1aa393d27e33a08f372c1c9965be81658937c85698f4a1c0f73be68a61ffce06d49d1366bf18464c20a29859ccf105fc2d5e35c7ae68919eab668bf8e" />
<InternalsVisibleTo Include="AvaloniaUI.Xpf.WpfAbstractions, PublicKey=0024000004800000940000000602000000240000525341310004000001000100a7f4b8b7db0bfb8d74992dc94ecafae031019197ff4263d87ac0a5835fab101c973ccab6fa6e7d90e8f987374f7c6de18dd0b5cd7d6c41e574a8bc66b64836b7c7e707e1aa393d27e33a08f372c1c9965be81658937c85698f4a1c0f73be68a61ffce06d49d1366bf18464c20a29859ccf105fc2d5e35c7ae68919eab668bf8e" />
<InternalsVisibleTo Include="System.Windows.Forms, PublicKey=00000000000000000400000000000000" />
<InternalsVisibleTo Include="System.Windows.Forms.Primitives, PublicKey=00000000000000000400000000000000" />
<InternalsVisibleTo Include="PresentationFramework-SystemData, PublicKey=00000000000000000400000000000000" />

8
native/Avalonia.Native/src/OSX/WindowBaseImpl.mm

@ -151,6 +151,14 @@ HRESULT WindowBaseImpl::Hide() {
@autoreleasepool {
if (Window != nullptr) {
auto frame = [Window frame];
AvnPoint point;
point.X = frame.origin.x;
point.Y = frame.origin.y + frame.size.height;
lastPositionSet = ConvertPointY(point);
hasPosition = true;
[Window orderOut:Window];
}

31
samples/ControlCatalog/Models/GDPdLengthConverter.cs

@ -0,0 +1,31 @@
using System;
using System.Globalization;
using Avalonia.Data.Converters;
namespace ControlCatalog.Models;
internal class GDPdLengthConverter : IValueConverter
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is double d)
{
return new Avalonia.Controls.DataGridLength(d,Avalonia.Controls.DataGridLengthUnitType.Pixel,d,d);
}
else if (value is decimal d2)
{
var dv =System.Convert.ToDouble(d2);
return new Avalonia.Controls.DataGridLength(dv, Avalonia.Controls.DataGridLengthUnitType.Pixel, dv, dv);
}
return value;
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is Avalonia.Controls.DataGridLength width)
{
return System.Convert.ToDecimal(width.DisplayValue);
}
return value;
}
}

21
samples/ControlCatalog/Pages/DataGridPage.xaml

@ -1,11 +1,14 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:local="using:ControlCatalog.Models"
xmlns:lc="using:ControlCatalog.Converter"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:pages="clr-namespace:ControlCatalog.Pages"
x:Class="ControlCatalog.Pages.DataGridPage"
x:DataType="pages:DataGridPage">
<UserControl.Resources>
<local:GDPValueConverter x:Key="GDPConverter" />
<local:GDPdLengthConverter x:Key="GDPWidthConverter"/>
<DataTemplate x:Key="Demo.DataTemplates.CountryHeader" x:DataType="local:Country">
<StackPanel Orientation="Horizontal" Spacing="5">
<PathIcon Height="12" Data="M 255 116 A 1 1 0 0 0 254 117 L 254 130 A 1 1 0 0 0 255 131 A 1 1 0 0 0 256 130 L 256 123.87109 C 256.1125 123.90694 256.2187 123.94195 256.33984 123.97852 C 257.18636 124.23404 258.19155 124.5 259 124.5 C 259.80845 124.5 260.52133 124.2168 261.17773 123.9668 C 261.83414 123.7168 262.43408 123.5 263 123.5 C 263.56592 123.5 264.5612 123.73404 265.37109 123.97852 C 266.18098 124.22299 266.82227 124.4668 266.82227 124.4668 A 0.50005 0.50005 0 0 0 267.5 124 L 267.5 118 A 0.50005 0.50005 0 0 0 267.17773 117.5332 C 267.17773 117.5332 266.50667 117.27701 265.66016 117.02148 C 264.81364 116.76596 263.80845 116.5 263 116.5 C 262.19155 116.5 261.47867 116.7832 260.82227 117.0332 C 260.16586 117.2832 259.56592 117.5 259 117.5 C 258.43408 117.5 257.4388 117.26596 256.62891 117.02148 C 256.39123 116.94974 256.17716 116.87994 255.98047 116.81445 A 1 1 0 0 0 255 116 z M 263 117.5 C 263.56592 117.5 264.5612 117.73404 265.37109 117.97852 C 266.00097 118.16865 266.29646 118.28239 266.5 118.35742 L 266.5 120.29297 C 266.25708 120.21012 265.97978 120.11797 265.66016 120.02148 C 264.81364 119.76596 263.80845 119.5 263 119.5 C 262.19155 119.5 261.47867 119.7832 260.82227 120.0332 C 260.16586 120.2832 259.56592 120.5 259 120.5 C 258.43408 120.5 257.4388 120.26596 256.62891 120.02148 C 256.39971 119.9523 256.19148 119.88388 256 119.82031 L 256 117.87109 C 256.1125 117.90694 256.2187 117.94195 256.33984 117.97852 C 257.18636 118.23404 258.19155 118.5 259 118.5 C 259.80845 118.5 260.52133 118.2168 261.17773 117.9668 C 261.83414 117.7168 262.43408 117.5 263 117.5 z M 263 120.5 C 263.56592 120.5 264.5612 120.73404 265.37109 120.97852 C 265.8714 121.12954 266.2398 121.25641 266.5 121.34961 L 266.5 123.30469 C 266.22286 123.20649 266.12863 123.1629 265.66016 123.02148 C 264.81364 122.76596 263.80845 122.5 263 122.5 C 262.19155 122.5 261.47867 122.7832 260.82227 123.0332 C 260.16586 123.2832 259.56592 123.5 259 123.5 C 258.43408 123.5 257.4388 123.26596 256.62891 123.02148 C 256.39971 122.9523 256.19148 122.88388 256 122.82031 L 256 120.87109 C 256.1125 120.90694 256.2187 120.94195 256.33984 120.97852 C 257.18636 121.23404 258.19155 121.5 259 121.5 C 259.80845 121.5 260.52133 121.2168 261.17773 120.9668 C 261.83414 120.7168 262.43408 120.5 263 120.5 z" />
@ -28,8 +31,18 @@
<TabControl Grid.Row="2">
<TabItem Header="DataGrid">
<DockPanel>
<CheckBox x:Name="ShowGDP" IsChecked="True" Content="Toggle GDP Column Visibility"
DockPanel.Dock="Top"/>
<StackPanel Orientation="Horizontal"
DockPanel.Dock="Top"
Spacing="5">
<CheckBox x:Name="ShowGDP" IsChecked="True" Content="Toggle GDP Column Visibility"/>
<TextBlock Text="GDP Width:" VerticalAlignment="Center"/>
<NumericUpDown x:Name="GDPWidth"
Minimum="200"
Maximum="350"
Width="200"
Increment="10"
Value="200"/>
</StackPanel>
<DataGrid Name="dataGrid1" Margin="12" CanUserResizeColumns="True" CanUserReorderColumns="True" CanUserSortColumns="True" HeadersVisibility="All"
RowBackground="#1000">
<DataGrid.Columns>
@ -38,9 +51,11 @@
<DataGridTextColumn Header="Region" Binding="{Binding Region}" Width="4*" x:DataType="local:Country" />
<DataGridTextColumn Header="Population" Binding="{Binding Population}" Width="3*" x:DataType="local:Country" />
<DataGridTextColumn Header="Area" Binding="{Binding Area}" Width="3*" x:DataType="local:Country" />
<DataGridTextColumn Header="GDP" Binding="{Binding GDP}" Width="3*"
<DataGridTextColumn Header="GDP" Binding="{Binding GDP}"
Width="{Binding #GDPWidth.Value, Mode=TwoWay, Converter={StaticResource GDPWidthConverter}}"
CellTheme="{StaticResource GdpCell}"
MinWidth="200"
MaxWidth="350"
IsVisible="{Binding #ShowGDP.IsChecked}"
x:DataType="local:Country" />
</DataGrid.Columns>

69
src/Android/Avalonia.Android/AndroidInputMethod.cs

@ -18,6 +18,8 @@ namespace Avalonia.Android
public bool IsActive { get; }
public InputMethodManager IMM { get; }
void OnBatchEditedEnded();
}
enum CustomImeFlags
@ -103,6 +105,13 @@ namespace Avalonia.Android
}
private void _client_SelectionChanged(object sender, EventArgs e)
{
if (_inputConnection.IsInBatchEdit)
return;
OnSelectionChanged();
}
private void OnSelectionChanged()
{
var selection = Client.Selection;
@ -113,17 +122,67 @@ namespace Avalonia.Android
private void _client_SurroundingTextChanged(object sender, EventArgs e)
{
if (_inputConnection.IsInBatchEdit)
return;
OnSurroundingTextChanged();
}
public void OnBatchEditedEnded()
{
if (_inputConnection.IsInBatchEdit)
return;
OnSurroundingTextChanged();
OnSelectionChanged();
}
private void OnSurroundingTextChanged()
{
if(_client is null)
{
return;
}
var surroundingText = _client.SurroundingText ?? "";
var editableText = _inputConnection.EditableWrapper.ToString();
_inputConnection.EditableWrapper.IgnoreChange = true;
if (editableText != surroundingText)
{
_inputConnection.EditableWrapper.IgnoreChange = true;
_inputConnection.Editable.Replace(0, _inputConnection.Editable.Length(), surroundingText);
var diff = GetDiff();
_inputConnection.EditableWrapper.IgnoreChange = false;
_inputConnection.Editable.Replace(diff.index, editableText.Length, diff.diff);
var selection = Client.Selection;
_inputConnection.EditableWrapper.IgnoreChange = false;
_imm.UpdateSelection(_host, selection.Start, selection.End, selection.Start, selection.End);
if(diff.index == 0)
{
var selection = _client.Selection;
_client.Selection = new TextSelection(selection.Start, 0);
_client.Selection = selection;
}
}
(int index, string diff) GetDiff()
{
int index = 0;
var longerLength = Math.Max(surroundingText.Length, editableText.Length);
for (int i = 0; i < longerLength; i++)
{
if (surroundingText.Length == i || editableText.Length == i || surroundingText[i] != editableText[i])
{
index = i;
break;
}
}
var diffString = surroundingText.Substring(index, surroundingText.Length - index);
return (index, diffString);
}
}
public void SetCursorRect(Rect rect)

108
src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Runtime.Versioning;
using System.Threading;
using Android.App;
using Android.Content;
using Android.Graphics;
@ -447,19 +448,22 @@ namespace Avalonia.Android.Platform.SkiaPlatform
{
private readonly AvaloniaInputConnection _inputConnection;
public event EventHandler<int> SelectionChanged;
public EditableWrapper(AvaloniaInputConnection inputConnection)
{
_inputConnection = inputConnection;
}
public TextSelection CurrentSelection => new TextSelection(Selection.GetSelectionStart(this), Selection.GetSelectionEnd(this));
public TextSelection CurrentComposition => new TextSelection(BaseInputConnection.GetComposingSpanStart(this), BaseInputConnection.GetComposingSpanEnd(this));
public bool IgnoreChange { get; set; }
public override IEditable Replace(int start, int end, ICharSequence tb)
{
if (!IgnoreChange && start != end)
{
var text = tb.SubSequence(0, tb.Length());
SelectSurroundingTextForDeletion(start, end);
}
@ -470,8 +474,6 @@ namespace Avalonia.Android.Platform.SkiaPlatform
{
if (!IgnoreChange && start != end)
{
var text = tb.SubSequence(tbstart, tbend);
SelectSurroundingTextForDeletion(start, end);
}
@ -490,8 +492,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
private readonly IAndroidInputMethod _inputMethod;
private readonly EditableWrapper _editable;
private bool _commitInProgress;
private (int Start, int End)? _composingRegion;
private TextSelection _selection;
private int _batchLevel = 0;
public AvaloniaInputConnection(TopLevelImpl toplevel, IAndroidInputMethod inputMethod) : base(inputMethod.View, true)
{
@ -510,63 +511,87 @@ namespace Avalonia.Android.Platform.SkiaPlatform
public TopLevelImpl Toplevel => _toplevel;
public bool IsInBatchEdit => _batchLevel > 0;
public override bool SetComposingRegion(int start, int end)
{
_composingRegion = new(start, end);
return base.SetComposingRegion(start, end);
}
public override bool SetComposingText(ICharSequence text, int newCursorPosition)
{
if(_composingRegion != null)
BeginBatchEdit();
_editable.IgnoreChange = true;
try
{
// Select the composing region.
InputMethod.Client.Selection = new TextSelection(_composingRegion.Value.Start, _composingRegion.Value.End);
}
var compositionText = text.SubSequence(0, text.Length());
if (_editable.CurrentComposition.Start > -1)
{
// Select the composing region.
InputMethod.Client.Selection = new TextSelection(_editable.CurrentComposition.Start, _editable.CurrentComposition.End);
}
var compositionText = text.SubSequence(0, text.Length());
if (_inputMethod.IsActive && !_commitInProgress)
if (_inputMethod.IsActive && !_commitInProgress)
{
if (string.IsNullOrEmpty(compositionText))
_inputMethod.View.DispatchKeyEvent(new KeyEvent(KeyEventActions.Down, Keycode.ForwardDel));
else
_toplevel.TextInput(compositionText);
}
base.SetComposingText(text, newCursorPosition);
}
finally
{
if (string.IsNullOrEmpty(compositionText))
_inputMethod.View.DispatchKeyEvent(new KeyEvent(KeyEventActions.Down, Keycode.ForwardDel));
_editable.IgnoreChange = false;
else
_toplevel.TextInput(compositionText);
EndBatchEdit();
}
return true;
}
public override bool BeginBatchEdit()
{
_batchLevel = Interlocked.Increment(ref _batchLevel);
return base.BeginBatchEdit();
}
public override bool EndBatchEdit()
{
_batchLevel = Interlocked.Decrement(ref _batchLevel);
_inputMethod.OnBatchEditedEnded();
return base.EndBatchEdit();
}
public override bool CommitText(ICharSequence text, int newCursorPosition)
{
BeginBatchEdit();
_commitInProgress = true;
var ret = base.CommitText(text, newCursorPosition);
var composingRegion = _editable.CurrentComposition;
var committedText = text.SubSequence(0, text.Length());
var ret = base.CommitText(text, newCursorPosition);
if (_inputMethod.IsActive && !string.IsNullOrEmpty(committedText))
if(composingRegion.Start != -1)
{
if(_composingRegion != null)
{
_inputMethod.Client.Selection = new TextSelection(_composingRegion.Value.Start, _composingRegion.Value.End);
}
InputMethod.Client.Selection = composingRegion;
}
_toplevel.TextInput(committedText);
var committedText = text.SubSequence(0, text.Length());
_composingRegion = null;
}
if (_inputMethod.IsActive)
if (string.IsNullOrEmpty(committedText))
_inputMethod.View.DispatchKeyEvent(new KeyEvent(KeyEventActions.Down, Keycode.ForwardDel));
else
_toplevel.TextInput(committedText);
_commitInProgress = false;
EndBatchEdit();
return ret;
}
public override bool FinishComposingText()
{
_composingRegion = null;
return base.FinishComposingText();
return true;
}
public override bool DeleteSurroundingText(int beforeLength, int afterLength)
@ -575,11 +600,10 @@ namespace Avalonia.Android.Platform.SkiaPlatform
{
EditableWrapper.IgnoreChange = true;
}
var result = base.DeleteSurroundingText(beforeLength, afterLength);
if (InputMethod.IsActive)
{
var selection = _selection;
var selection = _editable.CurrentSelection;
InputMethod.Client.Selection = new TextSelection(selection.Start - beforeLength, selection.End + afterLength);
@ -588,13 +612,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
EditableWrapper.IgnoreChange = true;
}
return result;
}
public override bool SetSelection(int start, int end)
{
_selection = new TextSelection(start, end);
return base.SetSelection(start, end);
return true;
}
public override bool PerformEditorAction([GeneratedEnum] ImeAction actionCode)
@ -630,7 +648,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
return null;
}
var selection = _selection;
var selection = _editable.CurrentSelection;
ExtractedText extract = new ExtractedText
{

4
src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs

@ -204,7 +204,7 @@ namespace Avalonia.Media.TextFormatting
if (_paragraphProperties.FlowDirection == FlowDirection.RightToLeft)
{
currentPosition = Length - firstRun.Length;
currentPosition += lineLength - firstRun.Length;
}
return GetRunCharacterHit(firstRun, currentPosition, 0);
@ -216,7 +216,7 @@ namespace Avalonia.Media.TextFormatting
if (_paragraphProperties.FlowDirection == FlowDirection.LeftToRight)
{
currentPosition = lineLength - lastRun.Length;
currentPosition += lineLength - lastRun.Length;
}
return GetRunCharacterHit(lastRun, currentPosition, distance);

122
src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs

@ -142,93 +142,6 @@ internal class CompositingRenderer : IRendererWithCompositor, IHitTester
QueueUpdate();
}
private static void SyncChildren(Visual v)
{
//TODO: Optimize by moving that logic to Visual itself
if(v.CompositionVisual == null)
return;
var compositionChildren = v.CompositionVisual.Children;
var visualChildren = (AvaloniaList<Visual>)v.GetVisualChildren();
PooledList<(Visual visual, int index)>? sortedChildren = null;
if (v.HasNonUniformZIndexChildren && visualChildren.Count > 1)
{
sortedChildren = new (visualChildren.Count);
for (var c = 0; c < visualChildren.Count; c++)
sortedChildren.Add((visualChildren[c], c));
// Regular Array.Sort is unstable, we need to provide indices as well to avoid reshuffling elements.
sortedChildren.Sort(static (lhs, rhs) =>
{
var result = lhs.visual.ZIndex.CompareTo(rhs.visual.ZIndex);
return result == 0 ? lhs.index.CompareTo(rhs.index) : result;
});
}
var childVisual = v.ChildCompositionVisual;
// Check if the current visual somehow got migrated to another compositor
if (childVisual != null && childVisual.Compositor != v.CompositionVisual.Compositor)
childVisual = null;
var expectedCount = visualChildren.Count;
if (childVisual != null)
expectedCount++;
if (compositionChildren.Count == expectedCount)
{
bool mismatch = false;
if (sortedChildren != null)
for (var c = 0; c < visualChildren.Count; c++)
{
if (!ReferenceEquals(compositionChildren[c], sortedChildren[c].visual.CompositionVisual))
{
mismatch = true;
break;
}
}
else
for (var c = 0; c < visualChildren.Count; c++)
if (!ReferenceEquals(compositionChildren[c], visualChildren[c].CompositionVisual))
{
mismatch = true;
break;
}
if (childVisual != null &&
!ReferenceEquals(compositionChildren[compositionChildren.Count - 1], childVisual))
mismatch = true;
if (!mismatch)
{
sortedChildren?.Dispose();
return;
}
}
compositionChildren.Clear();
if (sortedChildren != null)
{
foreach (var ch in sortedChildren)
{
var compositionChild = ch.visual.CompositionVisual;
if (compositionChild != null)
compositionChildren.Add(compositionChild);
}
sortedChildren.Dispose();
}
else
foreach (var ch in visualChildren)
{
var compositionChild = ch.CompositionVisual;
if (compositionChild != null)
compositionChildren.Add(compositionChild);
}
if (childVisual != null)
compositionChildren.Add(childVisual);
}
private void UpdateCore()
{
_queuedUpdate = false;
@ -238,36 +151,7 @@ internal class CompositingRenderer : IRendererWithCompositor, IHitTester
if(comp == null)
continue;
// TODO: Optimize all of that by moving to the Visual itself, so we won't have to recalculate every time
comp.Offset = new (visual.Bounds.Left, visual.Bounds.Top, 0);
comp.Size = new (visual.Bounds.Width, visual.Bounds.Height);
comp.Visible = visual.IsVisible;
comp.Opacity = (float)visual.Opacity;
comp.ClipToBounds = visual.ClipToBounds;
comp.Clip = visual.Clip?.PlatformImpl;
if (!Equals(comp.OpacityMask, visual.OpacityMask))
comp.OpacityMask = visual.OpacityMask?.ToImmutable();
if (!comp.Effect.EffectEquals(visual.Effect))
comp.Effect = visual.Effect?.ToImmutable();
comp.RenderOptions = visual.RenderOptions;
var renderTransform = Matrix.Identity;
if (visual.HasMirrorTransform)
renderTransform = new Matrix(-1.0, 0.0, 0.0, 1.0, visual.Bounds.Width, 0);
if (visual.RenderTransform != null)
{
var origin = visual.RenderTransformOrigin.ToPixels(new Size(visual.Bounds.Width, visual.Bounds.Height));
var offset = Matrix.CreateTranslation(origin);
renderTransform *= (-offset) * visual.RenderTransform.Value * (offset);
}
comp.TransformMatrix = renderTransform;
visual.SynchronizeCompositionProperties();
try
{
@ -279,11 +163,11 @@ internal class CompositingRenderer : IRendererWithCompositor, IHitTester
_recorder.Reset();
}
SyncChildren(visual);
visual.SynchronizeCompositionChildVisuals();
}
foreach(var v in _recalculateChildren)
if (!_dirty.Contains(v))
SyncChildren(v);
v.SynchronizeCompositionChildVisuals();
_dirty.Clear();
_recalculateChildren.Clear();
CompositionTarget.Size = _root.ClientSize;

165
src/Avalonia.Base/Visual.Composition.cs

@ -0,0 +1,165 @@
using Avalonia.Collections;
using Avalonia.Collections.Pooled;
using Avalonia.Media;
using Avalonia.Rendering.Composition;
using Avalonia.Rendering.Composition.Server;
using Avalonia.VisualTree;
namespace Avalonia;
public partial class Visual
{
internal CompositionDrawListVisual? CompositionVisual { get; private set; }
internal CompositionVisual? ChildCompositionVisual { get; set; }
private protected virtual CompositionDrawListVisual CreateCompositionVisual(Compositor compositor)
=> new CompositionDrawListVisual(compositor,
new ServerCompositionDrawListVisual(compositor.Server, this), this);
internal CompositionVisual AttachToCompositor(Compositor compositor)
{
if (CompositionVisual == null || CompositionVisual.Compositor != compositor)
{
CompositionVisual = CreateCompositionVisual(compositor);
}
return CompositionVisual;
}
internal virtual void DetachFromCompositor()
{
if (CompositionVisual != null)
{
if (ChildCompositionVisual != null)
CompositionVisual.Children.Remove(ChildCompositionVisual);
CompositionVisual.DrawList = null;
CompositionVisual = null;
}
}
internal virtual void SynchronizeCompositionChildVisuals()
{
if(CompositionVisual == null)
return;
var compositionChildren = CompositionVisual.Children;
var visualChildren = (AvaloniaList<Visual>)VisualChildren;
PooledList<(Visual visual, int index)>? sortedChildren = null;
if (HasNonUniformZIndexChildren && visualChildren.Count > 1)
{
sortedChildren = new (visualChildren.Count);
for (var c = 0; c < visualChildren.Count; c++)
sortedChildren.Add((visualChildren[c], c));
// Regular Array.Sort is unstable, we need to provide indices as well to avoid reshuffling elements.
sortedChildren.Sort(static (lhs, rhs) =>
{
var result = lhs.visual.ZIndex.CompareTo(rhs.visual.ZIndex);
return result == 0 ? lhs.index.CompareTo(rhs.index) : result;
});
}
var childVisual = ChildCompositionVisual;
// Check if the current visual somehow got migrated to another compositor
if (childVisual != null && childVisual.Compositor != CompositionVisual.Compositor)
childVisual = null;
var expectedCount = visualChildren.Count;
if (childVisual != null)
expectedCount++;
if (compositionChildren.Count == expectedCount)
{
bool mismatch = false;
if (sortedChildren != null)
for (var c = 0; c < visualChildren.Count; c++)
{
if (!ReferenceEquals(compositionChildren[c], sortedChildren[c].visual.CompositionVisual))
{
mismatch = true;
break;
}
}
else
for (var c = 0; c < visualChildren.Count; c++)
if (!ReferenceEquals(compositionChildren[c], visualChildren[c].CompositionVisual))
{
mismatch = true;
break;
}
if (childVisual != null &&
!ReferenceEquals(compositionChildren[compositionChildren.Count - 1], childVisual))
mismatch = true;
if (!mismatch)
{
sortedChildren?.Dispose();
return;
}
}
compositionChildren.Clear();
if (sortedChildren != null)
{
foreach (var ch in sortedChildren)
{
var compositionChild = ch.visual.CompositionVisual;
if (compositionChild != null)
compositionChildren.Add(compositionChild);
}
sortedChildren.Dispose();
}
else
foreach (var ch in visualChildren)
{
var compositionChild = ch.CompositionVisual;
if (compositionChild != null)
compositionChildren.Add(compositionChild);
}
if (childVisual != null)
compositionChildren.Add(childVisual);
}
internal virtual void SynchronizeCompositionProperties()
{
if(CompositionVisual == null)
return;
var comp = CompositionVisual;
// TODO: Introduce a dirty mask like WPF has, so we don't overwrite properties every time
comp.Offset = new (Bounds.Left, Bounds.Top, 0);
comp.Size = new (Bounds.Width, Bounds.Height);
comp.Visible = IsVisible;
comp.Opacity = (float)Opacity;
comp.ClipToBounds = ClipToBounds;
comp.Clip = Clip?.PlatformImpl;
if (!Equals(comp.OpacityMask, OpacityMask))
comp.OpacityMask = OpacityMask?.ToImmutable();
if (!comp.Effect.EffectEquals(Effect))
comp.Effect = Effect?.ToImmutable();
comp.RenderOptions = RenderOptions;
var renderTransform = Matrix.Identity;
if (HasMirrorTransform)
renderTransform = new Matrix(-1.0, 0.0, 0.0, 1.0, Bounds.Width, 0);
if (RenderTransform != null)
{
var origin = RenderTransformOrigin.ToPixels(new Size(Bounds.Width, Bounds.Height));
var offset = Matrix.CreateTranslation(origin);
renderTransform *= (-offset) * RenderTransform.Value * (offset);
}
comp.TransformMatrix = renderTransform;
}
}

28
src/Avalonia.Base/Visual.cs

@ -29,7 +29,7 @@ namespace Avalonia
/// extension methods defined in <see cref="VisualExtensions"/>.
/// </remarks>
[UsableDuringInitialization]
public class Visual : StyledElement
public partial class Visual : StyledElement
{
/// <summary>
/// Defines the <see cref="Bounds"/> property.
@ -316,9 +316,6 @@ namespace Avalonia
/// </summary>
protected internal IRenderRoot? VisualRoot => _visualRoot ?? (this as IRenderRoot);
internal CompositionDrawListVisual? CompositionVisual { get; private set; }
internal CompositionVisual? ChildCompositionVisual { get; set; }
internal RenderOptions RenderOptions { get; set; }
internal bool HasNonUniformZIndexChildren { get; private set; }
@ -515,20 +512,6 @@ namespace Avalonia
}
}
private protected virtual CompositionDrawListVisual CreateCompositionVisual(Compositor compositor)
=> new CompositionDrawListVisual(compositor,
new ServerCompositionDrawListVisual(compositor.Server, this), this);
internal CompositionVisual AttachToCompositor(Compositor compositor)
{
if (CompositionVisual == null || CompositionVisual.Compositor != compositor)
{
CompositionVisual = CreateCompositionVisual(compositor);
}
return CompositionVisual;
}
/// <summary>
/// Calls the <see cref="OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs)"/> method
/// for this control and all of its visual descendants.
@ -547,14 +530,7 @@ namespace Avalonia
DisableTransitions();
OnDetachedFromVisualTree(e);
if (CompositionVisual != null)
{
if (ChildCompositionVisual != null)
CompositionVisual.Children.Remove(ChildCompositionVisual);
CompositionVisual.DrawList = null;
CompositionVisual = null;
}
DetachFromCompositor();
DetachedFromVisualTree?.Invoke(this, e);
e.Root.Renderer.AddDirty(this);

16
src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj

@ -1,12 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<OutputType>exe</OutputType>
<GenerateDocumentationFile>false</GenerateDocumentationFile>
<BuildOutputTargetFolder>tools</BuildOutputTargetFolder>
<DefineConstants>$(DefineConstants);BUILDTASK;XAMLX_CECIL_INTERNAL;XAMLX_INTERNAL</DefineConstants>
<CopyLocalLockFileAssemblies Condition="$(TargetFramework) == 'netstandard2.0'">true</CopyLocalLockFileAssemblies>
<NoWarn>$(NoWarn);NU1605;CS8632</NoWarn>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<OutputType>exe</OutputType>
<GenerateDocumentationFile>false</GenerateDocumentationFile>
<BuildOutputTargetFolder>tools</BuildOutputTargetFolder>
<DefineConstants>$(DefineConstants);BUILDTASK;XAMLX_CECIL_INTERNAL;XAMLX_INTERNAL</DefineConstants>
<CopyLocalLockFileAssemblies Condition="$(TargetFramework) == 'netstandard2.0'">true</CopyLocalLockFileAssemblies>
<NoWarn>$(NoWarn);NU1605;CS8632</NoWarn>
<DebugType>embedded</DebugType>
<IncludeSymbols>false</IncludeSymbols>
</PropertyGroup>
<!--Disable Net Perf. analyzer for submodule to avoid commit issue -->

159
src/Avalonia.Controls.DataGrid/DataGridColumn.cs

@ -7,7 +7,6 @@ using Avalonia.Data;
using Avalonia.Interactivity;
using Avalonia.VisualTree;
using Avalonia.Collections;
using Avalonia.Utilities;
using System;
using System.ComponentModel;
using System.Linq;
@ -24,8 +23,6 @@ namespace Avalonia.Controls
{
internal const int DATAGRIDCOLUMN_maximumWidth = 65536;
private const bool DATAGRIDCOLUMN_defaultIsReadOnly = false;
private DataGridLength? _width; // Null by default, null means inherit the Width from the DataGrid
private bool? _isReadOnly;
private double? _maxWidth;
private double? _minWidth;
@ -39,6 +36,7 @@ namespace Avalonia.Controls
private IBinding _clipboardContentBinding;
private ControlTheme _cellTheme;
private Classes _cellStyleClasses;
private bool _setWidthInternalNoCallback;
/// <summary>
/// Initializes a new instance of the <see cref="T:Avalonia.Controls.DataGridColumn" /> class.
@ -214,6 +212,36 @@ namespace Avalonia.Controls
OwningGrid?.OnColumnVisibleStateChanged(this);
NotifyPropertyChanged(change.Property.Name);
}
else if (change.Property == WidthProperty)
{
if (!_settingWidthInternally)
{
InheritsWidth = false;
}
if (_setWidthInternalNoCallback == false)
{
var grid = OwningGrid;
var width = (change as AvaloniaPropertyChangedEventArgs<DataGridLength>).NewValue.Value;
if (grid != null)
{
var oldWidth = (change as AvaloniaPropertyChangedEventArgs<DataGridLength>).OldValue.Value;
if (width.IsStar != oldWidth.IsStar)
{
SetWidthInternalNoCallback(width);
IsInitialDesiredWidthDetermined = false;
grid.OnColumnWidthChanged(this);
}
else
{
Resize(oldWidth, width, false);
}
}
else
{
SetWidthInternalNoCallback(width);
}
}
}
}
@ -549,48 +577,15 @@ namespace Avalonia.Controls
}
}
public static readonly StyledProperty<DataGridLength> WidthProperty = AvaloniaProperty
.Register<DataGridColumn, DataGridLength>(nameof(Width)
, coerce: CoerceWidth
);
public DataGridLength Width
{
get
{
return
_width ??
OwningGrid?.ColumnWidth ??
// We don't have a good choice here because we don't want to make this property nullable, see DevDiv Bugs 196581
DataGridLength.Auto;
}
set
{
if (!_width.HasValue || _width.Value != value)
{
if (!_settingWidthInternally)
{
InheritsWidth = false;
}
if (OwningGrid != null)
{
DataGridLength width = CoerceWidth(value);
if (width.IsStar != Width.IsStar)
{
// If a column has changed either from or to a star value, we want to recalculate all
// star column widths. They are recalculated during Measure based off what the value we set here.
SetWidthInternalNoCallback(width);
IsInitialDesiredWidthDetermined = false;
OwningGrid.OnColumnWidthChanged(this);
}
else
{
// If a column width's value is simply changing, we resize it (to the right only).
Resize(width.Value, width.UnitType, width.DesiredValue, width.DisplayValue, false);
}
}
else
{
SetWidthInternalNoCallback(value);
}
}
}
get => this.GetValue(WidthProperty);
set => SetValue(WidthProperty, value);
}
/// <summary>
@ -812,19 +807,34 @@ namespace Avalonia.Controls
/// on the rest of the star columns. For pixel widths, the desired value is based on the pixel value.
/// For auto widths, the desired value is initialized as the column's minimum width.
/// </summary>
/// <param name="source"></param>
/// <param name="width">The DataGridLength to coerce.</param>
/// <returns>The resultant (coerced) DataGridLength.</returns>
internal DataGridLength CoerceWidth(DataGridLength width)
static DataGridLength CoerceWidth(AvaloniaObject source, DataGridLength width)
{
var target = (DataGridColumn)source;
if (target._setWidthInternalNoCallback)
{
return width;
}
if (!target.IsSet(WidthProperty))
{
return target.OwningGrid?.ColumnWidth ??
DataGridLength.Auto;
}
double desiredValue = width.DesiredValue;
if (double.IsNaN(desiredValue))
{
if (width.IsStar && OwningGrid != null && OwningGrid.ColumnsInternal != null)
if (width.IsStar && target.OwningGrid != null && target.OwningGrid.ColumnsInternal != null)
{
double totalStarValues = 0;
double totalStarDesiredValues = 0;
double totalNonStarDisplayWidths = 0;
foreach (DataGridColumn column in OwningGrid.ColumnsInternal.GetDisplayedColumns(c => c.IsVisible && c != this && !double.IsNaN(c.Width.DesiredValue)))
foreach (DataGridColumn column in target.OwningGrid.ColumnsInternal.GetDisplayedColumns(c => c.IsVisible && c != target && !double.IsNaN(c.Width.DesiredValue)))
{
if (column.Width.IsStar)
{
@ -839,7 +849,7 @@ namespace Avalonia.Controls
if (totalStarValues == 0)
{
// Compute the new star column's desired value based on the available space if there are no other visible star columns
desiredValue = Math.Max(ActualMinWidth, OwningGrid.CellsWidth - totalNonStarDisplayWidths);
desiredValue = Math.Max(target.ActualMinWidth, target.OwningGrid.CellsWidth - totalNonStarDisplayWidths);
}
else
{
@ -853,7 +863,7 @@ namespace Avalonia.Controls
}
else
{
desiredValue = ActualMinWidth;
desiredValue = target.ActualMinWidth;
}
}
@ -862,7 +872,7 @@ namespace Avalonia.Controls
{
displayValue = desiredValue;
}
displayValue = Math.Max(ActualMinWidth, Math.Min(ActualMaxWidth, displayValue));
displayValue = Math.Max(target.ActualMinWidth, Math.Min(target.ActualMaxWidth, displayValue));
return new DataGridLength(width.Value, width.UnitType, desiredValue, displayValue);
}
@ -896,7 +906,7 @@ namespace Avalonia.Controls
};
result[!ContentControl.ContentProperty] = this[!HeaderProperty];
result[!ContentControl.ContentTemplateProperty] = this[!HeaderTemplateProperty];
if (OwningGrid.ColumnHeaderTheme is {} columnTheme)
if (OwningGrid.ColumnHeaderTheme is { } columnTheme)
{
result.SetValue(StyledElement.ThemeProperty, columnTheme, BindingPriority.Template);
}
@ -909,7 +919,7 @@ namespace Avalonia.Controls
/// </summary>
internal void EnsureWidth()
{
SetWidthInternalNoCallback(CoerceWidth(Width));
SetWidthInternalNoCallback(CoerceWidth(this, Width));
}
internal Control GenerateElementInternal(DataGridCell cell, object dataItem)
@ -931,17 +941,17 @@ namespace Avalonia.Controls
/// can only decrease in size by the amount that the columns after it can increase in size.
/// Likewise, the column can only increase in size if other columns can spare the width.
/// </summary>
/// <param name="value">The new Value.</param>
/// <param name="unitType">The new UnitType.</param>
/// <param name="desiredValue">The new DesiredValue.</param>
/// <param name="displayValue">The new DisplayValue.</param>
/// <param name="oldWidth">with before resize.</param>
/// <param name="newWidth">with after resize.</param>
/// <param name="userInitiated">Whether or not this resize was initiated by a user action.</param>
internal void Resize(double value, DataGridLengthUnitType unitType, double desiredValue, double displayValue, bool userInitiated)
// double value, DataGridLengthUnitType unitType, double desiredValue, double displayValue
internal void Resize(DataGridLength oldWidth, DataGridLength newWidth, bool userInitiated)
{
double newValue = value;
double newDesiredValue = desiredValue;
double newDisplayValue = Math.Max(ActualMinWidth, Math.Min(ActualMaxWidth, displayValue));
DataGridLengthUnitType newUnitType = unitType;
double newValue = newWidth.Value;
double newDesiredValue = newWidth.DesiredValue;
double newDisplayValue = Math.Max(ActualMinWidth, Math.Min(ActualMaxWidth, newWidth.DisplayValue));
DataGridLengthUnitType newUnitType = newWidth.UnitType;
int starColumnsCount = 0;
double totalDisplayWidth = 0;
@ -955,11 +965,11 @@ namespace Avalonia.Controls
// If we're using star sizing, we can only resize the column as much as the columns to the
// right will allow (i.e. until they hit their max or min widths).
if (!hasInfiniteAvailableWidth && (starColumnsCount > 0 || (unitType == DataGridLengthUnitType.Star && Width.IsStar && userInitiated)))
if (!hasInfiniteAvailableWidth && (starColumnsCount > 0 || (newUnitType == DataGridLengthUnitType.Star && newWidth.IsStar && userInitiated)))
{
double limitedDisplayValue = Width.DisplayValue;
double limitedDisplayValue = oldWidth.DisplayValue;
double availableIncrease = Math.Max(0, OwningGrid.CellsWidth - totalDisplayWidth);
double desiredChange = newDisplayValue - Width.DisplayValue;
double desiredChange = newDisplayValue - oldWidth.DisplayValue;
if (desiredChange > availableIncrease)
{
// The desired change is greater than the amount of available space,
@ -979,7 +989,7 @@ namespace Avalonia.Controls
// The desired change is negative, so we need to increase the widths of columns to the right.
limitedDisplayValue += desiredChange + OwningGrid.IncreaseColumnWidths(DisplayIndex + 1, -desiredChange, userInitiated);
}
if (ActualCanUserResize || (Width.IsStar && !userInitiated))
if (ActualCanUserResize || (oldWidth.IsStar && !userInitiated))
{
newDisplayValue = limitedDisplayValue;
}
@ -1002,9 +1012,10 @@ namespace Avalonia.Controls
}
}
DataGridLength oldWidth = Width;
SetWidthInternalNoCallback(new DataGridLength(Math.Min(double.MaxValue, newValue), newUnitType, newDesiredValue, newDisplayValue));
if (Width != oldWidth)
newDisplayValue = Math.Min(double.MaxValue, newValue);
newWidth = new DataGridLength(newDisplayValue, newUnitType, newDesiredValue, newDisplayValue);
SetWidthInternalNoCallback(newWidth);
if (newWidth != oldWidth)
{
OwningGrid.OnColumnWidthChanged(this);
}
@ -1052,7 +1063,17 @@ namespace Avalonia.Controls
/// <param name="width">The new Width.</param>
internal void SetWidthInternalNoCallback(DataGridLength width)
{
_width = width;
var originalValue = _setWidthInternalNoCallback;
_setWidthInternalNoCallback = true;
try
{
Width = width;
}
finally
{
_setWidthInternalNoCallback = originalValue;
}
}
/// <summary>
@ -1122,7 +1143,7 @@ namespace Avalonia.Controls
&& OwningGrid.DataConnection != null
&& OwningGrid.DataConnection.SortDescriptions != null)
{
if(CustomSortComparer != null)
if (CustomSortComparer != null)
{
return
OwningGrid.DataConnection.SortDescriptions

4
src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs

@ -785,7 +785,9 @@ namespace Avalonia.Controls
double desiredWidth = _originalWidth + mouseDelta;
desiredWidth = Math.Max(_dragColumn.ActualMinWidth, Math.Min(_dragColumn.ActualMaxWidth, desiredWidth));
_dragColumn.Resize(_dragColumn.Width.Value, _dragColumn.Width.UnitType, _dragColumn.Width.DesiredValue, desiredWidth, true);
_dragColumn.Resize(_dragColumn.Width,
new(_dragColumn.Width.Value, _dragColumn.Width.UnitType, _dragColumn.Width.DesiredValue, desiredWidth),
true);
OwningGrid.UpdateHorizontalOffset(_originalHorizontalOffset);

17
src/Avalonia.Controls.DataGrid/DataGridColumns.cs

@ -91,7 +91,10 @@ namespace Avalonia.Controls
// this column is newly added, we'll just set its display value directly.
if (UsesStarSizing && column.IsInitialDesiredWidthDetermined)
{
column.Resize(column.Width.Value, column.Width.UnitType, desiredWidth, desiredWidth, false);
var oldWidth = column.Width;
column.Resize(oldWidth,
new(column.Width.Value, column.Width.UnitType, desiredWidth, desiredWidth),
false);
}
else
{
@ -142,7 +145,7 @@ namespace Avalonia.Controls
{
Debug.Assert(dataGridColumn != null);
if (dataGridColumn is DataGridBoundColumn dataGridBoundColumn &&
if (dataGridColumn is DataGridBoundColumn dataGridBoundColumn &&
dataGridBoundColumn.Binding is BindingBase binding)
{
var path = (binding as Binding)?.Path ?? (binding as CompiledBindingExtension)?.Path.ToString();
@ -359,6 +362,7 @@ namespace Avalonia.Controls
if (column.IsVisible && oldValue != column.ActualMaxWidth)
{
DataGridLength oldWitdh = new(oldValue, column.Width.UnitType, column.Width.DesiredValue, column.Width.DesiredValue);
if (column.ActualMaxWidth < column.Width.DisplayValue)
{
// If the maximum width has caused the column to decrease in size, try first to resize
@ -371,7 +375,9 @@ namespace Avalonia.Controls
{
// If the column was previously limited by its maximum value but has more room now,
// attempt to resize the column to its desired width.
column.Resize(column.Width.Value, column.Width.UnitType, column.Width.DesiredValue, column.Width.DesiredValue, false);
column.Resize(oldWitdh,
new (column.Width.Value, column.Width.UnitType, column.Width.DesiredValue, column.Width.DesiredValue),
false);
}
OnColumnWidthChanged(column);
}
@ -388,6 +394,7 @@ namespace Avalonia.Controls
if (column.IsVisible && oldValue != column.ActualMinWidth)
{
DataGridLength oldWitdh = new(oldValue, column.Width.UnitType, column.Width.DesiredValue, column.Width.DesiredValue);
if (column.ActualMinWidth > column.Width.DisplayValue)
{
// If the minimum width has caused the column to increase in size, try first to resize
@ -400,7 +407,9 @@ namespace Avalonia.Controls
{
// If the column was previously limited by its minimum value but but can be smaller now,
// attempt to resize the column to its desired width.
column.Resize(column.Width.Value, column.Width.UnitType, column.Width.DesiredValue, column.Width.DesiredValue, false);
column.Resize(oldWitdh,
new(column.Width.Value, column.Width.UnitType, column.Width.DesiredValue, column.Width.DesiredValue),
false);
}
OnColumnWidthChanged(column);
}

1
src/Avalonia.Controls.DataGrid/DataGridLength.cs

@ -7,7 +7,6 @@ using Avalonia.Utilities;
using System;
using System.ComponentModel;
using System.Globalization;
using Avalonia.Controls.Utils;
namespace Avalonia.Controls
{

5
src/Avalonia.Controls.DataGrid/DataGridRows.cs

@ -2969,11 +2969,8 @@ namespace Avalonia.Controls
var detailsContent = RowDetailsTemplate.Build(dataItem);
if (detailsContent != null)
{
detailsContent.DataContext = dataItem;
_rowsPresenter.Children.Add(detailsContent);
if (dataItem != null)
{
detailsContent.DataContext = dataItem;
}
detailsContent.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
RowDetailsHeightEstimate = detailsContent.DesiredSize.Height;
_rowsPresenter.Children.Remove(detailsContent);

20
src/Avalonia.X11/X11Info.cs

@ -34,7 +34,10 @@ namespace Avalonia.X11
public bool HasXim { get; set; }
public bool HasXSync { get; set; }
public IntPtr DefaultFontSet { get; set; }
[DllImport("libc")]
private static extern void setlocale(int type, string s);
public unsafe X11Info(IntPtr display, IntPtr deferredDisplay, bool useXim)
{
Display = display;
@ -48,7 +51,10 @@ namespace Avalonia.X11
DefaultFontSet = XCreateFontSet(Display, "-*-*-*-*-*-*-*-*-*-*-*-*-*-*",
out var _, out var _, IntPtr.Zero);
// We have problems with text input otherwise
setlocale(0, "");
if (useXim)
{
XSetLocaleModifiers("");
@ -59,7 +65,15 @@ namespace Avalonia.X11
if (Xim == IntPtr.Zero)
{
XSetLocaleModifiers("@im=none");
if (XSetLocaleModifiers("@im=none") == IntPtr.Zero)
{
setlocale(0, "en_US.UTF-8");
if (XSetLocaleModifiers("@im=none") == IntPtr.Zero)
{
setlocale(0, "C.UTF-8");
XSetLocaleModifiers("@im=none");
}
}
Xim = XOpenIM(display, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
}

10
src/Avalonia.X11/X11Platform.cs

@ -36,12 +36,11 @@ namespace Avalonia.X11
public IntPtr OrphanedWindow { get; private set; }
public X11Globals Globals { get; private set; }
public ManualRawEventGrouperDispatchQueue EventGrouperDispatchQueue { get; } = new();
[DllImport("libc")]
private static extern void setlocale(int type, string s);
public void Initialize(X11PlatformOptions options)
{
Options = options;
bool useXim = false;
if (EnableIme(options))
{
@ -50,9 +49,6 @@ namespace Avalonia.X11
useXim = true;
}
// We have problems with text input otherwise
setlocale(0, "");
XInitThreads();
Display = XOpenDisplay(IntPtr.Zero);
if (Display == IntPtr.Zero)
@ -64,7 +60,7 @@ namespace Avalonia.X11
OrphanedWindow = XCreateSimpleWindow(Display, XDefaultRootWindow(Display), 0, 0, 1, 1, 0, IntPtr.Zero,
IntPtr.Zero);
XError.Init();
Info = new X11Info(Display, DeferredDisplay, useXim);
Globals = new X11Globals(this);
//TODO: log

15
src/Avalonia.X11/X11Window.Ime.cs

@ -133,14 +133,21 @@ namespace Avalonia.X11
{
if (ImeBuffer == IntPtr.Zero)
ImeBuffer = Marshal.AllocHGlobal(ImeBufferSize);
var len = Xutf8LookupString(_xic, ref ev, ImeBuffer.ToPointer(), ImeBufferSize,
out _, out var istatus);
var status = (XLookupStatus)istatus;
IntPtr istatus;
int len;
if(_xic != IntPtr.Zero)
len = Xutf8LookupString(_xic, ref ev, ImeBuffer.ToPointer(),
ImeBufferSize, out _, out istatus);
else
len = XLookupString(ref ev, ImeBuffer.ToPointer(), ImeBufferSize,
out _, out istatus);
if (len == 0)
return null;
var status = (XLookupStatus)istatus;
string text;
if (status == XLookupStatus.XBufferOverflow)
return null;

2
src/Avalonia.X11/XLib.cs

@ -468,7 +468,7 @@ namespace Avalonia.X11
public static extern unsafe int XLookupString(ref XEvent xevent, void* buffer, int num_bytes, out IntPtr keysym, out IntPtr status);
[DllImport (libX11)]
public static extern unsafe int Xutf8LookupString(IntPtr xic, ref XEvent xevent, void* buffer, int num_bytes, out IntPtr keysym, out UIntPtr status);
public static extern unsafe int Xutf8LookupString(IntPtr xic, ref XEvent xevent, void* buffer, int num_bytes, out IntPtr keysym, out IntPtr status);
[DllImport (libX11)]
public static extern unsafe int Xutf8LookupString(IntPtr xic, XEvent* xevent, void* buffer, int num_bytes, out IntPtr keysym, out IntPtr status);

2
src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs

@ -59,7 +59,7 @@ namespace Avalonia.Win32
case WindowsMessage.WM_NCCALCSIZE:
{
if (ToInt32(wParam) == 1 && !HasFullDecorations || _isClientAreaExtended)
if (ToInt32(wParam) == 1 && _windowProperties.Decorations == SystemDecorations.None || _isClientAreaExtended)
{
return IntPtr.Zero;
}

10
src/Windows/Avalonia.Win32/WindowImpl.cs

@ -1340,14 +1340,12 @@ namespace Avalonia.Win32
if (!_isFullScreenActive)
{
var margin = newProperties.Decorations == SystemDecorations.BorderOnly ? 1 : 0;
var margins = new MARGINS
{
cyBottomHeight = margin,
cxRightWidth = margin,
cxLeftWidth = margin,
cyTopHeight = margin
cyBottomHeight = 0,
cxRightWidth = 0,
cxLeftWidth = 0,
cyTopHeight = 0
};
DwmExtendFrameIntoClientArea(_hwnd, ref margins);

1
src/tools/Avalonia.Analyzers/Avalonia.Analyzers.csproj

@ -4,6 +4,7 @@
<IncludeBuildOutput>false</IncludeBuildOutput>
<PackageId>Avalonia.Analyzers</PackageId>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
<DebugType>embedded</DebugType>
<IsPackable>true</IsPackable>
<IncludeSymbols>false</IncludeSymbols>
<IsRoslynComponent>true</IsRoslynComponent>

1
src/tools/Avalonia.Generators/Avalonia.Generators.csproj

@ -4,6 +4,7 @@
<IncludeBuildOutput>false</IncludeBuildOutput>
<PackageId>Avalonia.Generators</PackageId>
<DefineConstants>$(DefineConstants);XAMLX_INTERNAL</DefineConstants>
<DebugType>embedded</DebugType>
<IsPackable>true</IsPackable>
<IncludeSymbols>false</IncludeSymbols>
<IsRoslynComponent>true</IsRoslynComponent>

6
src/tools/Avalonia.Generators/Common/XamlXNameResolver.cs

@ -38,6 +38,7 @@ internal class XamlXNameResolver : INameResolver, IXamlAstVisitor
{
if (child is XamlAstXamlPropertyValueNode propertyValueNode &&
propertyValueNode.Property is XamlAstNamePropertyReference namedProperty &&
!IsAttachedProperty(namedProperty) &&
namedProperty.Name == "Name" &&
propertyValueNode.Values.Count > 0 &&
propertyValueNode.Values[0] is XamlAstTextNode text)
@ -89,4 +90,9 @@ internal class XamlXNameResolver : INameResolver, IXamlAstVisitor
_ => _defaultFieldModifier
};
}
private static bool IsAttachedProperty(XamlAstNamePropertyReference namedProperty)
{
return !namedProperty.DeclaringType.Equals(namedProperty.TargetType);
}
}

3
tests/Avalonia.Generators.Tests/Views/NamedControl.xml

@ -2,6 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Sample.App.NamedControl">
<TextBox Name="UserNameTextBox"
AutomationProperties.Name="The user name"
Watermark="Username input"
UseFloatingWatermark="True" />
</Window>
</Window>

3
tests/Avalonia.Generators.Tests/Views/xNamedControl.xml

@ -2,6 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Sample.App.xNamedControl">
<TextBox x:Name="UserNameTextBox"
AutomationProperties.Name="The user name"
Watermark="Username input"
UseFloatingWatermark="True" />
</Window>
</Window>

20
tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs

@ -822,6 +822,26 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
}
}
[Fact]
public void Should_GetCharacterHitFromDistance_From_Mixed_TextBuffer()
{
using (Start())
{
var defaultProperties = new GenericTextRunProperties(Typeface.Default);
var textSource = new MixedTextBufferTextSource();
var formatter = new TextFormatterImpl();
var textLine =
formatter.FormatLine(textSource, 20, double.PositiveInfinity,
new GenericTextParagraphProperties(defaultProperties));
var characterHit = textLine.GetCharacterHitFromDistance(double.PositiveInfinity);
Assert.Equal(40, characterHit.FirstCharacterIndex + characterHit.TrailingLength);
}
}
private class MixedTextBufferTextSource : ITextSource
{
public TextRun? GetTextRun(int textSourceIndex)

Loading…
Cancel
Save