Browse Source

Merge branch 'master' into fixes/5366

pull/5370/head
Benedikt Stebner 5 years ago
committed by GitHub
parent
commit
3479cbcbb8
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      .github/ISSUE_TEMPLATE/bug_report.md
  2. 2
      .github/ISSUE_TEMPLATE/feature_request.md
  3. 22
      src/Avalonia.Base/Data/IndexerBinding.cs
  4. 2
      src/Avalonia.Controls/Primitives/VisualLayerManager.cs
  5. 111
      src/Avalonia.Controls/Slider.cs
  6. 13
      src/Avalonia.Controls/ToolTipService.cs
  7. 1
      src/Avalonia.Themes.Default/MenuItem.xaml
  8. 1
      src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml
  9. 1
      src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml
  10. 1
      src/Avalonia.Themes.Fluent/Controls/RadioButton.xaml
  11. 2
      src/Avalonia.X11/X11Atoms.cs
  12. 4
      src/Avalonia.X11/X11Screens.cs
  13. 28
      tests/Avalonia.Controls.UnitTests/ToolTipTests.cs

4
.github/ISSUE_TEMPLATE/bug_report.md

@ -1,8 +1,8 @@
--- ---
name: Bug report name: Bug report
about: Create a report to help us improve about: Create a report to help us improve Avalonia
title: '' title: ''
labels: '' labels: bug
assignees: '' assignees: ''
--- ---

2
.github/ISSUE_TEMPLATE/feature_request.md

@ -2,7 +2,7 @@
name: Feature request name: Feature request
about: Suggest an idea for this project about: Suggest an idea for this project
title: '' title: ''
labels: '' labels: enhancement
assignees: '' assignees: ''
--- ---

22
src/Avalonia.Base/Data/IndexerBinding.cs

@ -1,6 +1,4 @@
using System; namespace Avalonia.Data
namespace Avalonia.Data
{ {
public class IndexerBinding : IBinding public class IndexerBinding : IBinding
{ {
@ -24,23 +22,7 @@ namespace Avalonia.Data
object anchor = null, object anchor = null,
bool enableDataValidation = false) bool enableDataValidation = false)
{ {
var mode = Mode == BindingMode.Default ? return new InstancedBinding(Source.GetSubject(Property), Mode, BindingPriority.LocalValue);
targetProperty.GetMetadata(target.GetType()).DefaultBindingMode :
Mode;
switch (mode)
{
case BindingMode.OneTime:
return InstancedBinding.OneTime(Source.GetObservable(Property));
case BindingMode.OneWay:
return InstancedBinding.OneWay(Source.GetObservable(Property));
case BindingMode.OneWayToSource:
return InstancedBinding.OneWayToSource(Source.GetSubject(Property));
case BindingMode.TwoWay:
return InstancedBinding.TwoWay(Source.GetSubject(Property));
default:
throw new NotSupportedException("Unsupported BindingMode.");
}
} }
} }
} }

2
src/Avalonia.Controls/Primitives/VisualLayerManager.cs

@ -67,8 +67,6 @@ namespace Avalonia.Controls.Primitives
{ {
get get
{ {
if (IsPopup)
return null;
var rv = FindLayer<LightDismissOverlayLayer>(); var rv = FindLayer<LightDismissOverlayLayer>();
if (rv == null) if (rv == null)
{ {

111
src/Avalonia.Controls/Slider.cs

@ -11,7 +11,6 @@ using Avalonia.Utilities;
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
/// <summary> /// <summary>
/// Enum which describes how to position the ticks in a <see cref="Slider"/>. /// Enum which describes how to position the ticks in a <see cref="Slider"/>.
/// </summary> /// </summary>
@ -84,6 +83,9 @@ namespace Avalonia.Controls
private IDisposable _increaseButtonSubscription; private IDisposable _increaseButtonSubscription;
private IDisposable _increaseButtonReleaseDispose; private IDisposable _increaseButtonReleaseDispose;
private IDisposable _pointerMovedDispose; private IDisposable _pointerMovedDispose;
private IDisposable _trackOnKeyDownDispose;
private const double Tolerance = 0.0001;
/// <summary> /// <summary>
/// Initializes static members of the <see cref="Slider"/> class. /// Initializes static members of the <see cref="Slider"/> class.
@ -95,7 +97,7 @@ namespace Avalonia.Controls
Thumb.DragStartedEvent.AddClassHandler<Slider>((x, e) => x.OnThumbDragStarted(e), RoutingStrategies.Bubble); Thumb.DragStartedEvent.AddClassHandler<Slider>((x, e) => x.OnThumbDragStarted(e), RoutingStrategies.Bubble);
Thumb.DragCompletedEvent.AddClassHandler<Slider>((x, e) => x.OnThumbDragCompleted(e), Thumb.DragCompletedEvent.AddClassHandler<Slider>((x, e) => x.OnThumbDragCompleted(e),
RoutingStrategies.Bubble); RoutingStrategies.Bubble);
ValueProperty.OverrideMetadata<Slider>(new DirectPropertyMetadata<double>(enableDataValidation: true)); ValueProperty.OverrideMetadata<Slider>(new DirectPropertyMetadata<double>(enableDataValidation: true));
} }
@ -157,13 +159,14 @@ namespace Avalonia.Controls
protected override void OnApplyTemplate(TemplateAppliedEventArgs e) protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{ {
base.OnApplyTemplate(e); base.OnApplyTemplate(e);
_decreaseButtonPressDispose?.Dispose(); _decreaseButtonPressDispose?.Dispose();
_decreaseButtonReleaseDispose?.Dispose(); _decreaseButtonReleaseDispose?.Dispose();
_increaseButtonSubscription?.Dispose(); _increaseButtonSubscription?.Dispose();
_increaseButtonReleaseDispose?.Dispose(); _increaseButtonReleaseDispose?.Dispose();
_pointerMovedDispose?.Dispose(); _pointerMovedDispose?.Dispose();
_trackOnKeyDownDispose?.Dispose();
_decreaseButton = e.NameScope.Find<Button>("PART_DecreaseButton"); _decreaseButton = e.NameScope.Find<Button>("PART_DecreaseButton");
_track = e.NameScope.Find<Track>("PART_Track"); _track = e.NameScope.Find<Track>("PART_Track");
_increaseButton = e.NameScope.Find<Button>("PART_IncreaseButton"); _increaseButton = e.NameScope.Find<Button>("PART_IncreaseButton");
@ -171,6 +174,7 @@ namespace Avalonia.Controls
if (_track != null) if (_track != null)
{ {
_track.IsThumbDragHandled = true; _track.IsThumbDragHandled = true;
_trackOnKeyDownDispose = _track.AddDisposableHandler(KeyDownEvent, TrackOnKeyDown);
} }
if (_decreaseButton != null) if (_decreaseButton != null)
@ -188,6 +192,94 @@ namespace Avalonia.Controls
_pointerMovedDispose = this.AddDisposableHandler(PointerMovedEvent, TrackMoved, RoutingStrategies.Tunnel); _pointerMovedDispose = this.AddDisposableHandler(PointerMovedEvent, TrackMoved, RoutingStrategies.Tunnel);
} }
private void TrackOnKeyDown(object sender, KeyEventArgs e)
{
if (e.KeyModifiers != KeyModifiers.None) return;
switch (e.Key)
{
case Key.Left:
MoveToNextTick(-SmallChange);
break;
case Key.Right:
MoveToNextTick(SmallChange);
break;
case Key.PageUp:
MoveToNextTick(-LargeChange);
break;
case Key.PageDown:
MoveToNextTick(LargeChange);
break;
case Key.Home:
Value = Minimum;
break;
case Key.End:
Value = Maximum;
break;
}
}
private void MoveToNextTick(double direction)
{
if (direction == 0.0) return;
var value = Value;
// Find the next value by snapping
var next = SnapToTick(Math.Max(Minimum, Math.Min(Maximum, value + direction)));
var greaterThan = direction > 0; //search for the next tick greater than value?
// If the snapping brought us back to value, find the next tick point
if (Math.Abs(next - value) < Tolerance
&& !(greaterThan && Math.Abs(value - Maximum) < Tolerance) // Stop if searching up if already at Max
&& !(!greaterThan && Math.Abs(value - Minimum) < Tolerance)) // Stop if searching down if already at Min
{
var ticks = Ticks;
// If ticks collection is available, use it.
// Note that ticks may be unsorted.
if (ticks != null && ticks.Count > 0)
{
foreach (var tick in ticks)
{
// Find the smallest tick greater than value or the largest tick less than value
if (greaterThan && MathUtilities.GreaterThan(tick, value) &&
(MathUtilities.LessThan(tick, next) || Math.Abs(next - value) < Tolerance)
|| !greaterThan && MathUtilities.LessThan(tick, value) &&
(MathUtilities.GreaterThan(tick, next) || Math.Abs(next - value) < Tolerance))
{
next = tick;
}
}
}
else if (MathUtilities.GreaterThan(TickFrequency, 0.0))
{
// Find the current tick we are at
var tickNumber = Math.Round((value - Minimum) / TickFrequency);
if (greaterThan)
tickNumber += 1.0;
else
tickNumber -= 1.0;
next = Minimum + tickNumber * TickFrequency;
}
}
// Update if we've found a better value
if (Math.Abs(next - value) > Tolerance)
{
Value = next;
}
}
private void TrackMoved(object sender, PointerEventArgs e) private void TrackMoved(object sender, PointerEventArgs e)
{ {
if (_isDragging) if (_isDragging)
@ -272,19 +364,18 @@ namespace Avalonia.Controls
{ {
if (IsSnapToTickEnabled) if (IsSnapToTickEnabled)
{ {
double previous = Minimum; var previous = Minimum;
double next = Maximum; var next = Maximum;
// This property is rarely set so let's try to avoid the GetValue // This property is rarely set so let's try to avoid the GetValue
var ticks = Ticks; var ticks = Ticks;
// If ticks collection is available, use it. // If ticks collection is available, use it.
// Note that ticks may be unsorted. // Note that ticks may be unsorted.
if ((ticks != null) && (ticks.Count > 0)) if (ticks != null && ticks.Count > 0)
{ {
for (int i = 0; i < ticks.Count; i++) foreach (var tick in ticks)
{ {
double tick = ticks[i];
if (MathUtilities.AreClose(tick, value)) if (MathUtilities.AreClose(tick, value))
{ {
return value; return value;
@ -302,7 +393,7 @@ namespace Avalonia.Controls
} }
else if (MathUtilities.GreaterThan(TickFrequency, 0.0)) else if (MathUtilities.GreaterThan(TickFrequency, 0.0))
{ {
previous = Minimum + (Math.Round(((value - Minimum) / TickFrequency)) * TickFrequency); previous = Minimum + Math.Round((value - Minimum) / TickFrequency) * TickFrequency;
next = Math.Min(Maximum, previous + TickFrequency); next = Math.Min(Maximum, previous + TickFrequency);
} }

13
src/Avalonia.Controls/ToolTipService.cs

@ -38,9 +38,16 @@ namespace Avalonia.Controls
if (ToolTip.GetIsOpen(control) && e.NewValue != e.OldValue && !(e.NewValue is ToolTip)) if (ToolTip.GetIsOpen(control) && e.NewValue != e.OldValue && !(e.NewValue is ToolTip))
{ {
var tip = control.GetValue(ToolTip.ToolTipProperty); if (e.NewValue is null)
{
tip.Content = e.NewValue; Close(control);
}
else
{
var tip = control.GetValue(ToolTip.ToolTipProperty);
tip.Content = e.NewValue;
}
} }
} }

1
src/Avalonia.Themes.Default/MenuItem.xaml

@ -60,6 +60,7 @@
<Popup Name="PART_Popup" <Popup Name="PART_Popup"
PlacementMode="Right" PlacementMode="Right"
IsLightDismissEnabled="True" IsLightDismissEnabled="True"
OverlayInputPassThroughElement="{Binding $parent[MenuItem]}"
IsOpen="{TemplateBinding IsSubMenuOpen, Mode=TwoWay}"> IsOpen="{TemplateBinding IsSubMenuOpen, Mode=TwoWay}">
<Border Background="{TemplateBinding Background}" <Border Background="{TemplateBinding Background}"
BorderBrush="{DynamicResource ThemeBorderMidBrush}" BorderBrush="{DynamicResource ThemeBorderMidBrush}"

1
src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml

@ -11,7 +11,6 @@
<Setter Property="HorizontalContentAlignment" Value="Left" /> <Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="VerticalContentAlignment" Value="Center" /> <Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" /> <Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" />
<Setter Property="MinWidth" Value="120" />
<Setter Property="MinHeight" Value="32" /> <Setter Property="MinHeight" Value="32" />
<!--<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}" /> <!--<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}" />
<Setter Property="FocusVisualMargin" Value="-7,-3,-7,-3" />--> <Setter Property="FocusVisualMargin" Value="-7,-3,-7,-3" />-->

1
src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml

@ -112,6 +112,7 @@
<Popup Name="PART_Popup" <Popup Name="PART_Popup"
WindowManagerAddShadowHint="False" WindowManagerAddShadowHint="False"
PlacementMode="Right" PlacementMode="Right"
OverlayInputPassThroughElement="{Binding $parent[MenuItem]}"
HorizontalOffset="{DynamicResource MenuFlyoutSubItemPopupHorizontalOffset}" HorizontalOffset="{DynamicResource MenuFlyoutSubItemPopupHorizontalOffset}"
IsLightDismissEnabled="True" IsLightDismissEnabled="True"
IsOpen="{TemplateBinding IsSubMenuOpen, Mode=TwoWay}"> IsOpen="{TemplateBinding IsSubMenuOpen, Mode=TwoWay}">

1
src/Avalonia.Themes.Fluent/Controls/RadioButton.xaml

@ -19,7 +19,6 @@
<Setter Property="HorizontalContentAlignment" Value="Left" /> <Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="VerticalContentAlignment" Value="Center" /> <Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" /> <Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" />
<Setter Property="MinWidth" Value="120" />
<Setter Property="Template"> <Setter Property="Template">
<ControlTemplate TargetType="RadioButton"> <ControlTemplate TargetType="RadioButton">
<Border Name="RootBorder" <Border Name="RootBorder"

2
src/Avalonia.X11/X11Atoms.cs

@ -114,7 +114,7 @@ namespace Avalonia.X11
public readonly IntPtr XA_WM_CLASS = (IntPtr)67; public readonly IntPtr XA_WM_CLASS = (IntPtr)67;
public readonly IntPtr XA_WM_TRANSIENT_FOR = (IntPtr)68; public readonly IntPtr XA_WM_TRANSIENT_FOR = (IntPtr)68;
public readonly IntPtr RR_PROPERTY_RANDR_EDID = (IntPtr)82; public readonly IntPtr EDID;
public readonly IntPtr WM_PROTOCOLS; public readonly IntPtr WM_PROTOCOLS;
public readonly IntPtr WM_DELETE_WINDOW; public readonly IntPtr WM_DELETE_WINDOW;

4
src/Avalonia.X11/X11Screens.cs

@ -91,12 +91,12 @@ namespace Avalonia.X11
var hasEDID = false; var hasEDID = false;
for(var pc = 0; pc < propertyCount; pc++) for(var pc = 0; pc < propertyCount; pc++)
{ {
if(properties[pc] == _x11.Atoms.RR_PROPERTY_RANDR_EDID) if(properties[pc] == _x11.Atoms.EDID)
hasEDID = true; hasEDID = true;
} }
if(!hasEDID) if(!hasEDID)
return null; return null;
XRRGetOutputProperty(_x11.Display, rrOutput, _x11.Atoms.RR_PROPERTY_RANDR_EDID, 0, EDIDStructureLength, false, false, _x11.Atoms.AnyPropertyType, out IntPtr actualType, out int actualFormat, out int bytesAfter, out _, out IntPtr prop); XRRGetOutputProperty(_x11.Display, rrOutput, _x11.Atoms.EDID, 0, EDIDStructureLength, false, false, _x11.Atoms.AnyPropertyType, out IntPtr actualType, out int actualFormat, out int bytesAfter, out _, out IntPtr prop);
if(actualType != _x11.Atoms.XA_INTEGER) if(actualType != _x11.Atoms.XA_INTEGER)
return null; return null;
if(actualFormat != 8) // Expecting an byte array if(actualFormat != 8) // Expecting an byte array

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

@ -270,6 +270,34 @@ namespace Avalonia.Controls.UnitTests
Assert.Empty(toolTip.Classes); Assert.Empty(toolTip.Classes);
} }
} }
[Fact]
public void Should_Close_On_Null_Tip()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var window = new Window();
var target = new Decorator()
{
[ToolTip.TipProperty] = "Tip",
[ToolTip.ShowDelayProperty] = 0
};
window.Content = target;
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
_mouseHelper.Enter(target);
Assert.True(ToolTip.GetIsOpen(target));
target[ToolTip.TipProperty] = null;
Assert.False(ToolTip.GetIsOpen(target));
}
}
} }
internal class ToolTipViewModel internal class ToolTipViewModel

Loading…
Cancel
Save