Browse Source

Merge branch 'master' into code-cleanup

pull/2358/head
Steven Kirk 7 years ago
committed by GitHub
parent
commit
a0bedbc62d
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      samples/interop/Direct3DInteropSample/Program.cs
  2. 21
      src/Avalonia.Controls/AppBuilderBase.cs
  3. 65
      src/Avalonia.Native/AvaloniaNativePlatform.cs
  4. 32
      src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs
  5. 2
      src/Avalonia.Native/PopupImpl.cs
  6. 2
      src/Avalonia.Native/WindowImpl.cs
  7. 4
      src/Avalonia.Native/WindowImplBase.cs
  8. 6
      src/Avalonia.X11/X11Platform.cs
  9. 24
      src/Markup/Avalonia.Markup/Data/MultiBinding.cs
  10. 20
      src/Windows/Avalonia.Win32/Win32Platform.cs
  11. 81
      tests/Avalonia.Markup.UnitTests/Data/MultiBindingTests_Converters.cs

4
samples/interop/Direct3DInteropSample/Program.cs

@ -11,7 +11,9 @@ namespace Direct3DInteropSample
{
static void Main(string[] args)
{
AppBuilder.Configure<App>().UseWin32(deferredRendering: false).UseDirect2D1().Start<MainWindow>();
AppBuilder.Configure<App>()
.With(new Win32PlatformOptions {UseDeferredRendering = false})
.UseWin32().UseDirect2D1().Start<MainWindow>();
}
}
}

21
src/Avalonia.Controls/AppBuilderBase.cs

@ -15,6 +15,7 @@ namespace Avalonia.Controls
public abstract class AppBuilderBase<TAppBuilder> where TAppBuilder : AppBuilderBase<TAppBuilder>, new()
{
private static bool s_setupWasAlreadyCalled;
private Action _optionsInitializers;
/// <summary>
/// Gets or sets the <see cref="IRuntimePlatform"/> instance.
@ -249,6 +250,24 @@ namespace Avalonia.Controls
Delegate.Combine(moduleInitializers.ToArray()).DynamicInvoke();
}
/// <summary>
/// Configures platform-specific options
/// </summary>
public TAppBuilder With<T>(T options)
{
_optionsInitializers += () => { AvaloniaLocator.CurrentMutable.Bind<T>().ToConstant(options); };
return Self;
}
/// <summary>
/// Configures platform-specific options
/// </summary>
public TAppBuilder With<T>(Func<T> options)
{
_optionsInitializers += () => { AvaloniaLocator.CurrentMutable.Bind<T>().ToFunc(options); };
return Self;
}
/// <summary>
/// Sets up the platform-speciic services for the <see cref="Application"/>.
/// </summary>
@ -280,7 +299,7 @@ namespace Avalonia.Controls
}
s_setupWasAlreadyCalled = true;
_optionsInitializers?.Invoke();
RuntimePlatformServicesInitializer();
WindowingSubsystemInitializer();
RenderingSubsystemInitializer();

65
src/Avalonia.Native/AvaloniaNativePlatform.cs

@ -16,6 +16,7 @@ namespace Avalonia.Native
class AvaloniaNativePlatform : IPlatformSettings, IWindowingPlatform
{
private readonly IAvaloniaNativeFactory _factory;
private AvaloniaNativePlatformOptions _options;
[DllImport("libAvaloniaNative")]
static extern IntPtr CreateAvaloniaNative();
@ -27,29 +28,31 @@ namespace Avalonia.Native
public TimeSpan DoubleClickTime => TimeSpan.FromMilliseconds(500); //TODO
public static void Initialize(IntPtr factory, Action<AvaloniaNativeOptions> configure)
public static void Initialize(IntPtr factory, AvaloniaNativePlatformOptions options)
{
new AvaloniaNativePlatform(new IAvaloniaNativeFactory(factory))
.DoInitialize(configure);
.DoInitialize(options);
}
delegate IntPtr CreateAvaloniaNativeDelegate();
public static void Initialize(string library, Action<AvaloniaNativeOptions> configure)
public static void Initialize(AvaloniaNativePlatformOptions options)
{
var loader = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? (IDynLoader)new Win32Loader() : new UnixLoader();
var lib = loader.LoadLibrary(library);
var proc = loader.GetProcAddress(lib, "CreateAvaloniaNative", false);
var d = Marshal.GetDelegateForFunctionPointer<CreateAvaloniaNativeDelegate>(proc);
if (options.AvaloniaNativeLibraryPath != null)
{
var loader = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
(IDynLoader)new Win32Loader() :
new UnixLoader();
var lib = loader.LoadLibrary(options.AvaloniaNativeLibraryPath);
var proc = loader.GetProcAddress(lib, "CreateAvaloniaNative", false);
var d = Marshal.GetDelegateForFunctionPointer<CreateAvaloniaNativeDelegate>(proc);
Initialize(d(), configure);
}
public static void Initialize(Action<AvaloniaNativeOptions> configure)
{
Initialize(CreateAvaloniaNative(), configure);
Initialize(d(), options);
}
else
Initialize(CreateAvaloniaNative(), options);
}
private AvaloniaNativePlatform(IAvaloniaNativeFactory factory)
@ -57,14 +60,20 @@ namespace Avalonia.Native
_factory = factory;
}
void DoInitialize(Action<AvaloniaNativeOptions> configure)
void DoInitialize(AvaloniaNativePlatformOptions options)
{
var opts = new AvaloniaNativeOptions(_factory);
configure?.Invoke(opts);
_options = options;
_factory.Initialize();
if (_factory.MacOptions != null)
{
var macOpts = AvaloniaLocator.Current.GetService<MacOSPlatformOptions>();
if (macOpts != null)
_factory.MacOptions.ShowInDock = macOpts.ShowInDock ? 1 : 0;
}
AvaloniaLocator.CurrentMutable
.Bind<IPlatformThreadingInterface>().ToConstant(new PlatformThreadingInterface(_factory.CreatePlatformThreadingInterface()))
.Bind<IPlatformThreadingInterface>()
.ToConstant(new PlatformThreadingInterface(_factory.CreatePlatformThreadingInterface()))
.Bind<IStandardCursorFactory>().ToConstant(new CursorFactory(_factory.CreateCursorFactory()))
.Bind<IPlatformIconLoader>().ToSingleton<IconLoader>()
.Bind<IKeyboardDevice>().ToConstant(KeyboardDevice)
@ -76,13 +85,13 @@ namespace Avalonia.Native
.Bind<IRenderTimer>().ToConstant(new DefaultRenderTimer(60))
.Bind<ISystemDialogImpl>().ToConstant(new SystemDialogs(_factory.CreateSystemDialogs()))
.Bind<IWindowingPlatformGlFeature>().ToConstant(new GlPlatformFeature(_factory.ObtainGlFeature()))
.Bind<PlatformHotkeyConfiguration>().ToConstant(new PlatformHotkeyConfiguration(InputModifiers.Windows))
.Bind<AvaloniaNativeOptions>().ToConstant(opts);
.Bind<PlatformHotkeyConfiguration>()
.ToConstant(new PlatformHotkeyConfiguration(InputModifiers.Windows));
}
public IWindowImpl CreateWindow()
{
return new WindowImpl(_factory);
return new WindowImpl(_factory, _options);
}
public IEmbeddableWindowImpl CreateEmbeddableWindow()
@ -92,7 +101,7 @@ namespace Avalonia.Native
public IPopupImpl CreatePopup()
{
return new PopupImpl(_factory);
return new PopupImpl(_factory, _options);
}
}
@ -116,18 +125,4 @@ namespace Avalonia.Native
}
}
}
public class AvaloniaNativeOptions
{
public AvaloniaNativeMacOptions MacOptions { get; set; }
public bool UseDeferredRendering { get; set; } = true;
public bool UseGpu { get; set; } = true;
internal AvaloniaNativeOptions(IAvaloniaNativeFactory factory)
{
var mac = factory.GetMacOptions();
if (mac != null)
MacOptions = new AvaloniaNativeMacOptions(mac);
}
}
}

32
src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs

@ -9,21 +9,27 @@ namespace Avalonia
{
public static class AvaloniaNativePlatformExtensions
{
public static T UseAvaloniaNative<T>(this T builder,
string libraryPath = null,
Action<AvaloniaNativeOptions> configure = null)
where T : AppBuilderBase<T>, new()
public static T UseAvaloniaNative<T>(this T builder)
where T : AppBuilderBase<T>, new()
{
if (libraryPath == null)
{
builder.UseWindowingSubsystem(() => AvaloniaNativePlatform.Initialize(configure));
}
else
{
builder.UseWindowingSubsystem(() => AvaloniaNativePlatform.Initialize(libraryPath, configure));
}
builder.UseWindowingSubsystem(() =>
AvaloniaNativePlatform.Initialize(
AvaloniaLocator.Current.GetService<AvaloniaNativePlatformOptions>() ??
new AvaloniaNativePlatformOptions()));
return builder;
}
}
public class AvaloniaNativePlatformOptions
{
public bool UseDeferredRendering { get; set; } = true;
public bool UseGpu { get; set; } = true;
public string AvaloniaNativeLibraryPath { get; set; }
}
// ReSharper disable once InconsistentNaming
public class MacOSPlatformOptions
{
public bool ShowInDock { get; set; } = true;
}
}

2
src/Avalonia.Native/PopupImpl.cs

@ -9,7 +9,7 @@ namespace Avalonia.Native
{
public class PopupImpl : WindowBaseImpl, IPopupImpl
{
public PopupImpl(IAvaloniaNativeFactory factory)
public PopupImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts) : base(opts)
{
using (var e = new PopupEvents(this))
{

2
src/Avalonia.Native/WindowImpl.cs

@ -12,7 +12,7 @@ namespace Avalonia.Native
public class WindowImpl : WindowBaseImpl, IWindowImpl
{
IAvnWindow _native;
public WindowImpl(IAvaloniaNativeFactory factory)
public WindowImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts) : base(opts)
{
using (var e = new WindowEvents(this))
{

4
src/Avalonia.Native/WindowImplBase.cs

@ -31,10 +31,8 @@ namespace Avalonia.Native
private double _savedScaling;
private GlPlatformSurface _glSurface;
public WindowBaseImpl()
public WindowBaseImpl(AvaloniaNativePlatformOptions opts)
{
var opts = AvaloniaLocator.Current.GetService<AvaloniaNativeOptions>();
_gpu = opts.UseGpu;
_deferredRendering = opts.UseDeferredRendering;

6
src/Avalonia.X11/X11Platform.cs

@ -94,9 +94,11 @@ namespace Avalonia
}
public static class AvaloniaX11PlatformExtensions
{
public static T UseX11<T>(this T builder, X11PlatformOptions options = null) where T : AppBuilderBase<T>, new()
public static T UseX11<T>(this T builder) where T : AppBuilderBase<T>, new()
{
builder.UseWindowingSubsystem(() => new AvaloniaX11Platform().Initialize(options ?? new X11PlatformOptions()));
builder.UseWindowingSubsystem(() =>
new AvaloniaX11Platform().Initialize(AvaloniaLocator.Current.GetService<X11PlatformOptions>() ??
new X11PlatformOptions()));
return builder;
}

24
src/Markup/Avalonia.Markup/Data/MultiBinding.cs

@ -23,10 +23,15 @@ namespace Avalonia.Data
public IList<IBinding> Bindings { get; set; } = new List<IBinding>();
/// <summary>
/// Gets or sets the <see cref="IValueConverter"/> to use.
/// Gets or sets the <see cref="IMultiValueConverter"/> to use.
/// </summary>
public IMultiValueConverter Converter { get; set; }
/// <summary>
/// Gets or sets a parameter to pass to <see cref="Converter"/>.
/// </summary>
public object ConverterParameter { get; set; }
/// <summary>
/// Gets or sets the value to use when the binding is unable to produce a value.
/// </summary>
@ -47,6 +52,11 @@ namespace Avalonia.Data
/// </summary>
public RelativeSource RelativeSource { get; set; }
/// <summary>
/// Gets or sets the string format.
/// </summary>
public string StringFormat { get; set; }
/// <inheritdoc/>
public InstancedBinding Initiate(
IAvaloniaObject target,
@ -79,13 +89,23 @@ namespace Avalonia.Data
private object ConvertValue(IList<object> values, Type targetType)
{
var converted = Converter.Convert(values, targetType, null, CultureInfo.CurrentCulture);
var culture = CultureInfo.CurrentCulture;
var converted = Converter.Convert(values, targetType, ConverterParameter, culture);
if (converted == AvaloniaProperty.UnsetValue && FallbackValue != null)
{
converted = FallbackValue;
}
// We only respect `StringFormat` if the type of the property we're assigning to will
// accept a string. Note that this is slightly different to WPF in that WPF only applies
// `StringFormat` for target type `string` (not `object`).
if (!string.IsNullOrWhiteSpace(StringFormat) &&
(targetType == typeof(string) || targetType == typeof(object)))
{
converted = string.Format(culture, StringFormat, converted);
}
return converted;
}
}

20
src/Windows/Avalonia.Win32/Win32Platform.cs

@ -26,15 +26,21 @@ namespace Avalonia
public static class Win32ApplicationExtensions
{
public static T UseWin32<T>(
this T builder,
bool deferredRendering = true, bool allowEgl = false)
this T builder)
where T : AppBuilderBase<T>, new()
{
return builder.UseWindowingSubsystem(
() => Win32.Win32Platform.Initialize(deferredRendering, allowEgl),
() => Win32.Win32Platform.Initialize(
AvaloniaLocator.Current.GetService<Win32PlatformOptions>() ?? new Win32PlatformOptions()),
"Win32");
}
}
public class Win32PlatformOptions
{
public bool UseDeferredRendering { get; set; } = true;
public bool AllowEglInitialization { get; set; }
}
}
namespace Avalonia.Win32
@ -63,10 +69,10 @@ namespace Avalonia.Win32
public static void Initialize()
{
Initialize(true);
Initialize(new Win32PlatformOptions());
}
public static void Initialize(bool deferredRendering = true, bool allowEgl = false)
public static void Initialize(Win32PlatformOptions options)
{
AvaloniaLocator.CurrentMutable
.Bind<IClipboard>().ToSingleton<ClipboardImpl>()
@ -80,9 +86,9 @@ namespace Avalonia.Win32
.Bind<IWindowingPlatform>().ToConstant(s_instance)
.Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>()
.Bind<IPlatformIconLoader>().ToConstant(s_instance);
if (allowEgl)
if (options.AllowEglInitialization)
Win32GlManager.Initialize();
UseDeferredRendering = deferredRendering;
UseDeferredRendering = options.UseDeferredRendering;
_uiThread = Thread.CurrentThread;
if (OleContext.Current != null)

81
tests/Avalonia.Markup.UnitTests/Data/MultiBindingTests_Converters.cs

@ -0,0 +1,81 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See license.md file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Data.Converters;
using Avalonia.Data.Core;
using Xunit;
namespace Avalonia.Markup.UnitTests.Data
{
public class MultiBindingTests_Converters
{
[Fact]
public void StringFormat_Should_Be_Applied()
{
var textBlock = new TextBlock
{
DataContext = new MultiBindingTests_Converters.Class1(),
};
var target = new MultiBinding
{
StringFormat = "Foo + Bar = {0}",
Converter = new SumOfDoublesConverter(),
Bindings =
{
new Binding(nameof(MultiBindingTests_Converters.Class1.Foo)),
new Binding(nameof(MultiBindingTests_Converters.Class1.Bar)),
}
};
textBlock.Bind(TextBlock.TextProperty, target);
Assert.Equal("Foo + Bar = 3", textBlock.Text);
}
[Fact]
public void StringFormat_Should_Not_Be_Applied_When_Binding_To_Non_String_Or_Object()
{
var textBlock = new TextBlock
{
DataContext = new MultiBindingTests_Converters.Class1(),
};
var target = new MultiBinding
{
StringFormat = "Hello {0}",
Converter = new SumOfDoublesConverter(),
Bindings =
{
new Binding(nameof(MultiBindingTests_Converters.Class1.Foo)),
new Binding(nameof(MultiBindingTests_Converters.Class1.Bar)),
}
};
textBlock.Bind(TextBlock.WidthProperty, target);
Assert.Equal(3.0, textBlock.Width);
}
private class SumOfDoublesConverter : IMultiValueConverter
{
public object Convert(IList<object> values, Type targetType, object parameter, CultureInfo culture)
{
return values.OfType<double>().Sum();
}
}
private class Class1
{
public double Foo { get; set; } = 1;
public double Bar { get; set; } = 2;
}
}
}
Loading…
Cancel
Save