183 changed files with 2039 additions and 1877 deletions
@ -0,0 +1,33 @@ |
|||
<UserControl xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|||
x:Class="ControlCatalog.Pages.TabStripPage" |
|||
xmlns="https://github.com/avaloniaui"> |
|||
<StackPanel Orientation="Vertical" Spacing="4"> |
|||
<TextBlock Classes="h1">TabStrip</TextBlock> |
|||
<TextBlock Classes="h2">A control which displays a selectable strip of tabs</TextBlock> |
|||
|
|||
<Separator Margin="0 16"/> |
|||
|
|||
<TextBlock Classes="h1">Defined in XAML</TextBlock> |
|||
<TabStrip> |
|||
<TabStripItem>Item 1</TabStripItem> |
|||
<TabStripItem>Item 2</TabStripItem> |
|||
<TabStripItem IsEnabled="False">Disabled</TabStripItem> |
|||
</TabStrip> |
|||
|
|||
<Separator Margin="0 16"/> |
|||
|
|||
<TextBlock Classes="h1">Dynamically generated</TextBlock> |
|||
<TabStrip Items="{Binding}"> |
|||
<TabStrip.Styles> |
|||
<Style Selector="TabStripItem"> |
|||
<Setter Property="IsEnabled" Value="{Binding IsEnabled}"/> |
|||
</Style> |
|||
</TabStrip.Styles> |
|||
<TabStrip.ItemTemplate> |
|||
<DataTemplate> |
|||
<TextBlock Text="{Binding Header}"/> |
|||
</DataTemplate> |
|||
</TabStrip.ItemTemplate> |
|||
</TabStrip> |
|||
</StackPanel> |
|||
</UserControl> |
|||
@ -0,0 +1,45 @@ |
|||
using System; |
|||
using Avalonia; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Markup.Xaml; |
|||
using Avalonia.Media.Imaging; |
|||
using Avalonia.Platform; |
|||
|
|||
namespace ControlCatalog.Pages |
|||
{ |
|||
public class TabStripPage : UserControl |
|||
{ |
|||
public TabStripPage() |
|||
{ |
|||
InitializeComponent(); |
|||
|
|||
DataContext = new[] |
|||
{ |
|||
new TabStripItemViewModel |
|||
{ |
|||
Header = "Item 1", |
|||
}, |
|||
new TabStripItemViewModel |
|||
{ |
|||
Header = "Item 2", |
|||
}, |
|||
new TabStripItemViewModel |
|||
{ |
|||
Header = "Disabled", |
|||
IsEnabled = false, |
|||
}, |
|||
}; |
|||
} |
|||
|
|||
private void InitializeComponent() |
|||
{ |
|||
AvaloniaXamlLoader.Load(this); |
|||
} |
|||
|
|||
private class TabStripItemViewModel |
|||
{ |
|||
public string Header { get; set; } |
|||
public bool IsEnabled { get; set; } = true; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,106 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
namespace Avalonia.Utilities |
|||
{ |
|||
/// <summary>
|
|||
/// A task-like operation that is guaranteed to finish continuations synchronously,
|
|||
/// can be used for parametrized one-shot events
|
|||
/// </summary>
|
|||
public struct SynchronousCompletionAsyncResult<T> : INotifyCompletion |
|||
{ |
|||
private readonly SynchronousCompletionAsyncResultSource<T> _source; |
|||
private readonly T _result; |
|||
private readonly bool _isValid; |
|||
internal SynchronousCompletionAsyncResult(SynchronousCompletionAsyncResultSource<T> source) |
|||
{ |
|||
_source = source; |
|||
_result = default; |
|||
_isValid = true; |
|||
} |
|||
|
|||
public SynchronousCompletionAsyncResult(T result) |
|||
{ |
|||
_result = result; |
|||
_source = null; |
|||
_isValid = true; |
|||
} |
|||
|
|||
static void ThrowNotInitialized() => |
|||
throw new InvalidOperationException("This SynchronousCompletionAsyncResult was not initialized"); |
|||
|
|||
public bool IsCompleted |
|||
{ |
|||
get |
|||
{ |
|||
if (!_isValid) |
|||
ThrowNotInitialized(); |
|||
return _source == null || _source.IsCompleted; |
|||
} |
|||
} |
|||
|
|||
public T GetResult() |
|||
{ |
|||
if (!_isValid) |
|||
ThrowNotInitialized(); |
|||
return _source == null ? _result : _source.Result; |
|||
} |
|||
|
|||
|
|||
public void OnCompleted(Action continuation) |
|||
{ |
|||
if (!_isValid) |
|||
ThrowNotInitialized(); |
|||
if (_source == null) |
|||
continuation(); |
|||
else |
|||
_source.OnCompleted(continuation); |
|||
} |
|||
|
|||
public SynchronousCompletionAsyncResult<T> GetAwaiter() => this; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Source for incomplete SynchronousCompletionAsyncResult
|
|||
/// </summary>
|
|||
/// <typeparam name="T"></typeparam>
|
|||
public class SynchronousCompletionAsyncResultSource<T> |
|||
{ |
|||
private T _result; |
|||
internal bool IsCompleted { get; private set; } |
|||
public SynchronousCompletionAsyncResult<T> AsyncResult => new SynchronousCompletionAsyncResult<T>(this); |
|||
|
|||
internal T Result => IsCompleted ? |
|||
_result : |
|||
throw new InvalidOperationException("Asynchronous operation is not yet completed"); |
|||
|
|||
private List<Action> _continuations; |
|||
|
|||
internal void OnCompleted(Action continuation) |
|||
{ |
|||
if(_continuations==null) |
|||
_continuations = new List<Action>(); |
|||
_continuations.Add(continuation); |
|||
} |
|||
|
|||
public void SetResult(T result) |
|||
{ |
|||
if (IsCompleted) |
|||
throw new InvalidOperationException("Asynchronous operation is already completed"); |
|||
_result = result; |
|||
IsCompleted = true; |
|||
if(_continuations!=null) |
|||
foreach (var c in _continuations) |
|||
c(); |
|||
_continuations = null; |
|||
} |
|||
|
|||
public void TrySetResult(T result) |
|||
{ |
|||
if(IsCompleted) |
|||
return; |
|||
SetResult(result); |
|||
} |
|||
} |
|||
} |
|||
@ -1,35 +0,0 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
|
|||
namespace Avalonia.Controls.Templates |
|||
{ |
|||
/// <summary>
|
|||
/// Selects a member of an object using a <see cref="Func{TObject, TMember}"/>.
|
|||
/// </summary>
|
|||
public class FuncMemberSelector<TObject, TMember> : IMemberSelector |
|||
{ |
|||
private readonly Func<TObject, TMember> _selector; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="FuncMemberSelector{TObject, TMember}"/>
|
|||
/// class.
|
|||
/// </summary>
|
|||
/// <param name="selector">The selector.</param>
|
|||
public FuncMemberSelector(Func<TObject, TMember> selector) |
|||
{ |
|||
this._selector = selector; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Selects a member of an object.
|
|||
/// </summary>
|
|||
/// <param name="o">The object.</param>
|
|||
/// <returns>The selected member.</returns>
|
|||
public object Select(object o) |
|||
{ |
|||
return (o is TObject) ? _selector((TObject)o) : default(TMember); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
using System; |
|||
|
|||
namespace Avalonia.Controls.Templates |
|||
{ |
|||
public static class FuncTemplateNameScopeExtensions |
|||
{ |
|||
public static T RegisterInNameScope<T>(this T control, INameScope scope) |
|||
where T : StyledElement |
|||
{ |
|||
scope.Register(control.Name, control); |
|||
return control; |
|||
} |
|||
} |
|||
} |
|||
@ -1,18 +0,0 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
namespace Avalonia.Controls.Templates |
|||
{ |
|||
/// <summary>
|
|||
/// Selects a member of an object.
|
|||
/// </summary>
|
|||
public interface IMemberSelector |
|||
{ |
|||
/// <summary>
|
|||
/// Selects a member of an object.
|
|||
/// </summary>
|
|||
/// <param name="o">The object.</param>
|
|||
/// <returns>The selected member.</returns>
|
|||
object Select(object o); |
|||
} |
|||
} |
|||
@ -1,23 +1,24 @@ |
|||
<UserControl xmlns="https://github.com/avaloniaui" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|||
x:Class="Avalonia.Diagnostics.DevTools"> |
|||
<Grid RowDefinitions="Auto,*,Auto"> |
|||
<TabStrip SelectedIndex="{Binding SelectedTab, Mode=TwoWay}"> |
|||
<TabStripItem Content="Logical Tree"/> |
|||
<TabStripItem Content="Visual Tree"/> |
|||
<TabStripItem Content="Events"/> |
|||
</TabStrip> |
|||
<Grid RowDefinitions="*,Auto" Margin="4"> |
|||
|
|||
<ContentControl Content="{Binding Content}" Grid.Row="1"/> |
|||
|
|||
<StackPanel Spacing="4" Orientation="Horizontal" Grid.Row="2"> |
|||
<TextBlock>Hold Ctrl+Shift over a control to inspect.</TextBlock> |
|||
<Separator Width="8"/> |
|||
<TextBlock>Focused:</TextBlock> |
|||
<TextBlock Text="{Binding FocusedControl}"/> |
|||
<Separator Width="8"/> |
|||
<TextBlock>Pointer Over:</TextBlock> |
|||
<TextBlock Text="{Binding PointerOverElement}"/> |
|||
</StackPanel> |
|||
</Grid> |
|||
<TabControl Grid.Row="0" Items="{Binding Tools}" SelectedItem="{Binding SelectedTool}"> |
|||
<TabControl.ItemTemplate> |
|||
<DataTemplate> |
|||
<TextBlock Text="{Binding Name}" /> |
|||
</DataTemplate> |
|||
</TabControl.ItemTemplate> |
|||
</TabControl> |
|||
|
|||
<StackPanel Grid.Row="1" Spacing="4" Orientation="Horizontal"> |
|||
<TextBlock>Hold Ctrl+Shift over a control to inspect.</TextBlock> |
|||
<Separator Width="8" /> |
|||
<TextBlock>Focused:</TextBlock> |
|||
<TextBlock Text="{Binding FocusedControl}" /> |
|||
<Separator Width="8" /> |
|||
<TextBlock>Pointer Over:</TextBlock> |
|||
<TextBlock Text="{Binding PointerOverElement}" /> |
|||
</StackPanel> |
|||
</Grid> |
|||
</UserControl> |
|||
|
|||
@ -0,0 +1,16 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
namespace Avalonia.Diagnostics.ViewModels |
|||
{ |
|||
/// <summary>
|
|||
/// View model interface for tool showing up in DevTools
|
|||
/// </summary>
|
|||
public interface IDevToolViewModel |
|||
{ |
|||
/// <summary>
|
|||
/// Name of a tool.
|
|||
/// </summary>
|
|||
string Name { get; } |
|||
} |
|||
} |
|||
@ -1,4 +1,7 @@ |
|||
using System; |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
using System.ComponentModel; |
|||
using System.Reactive.Linq; |
|||
using System.Reflection; |
|||
@ -0,0 +1,73 @@ |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Utilities; |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
public class ChildNameScope : INameScope |
|||
{ |
|||
private readonly INameScope _parentScope; |
|||
private readonly NameScope _inner = new NameScope(); |
|||
|
|||
public ChildNameScope(INameScope parentScope) |
|||
{ |
|||
_parentScope = parentScope; |
|||
} |
|||
|
|||
public void Register(string name, object element) => _inner.Register(name, element); |
|||
|
|||
public SynchronousCompletionAsyncResult<object> FindAsync(string name) |
|||
{ |
|||
var found = Find(name); |
|||
if (found != null) |
|||
return new SynchronousCompletionAsyncResult<object>(found); |
|||
// Not found and both current and parent scope are in completed state
|
|||
if(IsCompleted) |
|||
return new SynchronousCompletionAsyncResult<object>(null); |
|||
return DoFindAsync(name); |
|||
} |
|||
|
|||
public SynchronousCompletionAsyncResult<object> DoFindAsync(string name) |
|||
{ |
|||
var src = new SynchronousCompletionAsyncResultSource<object>(); |
|||
|
|||
void ParentSearch() |
|||
{ |
|||
var parentSearch = _parentScope.FindAsync(name); |
|||
if (parentSearch.IsCompleted) |
|||
src.SetResult(parentSearch.GetResult()); |
|||
else |
|||
parentSearch.OnCompleted(() => src.SetResult(parentSearch.GetResult())); |
|||
} |
|||
if (!_inner.IsCompleted) |
|||
{ |
|||
// Guaranteed to be incomplete at this point
|
|||
var innerSearch = _inner.FindAsync(name); |
|||
innerSearch.OnCompleted(() => |
|||
{ |
|||
var value = innerSearch.GetResult(); |
|||
if (value != null) |
|||
src.SetResult(value); |
|||
else ParentSearch(); |
|||
}); |
|||
} |
|||
else |
|||
ParentSearch(); |
|||
|
|||
return src.AsyncResult; |
|||
} |
|||
|
|||
public object Find(string name) |
|||
{ |
|||
var found = _inner.Find(name); |
|||
if (found != null) |
|||
return found; |
|||
if (_inner.IsCompleted) |
|||
return _parentScope.Find(name); |
|||
return null; |
|||
} |
|||
|
|||
public void Complete() => _inner.Complete(); |
|||
|
|||
public bool IsCompleted => _inner.IsCompleted && _parentScope.IsCompleted; |
|||
} |
|||
} |
|||
@ -0,0 +1,62 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using System.Reactive.Disposables; |
|||
using System.Reactive.Threading.Tasks; |
|||
using System.Reflection; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.LogicalTree; |
|||
using Avalonia.Reactive; |
|||
using Avalonia.Utilities; |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
public class NameScopeLocator |
|||
{ |
|||
/// <summary>
|
|||
/// Tracks a named control relative to another control.
|
|||
/// </summary>
|
|||
/// <param name="scope">The scope relative from which the object should be resolved.</param>
|
|||
/// <param name="name">The name of the object to find.</param>
|
|||
public static IObservable<object> Track(INameScope scope, string name) |
|||
{ |
|||
return new NeverEndingSynchronousCompletionAsyncResultObservable<object>(scope.FindAsync(name)); |
|||
} |
|||
|
|||
// This class is implemented in such weird way because for some reason
|
|||
// our binding system doesn't expect OnCompleted to be ever called and
|
|||
// seems to treat it as binding cancellation or something
|
|||
|
|||
private class NeverEndingSynchronousCompletionAsyncResultObservable<T> : IObservable<T> |
|||
{ |
|||
private T _value; |
|||
private SynchronousCompletionAsyncResult<T>? _asyncResult; |
|||
|
|||
public NeverEndingSynchronousCompletionAsyncResultObservable(SynchronousCompletionAsyncResult<T> task) |
|||
{ |
|||
if (task.IsCompleted) |
|||
_value = task.GetResult(); |
|||
else |
|||
_asyncResult = task; |
|||
} |
|||
|
|||
public IDisposable Subscribe(IObserver<T> observer) |
|||
{ |
|||
if (_asyncResult?.IsCompleted == true) |
|||
{ |
|||
_value = _asyncResult.Value.GetResult(); |
|||
_asyncResult = null; |
|||
} |
|||
|
|||
if (_asyncResult != null) |
|||
_asyncResult.Value.OnCompleted(() => |
|||
{ |
|||
observer.OnNext(_asyncResult.Value.GetResult()); |
|||
}); |
|||
else |
|||
observer.OnNext(_value); |
|||
|
|||
return Disposable.Empty; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,24 +0,0 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
using System.Globalization; |
|||
using Avalonia.Markup.Xaml.Templates; |
|||
|
|||
namespace Avalonia.Markup.Xaml.Converters |
|||
{ |
|||
using System.ComponentModel; |
|||
|
|||
public class MemberSelectorTypeConverter : TypeConverter |
|||
{ |
|||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) |
|||
{ |
|||
return sourceType == typeof(string); |
|||
} |
|||
|
|||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) |
|||
{ |
|||
return MemberSelector.Parse((string)value); |
|||
} |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue