Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/jcyuan/Avalonia
Browse files Browse the repository at this point in the history
  • Loading branch information
jcyuan committed Dec 17, 2023
2 parents 8661b9d + 23ea706 commit 276562c
Show file tree
Hide file tree
Showing 16 changed files with 225 additions and 41 deletions.
3 changes: 2 additions & 1 deletion src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1245,8 +1245,9 @@ private TextLineMetrics CreateLineMetrics()
{
var textMetrics = textRun.TextMetrics;
var glyphRun = textRun.GlyphRun;
var runBounds = glyphRun.InkBounds.WithX(widthIncludingWhitespace + glyphRun.InkBounds.X);

bounds = bounds.Union(glyphRun.InkBounds);
bounds = bounds.Union(runBounds);

if (fontRenderingEmSize < textMetrics.FontRenderingEmSize)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1153,15 +1153,23 @@ public object this[int index]
get { return GetItemAt(index); }
}

bool IList.IsFixedSize => false;
bool IList.IsReadOnly => true;
bool IList.IsFixedSize => SourceList?.IsFixedSize ?? true;
bool IList.IsReadOnly => SourceList?.IsReadOnly ?? true;
bool ICollection.IsSynchronized => false;
object ICollection.SyncRoot => this;

object IList.this[int index]
{
get => this[index];
set => throw new NotSupportedException();
set
{
SourceList[index] = value;
if (SourceList is not INotifyCollectionChanged)
{
// TODO: implement Replace
ProcessCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset, value));
}
}
}

/// <summary>
Expand Down Expand Up @@ -3992,9 +4000,36 @@ private void VerifyRefreshNotDeferred()
}
}

int IList.Add(object value) => throw new NotSupportedException();
void IList.Clear() => throw new NotSupportedException();
void IList.Insert(int index, object value) => throw new NotSupportedException();
int IList.Add(object value)
{
var index = SourceList.Add(value);
if (SourceList is not INotifyCollectionChanged)
{
ProcessCollectionChanged(
new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value));
}
return index;
}

void IList.Clear()
{
SourceList.Clear();
if (SourceList is not INotifyCollectionChanged)
{
ProcessCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}

void IList.Insert(int index, object value)
{
SourceList.Insert(index, value);
if (SourceList is not INotifyCollectionChanged)
{
// TODO: implement Insert
ProcessCollectionChanged(
new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset, value));
}
}
void ICollection.CopyTo(Array array, int index) => InternalList.CopyTo(array, index);

/// <summary>
Expand Down
6 changes: 0 additions & 6 deletions src/Avalonia.Controls.DataGrid/DataGrid.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1828,12 +1828,6 @@ internal int SlotCount
private set;
}

internal bool UpdatedStateOnMouseLeftButtonDown
{
get;
set;
}

/// <summary>
/// Indicates whether or not to use star-sizing logic. If the DataGrid has infinite available space,
/// then star sizing doesn't make sense. In this case, all star columns grow to a predefined size of
Expand Down
2 changes: 0 additions & 2 deletions src/Avalonia.Controls.DataGrid/DataGridCell.cs
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,6 @@ private void DataGridCell_PointerPressed(PointerPressedEventArgs e)
{
e.Handled = handled;
}

OwningGrid.UpdatedStateOnMouseLeftButtonDown = true;
}
}
else if (e.GetCurrentPoint(this).Properties.IsRightButtonPressed)
Expand Down
20 changes: 10 additions & 10 deletions src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -349,15 +349,15 @@ public object GetDataItem(int index)
{
Debug.Assert(index >= 0);

IList list = List;
if (list != null)
if (DataSource is DataGridCollectionView collectionView)
{
return (index < list.Count) ? list[index] : null;
return (index < collectionView.Count) ? collectionView.GetItemAt(index) : null;
}

if (DataSource is DataGridCollectionView collectionView)
IList list = List;
if (list != null)
{
return (index < collectionView.Count) ? collectionView.GetItemAt(index) : null;
return (index < list.Count) ? list[index] : null;
}

IEnumerable enumerable = DataSource;
Expand Down Expand Up @@ -419,15 +419,15 @@ public bool GetPropertyIsReadOnly(string propertyName)

public int IndexOf(object dataItem)
{
IList list = List;
if (list != null)
if (DataSource is DataGridCollectionView cv)
{
return list.IndexOf(dataItem);
return cv.IndexOf(dataItem);
}

if (DataSource is DataGridCollectionView cv)
IList list = List;
if (list != null)
{
return cv.IndexOf(dataItem);
return list.IndexOf(dataItem);
}

IEnumerable enumerable = DataSource;
Expand Down
8 changes: 0 additions & 8 deletions src/Avalonia.Controls.DataGrid/DataGridRow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -771,14 +771,6 @@ private void DataGridRow_PointerPressed(PointerPressedEventArgs e)
if (OwningGrid != null)
{
OwningGrid.IsDoubleClickRecordsClickOnCall(this);
if (OwningGrid.UpdatedStateOnMouseLeftButtonDown)
{
OwningGrid.UpdatedStateOnMouseLeftButtonDown = false;
}
else
{
e.Handled = OwningGrid.UpdateStateOnMouseLeftButtonDown(e, -1, Slot, false);
}
}
}

Expand Down
1 change: 0 additions & 1 deletion src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,6 @@ private void DataGridRowHeader_PointerPressed(object sender, PointerPressedEvent
Debug.Assert(sender is DataGridRowHeader);
Debug.Assert(sender == this);
e.Handled = OwningGrid.UpdateStateOnMouseLeftButtonDown(e, -1, Slot, false);
OwningGrid.UpdatedStateOnMouseLeftButtonDown = true;
}
}
else if (e.GetCurrentPoint(this).Properties.IsRightButtonPressed)
Expand Down
14 changes: 12 additions & 2 deletions src/Avalonia.Controls/Selection/SelectionModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,18 @@ public int SelectedIndex
get => _selectedIndex;
set
{
if (_operation is not null && _operation.UpdateCount == 0)
{
// An operation is in the process of being committed. In this case, if the new
// value for SelectedIndex is unchanged then we need to ignore it. It could be
// the result of a two-way binding to SelectedIndex writing back to the
// property. The binding system should really be fixed to ensure that it's not
// writing back the same value, but this is a workaround until the binding
// refactor is complete. See #13676.
if (value == _selectedIndex)
return;
}

using var update = BatchUpdate();
Clear();
Select(value);
Expand Down Expand Up @@ -675,8 +687,6 @@ private void CommitOperation(Operation operation, bool raisePropertyChanged = tr
}
}



if (raisePropertyChanged)
{
if (oldSelectedIndex != _selectedIndex)
Expand Down
8 changes: 6 additions & 2 deletions src/Avalonia.Controls/TabControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ static TabControl()
{
SelectionModeProperty.OverrideDefaultValue<TabControl>(SelectionMode.AlwaysSelected);
ItemsPanelProperty.OverrideDefaultValue<TabControl>(DefaultPanel);
TabStripPlacementProperty.Changed.AddClassHandler<TabControl>((x, e) => x.UpdateTabStripPlacement());
AffectsMeasure<TabControl>(TabStripPlacementProperty);
SelectedItemProperty.Changed.AddClassHandler<TabControl>((x, e) => x.UpdateSelectedContent());
AutomationProperties.ControlTypeOverrideProperty.OverrideDefaultValue<TabControl>(AutomationControlType.Tab);
Expand Down Expand Up @@ -154,7 +153,7 @@ bool IContentPresenterHost.RegisterContentPresenter(ContentPresenter presenter)

protected internal override Control CreateContainerForItemOverride(object? item, int index, object? recycleKey)
{
return new TabItem { TabStripPlacement = TabStripPlacement };
return new TabItem();
}

protected internal override bool NeedsContainerOverride(object? item, int index, out object? recycleKey)
Expand All @@ -166,6 +165,11 @@ protected internal override void PrepareContainerForItemOverride(Control element
{
base.PrepareContainerForItemOverride(element, item, index);

if (element is TabItem tabItem)
{
tabItem.TabStripPlacement = TabStripPlacement;
}

if (index == SelectedIndex)
{
UpdateSelectedContent(element);
Expand Down
2 changes: 2 additions & 0 deletions src/Browser/Avalonia.Browser/BrowserAppBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Threading.Tasks;
using Avalonia.Browser.Interop;
using Avalonia.Metadata;

namespace Avalonia.Browser;

Expand All @@ -17,6 +18,7 @@ public class BrowserPlatformOptions
/// If registered, service worker can work as a save file picker fallback on the browsers that don't support native implementation.
/// For more details, see https://github.com/jimmywarting/native-file-system-adapter#a-note-when-downloading-with-the-polyfilled-version.
/// </summary>
[Unstable("This property might not work reliably.")]
public bool RegisterAvaloniaServiceWorker { get; set; }

/// <summary>
Expand Down
4 changes: 3 additions & 1 deletion src/Browser/Avalonia.Browser/webapp/modules/sw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ self.addEventListener("activate", event /* ExtendableEvent */ => {
(event as any).waitUntil((self as any).clients.claim());
});

const map = new Map();
(self as any).map = new Map();

// This should be called once per download
// Each event has a dataChannel that the data will be piped through
Expand All @@ -61,12 +61,14 @@ globalThis.addEventListener("message", evt => {
new MessagePortSource(evt.data.readablePort),
new CountQueuingStrategy({ highWaterMark: 4 })
);
const map = (self as any).map;
map.set(data.url, data);
}
});

globalThis.addEventListener("fetch", evt => {
const url = (evt as any).request.url;
const map = (self as any).map;
const data = map.get(url);
if (!data) return null;
map.delete(url);
Expand Down
2 changes: 1 addition & 1 deletion src/Windows/Avalonia.Win32/DirectX/directx.idl
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ interface IDXGIAdapter : IDXGIObject
[uuid(310d36a0-d2e7-4c0a-aa04-6a9d23b8886a)]
interface IDXGISwapChain : IDXGIDeviceSubObject
{
HRESULT Present([in] UINT SyncInterval, [in] UINT Flags);
INT32 Present([in] UINT SyncInterval, [in] UINT Flags);
HRESULT GetBuffer([in] UINT Buffer, [in, annotation("_In_")] REFIID riid, [in, out, annotation("_COM_Outptr_")] void** ppSurface);
HRESULT SetFullscreenState([in] BOOL Fullscreen, [in, annotation("_In_opt_")] IDXGIOutput* pTarget);
HRESULT GetFullscreenState([out, annotation("_Out_opt_")] BOOL* pFullscreen, [out, annotation("_COM_Outptr_opt_result_maybenull_")] IDXGIOutput** ppTarget);
Expand Down
2 changes: 1 addition & 1 deletion src/Windows/Avalonia.Win32/WindowImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1348,7 +1348,7 @@ private void UpdateWindowProperties(WindowProperties newProperties, bool forceCh
else
SetFullScreen(newProperties.IsFullScreen);

if (!_isFullScreenActive)
if (!_isFullScreenActive && ((oldProperties.Decorations != newProperties.Decorations) || forceChanges))
{
var style = GetStyle();

Expand Down
59 changes: 59 additions & 0 deletions tests/Avalonia.Controls.UnitTests/ListBoxTests_Multiple.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Styling;
Expand Down Expand Up @@ -577,6 +578,64 @@ public void Shift_Down_Key_Selecting_Selects_Range_End_From_Focus_Moved_With_Ctr
Assert.True(target.ContainerFromIndex(3).IsFocused);
}

[Fact]
public void SelectAll_Works_From_No_Selection_When_SelectedItem_Is_Bound_TwoWay()
{
// Issue #13676
using var app = UnitTestApplication.Start(TestServices.RealFocus);
var target = new ListBox
{
Template = new FuncControlTemplate(CreateListBoxTemplate),
ItemsSource = new[] { "Foo", "Bar", "Baz", "Qux" },
SelectionMode = SelectionMode.Multiple,
Width = 100,
Height = 100,
};

var root = new TestRoot(target);
root.LayoutManager.ExecuteInitialLayoutPass();

target.Bind(ListBox.SelectedItemProperty, new Binding("Tag")
{
Mode = BindingMode.TwoWay,
RelativeSource = new RelativeSource(RelativeSourceMode.Self),
});

target.SelectAll();

Assert.Equal(new[] { 0, 1, 2, 3 }, target.Selection.SelectedIndexes);
Assert.Equal(new[] { "Foo", "Bar", "Baz", "Qux" }, target.SelectedItems);
}

[Fact]
public void SelectAll_Works_From_No_Selection_When_SelectedIndex_Is_Bound_TwoWay()
{
// Issue #13676
using var app = UnitTestApplication.Start(TestServices.RealFocus);
var target = new ListBox
{
Template = new FuncControlTemplate(CreateListBoxTemplate),
ItemsSource = new[] { "Foo", "Bar", "Baz", "Qux" },
SelectionMode = SelectionMode.Multiple,
Width = 100,
Height = 100,
};

var root = new TestRoot(target);
root.LayoutManager.ExecuteInitialLayoutPass();

target.Bind(ListBox.SelectedIndexProperty, new Binding("Tag")
{
Mode = BindingMode.TwoWay,
RelativeSource = new RelativeSource(RelativeSourceMode.Self),
});

target.SelectAll();

Assert.Equal(new[] { 0, 1, 2, 3 }, target.Selection.SelectedIndexes);
Assert.Equal(new[] { "Foo", "Bar", "Baz", "Qux" }, target.SelectedItems);
}

private Control CreateListBoxTemplate(TemplatedControl parent, INameScope scope)
{
return new ScrollViewer
Expand Down
Loading

0 comments on commit 276562c

Please sign in to comment.