Browse Source

Merge branch 'fixes/TextWrapAndTrim' of https://github.com/Gillibald/Avalonia into fixes/TextWrapAndTrim

pull/4260/head
Benedikt Schroeder 6 years ago
parent
commit
de4df73870
  1. 24
      samples/ControlCatalog/App.xaml.cs
  2. 1
      samples/ControlCatalog/MainView.xaml
  3. 5
      samples/RenderDemo/App.xaml
  4. 125
      samples/RenderDemo/SideBar.xaml
  5. 27
      src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs
  6. 32
      src/Avalonia.Controls/RelativePanel.cs
  7. 2
      src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs
  8. 8
      src/Avalonia.Controls/ToolTipService.cs
  9. 2
      src/Avalonia.FreeDesktop/DBusHelper.cs
  10. 29
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  11. 15
      src/Windows/Avalonia.Win32/NonPumpingWaitProvider.cs
  12. 29
      src/Windows/Avalonia.Win32/PopupImpl.cs
  13. 1
      src/Windows/Avalonia.Win32/Win32Platform.cs
  14. 50
      tests/Avalonia.Controls.UnitTests/RelativePanelTests.cs
  15. 34
      tests/Avalonia.Controls.UnitTests/ToolTipTests.cs

24
samples/ControlCatalog/App.xaml.cs

@ -29,11 +29,19 @@ namespace ControlCatalog
{
new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
{
Source = new Uri("resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default")
Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/Base.xaml")
},
new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
{
Source = new Uri("resm:Avalonia.Themes.Default.DefaultTheme.xaml?assembly=Avalonia.Themes.Default")
Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/BaseLight.xaml")
},
new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
{
Source = new Uri("avares://Avalonia.Themes.Default/Accents/BaseLight.xaml")
},
new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
{
Source = new Uri("avares://Avalonia.Themes.Default/DefaultTheme.xaml")
},
};
@ -41,11 +49,19 @@ namespace ControlCatalog
{
new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
{
Source = new Uri("resm:Avalonia.Themes.Default.Accents.BaseDark.xaml?assembly=Avalonia.Themes.Default")
Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/Base.xaml")
},
new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
{
Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/BaseDark.xaml")
},
new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
{
Source = new Uri("avares://Avalonia.Themes.Default/Accents/BaseDark.xaml")
},
new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
{
Source = new Uri("resm:Avalonia.Themes.Default.DefaultTheme.xaml?assembly=Avalonia.Themes.Default")
Source = new Uri("avares://Avalonia.Themes.Default/DefaultTheme.xaml")
},
};

1
samples/ControlCatalog/MainView.xaml

@ -2,7 +2,6 @@
xmlns:pages="clr-namespace:ControlCatalog.Pages"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.MainView"
Background="Black"
Foreground="{DynamicResource ThemeForegroundBrush}"
FontSize="{DynamicResource FontSizeNormal}">
<Grid>

5
samples/RenderDemo/App.xaml

@ -3,8 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="RenderDemo.App">
<Application.Styles>
<StyleInclude Source="avares://Avalonia.Themes.Default/DefaultTheme.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Default/Accents/BaseLight.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Accents/FluentLight.xaml"/>
<StyleInclude Source="avares://RenderDemo/SideBar.xaml"/>
</Application.Styles>
</Application>
</Application>

125
samples/RenderDemo/SideBar.xaml

@ -1,65 +1,68 @@
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style Selector="TabControl.sidebar">
<Setter Property="TabStripPlacement" Value="Left"/>
<Setter Property="Padding" Value="8 0 0 0"/>
<Setter Property="Background" Value="{DynamicResource SystemAccentColor}"/>
<Setter Property="Template">
<ControlTemplate>
<Border
Margin="{TemplateBinding Margin}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<DockPanel>
<ScrollViewer
Name="PART_ScrollViewer"
HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}"
VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}"
Background="{TemplateBinding Background}">
<ItemsPresenter
Name="PART_ItemsPresenter"
Items="{TemplateBinding Items}"
ItemsPanel="{TemplateBinding ItemsPanel}"
ItemTemplate="{TemplateBinding ItemTemplate}">
</ItemsPresenter>
</ScrollViewer>
<ContentPresenter
Name="PART_SelectedContentHost"
Margin="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding SelectedContent}"
ContentTemplate="{TemplateBinding SelectedContentTemplate}">
</ContentPresenter>
</DockPanel>
</Border>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="TabControl.sidebar">
<Setter Property="TabStripPlacement" Value="Left"/>
<Setter Property="Padding" Value="8 0 0 0"/>
<Setter Property="Background" Value="{DynamicResource SystemAccentColor}"/>
<Setter Property="Template">
<ControlTemplate>
<Border
Margin="{TemplateBinding Margin}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<DockPanel>
<ScrollViewer
Name="PART_ScrollViewer"
HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}"
VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}"
Background="{TemplateBinding Background}">
<ItemsPresenter
Name="PART_ItemsPresenter"
Items="{TemplateBinding Items}"
ItemsPanel="{TemplateBinding ItemsPanel}"
ItemTemplate="{TemplateBinding ItemTemplate}">
</ItemsPresenter>
</ScrollViewer>
<ContentPresenter
Name="PART_SelectedContentHost"
Margin="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding SelectedContent}"
ContentTemplate="{TemplateBinding SelectedContentTemplate}">
</ContentPresenter>
</DockPanel>
</Border>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="TabControl.sidebar > TabItem">
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Margin" Value="0"/>
<Setter Property="Padding" Value="16"/>
<Setter Property="Opacity" Value="0.5"/>
<Setter Property="Transitions">
<Transitions>
<DoubleTransition Property="Opacity" Duration="0:0:0.150"/>
</Transitions>
</Setter>
</Style>
<Style Selector="TabControl.sidebar > TabItem:pointerover">
<Setter Property="Opacity" Value="1"/>
</Style>
<Style Selector="TabControl.sidebar > TabItem:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="Transparent"/>
</Style>
<Style Selector="TabControl.sidebar > TabItem:selected">
<Setter Property="Opacity" Value="1"/>
</Style>
<Style Selector="TabControl.sidebar > TabItem:selected /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource SystemAccentColorLight1}"/>
</Style>
<Style Selector="TabControl.sidebar > TabItem">
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Margin" Value="0"/>
<Setter Property="Padding" Value="16"/>
<Setter Property="Opacity" Value="0.5"/>
<Setter Property="Transitions">
<Transitions>
<DoubleTransition Property="Opacity" Duration="0:0:0.150"/>
</Transitions>
</Setter>
</Style>
<Style Selector="TabControl.sidebar > TabItem:selected /template/ Border#PART_SelectedPipe">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="TabControl.sidebar > TabItem:pointerover">
<Setter Property="Opacity" Value="1"/>
</Style>
<Style Selector="TabControl.sidebar > TabItem:pointerover /template/ Border#PART_LayoutRoot">
<Setter Property="Background" Value="{DynamicResource SystemAccentColorLight2}"/>
</Style>
<Style Selector="TabControl.sidebar > TabItem:selected">
<Setter Property="Opacity" Value="1"/>
</Style>
<Style Selector="TabControl.sidebar > TabItem:selected /template/ Border#PART_LayoutRoot">
<Setter Property="Background" Value="{DynamicResource SystemAccentColorLight1}"/>
</Style>
</Styles>

27
src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs

@ -1,3 +1,5 @@
using System;
using System.Runtime.ConstrainedExecution;
using System.Threading;
namespace Avalonia.Threading
@ -7,6 +9,20 @@ namespace Avalonia.Threading
/// </summary>
public class AvaloniaSynchronizationContext : SynchronizationContext
{
public interface INonPumpingPlatformWaitProvider
{
int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout);
}
private readonly INonPumpingPlatformWaitProvider _waitProvider;
public AvaloniaSynchronizationContext(INonPumpingPlatformWaitProvider waitProvider)
{
_waitProvider = waitProvider;
if (_waitProvider != null)
SetWaitNotificationRequired();
}
/// <summary>
/// Controls if SynchronizationContext should be installed in InstallIfNeeded. Used by Designer.
/// </summary>
@ -22,7 +38,8 @@ namespace Avalonia.Threading
return;
}
SetSynchronizationContext(new AvaloniaSynchronizationContext());
SetSynchronizationContext(new AvaloniaSynchronizationContext(AvaloniaLocator.Current
.GetService<INonPumpingPlatformWaitProvider>()));
}
/// <inheritdoc/>
@ -39,5 +56,13 @@ namespace Avalonia.Threading
else
Dispatcher.UIThread.InvokeAsync(() => d(state), DispatcherPriority.Send).Wait();
}
[PrePrepareMethod]
public override int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout)
{
if (_waitProvider != null)
return _waitProvider.Wait(waitHandles, waitAll, millisecondsTimeout);
return base.Wait(waitHandles, waitAll, millisecondsTimeout);
}
}
}

32
src/Avalonia.Controls/RelativePanel.cs

@ -54,7 +54,7 @@ namespace Avalonia.Controls
}
_childGraph.Measure(availableSize);
_childGraph.Reset();
_childGraph.Reset(false);
var boundingSize = _childGraph.GetBoundingSize(Width.IsNaN(), Height.IsNaN());
_childGraph.Reset();
_childGraph.Measure(boundingSize);
@ -119,17 +119,22 @@ namespace Avalonia.Controls
public void Arrange(Size arrangeSize) => Element.Arrange(new Rect(Left, Top, Math.Max(arrangeSize.Width - Left - Right, 0), Math.Max(arrangeSize.Height - Top - Bottom, 0)));
public void Reset()
public void Reset(bool clearPos)
{
Left = double.NaN;
Top = double.NaN;
Right = double.NaN;
Bottom = double.NaN;
if (clearPos)
{
Left = double.NaN;
Top = double.NaN;
Right = double.NaN;
Bottom = double.NaN;
}
Measured = false;
}
public Size GetBoundingSize()
{
if (Left < 0 || Top < 0) return default;
if (Measured)
return BoundingSize;
@ -209,7 +214,7 @@ namespace Avalonia.Controls
_nodeDic.Clear();
}
public void Reset() => _nodeDic.Values.Do(node => node.Reset());
public void Reset(bool clearPos = true) => _nodeDic.Values.Do(node => node.Reset(clearPos));
public GraphNode? AddLink(GraphNode from, Layoutable? to)
{
@ -255,28 +260,21 @@ namespace Avalonia.Controls
foreach (var node in nodes)
{
/*
*
*
*/
if (!node.Measured && !node.OutgoingNodes.Any())
{
MeasureChild(node);
continue;
}
// 判断依赖元素是否全部排列完毕
if (node.OutgoingNodes.All(item => item.Measured))
{
MeasureChild(node);
continue;
}
// 判断是否有循环
if (!set.Add(node.Element))
throw new Exception("RelativePanel error: Circular dependency detected. Layout could not complete.");
// 没有循环,且有依赖,则继续往下
Measure(node.OutgoingNodes, set);
if (!node.Measured)

2
src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs

@ -192,7 +192,7 @@ namespace Avalonia.Controls.Remote.Server
GetAvaloniaInputModifiers(pressed.Modifiers)));
}, DispatcherPriority.Input);
}
if (obj is PointerPressedEventMessage released)
if (obj is PointerReleasedEventMessage released)
{
Dispatcher.UIThread.Post(() =>
{

8
src/Avalonia.Controls/ToolTipService.cs

@ -28,14 +28,22 @@ namespace Avalonia.Controls
{
control.PointerEnter -= ControlPointerEnter;
control.PointerLeave -= ControlPointerLeave;
control.DetachedFromVisualTree -= ControlDetaching;
}
if (e.NewValue != null)
{
control.PointerEnter += ControlPointerEnter;
control.PointerLeave += ControlPointerLeave;
control.DetachedFromVisualTree += ControlDetaching;
}
}
private void ControlDetaching(object sender, VisualTreeAttachmentEventArgs e)
{
var control = (Control)sender;
Close(control);
}
/// <summary>
/// Called when the pointer enters a control with an attached tooltip.

2
src/Avalonia.FreeDesktop/DBusHelper.cs

@ -43,7 +43,7 @@ namespace Avalonia.FreeDesktop
public void Initialized()
{
lock (_lock)
_ctx = new AvaloniaSynchronizationContext();
_ctx = new AvaloniaSynchronizationContext(null);
}
}
public static Connection Connection { get; private set; }

29
src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs

@ -1,4 +1,5 @@
using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@ -1059,9 +1060,21 @@ namespace Avalonia.Win32.Interop
[DllImport("user32.dll")]
public static extern bool SetFocus(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern IntPtr GetFocus();
[DllImport("user32.dll")]
public static extern bool SetParent(IntPtr hWnd, IntPtr hWndNewParent);
[DllImport("user32.dll")]
public static extern IntPtr GetParent(IntPtr hWnd);
public enum GetAncestorFlags
{
GA_PARENT = 1,
GA_ROOT = 2,
GA_ROOTOWNER = 3
}
[DllImport("user32.dll")]
public static extern IntPtr GetAncestor(IntPtr hwnd, GetAncestorFlags gaFlags);
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommand nCmdShow);
@ -1382,6 +1395,22 @@ namespace Avalonia.Win32.Interop
throw new Exception("RtlGetVersion failed!");
}
}
[DllImport("kernel32", EntryPoint="WaitForMultipleObjectsEx", SetLastError = true, CharSet = CharSet.Auto)]
private static extern int IntWaitForMultipleObjectsEx(int nCount, IntPtr[] pHandles, bool bWaitAll, int dwMilliseconds, bool bAlertable);
public const int WAIT_FAILED = unchecked((int)0xFFFFFFFF);
internal static int WaitForMultipleObjectsEx(int nCount, IntPtr[] pHandles, bool bWaitAll, int dwMilliseconds, bool bAlertable)
{
int result = IntWaitForMultipleObjectsEx(nCount, pHandles, bWaitAll, dwMilliseconds, bAlertable);
if(result == WAIT_FAILED)
{
throw new Win32Exception();
}
return result;
}
[DllImport("user32.dll")]
internal static extern int SetWindowCompositionAttribute(IntPtr hwnd, ref WindowCompositionAttributeData data);

15
src/Windows/Avalonia.Win32/NonPumpingWaitProvider.cs

@ -0,0 +1,15 @@
using System;
using Avalonia.Threading;
using Avalonia.Win32.Interop;
namespace Avalonia.Win32
{
internal class NonPumpingWaitProvider : AvaloniaSynchronizationContext.INonPumpingPlatformWaitProvider
{
public int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout)
{
return UnmanagedMethods.WaitForMultipleObjectsEx(waitHandles.Length, waitHandles, waitAll,
millisecondsTimeout, false);
}
}
}

29
src/Windows/Avalonia.Win32/PopupImpl.cs

@ -7,6 +7,7 @@ namespace Avalonia.Win32
{
class PopupImpl : WindowImpl, IPopupImpl
{
private readonly IWindowBaseImpl _parent;
private bool _dropShadowHint = true;
private Size? _maxAutoSize;
@ -19,18 +20,25 @@ namespace Avalonia.Win32
public override void Show()
{
UnmanagedMethods.ShowWindow(Handle.Handle, UnmanagedMethods.ShowWindowCommand.ShowNoActivate);
var parent = UnmanagedMethods.GetParent(Handle.Handle);
if (parent != IntPtr.Zero)
{
IntPtr nextParent = parent;
while (nextParent != IntPtr.Zero)
{
parent = nextParent;
nextParent = UnmanagedMethods.GetParent(parent);
}
UnmanagedMethods.SetFocus(parent);
// We need to steal focus if it's held by a child window of our toplevel window
var parent = _parent;
while(parent != null)
{
if(parent is PopupImpl pi)
parent = pi._parent;
else
break;
}
if(parent == null)
return;
var focusOwner = UnmanagedMethods.GetFocus();
if (focusOwner != IntPtr.Zero &&
UnmanagedMethods.GetAncestor(focusOwner, UnmanagedMethods.GetAncestorFlags.GA_ROOT)
== parent.Handle.Handle)
UnmanagedMethods.SetFocus(parent.Handle.Handle);
}
protected override bool ShouldTakeFocusOnClick => false;
@ -118,6 +126,7 @@ namespace Avalonia.Win32
private PopupImpl(IWindowBaseImpl parent, bool dummy) : base()
{
_parent = parent;
PopupPositioner = new ManagedPopupPositioner(new ManagedPopupPositionerPopupImplHelper(parent, MoveResize));
}

1
src/Windows/Avalonia.Win32/Win32Platform.cs

@ -93,6 +93,7 @@ namespace Avalonia.Win32
.Bind<IWindowingPlatform>().ToConstant(s_instance)
.Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>()
.Bind<IPlatformIconLoader>().ToConstant(s_instance)
.Bind<AvaloniaSynchronizationContext.INonPumpingPlatformWaitProvider>().ToConstant(new NonPumpingWaitProvider())
.Bind<IMountedVolumeInfoProvider>().ToConstant(new WindowsMountedVolumeInfoProvider());
if (options.AllowEglInitialization)

50
tests/Avalonia.Controls.UnitTests/RelativePanelTests.cs

@ -82,5 +82,55 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(new Rect(0, 0, 20, 20), target.Children[0].Bounds);
Assert.Equal(new Rect(0, 20, 20, 20), target.Children[1].Bounds);
}
[Fact]
public void LeftOf_Measures_Correctly()
{
var rect1 = new Rectangle { Height = 20, Width = 20 };
var rect2 = new Rectangle { Height = 20, Width = 20 };
var target = new RelativePanel
{
VerticalAlignment = Layout.VerticalAlignment.Center,
HorizontalAlignment = Layout.HorizontalAlignment.Center,
Children =
{
rect1, rect2
}
};
RelativePanel.SetLeftOf(rect2, rect1);
target.Measure(new Size(400, 400));
target.Arrange(new Rect(target.DesiredSize));
Assert.Equal(new Size(20, 20), target.Bounds.Size);
Assert.Equal(new Rect(0, 0, 20, 20), target.Children[0].Bounds);
Assert.Equal(new Rect(-20, 0, 20, 20), target.Children[1].Bounds);
}
[Fact]
public void Above_Measures_Correctly()
{
var rect1 = new Rectangle { Height = 20, Width = 20 };
var rect2 = new Rectangle { Height = 20, Width = 20 };
var target = new RelativePanel
{
VerticalAlignment = Layout.VerticalAlignment.Center,
HorizontalAlignment = Layout.HorizontalAlignment.Center,
Children =
{
rect1, rect2
}
};
RelativePanel.SetAbove(rect2, rect1);
target.Measure(new Size(400, 400));
target.Arrange(new Rect(target.DesiredSize));
Assert.Equal(new Size(20, 20), target.Bounds.Size);
Assert.Equal(new Rect(0, 0, 20, 20), target.Children[0].Bounds);
Assert.Equal(new Rect(0, -20, 20, 20), target.Children[1].Bounds);
}
}
}

34
tests/Avalonia.Controls.UnitTests/ToolTipTests.cs

@ -30,6 +30,40 @@ namespace Avalonia.Controls.UnitTests
Assert.False(ToolTip.GetIsOpen(control));
}
[Fact]
public void Should_Close_When_Control_Detaches()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var window = new Window();
var panel = new Panel();
var target = new Decorator()
{
[ToolTip.TipProperty] = "Tip",
[ToolTip.ShowDelayProperty] = 0
};
panel.Children.Add(target);
window.Content = panel;
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
Assert.True((target as IVisual).IsAttachedToVisualTree);
_mouseHelper.Enter(target);
Assert.True(ToolTip.GetIsOpen(target));
panel.Children.Remove(target);
Assert.False(ToolTip.GetIsOpen(target));
}
}
[Fact]
public void Should_Open_On_Pointer_Enter()

Loading…
Cancel
Save