Browse Source

Merge branch 'master' into fixes/TextWrapAndTrim

pull/4260/head
Benedikt Stebner 6 years ago
committed by GitHub
parent
commit
7fccd3f25b
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  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. 32
      src/Avalonia.Controls/RelativePanel.cs
  6. 8
      src/Avalonia.Controls/ToolTipService.cs
  7. 12
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  8. 29
      src/Windows/Avalonia.Win32/PopupImpl.cs
  9. 50
      tests/Avalonia.Controls.UnitTests/RelativePanelTests.cs
  10. 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")) 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")) 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")) 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")) 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:pages="clr-namespace:ControlCatalog.Pages"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.MainView" x:Class="ControlCatalog.MainView"
Background="Black"
Foreground="{DynamicResource ThemeForegroundBrush}" Foreground="{DynamicResource ThemeForegroundBrush}"
FontSize="{DynamicResource FontSizeNormal}"> FontSize="{DynamicResource FontSizeNormal}">
<Grid> <Grid>

5
samples/RenderDemo/App.xaml

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

125
samples/RenderDemo/SideBar.xaml

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

32
src/Avalonia.Controls/RelativePanel.cs

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

8
src/Avalonia.Controls/ToolTipService.cs

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

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

@ -1060,9 +1060,21 @@ namespace Avalonia.Win32.Interop
[DllImport("user32.dll")] [DllImport("user32.dll")]
public static extern bool SetFocus(IntPtr hWnd); public static extern bool SetFocus(IntPtr hWnd);
[DllImport("user32.dll")] [DllImport("user32.dll")]
public static extern IntPtr GetFocus();
[DllImport("user32.dll")]
public static extern bool SetParent(IntPtr hWnd, IntPtr hWndNewParent); public static extern bool SetParent(IntPtr hWnd, IntPtr hWndNewParent);
[DllImport("user32.dll")] [DllImport("user32.dll")]
public static extern IntPtr GetParent(IntPtr hWnd); 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")] [DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommand nCmdShow); public static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommand nCmdShow);

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

@ -7,6 +7,7 @@ namespace Avalonia.Win32
{ {
class PopupImpl : WindowImpl, IPopupImpl class PopupImpl : WindowImpl, IPopupImpl
{ {
private readonly IWindowBaseImpl _parent;
private bool _dropShadowHint = true; private bool _dropShadowHint = true;
private Size? _maxAutoSize; private Size? _maxAutoSize;
@ -19,18 +20,25 @@ namespace Avalonia.Win32
public override void Show() public override void Show()
{ {
UnmanagedMethods.ShowWindow(Handle.Handle, UnmanagedMethods.ShowWindowCommand.ShowNoActivate); 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; protected override bool ShouldTakeFocusOnClick => false;
@ -118,6 +126,7 @@ namespace Avalonia.Win32
private PopupImpl(IWindowBaseImpl parent, bool dummy) : base() private PopupImpl(IWindowBaseImpl parent, bool dummy) : base()
{ {
_parent = parent;
PopupPositioner = new ManagedPopupPositioner(new ManagedPopupPositionerPopupImplHelper(parent, MoveResize)); PopupPositioner = new ManagedPopupPositioner(new ManagedPopupPositionerPopupImplHelper(parent, MoveResize));
} }

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, 0, 20, 20), target.Children[0].Bounds);
Assert.Equal(new Rect(0, 20, 20, 20), target.Children[1].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)); 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] [Fact]
public void Should_Open_On_Pointer_Enter() public void Should_Open_On_Pointer_Enter()

Loading…
Cancel
Save