Skip to content

Commit f0a747d

Browse files
authored
Merge branch 'master' into feature/FormattedTextPort
2 parents f4814f5 + d53920b commit f0a747d

File tree

13 files changed

+149
-43
lines changed

13 files changed

+149
-43
lines changed

.github/ISSUE_TEMPLATE/bug_report.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
---
22
name: Bug report
3-
about: Create a report to help us improve
3+
about: Create a report to help us improve Avalonia
44
title: ''
5-
labels: ''
5+
labels: bug
66
assignees: ''
77

88
---

.github/ISSUE_TEMPLATE/feature_request.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name: Feature request
33
about: Suggest an idea for this project
44
title: ''
5-
labels: ''
5+
labels: enhancement
66
assignees: ''
77

88
---
Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
using System;
2-
3-
namespace Avalonia.Data
1+
namespace Avalonia.Data
42
{
53
public class IndexerBinding : IBinding
64
{
@@ -24,23 +22,7 @@ public InstancedBinding Initiate(
2422
object anchor = null,
2523
bool enableDataValidation = false)
2624
{
27-
var mode = Mode == BindingMode.Default ?
28-
targetProperty.GetMetadata(target.GetType()).DefaultBindingMode :
29-
Mode;
30-
31-
switch (mode)
32-
{
33-
case BindingMode.OneTime:
34-
return InstancedBinding.OneTime(Source.GetObservable(Property));
35-
case BindingMode.OneWay:
36-
return InstancedBinding.OneWay(Source.GetObservable(Property));
37-
case BindingMode.OneWayToSource:
38-
return InstancedBinding.OneWayToSource(Source.GetSubject(Property));
39-
case BindingMode.TwoWay:
40-
return InstancedBinding.TwoWay(Source.GetSubject(Property));
41-
default:
42-
throw new NotSupportedException("Unsupported BindingMode.");
43-
}
25+
return new InstancedBinding(Source.GetSubject(Property), Mode, BindingPriority.LocalValue);
4426
}
4527
}
4628
}

src/Avalonia.Controls/Primitives/VisualLayerManager.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,6 @@ public LightDismissOverlayLayer LightDismissOverlayLayer
6767
{
6868
get
6969
{
70-
if (IsPopup)
71-
return null;
7270
var rv = FindLayer<LightDismissOverlayLayer>();
7371
if (rv == null)
7472
{

src/Avalonia.Controls/Slider.cs

Lines changed: 101 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
namespace Avalonia.Controls
1313
{
14-
1514
/// <summary>
1615
/// Enum which describes how to position the ticks in a <see cref="Slider"/>.
1716
/// </summary>
@@ -84,6 +83,9 @@ public class Slider : RangeBase
8483
private IDisposable _increaseButtonSubscription;
8584
private IDisposable _increaseButtonReleaseDispose;
8685
private IDisposable _pointerMovedDispose;
86+
private IDisposable _trackOnKeyDownDispose;
87+
88+
private const double Tolerance = 0.0001;
8789

8890
/// <summary>
8991
/// Initializes static members of the <see cref="Slider"/> class.
@@ -95,7 +97,7 @@ static Slider()
9597
Thumb.DragStartedEvent.AddClassHandler<Slider>((x, e) => x.OnThumbDragStarted(e), RoutingStrategies.Bubble);
9698
Thumb.DragCompletedEvent.AddClassHandler<Slider>((x, e) => x.OnThumbDragCompleted(e),
9799
RoutingStrategies.Bubble);
98-
100+
99101
ValueProperty.OverrideMetadata<Slider>(new DirectPropertyMetadata<double>(enableDataValidation: true));
100102
}
101103

@@ -157,20 +159,22 @@ public TickPlacement TickPlacement
157159
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
158160
{
159161
base.OnApplyTemplate(e);
160-
162+
161163
_decreaseButtonPressDispose?.Dispose();
162164
_decreaseButtonReleaseDispose?.Dispose();
163165
_increaseButtonSubscription?.Dispose();
164166
_increaseButtonReleaseDispose?.Dispose();
165167
_pointerMovedDispose?.Dispose();
166-
168+
_trackOnKeyDownDispose?.Dispose();
169+
167170
_decreaseButton = e.NameScope.Find<Button>("PART_DecreaseButton");
168171
_track = e.NameScope.Find<Track>("PART_Track");
169172
_increaseButton = e.NameScope.Find<Button>("PART_IncreaseButton");
170173

171174
if (_track != null)
172175
{
173176
_track.IsThumbDragHandled = true;
177+
_trackOnKeyDownDispose = _track.AddDisposableHandler(KeyDownEvent, TrackOnKeyDown);
174178
}
175179

176180
if (_decreaseButton != null)
@@ -188,6 +192,94 @@ protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
188192
_pointerMovedDispose = this.AddDisposableHandler(PointerMovedEvent, TrackMoved, RoutingStrategies.Tunnel);
189193
}
190194

195+
private void TrackOnKeyDown(object sender, KeyEventArgs e)
196+
{
197+
if (e.KeyModifiers != KeyModifiers.None) return;
198+
199+
switch (e.Key)
200+
{
201+
case Key.Left:
202+
MoveToNextTick(-SmallChange);
203+
break;
204+
205+
case Key.Right:
206+
MoveToNextTick(SmallChange);
207+
break;
208+
209+
case Key.PageUp:
210+
MoveToNextTick(-LargeChange);
211+
break;
212+
213+
case Key.PageDown:
214+
MoveToNextTick(LargeChange);
215+
break;
216+
217+
case Key.Home:
218+
Value = Minimum;
219+
break;
220+
221+
case Key.End:
222+
Value = Maximum;
223+
break;
224+
}
225+
}
226+
227+
private void MoveToNextTick(double direction)
228+
{
229+
if (direction == 0.0) return;
230+
231+
var value = Value;
232+
233+
// Find the next value by snapping
234+
var next = SnapToTick(Math.Max(Minimum, Math.Min(Maximum, value + direction)));
235+
236+
var greaterThan = direction > 0; //search for the next tick greater than value?
237+
238+
// If the snapping brought us back to value, find the next tick point
239+
if (Math.Abs(next - value) < Tolerance
240+
&& !(greaterThan && Math.Abs(value - Maximum) < Tolerance) // Stop if searching up if already at Max
241+
&& !(!greaterThan && Math.Abs(value - Minimum) < Tolerance)) // Stop if searching down if already at Min
242+
{
243+
var ticks = Ticks;
244+
245+
// If ticks collection is available, use it.
246+
// Note that ticks may be unsorted.
247+
if (ticks != null && ticks.Count > 0)
248+
{
249+
foreach (var tick in ticks)
250+
{
251+
// Find the smallest tick greater than value or the largest tick less than value
252+
if (greaterThan && MathUtilities.GreaterThan(tick, value) &&
253+
(MathUtilities.LessThan(tick, next) || Math.Abs(next - value) < Tolerance)
254+
|| !greaterThan && MathUtilities.LessThan(tick, value) &&
255+
(MathUtilities.GreaterThan(tick, next) || Math.Abs(next - value) < Tolerance))
256+
{
257+
next = tick;
258+
}
259+
}
260+
}
261+
else if (MathUtilities.GreaterThan(TickFrequency, 0.0))
262+
{
263+
// Find the current tick we are at
264+
var tickNumber = Math.Round((value - Minimum) / TickFrequency);
265+
266+
if (greaterThan)
267+
tickNumber += 1.0;
268+
else
269+
tickNumber -= 1.0;
270+
271+
next = Minimum + tickNumber * TickFrequency;
272+
}
273+
}
274+
275+
276+
// Update if we've found a better value
277+
if (Math.Abs(next - value) > Tolerance)
278+
{
279+
Value = next;
280+
}
281+
}
282+
191283
private void TrackMoved(object sender, PointerEventArgs e)
192284
{
193285
if (_isDragging)
@@ -272,19 +364,18 @@ private double SnapToTick(double value)
272364
{
273365
if (IsSnapToTickEnabled)
274366
{
275-
double previous = Minimum;
276-
double next = Maximum;
367+
var previous = Minimum;
368+
var next = Maximum;
277369

278370
// This property is rarely set so let's try to avoid the GetValue
279371
var ticks = Ticks;
280372

281373
// If ticks collection is available, use it.
282374
// Note that ticks may be unsorted.
283-
if ((ticks != null) && (ticks.Count > 0))
375+
if (ticks != null && ticks.Count > 0)
284376
{
285-
for (int i = 0; i < ticks.Count; i++)
377+
foreach (var tick in ticks)
286378
{
287-
double tick = ticks[i];
288379
if (MathUtilities.AreClose(tick, value))
289380
{
290381
return value;
@@ -302,7 +393,7 @@ private double SnapToTick(double value)
302393
}
303394
else if (MathUtilities.GreaterThan(TickFrequency, 0.0))
304395
{
305-
previous = Minimum + (Math.Round(((value - Minimum) / TickFrequency)) * TickFrequency);
396+
previous = Minimum + Math.Round((value - Minimum) / TickFrequency) * TickFrequency;
306397
next = Math.Min(Maximum, previous + TickFrequency);
307398
}
308399

src/Avalonia.Controls/ToolTipService.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,16 @@ internal void TipChanged(AvaloniaPropertyChangedEventArgs e)
3838

3939
if (ToolTip.GetIsOpen(control) && e.NewValue != e.OldValue && !(e.NewValue is ToolTip))
4040
{
41-
var tip = control.GetValue(ToolTip.ToolTipProperty);
42-
43-
tip.Content = e.NewValue;
41+
if (e.NewValue is null)
42+
{
43+
Close(control);
44+
}
45+
else
46+
{
47+
var tip = control.GetValue(ToolTip.ToolTipProperty);
48+
49+
tip.Content = e.NewValue;
50+
}
4451
}
4552
}
4653

src/Avalonia.Themes.Default/MenuItem.xaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
<Popup Name="PART_Popup"
6161
PlacementMode="Right"
6262
IsLightDismissEnabled="True"
63+
OverlayInputPassThroughElement="{Binding $parent[MenuItem]}"
6364
IsOpen="{TemplateBinding IsSubMenuOpen, Mode=TwoWay}">
6465
<Border Background="{TemplateBinding Background}"
6566
BorderBrush="{DynamicResource ThemeBorderMidBrush}"

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
<Setter Property="HorizontalContentAlignment" Value="Left" />
1212
<Setter Property="VerticalContentAlignment" Value="Center" />
1313
<Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" />
14-
<Setter Property="MinWidth" Value="120" />
1514
<Setter Property="MinHeight" Value="32" />
1615
<!--<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}" />
1716
<Setter Property="FocusVisualMargin" Value="-7,-3,-7,-3" />-->

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@
112112
<Popup Name="PART_Popup"
113113
WindowManagerAddShadowHint="False"
114114
PlacementMode="Right"
115+
OverlayInputPassThroughElement="{Binding $parent[MenuItem]}"
115116
HorizontalOffset="{DynamicResource MenuFlyoutSubItemPopupHorizontalOffset}"
116117
IsLightDismissEnabled="True"
117118
IsOpen="{TemplateBinding IsSubMenuOpen, Mode=TwoWay}">

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
<Setter Property="HorizontalContentAlignment" Value="Left" />
2020
<Setter Property="VerticalContentAlignment" Value="Center" />
2121
<Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" />
22-
<Setter Property="MinWidth" Value="120" />
2322
<Setter Property="Template">
2423
<ControlTemplate TargetType="RadioButton">
2524
<Border Name="RootBorder"

src/Avalonia.X11/X11Atoms.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ internal class X11Atoms
114114
public readonly IntPtr XA_WM_CLASS = (IntPtr)67;
115115
public readonly IntPtr XA_WM_TRANSIENT_FOR = (IntPtr)68;
116116

117-
public readonly IntPtr RR_PROPERTY_RANDR_EDID = (IntPtr)82;
117+
public readonly IntPtr EDID;
118118

119119
public readonly IntPtr WM_PROTOCOLS;
120120
public readonly IntPtr WM_DELETE_WINDOW;

src/Avalonia.X11/X11Screens.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,12 @@ private void OnEvent(ref XEvent ev)
9191
var hasEDID = false;
9292
for(var pc = 0; pc < propertyCount; pc++)
9393
{
94-
if(properties[pc] == _x11.Atoms.RR_PROPERTY_RANDR_EDID)
94+
if(properties[pc] == _x11.Atoms.EDID)
9595
hasEDID = true;
9696
}
9797
if(!hasEDID)
9898
return null;
99-
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);
99+
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);
100100
if(actualType != _x11.Atoms.XA_INTEGER)
101101
return null;
102102
if(actualFormat != 8) // Expecting an byte array

tests/Avalonia.Controls.UnitTests/ToolTipTests.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,34 @@ public void Clearing_IsOpen_Should_Remove_Open_Class()
270270
Assert.Empty(toolTip.Classes);
271271
}
272272
}
273+
274+
[Fact]
275+
public void Should_Close_On_Null_Tip()
276+
{
277+
using (UnitTestApplication.Start(TestServices.StyledWindow))
278+
{
279+
var window = new Window();
280+
281+
var target = new Decorator()
282+
{
283+
[ToolTip.TipProperty] = "Tip",
284+
[ToolTip.ShowDelayProperty] = 0
285+
};
286+
287+
window.Content = target;
288+
289+
window.ApplyTemplate();
290+
window.Presenter.ApplyTemplate();
291+
292+
_mouseHelper.Enter(target);
293+
294+
Assert.True(ToolTip.GetIsOpen(target));
295+
296+
target[ToolTip.TipProperty] = null;
297+
298+
Assert.False(ToolTip.GetIsOpen(target));
299+
}
300+
}
273301
}
274302

275303
internal class ToolTipViewModel

0 commit comments

Comments
 (0)