diff --git a/Avalonia.sln b/Avalonia.sln index f86c18ba1e..ac678ba9ba 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -63,8 +63,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{A689DE src\Shared\SharedAssemblyInfo.cs = src\Shared\SharedAssemblyInfo.cs EndProjectSection EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Gtk", "Gtk", "{B9894058-278A-46B5-B6ED-AD613FCC03B3}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.ReactiveUI", "src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj", "{6417B24E-49C2-4985-8DB2-3AB9D898EC91}" EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "PlatformSupport", "src\Shared\PlatformSupport\PlatformSupport.shproj", "{E4D9629C-F168-4224-3F51-A5E482FFBC42}" @@ -123,8 +121,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.Android", "s EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Skia", "src\Skia\Avalonia.Skia\Avalonia.Skia.csproj", "{7D2D3083-71DD-4CC9-8907-39A0D86FB322}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Gtk3", "src\Gtk\Avalonia.Gtk3\Avalonia.Gtk3.csproj", "{BB1F7BB5-6AD4-4776-94D9-C09D0A972658}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.NetCore", "samples\ControlCatalog.NetCore\ControlCatalog.NetCore.csproj", "{39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1-27F5-4255-9AFC-04ABFD11683A}" @@ -1318,30 +1314,6 @@ Global {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Release|iPhone.Build.0 = Release|Any CPU {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.AppStore|Any CPU.Build.0 = Release|Any CPU - {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.AppStore|iPhone.Build.0 = Release|Any CPU - {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Debug|iPhone.Build.0 = Debug|Any CPU - {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Release|Any CPU.Build.0 = Release|Any CPU - {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Release|iPhone.ActiveCfg = Release|Any CPU - {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Release|iPhone.Build.0 = Release|Any CPU - {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU @@ -1911,7 +1883,6 @@ Global {F1FDC5B0-4654-416F-AE69-E3E9BBD87801} = {9B9E3891-2366-4253-A952-D08BCEB71098} {29132311-1848-4FD6-AE0C-4FF841151BD3} = {9B9E3891-2366-4253-A952-D08BCEB71098} {7D2D3083-71DD-4CC9-8907-39A0D86FB322} = {3743B0F2-CC41-4F14-A8C8-267F579BF91E} - {BB1F7BB5-6AD4-4776-94D9-C09D0A972658} = {B9894058-278A-46B5-B6ED-AD613FCC03B3} {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3} = {9B9E3891-2366-4253-A952-D08BCEB71098} {854568D5-13D1-4B4F-B50D-534DC7EFD3C9} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B} {638580B0-7910-40EF-B674-DCB34DA308CD} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9} diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs index 84092d52eb..a99ac4d026 100644 --- a/nukebuild/Build.cs +++ b/nukebuild/Build.cs @@ -19,7 +19,7 @@ using static Nuke.Common.IO.PathConstruction; using static Nuke.Common.Tools.MSBuild.MSBuildTasks; using static Nuke.Common.Tools.DotNet.DotNetTasks; using static Nuke.Common.Tools.Xunit.XunitTasks; - +using static Nuke.Common.Tools.VSWhere.VSWhereTasks; /* Before editing this file, install support plugin for your IDE, @@ -30,7 +30,26 @@ using static Nuke.Common.Tools.Xunit.XunitTasks; */ partial class Build : NukeBuild -{ +{ + static Lazy MsBuildExe = new Lazy(() => + { + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + return null; + + var msBuildDirectory = VSWhere("-latest -nologo -property installationPath -format value -prerelease").FirstOrDefault().Text; + + if (!string.IsNullOrWhiteSpace(msBuildDirectory)) + { + string msBuildExe = Path.Combine(msBuildDirectory, @"MSBuild\Current\Bin\MSBuild.exe"); + if (!System.IO.File.Exists(msBuildExe)) + msBuildExe = Path.Combine(msBuildDirectory, @"MSBuild\15.0\Bin\MSBuild.exe"); + + return msBuildExe; + } + + return null; + }, false); + BuildParameters Parameters { get; set; } protected override void OnBuildInitialized() { @@ -85,7 +104,6 @@ partial class Build : NukeBuild .DependsOn(Clean) .Executes(() => { - if (Parameters.IsRunningOnWindows) MSBuild(Parameters.MSBuildSolution, c => c .SetArgumentConfigurator(a => a.Add("/r")) @@ -93,7 +111,7 @@ partial class Build : NukeBuild .SetVerbosity(MSBuildVerbosity.Minimal) .AddProperty("PackageVersion", Parameters.Version) .AddProperty("iOSRoslynPathHackRequired", "true") - .SetToolsVersion(MSBuildToolsVersion._15_0) + .SetToolPath(MsBuildExe.Value) .AddTargets("Build") ); @@ -224,7 +242,7 @@ partial class Build : NukeBuild .SetVerbosity(MSBuildVerbosity.Minimal) .AddProperty("PackageVersion", Parameters.Version) .AddProperty("iOSRoslynPathHackRequired", "true") - .SetToolsVersion(MSBuildToolsVersion._15_0) + .SetToolPath(MsBuildExe.Value) .AddTargets("Pack")); else DotNetPack(Parameters.MSBuildSolution, c => diff --git a/nukebuild/_build.csproj b/nukebuild/_build.csproj index e02acff007..2a736e4653 100644 --- a/nukebuild/_build.csproj +++ b/nukebuild/_build.csproj @@ -1,4 +1,4 @@ - + Exe @@ -13,6 +13,7 @@ + diff --git a/readme.md b/readme.md index cf995f10fb..7a03fda384 100644 --- a/readme.md +++ b/readme.md @@ -8,9 +8,9 @@ ## About -Avalonia is a WPF/UWP-inspired cross-platform XAML-based UI framework providing a flexible styling system and supporting a wide range of OSs: Windows (.NET Framework, .NET Core), Linux (libX11), MacOS, Android (experimental) and iOS (exprerimental). +**Avalonia** is a WPF/UWP-inspired cross-platform XAML-based UI framework providing a flexible styling system and supporting a wide range of Operating Systems such as Windows (.NET Framework, .NET Core), Linux (via Xorg), MacOS and with experimental support for Android and iOS. -**Avalonia is currently in beta** which means that the framework is generally usable for writing applications, but there may be some bugs and breaking changes as we continue development, for more details about the status see https://github.com/AvaloniaUI/Avalonia/issues/2239 +**Avalonia** is ready for **General-Purpose Desktop App Development**. However there may be some bugs and breaking changes as we continue along into this project's development. To see the status for some of our features, please see our [Roadmap here](https://github.com/AvaloniaUI/Avalonia/issues/2239). | Control catalog | Desktop platforms | Mobile platforms | |---|---|---| @@ -40,11 +40,11 @@ https://github.com/AvaloniaUI/Avalonia/wiki/Using-nightly-build-feed ## Documentation -As mentioned above, Avalonia is still in beta and as such there's not much documentation yet. You can take a look at the [getting started page](http://avaloniaui.net/docs/quickstart/) for an overview of how to get started but probably the best thing to do for now is to already know a little bit about WPF/Silverlight/UWP/XAML and ask questions in our [Gitter room](https://gitter.im/AvaloniaUI/Avalonia). +You can take a look at the [getting started page](http://avaloniaui.net/docs/quickstart/) for an overview of how to get started but probably the best thing to do for now is to already know a little bit about WPF/Silverlight/UWP/XAML and ask questions in our [Gitter room](https://gitter.im/AvaloniaUI/Avalonia). There's also a high-level [architecture document](http://avaloniaui.net/architecture/project-structure) that is currently a little bit out of date, and I've also started writing blog posts on Avalonia at http://grokys.github.io/. -Contributions are always welcome! +Contributions for our docs are always welcome! ## Building and Using @@ -80,6 +80,4 @@ Support this project by becoming a sponsor. Your logo will show up here with a l - - - + diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs index 4027c5cd63..40321496c0 100644 --- a/samples/ControlCatalog.NetCore/Program.cs +++ b/samples/ControlCatalog.NetCore/Program.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.Linq; using System.Threading; using Avalonia; +using Avalonia.Controls; using Avalonia.Skia; using Avalonia.ReactiveUI; @@ -11,7 +12,7 @@ namespace ControlCatalog.NetCore static class Program { - static void Main(string[] args) + static int Main(string[] args) { Thread.CurrentThread.TrySetApartmentState(ApartmentState.STA); if (args.Contains("--wait-for-attach")) @@ -25,21 +26,16 @@ namespace ControlCatalog.NetCore } } + var builder = BuildAvaloniaApp(); if (args.Contains("--fbdev")) - AppBuilder.Configure().InitializeWithLinuxFramebuffer(tl => - { - tl.Content = new MainView(); - System.Threading.ThreadPool.QueueUserWorkItem(_ => ConsoleSilencer()); - }); + { + System.Threading.ThreadPool.QueueUserWorkItem(_ => ConsoleSilencer()); + return builder.StartLinuxFramebuffer(args); + } else - BuildAvaloniaApp().Start(AppMain, args); + return builder.StartWithClassicDesktopLifetime(args); } - - static void AppMain(Application app, string[] args) - { - app.Run(new MainWindow()); - } - + /// /// This method is needed for IDE previewer infrastructure /// diff --git a/samples/ControlCatalog/App.xaml.cs b/samples/ControlCatalog/App.xaml.cs index d862749132..07c42c60c4 100644 --- a/samples/ControlCatalog/App.xaml.cs +++ b/samples/ControlCatalog/App.xaml.cs @@ -1,4 +1,5 @@ using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; namespace ControlCatalog @@ -9,5 +10,15 @@ namespace ControlCatalog { AvaloniaXamlLoader.Load(this); } + + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktopLifetime) + desktopLifetime.MainWindow = new MainWindow(); + else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewLifetime) + singleViewLifetime.MainView = new MainView(); + + base.OnFrameworkInitializationCompleted(); + } } } diff --git a/samples/ControlCatalog/Pages/ContextMenuPage.xaml.cs b/samples/ControlCatalog/Pages/ContextMenuPage.xaml.cs index 96e8b49f89..f861bfab33 100644 --- a/samples/ControlCatalog/Pages/ContextMenuPage.xaml.cs +++ b/samples/ControlCatalog/Pages/ContextMenuPage.xaml.cs @@ -1,3 +1,4 @@ +using System; using Avalonia.Controls; using Avalonia.Markup.Xaml; using ControlCatalog.ViewModels; @@ -12,6 +13,18 @@ namespace ControlCatalog.Pages DataContext = new ContextMenuPageViewModel(); } + private ContextMenuPageViewModel _model; + protected override void OnDataContextChanged(EventArgs e) + { + if (_model != null) + _model.View = null; + _model = DataContext as ContextMenuPageViewModel; + if (_model != null) + _model.View = this; + + base.OnDataContextChanged(e); + } + private void InitializeComponent() { AvaloniaXamlLoader.Load(this); diff --git a/samples/ControlCatalog/Pages/MenuPage.xaml.cs b/samples/ControlCatalog/Pages/MenuPage.xaml.cs index 0a77607719..46dbe3dcad 100644 --- a/samples/ControlCatalog/Pages/MenuPage.xaml.cs +++ b/samples/ControlCatalog/Pages/MenuPage.xaml.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Reactive; using System.Threading.Tasks; @@ -21,5 +22,18 @@ namespace ControlCatalog.Pages { AvaloniaXamlLoader.Load(this); } + + private MenuPageViewModel _model; + protected override void OnDataContextChanged(EventArgs e) + { + if (_model != null) + _model.View = null; + _model = DataContext as MenuPageViewModel; + if (_model != null) + _model.View = this; + + base.OnDataContextChanged(e); + } + } } diff --git a/samples/ControlCatalog/ViewModels/ContextMenuPageViewModel.cs b/samples/ControlCatalog/ViewModels/ContextMenuPageViewModel.cs index d34e9af017..5c2f74d2d5 100644 --- a/samples/ControlCatalog/ViewModels/ContextMenuPageViewModel.cs +++ b/samples/ControlCatalog/ViewModels/ContextMenuPageViewModel.cs @@ -2,12 +2,14 @@ using System.Reactive; using System.Threading.Tasks; using Avalonia.Controls; +using Avalonia.VisualTree; using ReactiveUI; namespace ControlCatalog.ViewModels { public class ContextMenuPageViewModel { + public Control View { get; set; } public ContextMenuPageViewModel() { OpenCommand = ReactiveCommand.CreateFromTask(Open); @@ -48,8 +50,11 @@ namespace ControlCatalog.ViewModels public async Task Open() { + var window = View?.GetVisualRoot() as Window; + if (window == null) + return; var dialog = new OpenFileDialog(); - var result = await dialog.ShowAsync(App.Current.MainWindow); + var result = await dialog.ShowAsync(window); if (result != null) { diff --git a/samples/ControlCatalog/ViewModels/MenuPageViewModel.cs b/samples/ControlCatalog/ViewModels/MenuPageViewModel.cs index 88b1bf0b6b..dc9c4a8f49 100644 --- a/samples/ControlCatalog/ViewModels/MenuPageViewModel.cs +++ b/samples/ControlCatalog/ViewModels/MenuPageViewModel.cs @@ -3,12 +3,14 @@ using System.Reactive; using System.Reactive.Linq; using System.Threading.Tasks; using Avalonia.Controls; +using Avalonia.VisualTree; using ReactiveUI; namespace ControlCatalog.ViewModels { public class MenuPageViewModel { + public Control View { get; set; } public MenuPageViewModel() { OpenCommand = ReactiveCommand.CreateFromTask(Open); @@ -65,8 +67,11 @@ namespace ControlCatalog.ViewModels public async Task Open() { + var window = View?.GetVisualRoot() as Window; + if (window == null) + return; var dialog = new OpenFileDialog(); - var result = await dialog.ShowAsync(App.Current.MainWindow); + var result = await dialog.ShowAsync(window); if (result != null) { diff --git a/src/Avalonia.Base/Data/Converters/StringFormatMultiValueConverter.cs b/src/Avalonia.Base/Data/Converters/StringFormatMultiValueConverter.cs new file mode 100644 index 0000000000..b190f06be5 --- /dev/null +++ b/src/Avalonia.Base/Data/Converters/StringFormatMultiValueConverter.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; + +namespace Avalonia.Data.Converters +{ + /// + /// A multi-value converter which calls + /// + public class StringFormatMultiValueConverter : IMultiValueConverter + { + /// + /// Initializes a new instance of the class. + /// + /// The format string. + /// + /// An optional inner converter to be called before the format takes place. + /// + public StringFormatMultiValueConverter(string format, IMultiValueConverter inner) + { + Contract.Requires(format != null); + + Format = format; + Inner = inner; + } + + /// + /// Gets an inner value converter which will be called before the string format takes place. + /// + public IMultiValueConverter Inner { get; } + + /// + /// Gets the format string. + /// + public string Format { get; } + + /// + public object Convert(IList values, Type targetType, object parameter, CultureInfo culture) + { + return Inner == null + ? string.Format(culture, Format, values.ToArray()) + : string.Format(culture, Format, Inner.Convert(values, targetType, parameter, culture)); + } + } +} diff --git a/src/Avalonia.Controls/AppBuilderBase.cs b/src/Avalonia.Controls/AppBuilderBase.cs index 419064b051..59ff35be76 100644 --- a/src/Avalonia.Controls/AppBuilderBase.cs +++ b/src/Avalonia.Controls/AppBuilderBase.cs @@ -4,6 +4,7 @@ using System; using System.Reflection; using System.Linq; +using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Platform; namespace Avalonia.Controls @@ -57,10 +58,6 @@ namespace Avalonia.Controls /// public Action AfterSetupCallback { get; private set; } = builder => { }; - /// - /// Gets or sets a method to call before Start is called on the . - /// - public Action BeforeStartCallback { get; private set; } = builder => { }; protected AppBuilderBase(IRuntimePlatform platform, Action platformServices) { @@ -95,17 +92,6 @@ namespace Avalonia.Controls protected TAppBuilder Self => (TAppBuilder)this; - /// - /// Registers a callback to call before Start is called on the . - /// - /// The callback. - /// An instance. - public TAppBuilder BeforeStarting(Action callback) - { - BeforeStartCallback = (Action)Delegate.Combine(BeforeStartCallback, callback); - return Self; - } - public TAppBuilder AfterSetup(Action callback) { AfterSetupCallback = (Action)Delegate.Combine(AfterSetupCallback, callback); @@ -117,48 +103,37 @@ namespace Avalonia.Controls /// /// The window type. /// A delegate that will be called to create a data context for the window (optional). + [Obsolete("Use either lifetimes or AppMain overload. See see https://github.com/AvaloniaUI/Avalonia/wiki/Application-lifetimes for details")] public void Start(Func dataContextProvider = null) where TMainWindow : Window, new() { - Setup(); - BeforeStartCallback(Self); - - var window = new TMainWindow(); - if (dataContextProvider != null) - window.DataContext = dataContextProvider(); - Instance.Run(window); - } - - /// - /// Starts the application with the provided instance of . - /// - /// The window type. - /// Instance of type TMainWindow to use when starting the app - /// A delegate that will be called to create a data context for the window (optional). - public void Start(TMainWindow mainWindow, Func dataContextProvider = null) - where TMainWindow : Window - { - Setup(); - BeforeStartCallback(Self); - - if (dataContextProvider != null) - mainWindow.DataContext = dataContextProvider(); - Instance.Run(mainWindow); + AfterSetup(builder => + { + var window = new TMainWindow(); + if (dataContextProvider != null) + window.DataContext = dataContextProvider(); + ((IClassicDesktopStyleApplicationLifetime)builder.Instance.ApplicationLifetime) + .MainWindow = window; + }); + + // Copy-pasted because we can't call extension methods due to generic constraints + var lifetime = new ClassicDesktopStyleApplicationLifetime(Instance) {ShutdownMode = ShutdownMode.OnMainWindowClose}; + Instance.ApplicationLifetime = lifetime; + SetupWithoutStarting(); + lifetime.Start(Array.Empty()); } public delegate void AppMainDelegate(Application app, string[] args); + [Obsolete("Use either lifetimes or AppMain overload. See see https://github.com/AvaloniaUI/Avalonia/wiki/Application-lifetimes for details", true)] public void Start() { - Setup(); - BeforeStartCallback(Self); - Instance.Run(); + throw new NotSupportedException(); } public void Start(AppMainDelegate main, string[] args) { Setup(); - BeforeStartCallback(Self); main(Instance, args); } @@ -224,17 +199,6 @@ namespace Avalonia.Controls public TAppBuilder UseAvaloniaModules() => AfterSetup(builder => SetupAvaloniaModules()); - /// - /// Sets the shutdown mode of the application. - /// - /// The shutdown mode. - /// - public TAppBuilder SetShutdownMode(ShutdownMode shutdownMode) - { - Instance.ShutdownMode = shutdownMode; - return Self; - } - protected virtual bool CheckSetup => true; private void SetupAvaloniaModules() @@ -313,6 +277,7 @@ namespace Avalonia.Controls Instance.RegisterServices(); Instance.Initialize(); AfterSetupCallback(Self); + Instance.OnFrameworkInitializationCompleted(); } } } diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs index 9f9fe5eae1..382106de65 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -6,6 +6,7 @@ using System.Reactive.Concurrency; using System.Threading; using Avalonia.Animation; using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.Templates; using Avalonia.Input; using Avalonia.Input.Platform; @@ -31,7 +32,7 @@ namespace Avalonia /// method. /// - Tracks the lifetime of the application. /// - public class Application : IApplicationLifecycle, IGlobalDataTemplates, IGlobalStyles, IStyleRoot, IResourceNode + public class Application : IGlobalDataTemplates, IGlobalStyles, IStyleRoot, IResourceNode { /// /// The application-global data templates. @@ -43,22 +44,6 @@ namespace Avalonia private readonly Styler _styler = new Styler(); private Styles _styles; private IResourceDictionary _resources; - private CancellationTokenSource _mainLoopCancellationTokenSource; - private int _exitCode; - - /// - /// Initializes a new instance of the class. - /// - public Application() - { - Windows = new WindowCollection(this); - } - - /// - public event EventHandler Startup; - - /// - public event EventHandler Exit; /// public event EventHandler ResourcesChanged; @@ -166,198 +151,21 @@ namespace Avalonia /// IResourceNode IResourceNode.ResourceParent => null; - - /// - /// Gets or sets the . This property indicates whether the application is shutdown explicitly or implicitly. - /// If is set to OnExplicitShutdown the application is only closes if Shutdown is called. - /// The default is OnLastWindowClose - /// - /// - /// The shutdown mode. - /// - public ShutdownMode ShutdownMode { get; set; } - - /// - /// Gets or sets the main window of the application. - /// - /// - /// The main window. - /// - public Window MainWindow { get; set; } - - /// - /// Gets the open windows of the application. - /// - /// - /// The windows. - /// - public WindowCollection Windows { get; } - + /// - /// Gets or sets a value indicating whether this instance is shutting down. + /// Application lifetime, use it for things like setting the main window and exiting the app from code + /// Currently supported lifetimes are: + /// - + /// - + /// - /// - /// - /// true if this instance is shutting down; otherwise, false. - /// - internal bool IsShuttingDown { get; private set; } + public IApplicationLifetime ApplicationLifetime { get; set; } /// /// Initializes the application by loading XAML etc. /// public virtual void Initialize() { } - /// - /// Runs the application's main loop. - /// - /// - /// This will return when the condition is met - /// or was called. - /// - /// The application's exit code that is returned to the operating system on termination. - public int Run() - { - return Run(new CancellationTokenSource()); - } - - /// - /// Runs the application's main loop. - /// - /// - /// This will return when the condition is met - /// or was called. - /// This also returns when is closed. - /// - /// The closable to track. - /// The application's exit code that is returned to the operating system on termination. - public int Run(ICloseable closable) - { - closable.Closed += (s, e) => _mainLoopCancellationTokenSource?.Cancel(); - - return Run(new CancellationTokenSource()); - } - - /// - /// Runs the application's main loop. - /// - /// - /// This will return when the condition is met - /// or was called. - /// - /// The window that is used as - /// when the isn't already set. - /// The application's exit code that is returned to the operating system on termination. - public int Run(Window mainWindow) - { - if (mainWindow == null) - { - throw new ArgumentNullException(nameof(mainWindow)); - } - - if (MainWindow == null) - { - if (!mainWindow.IsVisible) - { - mainWindow.Show(); - } - - MainWindow = mainWindow; - } - - return Run(new CancellationTokenSource()); - } - /// - /// Runs the application's main loop. - /// - /// - /// This will return when the condition is met - /// or was called. - /// This also returns when the is canceled. - /// - /// The application's exit code that is returned to the operating system on termination. - /// The token to track. - public int Run(CancellationToken token) - { - return Run(CancellationTokenSource.CreateLinkedTokenSource(token)); - } - - private int Run(CancellationTokenSource tokenSource) - { - if (IsShuttingDown) - { - throw new InvalidOperationException("Application is shutting down."); - } - - if (_mainLoopCancellationTokenSource != null) - { - throw new InvalidOperationException("Application is already running."); - } - - _mainLoopCancellationTokenSource = tokenSource; - - Dispatcher.UIThread.Post(() => OnStartup(new StartupEventArgs()), DispatcherPriority.Send); - - Dispatcher.UIThread.MainLoop(_mainLoopCancellationTokenSource.Token); - - if (!IsShuttingDown) - { - Shutdown(_exitCode); - } - - return _exitCode; - } - - /// - /// Raises the event. - /// - /// A that contains the event data. - protected virtual void OnStartup(StartupEventArgs e) - { - Startup?.Invoke(this, e); - } - - /// - /// Raises the event. - /// - /// A that contains the event data. - protected virtual void OnExit(ExitEventArgs e) - { - Exit?.Invoke(this, e); - } - - /// - public void Shutdown(int exitCode = 0) - { - if (IsShuttingDown) - { - throw new InvalidOperationException("Application is already shutting down."); - } - - _exitCode = exitCode; - - IsShuttingDown = true; - - Windows.Clear(); - - try - { - var e = new ExitEventArgs { ApplicationExitCode = _exitCode }; - - OnExit(e); - - _exitCode = e.ApplicationExitCode; - } - finally - { - _mainLoopCancellationTokenSource?.Cancel(); - - _mainLoopCancellationTokenSource = null; - - IsShuttingDown = false; - - Environment.ExitCode = _exitCode; - } - } - /// bool IResourceProvider.TryGetResource(object key, out object value) { @@ -383,7 +191,6 @@ namespace Avalonia .Bind().ToConstant(InputManager) .Bind().ToTransient() .Bind().ToConstant(_styler) - .Bind().ToConstant(this) .Bind().ToConstant(AvaloniaScheduler.Instance) .Bind().ToConstant(DragDropDevice.Instance) .Bind().ToTransient(); @@ -394,6 +201,11 @@ namespace Avalonia .GetService()?.Add(clock); } + public virtual void OnFrameworkInitializationCompleted() + { + + } + private void ThisResourcesChanged(object sender, ResourcesChangedEventArgs e) { ResourcesChanged?.Invoke(this, e); diff --git a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs new file mode 100644 index 0000000000..abca7a64ee --- /dev/null +++ b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Interactivity; + +namespace Avalonia.Controls.ApplicationLifetimes +{ + public class ClassicDesktopStyleApplicationLifetime : IClassicDesktopStyleApplicationLifetime, IDisposable + { + private readonly Application _app; + private int _exitCode; + private CancellationTokenSource _cts; + private bool _isShuttingDown; + private HashSet _windows = new HashSet(); + + private static ClassicDesktopStyleApplicationLifetime _activeLifetime; + static ClassicDesktopStyleApplicationLifetime() + { + Window.WindowOpenedEvent.AddClassHandler(typeof(Window), OnWindowOpened); + Window.WindowClosedEvent.AddClassHandler(typeof(Window), WindowClosedEvent); + } + + private static void WindowClosedEvent(object sender, RoutedEventArgs e) + { + _activeLifetime?._windows.Remove((Window)sender); + _activeLifetime?.HandleWindowClosed((Window)sender); + } + + private static void OnWindowOpened(object sender, RoutedEventArgs e) + { + _activeLifetime?._windows.Add((Window)sender); + } + + public ClassicDesktopStyleApplicationLifetime(Application app) + { + if (_activeLifetime != null) + throw new InvalidOperationException( + "Can not have multiple active ClassicDesktopStyleApplicationLifetime instances and the previously created one was not disposed"); + _app = app; + _activeLifetime = this; + } + + /// + public event EventHandler Startup; + /// + public event EventHandler Exit; + + /// + public ShutdownMode ShutdownMode { get; set; } + + /// + public Window MainWindow { get; set; } + + public IReadOnlyList Windows => _windows.ToList(); + + private void HandleWindowClosed(Window window) + { + if (window == null) + return; + + if (_isShuttingDown) + return; + + if (ShutdownMode == ShutdownMode.OnLastWindowClose && _windows.Count == 0) + Shutdown(); + else if (ShutdownMode == ShutdownMode.OnMainWindowClose && window == MainWindow) + Shutdown(); + } + + + + + public void Shutdown(int exitCode = 0) + { + if (_isShuttingDown) + throw new InvalidOperationException("Application is already shutting down."); + + _exitCode = exitCode; + _isShuttingDown = true; + + try + { + foreach (var w in Windows) + w.Close(); + var e = new ControlledApplicationLifetimeExitEventArgs(exitCode); + Exit?.Invoke(this, e); + _exitCode = e.ApplicationExitCode; + } + finally + { + _cts?.Cancel(); + _cts = null; + _isShuttingDown = false; + } + } + + + public int Start(string[] args) + { + Startup?.Invoke(this, new ControlledApplicationLifetimeStartupEventArgs(args)); + _cts = new CancellationTokenSource(); + MainWindow?.Show(); + _app.Run(_cts.Token); + Environment.ExitCode = _exitCode; + return _exitCode; + } + + public void Dispose() + { + if (_activeLifetime == this) + _activeLifetime = null; + } + } +} + +namespace Avalonia +{ + public static class ClassicDesktopStyleApplicationLifetimeExtensions + { + public static int StartWithClassicDesktopLifetime( + this T builder, string[] args, ShutdownMode shutdownMode = ShutdownMode.OnLastWindowClose) + where T : AppBuilderBase, new() + { + var lifetime = new ClassicDesktopStyleApplicationLifetime(builder.Instance) {ShutdownMode = shutdownMode}; + builder.Instance.ApplicationLifetime = lifetime; + builder.SetupWithoutStarting(); + return lifetime.Start(args); + } + } +} diff --git a/src/Avalonia.Controls/ExitEventArgs.cs b/src/Avalonia.Controls/ApplicationLifetimes/ControlledApplicationLifetimeExitEventArgs.cs similarity index 54% rename from src/Avalonia.Controls/ExitEventArgs.cs rename to src/Avalonia.Controls/ApplicationLifetimes/ControlledApplicationLifetimeExitEventArgs.cs index c99f7fe047..d4c3b27f7a 100644 --- a/src/Avalonia.Controls/ExitEventArgs.cs +++ b/src/Avalonia.Controls/ApplicationLifetimes/ControlledApplicationLifetimeExitEventArgs.cs @@ -3,13 +3,18 @@ using System; -namespace Avalonia.Controls +namespace Avalonia.Controls.ApplicationLifetimes { /// - /// Contains the arguments for the event. + /// Contains the arguments for the event. /// - public class ExitEventArgs : EventArgs + public class ControlledApplicationLifetimeExitEventArgs : EventArgs { + public ControlledApplicationLifetimeExitEventArgs(int applicationExitCode) + { + ApplicationExitCode = applicationExitCode; + } + /// /// Gets or sets the exit code that an application returns to the operating system when the application exits. /// diff --git a/src/Avalonia.Controls/ApplicationLifetimes/IApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/IApplicationLifetime.cs new file mode 100644 index 0000000000..9860d0cb38 --- /dev/null +++ b/src/Avalonia.Controls/ApplicationLifetimes/IApplicationLifetime.cs @@ -0,0 +1,7 @@ +namespace Avalonia.Controls.ApplicationLifetimes +{ + public interface IApplicationLifetime + { + + } +} diff --git a/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs new file mode 100644 index 0000000000..a1006d907b --- /dev/null +++ b/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; + +namespace Avalonia.Controls.ApplicationLifetimes +{ + /// + /// Controls application lifetime in classic desktop style + /// + public interface IClassicDesktopStyleApplicationLifetime : IControlledApplicationLifetime + { + /// + /// Gets or sets the . This property indicates whether the application is shutdown explicitly or implicitly. + /// If is set to OnExplicitShutdown the application is only closes if Shutdown is called. + /// The default is OnLastWindowClose + /// + /// + /// The shutdown mode. + /// + ShutdownMode ShutdownMode { get; set; } + + /// + /// Gets or sets the main window of the application. + /// + /// + /// The main window. + /// + Window MainWindow { get; set; } + + IReadOnlyList Windows { get; } + } +} diff --git a/src/Avalonia.Controls/IApplicationLifecycle.cs b/src/Avalonia.Controls/ApplicationLifetimes/IControlledApplicationLifetime.cs similarity index 65% rename from src/Avalonia.Controls/IApplicationLifecycle.cs rename to src/Avalonia.Controls/ApplicationLifetimes/IControlledApplicationLifetime.cs index a3c6599b20..3f61aeb536 100644 --- a/src/Avalonia.Controls/IApplicationLifecycle.cs +++ b/src/Avalonia.Controls/ApplicationLifetimes/IControlledApplicationLifetime.cs @@ -1,22 +1,19 @@ using System; -namespace Avalonia.Controls +namespace Avalonia.Controls.ApplicationLifetimes { - /// - /// Sends events about the application lifecycle. - /// - public interface IApplicationLifecycle + public interface IControlledApplicationLifetime : IApplicationLifetime { /// /// Sent when the application is starting up. /// - event EventHandler Startup; + event EventHandler Startup; /// /// Sent when the application is exiting. /// - event EventHandler Exit; - + event EventHandler Exit; + /// /// Shuts down the application and sets the exit code that is returned to the operating system when the application exits. /// diff --git a/src/Avalonia.Controls/ApplicationLifetimes/ISingleViewApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/ISingleViewApplicationLifetime.cs new file mode 100644 index 0000000000..eb451f51af --- /dev/null +++ b/src/Avalonia.Controls/ApplicationLifetimes/ISingleViewApplicationLifetime.cs @@ -0,0 +1,7 @@ +namespace Avalonia.Controls.ApplicationLifetimes +{ + public interface ISingleViewApplicationLifetime : IApplicationLifetime + { + Control MainView { get; set; } + } +} diff --git a/src/Avalonia.Controls/ApplicationLifetimes/StartupEventArgs.cs b/src/Avalonia.Controls/ApplicationLifetimes/StartupEventArgs.cs new file mode 100644 index 0000000000..4c08712707 --- /dev/null +++ b/src/Avalonia.Controls/ApplicationLifetimes/StartupEventArgs.cs @@ -0,0 +1,22 @@ +// 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.Collections.Generic; +using System.Linq; + +namespace Avalonia.Controls.ApplicationLifetimes +{ + /// + /// Contains the arguments for the event. + /// + public class ControlledApplicationLifetimeStartupEventArgs : EventArgs + { + public ControlledApplicationLifetimeStartupEventArgs(IEnumerable args) + { + Args = args?.ToArray() ?? Array.Empty(); + } + + public string[] Args { get; } + } +} diff --git a/src/Avalonia.Controls/ColumnDefinition.cs b/src/Avalonia.Controls/ColumnDefinition.cs index e3d2489241..9c520c434e 100644 --- a/src/Avalonia.Controls/ColumnDefinition.cs +++ b/src/Avalonia.Controls/ColumnDefinition.cs @@ -62,8 +62,15 @@ namespace Avalonia.Controls /// public double MaxWidth { - get { return GetValue(MaxWidthProperty); } - set { SetValue(MaxWidthProperty, value); } + get + { + return GetValue(MaxWidthProperty); + } + set + { + Parent?.InvalidateMeasure(); + SetValue(MaxWidthProperty, value); + } } /// @@ -71,8 +78,15 @@ namespace Avalonia.Controls /// public double MinWidth { - get { return GetValue(MinWidthProperty); } - set { SetValue(MinWidthProperty, value); } + get + { + return GetValue(MinWidthProperty); + } + set + { + Parent?.InvalidateMeasure(); + SetValue(MinWidthProperty, value); + } } /// @@ -80,8 +94,15 @@ namespace Avalonia.Controls /// public GridLength Width { - get { return GetValue(WidthProperty); } - set { SetValue(WidthProperty, value); } + get + { + return GetValue(WidthProperty); + } + set + { + Parent?.InvalidateMeasure(); + SetValue(WidthProperty, value); + } } internal override GridLength UserSizeValueCache => this.Width; diff --git a/src/Avalonia.Controls/DesktopApplicationExtensions.cs b/src/Avalonia.Controls/DesktopApplicationExtensions.cs new file mode 100644 index 0000000000..ff6705cdc0 --- /dev/null +++ b/src/Avalonia.Controls/DesktopApplicationExtensions.cs @@ -0,0 +1,67 @@ +using System; +using System.Threading; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Threading; + +namespace Avalonia.Controls +{ + public static class DesktopApplicationExtensions + { + [Obsolete("Running application without a cancellation token and a lifetime is no longer supported, see https://github.com/AvaloniaUI/Avalonia/wiki/Application-lifetimes for details", true)] + public static void Run(this Application app) => throw new NotSupportedException(); + + /// + /// On desktop-style platforms runs the application's main loop until closable is closed + /// + /// + /// Consider using StartWithDesktopStyleLifetime instead, see https://github.com/AvaloniaUI/Avalonia/wiki/Application-lifetimes for details + /// + public static void Run(this Application app, ICloseable closable) + { + var cts = new CancellationTokenSource(); + closable.Closed += (s, e) => cts.Cancel(); + + app.Run(cts.Token); + } + + /// + /// On desktop-style platforms runs the application's main loop until main window is closed + /// + /// + /// Consider using StartWithDesktopStyleLifetime instead, see https://github.com/AvaloniaUI/Avalonia/wiki/Application-lifetimes for details + /// + public static void Run(this Application app, Window mainWindow) + { + if (mainWindow == null) + { + throw new ArgumentNullException(nameof(mainWindow)); + } + var cts = new CancellationTokenSource(); + mainWindow.Closed += (_, __) => cts.Cancel(); + if (!mainWindow.IsVisible) + { + mainWindow.Show(); + } + app.Run(cts.Token); + } + + /// + /// On desktop-style platforms runs the application's main loop with custom CancellationToken + /// without setting a lifetime. + /// + /// The token to track. + public static void Run(this Application app, CancellationToken token) + { + Dispatcher.UIThread.MainLoop(token); + } + + public static void RunWithMainWindow(this Application app) + where TWindow : Avalonia.Controls.Window, new() + { + var window = new TWindow(); + window.Show(); + app.Run(window); + } + } +} diff --git a/src/Avalonia.Controls/DockPanel.cs b/src/Avalonia.Controls/DockPanel.cs index e147fe1a52..8e23555c2d 100644 --- a/src/Avalonia.Controls/DockPanel.cs +++ b/src/Avalonia.Controls/DockPanel.cs @@ -1,7 +1,12 @@ +// This source file is adapted from the Windows Presentation Foundation project. +// (https://github.com/dotnet/wpf/) +// +// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation. + +using System; + namespace Avalonia.Controls { - using System; - /// /// Defines the available docking modes for a control in a . /// @@ -70,107 +75,137 @@ namespace Avalonia.Controls set { SetValue(LastChildFillProperty, value); } } - /// + /// + /// Updates DesiredSize of the DockPanel. Called by parent Control. This is the first pass of layout. + /// + /// + /// Children are measured based on their sizing properties and . + /// Each child is allowed to consume all of the space on the side on which it is docked; Left/Right docked + /// children are granted all vertical space for their entire width, and Top/Bottom docked children are + /// granted all horizontal space for their entire height. + /// + /// Constraint size is an "upper limit" that the return value should not exceed. + /// The Panel's desired size. protected override Size MeasureOverride(Size constraint) { - double usedWidth = 0.0; - double usedHeight = 0.0; - double maximumWidth = 0.0; - double maximumHeight = 0.0; + var children = Children; + + double parentWidth = 0; // Our current required width due to children thus far. + double parentHeight = 0; // Our current required height due to children thus far. + double accumulatedWidth = 0; // Total width consumed by children. + double accumulatedHeight = 0; // Total height consumed by children. - // Measure each of the Children - foreach (Control element in Children) + for (int i = 0, count = children.Count; i < count; ++i) { - // Get the child's desired size - Size remainingSize = new Size( - Math.Max(0.0, constraint.Width - usedWidth), - Math.Max(0.0, constraint.Height - usedHeight)); - element.Measure(remainingSize); - Size desiredSize = element.DesiredSize; - - // Decrease the remaining space for the rest of the children - switch (GetDock(element)) + var child = children[i]; + Size childConstraint; // Contains the suggested input constraint for this child. + Size childDesiredSize; // Contains the return size from child measure. + + if (child == null) + { continue; } + + // Child constraint is the remaining size; this is total size minus size consumed by previous children. + childConstraint = new Size(Math.Max(0.0, constraint.Width - accumulatedWidth), + Math.Max(0.0, constraint.Height - accumulatedHeight)); + + // Measure child. + child.Measure(childConstraint); + childDesiredSize = child.DesiredSize; + + // Now, we adjust: + // 1. Size consumed by children (accumulatedSize). This will be used when computing subsequent + // children to determine how much space is remaining for them. + // 2. Parent size implied by this child (parentSize) when added to the current children (accumulatedSize). + // This is different from the size above in one respect: A Dock.Left child implies a height, but does + // not actually consume any height for subsequent children. + // If we accumulate size in a given dimension, the next child (or the end conditions after the child loop) + // will deal with computing our minimum size (parentSize) due to that accumulation. + // Therefore, we only need to compute our minimum size (parentSize) in dimensions that this child does + // not accumulate: Width for Top/Bottom, Height for Left/Right. + switch (DockPanel.GetDock((Control)child)) { case Dock.Left: case Dock.Right: - maximumHeight = Math.Max(maximumHeight, usedHeight + desiredSize.Height); - usedWidth += desiredSize.Width; + parentHeight = Math.Max(parentHeight, accumulatedHeight + childDesiredSize.Height); + accumulatedWidth += childDesiredSize.Width; break; + case Dock.Top: case Dock.Bottom: - maximumWidth = Math.Max(maximumWidth, usedWidth + desiredSize.Width); - usedHeight += desiredSize.Height; + parentWidth = Math.Max(parentWidth, accumulatedWidth + childDesiredSize.Width); + accumulatedHeight += childDesiredSize.Height; break; } } - maximumWidth = Math.Max(maximumWidth, usedWidth); - maximumHeight = Math.Max(maximumHeight, usedHeight); - return new Size(maximumWidth, maximumHeight); + // Make sure the final accumulated size is reflected in parentSize. + parentWidth = Math.Max(parentWidth, accumulatedWidth); + parentHeight = Math.Max(parentHeight, accumulatedHeight); + + return (new Size(parentWidth, parentHeight)); } - /// + /// + /// DockPanel computes a position and final size for each of its children based upon their + /// enum and sizing properties. + /// + /// Size that DockPanel will assume to position children. protected override Size ArrangeOverride(Size arrangeSize) { - double left = 0.0; - double top = 0.0; - double right = 0.0; - double bottom = 0.0; - - // Arrange each of the Children var children = Children; - int dockedCount = children.Count - (LastChildFill ? 1 : 0); - int index = 0; + int totalChildrenCount = children.Count; + int nonFillChildrenCount = totalChildrenCount - (LastChildFill ? 1 : 0); + + double accumulatedLeft = 0; + double accumulatedTop = 0; + double accumulatedRight = 0; + double accumulatedBottom = 0; - foreach (Control element in children) + for (int i = 0; i < totalChildrenCount; ++i) { - // Determine the remaining space left to arrange the element - Rect remainingRect = new Rect( - left, - top, - Math.Max(0.0, arrangeSize.Width - left - right), - Math.Max(0.0, arrangeSize.Height - top - bottom)); - - // Trim the remaining Rect to the docked size of the element - // (unless the element should fill the remaining space because - // of LastChildFill) - if (index < dockedCount) + var child = children[i]; + if (child == null) + { continue; } + + Size childDesiredSize = child.DesiredSize; + Rect rcChild = new Rect( + accumulatedLeft, + accumulatedTop, + Math.Max(0.0, arrangeSize.Width - (accumulatedLeft + accumulatedRight)), + Math.Max(0.0, arrangeSize.Height - (accumulatedTop + accumulatedBottom))); + + if (i < nonFillChildrenCount) { - Size desiredSize = element.DesiredSize; - switch (GetDock(element)) + switch (DockPanel.GetDock((Control)child)) { case Dock.Left: - left += desiredSize.Width; - remainingRect = remainingRect.WithWidth(desiredSize.Width); - break; - case Dock.Top: - top += desiredSize.Height; - remainingRect = remainingRect.WithHeight(desiredSize.Height); + accumulatedLeft += childDesiredSize.Width; + rcChild = rcChild.WithWidth(childDesiredSize.Width); break; + case Dock.Right: - right += desiredSize.Width; - remainingRect = new Rect( - Math.Max(0.0, arrangeSize.Width - right), - remainingRect.Y, - desiredSize.Width, - remainingRect.Height); + accumulatedRight += childDesiredSize.Width; + rcChild = rcChild.WithX(Math.Max(0.0, arrangeSize.Width - accumulatedRight)); + rcChild = rcChild.WithWidth(childDesiredSize.Width); break; + + case Dock.Top: + accumulatedTop += childDesiredSize.Height; + rcChild = rcChild.WithHeight(childDesiredSize.Height); + break; + case Dock.Bottom: - bottom += desiredSize.Height; - remainingRect = new Rect( - remainingRect.X, - Math.Max(0.0, arrangeSize.Height - bottom), - remainingRect.Width, - desiredSize.Height); + accumulatedBottom += childDesiredSize.Height; + rcChild = rcChild.WithY(Math.Max(0.0, arrangeSize.Height - accumulatedBottom)); + rcChild = rcChild.WithHeight(childDesiredSize.Height); break; } } - element.Arrange(remainingRect); - index++; + child.Arrange(rcChild); } - return arrangeSize; + return (arrangeSize); } } } diff --git a/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs b/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs index 43beb923e5..83d1c4aae3 100644 --- a/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs +++ b/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs @@ -45,7 +45,10 @@ namespace Avalonia.Controls.Embedding { if (EnforceClientSize) availableSize = PlatformImpl?.ClientSize ?? default(Size); - return base.MeasureOverride(availableSize); + var rv = base.MeasureOverride(availableSize); + if (EnforceClientSize) + return availableSize; + return rv; } private readonly NameScope _nameScope = new NameScope(); diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs index 3dfeae52a4..a292ff7d0a 100644 --- a/src/Avalonia.Controls/ItemsControl.cs +++ b/src/Avalonia.Controls/ItemsControl.cs @@ -36,6 +36,12 @@ namespace Avalonia.Controls public static readonly DirectProperty ItemsProperty = AvaloniaProperty.RegisterDirect(nameof(Items), o => o.Items, (o, v) => o.Items = v); + /// + /// Defines the property. + /// + public static readonly DirectProperty ItemCountProperty = + AvaloniaProperty.RegisterDirect(nameof(ItemCount), o => o.ItemCount); + /// /// Defines the property. /// @@ -55,6 +61,7 @@ namespace Avalonia.Controls AvaloniaProperty.Register(nameof(MemberSelector)); private IEnumerable _items = new AvaloniaList(); + private int _itemCount; private IItemContainerGenerator _itemContainerGenerator; private IDisposable _itemsCollectionChangedSubscription; @@ -110,10 +117,13 @@ namespace Avalonia.Controls set { SetAndRaise(ItemsProperty, ref _items, value); } } + /// + /// Gets the number of items in . + /// public int ItemCount { - get; - private set; + get => _itemCount; + private set => SetAndRaise(ItemCountProperty, ref _itemCount, value); } /// diff --git a/src/Avalonia.Controls/ListBox.cs b/src/Avalonia.Controls/ListBox.cs index 041b81155a..3150b6be91 100644 --- a/src/Avalonia.Controls/ListBox.cs +++ b/src/Avalonia.Controls/ListBox.cs @@ -84,6 +84,16 @@ namespace Avalonia.Controls set { SetValue(VirtualizationModeProperty, value); } } + /// + /// Selects all items in the . + /// + public new void SelectAll() => base.SelectAll(); + + /// + /// Deselects all items in the . + /// + public new void UnselectAll() => base.UnselectAll(); + /// protected override IItemContainerGenerator CreateItemContainerGenerator() { @@ -118,7 +128,8 @@ namespace Avalonia.Controls e.Source, true, (e.InputModifiers & InputModifiers.Shift) != 0, - (e.InputModifiers & InputModifiers.Control) != 0); + (e.InputModifiers & InputModifiers.Control) != 0, + e.MouseButton == MouseButton.Right); } } diff --git a/src/Avalonia.Controls/Primitives/PopupRoot.cs b/src/Avalonia.Controls/Primitives/PopupRoot.cs index 90020839d6..d2e8f1ab92 100644 --- a/src/Avalonia.Controls/Primitives/PopupRoot.cs +++ b/src/Avalonia.Controls/Primitives/PopupRoot.cs @@ -80,7 +80,7 @@ namespace Avalonia.Controls.Primitives /// public void SnapInsideScreenEdges() { - var screen = Application.Current.MainWindow?.Screens.ScreenFromPoint(Position); + var screen = (VisualRoot as WindowBase)?.Screens?.ScreenFromPoint(Position); if (screen != null) { diff --git a/src/Avalonia.Controls/Primitives/RangeBase.cs b/src/Avalonia.Controls/Primitives/RangeBase.cs index ae175734b9..f1ee7c0e1a 100644 --- a/src/Avalonia.Controls/Primitives/RangeBase.cs +++ b/src/Avalonia.Controls/Primitives/RangeBase.cs @@ -75,10 +75,18 @@ namespace Avalonia.Controls.Primitives set { - value = ValidateMinimum(value); - SetAndRaise(MinimumProperty, ref _minimum, value); - Maximum = ValidateMaximum(Maximum); - Value = ValidateValue(Value); + ValidateDouble(value, "Minimum"); + + if (IsInitialized) + { + SetAndRaise(MinimumProperty, ref _minimum, value); + Maximum = ValidateMaximum(Maximum); + Value = ValidateValue(Value); + } + else + { + SetAndRaise(MinimumProperty, ref _minimum, value); + } } } @@ -94,9 +102,18 @@ namespace Avalonia.Controls.Primitives set { - value = ValidateMaximum(value); - SetAndRaise(MaximumProperty, ref _maximum, value); - Value = ValidateValue(Value); + ValidateDouble(value, "Maximum"); + + if (IsInitialized) + { + value = ValidateMaximum(value); + SetAndRaise(MaximumProperty, ref _maximum, value); + Value = ValidateValue(Value); + } + else + { + SetAndRaise(MaximumProperty, ref _maximum, value); + } } } @@ -112,8 +129,17 @@ namespace Avalonia.Controls.Primitives set { - value = ValidateValue(value); - SetAndRaise(ValueProperty, ref _value, value); + ValidateDouble(value, "Value"); + + if (IsInitialized) + { + value = ValidateValue(value); + SetAndRaise(ValueProperty, ref _value, value); + } + else + { + SetAndRaise(ValueProperty, ref _value, value); + } } } @@ -129,6 +155,14 @@ namespace Avalonia.Controls.Primitives set => SetValue(LargeChangeProperty, value); } + protected override void OnInitialized() + { + base.OnInitialized(); + + Maximum = ValidateMaximum(Maximum); + Value = ValidateValue(Value); + } + /// /// Throws an exception if the double value is NaN or Inf. /// @@ -142,17 +176,6 @@ namespace Avalonia.Controls.Primitives } } - /// - /// Validates the property. - /// - /// The value. - /// The coerced value. - private double ValidateMinimum(double value) - { - ValidateDouble(value, "Minimum"); - return value; - } - /// /// Validates/coerces the property. /// @@ -160,7 +183,6 @@ namespace Avalonia.Controls.Primitives /// The coerced value. private double ValidateMaximum(double value) { - ValidateDouble(value, "Maximum"); return Math.Max(value, Minimum); } @@ -171,7 +193,6 @@ namespace Avalonia.Controls.Primitives /// The coerced value. private double ValidateValue(double value) { - ValidateDouble(value, "Value"); return MathUtilities.Clamp(value, Minimum, Maximum); } } diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index 7b91d6235d..91a9fa7e40 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -12,6 +12,7 @@ using Avalonia.Data; using Avalonia.Input; using Avalonia.Input.Platform; using Avalonia.Interactivity; +using Avalonia.Logging; using Avalonia.Styling; using Avalonia.VisualTree; @@ -103,6 +104,7 @@ namespace Avalonia.Controls.Primitives private static readonly IList Empty = Array.Empty(); + private readonly Selection _selection = new Selection(); private int _selectedIndex = -1; private object _selectedItem; private IList _selectedItems; @@ -152,23 +154,8 @@ namespace Avalonia.Controls.Primitives { if (_updateCount == 0) { - SetAndRaise(SelectedIndexProperty, ref _selectedIndex, (int val, ref int backing, Action notifyWrapper) => - { - var old = backing; - var effective = (val >= 0 && val < Items?.Cast().Count()) ? val : -1; - - if (old != effective) - { - backing = effective; - notifyWrapper(() => - RaisePropertyChanged( - SelectedIndexProperty, - old, - effective, - BindingPriority.LocalValue)); - SelectedItem = ElementAt(Items, effective); - } - }, value); + var effective = (value >= 0 && value < ItemCount) ? value : -1; + UpdateSelectedItem(effective); } else { @@ -192,41 +179,7 @@ namespace Avalonia.Controls.Primitives { if (_updateCount == 0) { - SetAndRaise(SelectedItemProperty, ref _selectedItem, (object val, ref object backing, Action notifyWrapper) => - { - var old = backing; - var index = IndexOf(Items, val); - var effective = index != -1 ? val : null; - - if (!object.Equals(effective, old)) - { - backing = effective; - - notifyWrapper(() => - RaisePropertyChanged( - SelectedItemProperty, - old, - effective, - BindingPriority.LocalValue)); - - SelectedIndex = index; - - if (effective != null) - { - if (SelectedItems.Count != 1 || SelectedItems[0] != effective) - { - _syncingSelectedItems = true; - SelectedItems.Clear(); - SelectedItems.Add(effective); - _syncingSelectedItems = false; - } - } - else if (SelectedItems.Count > 0) - { - SelectedItems.Clear(); - } - } - }, value); + UpdateSelectedItem(IndexOf(Items, value)); } else { @@ -354,31 +307,23 @@ namespace Avalonia.Controls.Primitives { SelectedIndex = 0; } + else + { + _selection.ItemsInserted(e.NewStartingIndex, e.NewItems.Count); + UpdateSelectedItem(_selection.First(), false); + } break; case NotifyCollectionChangedAction.Remove: - case NotifyCollectionChangedAction.Replace: - var selectedIndex = SelectedIndex; - - if (selectedIndex >= e.OldStartingIndex && - selectedIndex < e.OldStartingIndex + e.OldItems.Count) - { - if (!AlwaysSelected) - { - selectedIndex = SelectedIndex = -1; - } - else - { - LostSelection(); - } - } + _selection.ItemsRemoved(e.OldStartingIndex, e.OldItems.Count); + UpdateSelectedItem(_selection.First(), false); + ResetSelectedItems(); + break; - var items = Items?.Cast(); - if (selectedIndex >= items.Count()) - { - selectedIndex = SelectedIndex = items.Count() - 1; - } + case NotifyCollectionChangedAction.Replace: + UpdateSelectedItem(SelectedIndex, false); + ResetSelectedItems(); break; case NotifyCollectionChangedAction.Move: @@ -439,11 +384,7 @@ namespace Avalonia.Controls.Primitives { if (i.ContainerControl != null && i.Item != null) { - var ms = MemberSelector; - bool selected = ms == null ? - SelectedItems.Contains(i.Item) : - SelectedItems.OfType().Any(v => Equals(ms.Select(v), i.Item)); - + bool selected = _selection.Contains(i.Index); MarkContainerSelected(i.ContainerControl, selected); } } @@ -476,9 +417,12 @@ namespace Avalonia.Controls.Primitives var keymap = AvaloniaLocator.Current.GetService(); bool Match(List gestures) => gestures.Any(g => g.Matches(e)); - if (this.SelectionMode == SelectionMode.Multiple && Match(keymap.SelectAll)) + if (ItemCount > 0 && + Match(keymap.SelectAll) && + (((SelectionMode & SelectionMode.Multiple) != 0) || + (SelectionMode & SelectionMode.Toggle) != 0)) { - SynchronizeItems(SelectedItems, Items?.Cast()); + SelectAll(); e.Handled = true; } } @@ -520,6 +464,41 @@ namespace Avalonia.Controls.Primitives return false; } + /// + /// Selects all items in the control. + /// + protected void SelectAll() + { + if ((SelectionMode & (SelectionMode.Multiple | SelectionMode.Toggle)) == 0) + { + throw new NotSupportedException("Multiple selection is not enabled on this control."); + } + + UpdateSelectedItems(() => + { + _selection.Clear(); + + for (var i = 0; i < ItemCount; ++i) + { + _selection.Add(i); + } + + UpdateSelectedItem(0, false); + + foreach (var container in ItemContainerGenerator.Containers) + { + MarkItemSelected(container.Index, true); + } + + ResetSelectedItems(); + }); + } + + /// + /// Deselects all items in the control. + /// + protected void UnselectAll() => UpdateSelectedItem(-1); + /// /// Updates the selection for an item based on user interaction. /// @@ -527,51 +506,83 @@ namespace Avalonia.Controls.Primitives /// Whether the item should be selected or unselected. /// Whether the range modifier is enabled (i.e. shift key). /// Whether the toggle modifier is enabled (i.e. ctrl key). + /// Whether the event is a right-click. protected void UpdateSelection( int index, bool select = true, bool rangeModifier = false, - bool toggleModifier = false) + bool toggleModifier = false, + bool rightButton = false) { if (index != -1) { if (select) { var mode = SelectionMode; - var toggle = toggleModifier || (mode & SelectionMode.Toggle) != 0; var multi = (mode & SelectionMode.Multiple) != 0; - var range = multi && SelectedIndex != -1 && rangeModifier; + var toggle = (toggleModifier || (mode & SelectionMode.Toggle) != 0); + var range = multi && rangeModifier; - if (!toggle && !range) + if (range) { - SelectedIndex = index; - } - else if (multi && range) - { - SynchronizeItems( - SelectedItems, - GetRange(Items, SelectedIndex, index)); + UpdateSelectedItems(() => + { + var start = SelectedIndex != -1 ? SelectedIndex : 0; + var step = start < index ? 1 : -1; + + _selection.Clear(); + + for (var i = start; i != index; i += step) + { + _selection.Add(i); + } + + _selection.Add(index); + + var first = Math.Min(start, index); + var last = Math.Max(start, index); + + foreach (var container in ItemContainerGenerator.Containers) + { + MarkItemSelected( + container.Index, + container.Index >= first && container.Index <= last); + } + + ResetSelectedItems(); + }); } - else + else if (multi && toggle) { - var item = ElementAt(Items, index); - var i = SelectedItems.IndexOf(item); - - if (i != -1 && (!AlwaysSelected || SelectedItems.Count > 1)) - { - SelectedItems.Remove(item); - } - else + UpdateSelectedItems(() => { - if (multi) + if (!_selection.Contains(index)) { - SelectedItems.Add(item); + _selection.Add(index); + MarkItemSelected(index, true); + SelectedItems.Add(ElementAt(Items, index)); } else { - SelectedIndex = index; + _selection.Remove(index); + MarkItemSelected(index, false); + + if (index == _selectedIndex) + { + UpdateSelectedItem(_selection.First(), false); + } + + SelectedItems.Remove(ElementAt(Items, index)); } - } + }); + } + else if (toggle) + { + SelectedIndex = (SelectedIndex == index) ? -1 : index; + } + else + { + UpdateSelectedItem(index, !(rightButton && _selection.Contains(index))); } if (Presenter?.Panel != null) @@ -596,17 +607,19 @@ namespace Avalonia.Controls.Primitives /// Whether the container should be selected or unselected. /// Whether the range modifier is enabled (i.e. shift key). /// Whether the toggle modifier is enabled (i.e. ctrl key). + /// Whether the event is a right-click. protected void UpdateSelection( IControl container, bool select = true, bool rangeModifier = false, - bool toggleModifier = false) + bool toggleModifier = false, + bool rightButton = false) { var index = ItemContainerGenerator?.IndexFromContainer(container) ?? -1; if (index != -1) { - UpdateSelection(index, select, rangeModifier, toggleModifier); + UpdateSelection(index, select, rangeModifier, toggleModifier, rightButton); } } @@ -618,6 +631,7 @@ namespace Avalonia.Controls.Primitives /// Whether the container should be selected or unselected. /// Whether the range modifier is enabled (i.e. shift key). /// Whether the toggle modifier is enabled (i.e. ctrl key). + /// Whether the event is a right-click. /// /// True if the event originated from a container that belongs to the control; otherwise /// false. @@ -626,51 +640,20 @@ namespace Avalonia.Controls.Primitives IInteractive eventSource, bool select = true, bool rangeModifier = false, - bool toggleModifier = false) + bool toggleModifier = false, + bool rightButton = false) { var container = GetContainerFromEventSource(eventSource); if (container != null) { - UpdateSelection(container, select, rangeModifier, toggleModifier); + UpdateSelection(container, select, rangeModifier, toggleModifier, rightButton); return true; } return false; } - /// - /// Makes a list of objects equal another. - /// - /// The items collection. - /// The desired items. - internal static void SynchronizeItems(IList items, IEnumerable desired) - { - var index = 0; - - foreach (object item in desired) - { - int itemIndex = items.IndexOf(item); - - if (itemIndex == -1) - { - items.Insert(index, item); - } - else if(itemIndex != index) - { - items.RemoveAt(itemIndex); - items.Insert(index, item); - } - - ++index; - } - - while (index < items.Count) - { - items.RemoveAt(items.Count - 1); - } - } - /// /// Gets a range of items from an IEnumerable. /// @@ -678,17 +661,19 @@ namespace Avalonia.Controls.Primitives /// The index of the first item. /// The index of the last item. /// The items. - private static IEnumerable GetRange(IEnumerable items, int first, int last) + private static List GetRange(IEnumerable items, int first, int last) { var list = (items as IList) ?? items.Cast().ToList(); - int step = first > last ? -1 : 1; + var step = first > last ? -1 : 1; + var result = new List(); for (int i = first; i != last; i += step) { - yield return list[i]; + result.Add(list[i]); } - yield return list[last]; + result.Add(list[last]); + return result; } /// @@ -724,19 +709,14 @@ namespace Avalonia.Controls.Primitives private void LostSelection() { var items = Items?.Cast(); + var index = -1; if (items != null && AlwaysSelected) { - var index = Math.Min(SelectedIndex, items.Count() - 1); - - if (index > -1) - { - SelectedItem = items.ElementAt(index); - return; - } + index = Math.Min(SelectedIndex, items.Count() - 1); } - SelectedIndex = -1; + SelectedIndex = index; } /// @@ -793,7 +773,7 @@ namespace Avalonia.Controls.Primitives /// /// The item. /// Whether the item should be selected or deselected. - private void MarkItemSelected(object item, bool selected) + private int MarkItemSelected(object item, bool selected) { var index = IndexOf(Items, item); @@ -801,6 +781,21 @@ namespace Avalonia.Controls.Primitives { MarkItemSelected(index, selected); } + + return index; + } + + private void ResetSelectedItems() + { + UpdateSelectedItems(() => + { + SelectedItems.Clear(); + + foreach (var i in _selection) + { + SelectedItems.Add(ElementAt(Items, i)); + } + }); } /// @@ -810,95 +805,97 @@ namespace Avalonia.Controls.Primitives /// The event args. private void SelectedItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { - var generator = ItemContainerGenerator; + if (_syncingSelectedItems) + { + return; + } + + void Add(IList newItems, IList addedItems = null) + { + foreach (var item in newItems) + { + var index = MarkItemSelected(item, true); + + if (index != -1 && _selection.Add(index) && addedItems != null) + { + addedItems.Add(item); + } + } + } + + void UpdateSelection() + { + if ((SelectedIndex != -1 && !_selection.Contains(SelectedIndex)) || + (SelectedIndex == -1 && _selection.HasItems)) + { + _selectedIndex = _selection.First(); + _selectedItem = ElementAt(Items, _selectedIndex); + RaisePropertyChanged(SelectedIndexProperty, -1, _selectedIndex, BindingPriority.LocalValue); + RaisePropertyChanged(SelectedItemProperty, null, _selectedItem, BindingPriority.LocalValue); + + if (AutoScrollToSelectedItem) + { + ScrollIntoView(_selectedIndex); + } + } + } + IList added = null; IList removed = null; switch (e.Action) { case NotifyCollectionChangedAction.Add: - SelectedItemsAdded(e.NewItems.Cast().ToList()); - - if (AutoScrollToSelectedItem) { - ScrollIntoView(e.NewItems[0]); + Add(e.NewItems); + UpdateSelection(); + added = e.NewItems; } - added = e.NewItems; break; case NotifyCollectionChangedAction.Remove: if (SelectedItems.Count == 0) { - if (!_syncingSelectedItems) - { - SelectedIndex = -1; - } + SelectedIndex = -1; } foreach (var item in e.OldItems) { - MarkItemSelected(item, false); + var index = MarkItemSelected(item, false); + _selection.Remove(index); } removed = e.OldItems; break; + case NotifyCollectionChangedAction.Replace: + throw new NotSupportedException("Replacing items in a SelectedItems collection is not supported."); + + case NotifyCollectionChangedAction.Move: + throw new NotSupportedException("Moving items in a SelectedItems collection is not supported."); + case NotifyCollectionChangedAction.Reset: - if (generator != null) { removed = new List(); + added = new List(); - foreach (var item in generator.Containers) + foreach (var index in _selection.ToList()) { - if (item?.ContainerControl != null) + var item = ElementAt(Items, index); + + if (!SelectedItems.Contains(item)) { - if (MarkContainerSelected(item.ContainerControl, false)) - { - removed.Add(item.Item); - } + MarkItemSelected(index, false); + removed.Add(item); + _selection.Remove(index); } } - } - if (SelectedItems.Count > 0) - { - _selectedItem = null; - SelectedItemsAdded(SelectedItems); - added = SelectedItems; - } - else if (!_syncingSelectedItems) - { - SelectedIndex = -1; - } - - break; - - case NotifyCollectionChangedAction.Replace: - foreach (var item in e.OldItems) - { - MarkItemSelected(item, false); - } - - foreach (var item in e.NewItems) - { - MarkItemSelected(item, true); + Add(SelectedItems, added); + UpdateSelection(); } - if (SelectedItem != SelectedItems[0] && !_syncingSelectedItems) - { - var oldItem = SelectedItem; - var oldIndex = SelectedIndex; - var item = SelectedItems[0]; - var index = IndexOf(Items, item); - _selectedIndex = index; - _selectedItem = item; - RaisePropertyChanged(SelectedIndexProperty, oldIndex, index, BindingPriority.LocalValue); - RaisePropertyChanged(SelectedItemProperty, oldItem, item, BindingPriority.LocalValue); - } - - added = e.NewItems; - removed = e.OldItems; break; } @@ -912,34 +909,6 @@ namespace Avalonia.Controls.Primitives } } - /// - /// Called when items are added to the collection. - /// - /// The added items. - private void SelectedItemsAdded(IList items) - { - if (items.Count > 0) - { - foreach (var item in items) - { - MarkItemSelected(item, true); - } - - if (SelectedItem == null && !_syncingSelectedItems) - { - var index = IndexOf(Items, items[0]); - - if (index != -1) - { - _selectedItem = items[0]; - _selectedIndex = index; - RaisePropertyChanged(SelectedIndexProperty, -1, index, BindingPriority.LocalValue); - RaisePropertyChanged(SelectedItemProperty, null, items[0], BindingPriority.LocalValue); - } - } - } - } - /// /// Subscribes to the CollectionChanged event, if any. /// @@ -970,6 +939,112 @@ namespace Avalonia.Controls.Primitives } } + /// + /// Updates the selection due to a change to or + /// . + /// + /// The new selected index. + /// Whether to clear existing selection. + private void UpdateSelectedItem(int index, bool clear = true) + { + var oldIndex = _selectedIndex; + var oldItem = _selectedItem; + + if (index == -1 && AlwaysSelected) + { + index = Math.Min(SelectedIndex, ItemCount - 1); + } + + var item = ElementAt(Items, index); + var added = -1; + HashSet removed = null; + + _selectedIndex = index; + _selectedItem = item; + + if (oldIndex != index || _selection.HasMultiple) + { + if (clear) + { + removed = _selection.Clear(); + } + + if (index != -1) + { + if (_selection.Add(index)) + { + added = index; + } + + if (removed?.Contains(index) == true) + { + removed.Remove(index); + added = -1; + } + } + + if (removed != null) + { + foreach (var i in removed) + { + MarkItemSelected(i, false); + } + } + + MarkItemSelected(index, true); + + RaisePropertyChanged( + SelectedIndexProperty, + oldIndex, + index); + } + + if (!Equals(item, oldItem)) + { + RaisePropertyChanged( + SelectedItemProperty, + oldItem, + item); + } + + if (removed != null && index != -1) + { + removed.Remove(index); + } + + if (added != -1 || removed?.Count > 0) + { + ResetSelectedItems(); + + var e = new SelectionChangedEventArgs( + SelectionChangedEvent, + added != -1 ? new[] { ElementAt(Items, added) } : Array.Empty(), + removed?.Select(x => ElementAt(Items, x)).ToArray() ?? Array.Empty()); + RaiseEvent(e); + } + } + + private void UpdateSelectedItems(Action action) + { + try + { + _syncingSelectedItems = true; + action(); + } + catch (Exception ex) + { + Logger.Error( + LogArea.Property, + this, + "Error thrown updating SelectedItems: {Error}", + ex); + } + finally + { + _syncingSelectedItems = false; + } + } + private void UpdateFinished() { if (_updateSelectedIndex != int.MinValue) @@ -981,5 +1056,104 @@ namespace Avalonia.Controls.Primitives SelectedItems = _updateSelectedItems; } } + + private class Selection : IEnumerable + { + private readonly List _list = new List(); + private HashSet _set = new HashSet(); + + public bool HasItems => _set.Count > 0; + public bool HasMultiple => _set.Count > 1; + + public bool Add(int index) + { + if (index == -1) + { + throw new ArgumentException("Invalid index", "index"); + } + + if (_set.Add(index)) + { + _list.Add(index); + return true; + } + + return false; + } + + public bool Remove(int index) + { + if (_set.Remove(index)) + { + _list.RemoveAll(x => x == index); + return true; + } + + return false; + } + + public HashSet Clear() + { + var result = _set; + _list.Clear(); + _set = new HashSet(); + return result; + } + + public void ItemsInserted(int index, int count) + { + _set = new HashSet(); + + for (var i = 0; i < _list.Count; ++i) + { + var ix = _list[i]; + + if (ix >= index) + { + var newIndex = ix + count; + _list[i] = newIndex; + _set.Add(newIndex); + } + else + { + _set.Add(ix); + } + } + } + + public void ItemsRemoved(int index, int count) + { + var last = (index + count) - 1; + + _set = new HashSet(); + + for (var i = 0; i < _list.Count; ++i) + { + var ix = _list[i]; + + if (ix >= index && ix <= last) + { + _list.RemoveAt(i--); + } + else if (ix > last) + { + var newIndex = ix - count; + _list[i] = newIndex; + _set.Add(newIndex); + } + else + { + _set.Add(ix); + } + } + } + + public bool Contains(int index) => _set.Contains(index); + + public int First() => HasItems ? _list[0] : -1; + + public IEnumerator GetEnumerator() => _set.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } } } diff --git a/src/Avalonia.Controls/RowDefinition.cs b/src/Avalonia.Controls/RowDefinition.cs index ad7312d515..1f2f738670 100644 --- a/src/Avalonia.Controls/RowDefinition.cs +++ b/src/Avalonia.Controls/RowDefinition.cs @@ -29,7 +29,7 @@ namespace Avalonia.Controls /// /// Initializes a new instance of the class. /// - public RowDefinition() + public RowDefinition() { } @@ -38,7 +38,7 @@ namespace Avalonia.Controls /// /// The height of the row. /// The height unit of the column. - public RowDefinition(double value, GridUnitType type) + public RowDefinition(double value, GridUnitType type) { Height = new GridLength(value, type); } @@ -47,7 +47,7 @@ namespace Avalonia.Controls /// Initializes a new instance of the class. /// /// The height of the column. - public RowDefinition(GridLength height) + public RowDefinition(GridLength height) { Height = height; } @@ -62,8 +62,15 @@ namespace Avalonia.Controls /// public double MaxHeight { - get { return GetValue(MaxHeightProperty); } - set { SetValue(MaxHeightProperty, value); } + get + { + return GetValue(MaxHeightProperty); + } + set + { + Parent?.InvalidateMeasure(); + SetValue(MaxHeightProperty, value); + } } /// @@ -71,8 +78,15 @@ namespace Avalonia.Controls /// public double MinHeight { - get { return GetValue(MinHeightProperty); } - set { SetValue(MinHeightProperty, value); } + get + { + return GetValue(MinHeightProperty); + } + set + { + Parent?.InvalidateMeasure(); + SetValue(MinHeightProperty, value); + } } /// @@ -80,12 +94,19 @@ namespace Avalonia.Controls /// public GridLength Height { - get { return GetValue(HeightProperty); } - set { SetValue(HeightProperty, value); } + get + { + return GetValue(HeightProperty); + } + set + { + Parent?.InvalidateMeasure(); + SetValue(HeightProperty, value); + } } internal override GridLength UserSizeValueCache => this.Height; internal override double UserMinSizeValueCache => this.MinHeight; internal override double UserMaxSizeValueCache => this.MaxHeight; } -} \ No newline at end of file +} diff --git a/src/Avalonia.Controls/Screens.cs b/src/Avalonia.Controls/Screens.cs index 0f0adec790..8a0a0fa728 100644 --- a/src/Avalonia.Controls/Screens.cs +++ b/src/Avalonia.Controls/Screens.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using Avalonia.Platform; using Avalonia.Utilities; @@ -11,7 +12,7 @@ namespace Avalonia.Controls private readonly IScreenImpl _iScreenImpl; public int ScreenCount => _iScreenImpl.ScreenCount; - public IReadOnlyList All => _iScreenImpl?.AllScreens; + public IReadOnlyList All => _iScreenImpl?.AllScreens ?? Array.Empty(); public Screen Primary => All.FirstOrDefault(x => x.Primary); public Screens(IScreenImpl iScreenImpl) diff --git a/src/Avalonia.Controls/StartupEventArgs.cs b/src/Avalonia.Controls/StartupEventArgs.cs deleted file mode 100644 index 0e12f5c01a..0000000000 --- a/src/Avalonia.Controls/StartupEventArgs.cs +++ /dev/null @@ -1,36 +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.Collections.Generic; -using System.Linq; - -namespace Avalonia.Controls -{ - /// - /// Contains the arguments for the event. - /// - public class StartupEventArgs : EventArgs - { - private string[] _args; - - /// - /// Gets the command line arguments that were passed to the application. - /// - public IReadOnlyList Args => _args ?? (_args = GetArgs()); - - private static string[] GetArgs() - { - try - { - var args = Environment.GetCommandLineArgs(); - - return args.Length > 1 ? args.Skip(1).ToArray() : new string[0]; - } - catch (NotSupportedException) - { - return new string[0]; - } - } - } -} diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index 21bd0e4e57..87100ceeb0 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -51,7 +51,6 @@ namespace Avalonia.Controls private readonly IInputManager _inputManager; private readonly IAccessKeyHandler _accessKeyHandler; private readonly IKeyboardNavigationHandler _keyboardNavigationHandler; - private readonly IApplicationLifecycle _applicationLifecycle; private readonly IPlatformRenderInterface _renderInterface; private Size _clientSize; private ILayoutManager _layoutManager; @@ -96,7 +95,6 @@ namespace Avalonia.Controls _accessKeyHandler = TryGetService(dependencyResolver); _inputManager = TryGetService(dependencyResolver); _keyboardNavigationHandler = TryGetService(dependencyResolver); - _applicationLifecycle = TryGetService(dependencyResolver); _renderInterface = TryGetService(dependencyResolver); Renderer = impl.CreateRenderer(this); @@ -125,11 +123,6 @@ namespace Avalonia.Controls x => (x as InputElement)?.GetObservable(CursorProperty) ?? Observable.Empty()) .Switch().Subscribe(cursor => PlatformImpl?.SetCursor(cursor?.PlatformCursor)); - if (_applicationLifecycle != null) - { - _applicationLifecycle.Exit += OnApplicationExiting; - } - if (((IStyleHost)this).StylingParent is IResourceProvider applicationResources) { WeakSubscriptionManager.Subscribe( @@ -281,7 +274,6 @@ namespace Avalonia.Controls Closed?.Invoke(this, EventArgs.Empty); Renderer?.Dispose(); Renderer = null; - _applicationLifecycle.Exit -= OnApplicationExiting; } /// @@ -348,18 +340,6 @@ namespace Avalonia.Controls return result; } - private void OnApplicationExiting(object sender, EventArgs args) - { - HandleApplicationExiting(); - } - - /// - /// Handles the application exiting, either from the last window closing, or a call to . - /// - protected virtual void HandleApplicationExiting() - { - } - /// /// Handles input from . /// diff --git a/src/Avalonia.Controls/TreeView.cs b/src/Avalonia.Controls/TreeView.cs index c3fbce1d83..888f4a2013 100644 --- a/src/Avalonia.Controls/TreeView.cs +++ b/src/Avalonia.Controls/TreeView.cs @@ -409,7 +409,7 @@ namespace Avalonia.Controls if (this.SelectionMode == SelectionMode.Multiple && Match(keymap.SelectAll)) { - SelectingItemsControl.SynchronizeItems(SelectedItems, ItemContainerGenerator.Index.Items); + SynchronizeItems(SelectedItems, ItemContainerGenerator.Index.Items); e.Handled = true; } } @@ -521,7 +521,7 @@ namespace Avalonia.Controls } else if (multi && range) { - SelectingItemsControl.SynchronizeItems( + SynchronizeItems( SelectedItems, GetItemsInRange(selectedContainer as TreeViewItem, container as TreeViewItem)); } @@ -778,5 +778,27 @@ namespace Avalonia.Controls container.Classes.Set(":selected", selected); } } + + /// + /// Makes a list of objects equal another (though doesn't preserve order). + /// + /// The items collection. + /// The desired items. + private static void SynchronizeItems(IList items, IEnumerable desired) + { + var list = items.Cast().ToList(); + var toRemove = list.Except(desired).ToList(); + var toAdd = desired.Except(list).ToList(); + + foreach (var i in toRemove) + { + items.Remove(i); + } + + foreach (var i in toAdd) + { + items.Add(i); + } + } } } diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index 01614ba87b..5c117f508b 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -14,6 +14,7 @@ using System.Collections.Generic; using System.Linq; using JetBrains.Annotations; using System.ComponentModel; +using Avalonia.Interactivity; namespace Avalonia.Controls { @@ -97,6 +98,20 @@ namespace Avalonia.Controls public static readonly StyledProperty CanResizeProperty = AvaloniaProperty.Register(nameof(CanResize), true); + /// + /// Routed event that can be used for global tracking of window destruction + /// + public static readonly RoutedEvent WindowClosedEvent = + RoutedEvent.Register("WindowClosed", RoutingStrategies.Direct); + + /// + /// Routed event that can be used for global tracking of opening windows + /// + public static readonly RoutedEvent WindowOpenedEvent = + RoutedEvent.Register("WindowOpened", RoutingStrategies.Direct); + + + private readonly NameScope _nameScope = new NameScope(); private object _dialogResult; private readonly Size _maxPlatformClientSize; @@ -140,7 +155,6 @@ namespace Avalonia.Controls impl.Closing = HandleClosing; impl.WindowStateChanged = HandleWindowStateChanged; _maxPlatformClientSize = PlatformImpl?.MaxClientSize ?? default(Size); - Screens = new Screens(PlatformImpl?.Screen); } /// @@ -157,8 +171,6 @@ namespace Avalonia.Controls remove { _nameScope.Unregistered -= value; } } - public Screens Screens { get; private set; } - /// /// Gets the platform-specific window implementation. /// @@ -252,26 +264,6 @@ namespace Avalonia.Controls /// public event EventHandler Closing; - private static void AddWindow(Window window) - { - if (Application.Current == null) - { - return; - } - - Application.Current.Windows.Add(window); - } - - private static void RemoveWindow(Window window) - { - if (Application.Current == null) - { - return; - } - - Application.Current.Windows.Remove(window); - } - /// /// Closes the window. /// @@ -280,12 +272,6 @@ namespace Avalonia.Controls Close(false); } - protected override void HandleApplicationExiting() - { - base.HandleApplicationExiting(); - Close(true); - } - /// /// Closes a dialog window with the specified result. /// @@ -385,7 +371,7 @@ namespace Avalonia.Controls return; } - AddWindow(this); + this.RaiseEvent(new RoutedEventArgs(WindowOpenedEvent)); EnsureInitialized(); IsVisible = true; @@ -447,7 +433,7 @@ namespace Avalonia.Controls throw new InvalidOperationException("The window is already being shown."); } - AddWindow(this); + RaiseEvent(new RoutedEventArgs(WindowOpenedEvent)); EnsureInitialized(); IsVisible = true; @@ -560,7 +546,7 @@ namespace Avalonia.Controls protected override void HandleClosed() { - RemoveWindow(this); + RaiseEvent(new RoutedEventArgs(WindowClosedEvent)); base.HandleClosed(); } @@ -588,16 +574,3 @@ namespace Avalonia.Controls protected virtual void OnClosing(CancelEventArgs e) => Closing?.Invoke(this, e); } } - -namespace Avalonia -{ - public static class WindowApplicationExtensions - { - public static void RunWithMainWindow(this Application app) where TWindow : Avalonia.Controls.Window, new() - { - var window = new TWindow(); - window.Show(); - app.Run(window); - } - } -} diff --git a/src/Avalonia.Controls/WindowBase.cs b/src/Avalonia.Controls/WindowBase.cs index 363af05a0b..40c9fc94d2 100644 --- a/src/Avalonia.Controls/WindowBase.cs +++ b/src/Avalonia.Controls/WindowBase.cs @@ -63,6 +63,7 @@ namespace Avalonia.Controls public WindowBase(IWindowBaseImpl impl, IAvaloniaDependencyResolver dependencyResolver) : base(impl, dependencyResolver) { + Screens = new Screens(PlatformImpl?.Screen); impl.Activated = HandleActivated; impl.Deactivated = HandleDeactivated; impl.PositionChanged = HandlePositionChanged; @@ -108,6 +109,8 @@ namespace Avalonia.Controls impl.Position = value; } } + + public Screens Screens { get; private set; } /// /// Whether an auto-size operation is in progress. diff --git a/src/Avalonia.Controls/WindowCollection.cs b/src/Avalonia.Controls/WindowCollection.cs deleted file mode 100644 index 328bb9f147..0000000000 --- a/src/Avalonia.Controls/WindowCollection.cs +++ /dev/null @@ -1,134 +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.Collections; -using System.Collections.Generic; - -using Avalonia.Controls; - -namespace Avalonia -{ - public class WindowCollection : IReadOnlyList - { - private readonly Application _application; - private readonly List _windows = new List(); - - public WindowCollection(Application application) - { - _application = application; - } - - /// - /// - /// Gets the number of elements in the collection. - /// - public int Count => _windows.Count; - - /// - /// - /// Gets the at the specified index. - /// - /// - /// The . - /// - /// The index. - /// - public Window this[int index] => _windows[index]; - - /// - /// - /// Returns an enumerator that iterates through the collection. - /// - /// - /// An enumerator that can be used to iterate through the collection. - /// - public IEnumerator GetEnumerator() - { - return _windows.GetEnumerator(); - } - - /// - /// - /// Returns an enumerator that iterates through a collection. - /// - /// - /// An object that can be used to iterate through the collection. - /// - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - /// - /// Adds the specified window. - /// - /// The window. - internal void Add(Window window) - { - if (window == null) - { - return; - } - - _windows.Add(window); - } - - /// - /// Removes the specified window. - /// - /// The window. - internal void Remove(Window window) - { - if (window == null) - { - return; - } - - _windows.Remove(window); - - OnRemoveWindow(window); - } - - /// - /// Closes all windows and removes them from the underlying collection. - /// - internal void Clear() - { - while (_windows.Count > 0) - { - _windows[0].Close(true); - } - } - - private void OnRemoveWindow(Window window) - { - if (window == null) - { - return; - } - - if (_application.IsShuttingDown) - { - return; - } - - switch (_application.ShutdownMode) - { - case ShutdownMode.OnLastWindowClose: - if (Count == 0) - { - _application.Shutdown(); - } - - break; - case ShutdownMode.OnMainWindowClose: - if (window == _application.MainWindow) - { - _application.Shutdown(); - } - - break; - } - } - } -} diff --git a/src/Avalonia.Controls/WrapPanel.cs b/src/Avalonia.Controls/WrapPanel.cs index 4df1b39400..6f53d853c7 100644 --- a/src/Avalonia.Controls/WrapPanel.cs +++ b/src/Avalonia.Controls/WrapPanel.cs @@ -1,9 +1,7 @@ -// 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.Collections.Generic; -using System.Diagnostics; -using System.Linq; +// This source file is adapted from the Windows Presentation Foundation project. +// (https://github.com/dotnet/wpf/) +// +// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation. using Avalonia.Input; using Avalonia.Utilities; diff --git a/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs b/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs index a0e86a53b0..617fbc8005 100644 --- a/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs +++ b/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Net; using System.Reflection; +using System.Threading; using System.Xml; using Avalonia.Controls; using Avalonia.Input; @@ -122,15 +123,6 @@ namespace Avalonia.DesignerSupport.Remote } private const string BuilderMethodName = "BuildAvaloniaApp"; - - class NeverClose : ICloseable - { - public event EventHandler Closed - { - add {} - remove {} - } - } public static void Main(string[] cmdline) { @@ -155,7 +147,7 @@ namespace Avalonia.DesignerSupport.Remote transport.OnException += (t, e) => Die(e.ToString()); Log("Sending StartDesignerSessionMessage"); transport.Send(new StartDesignerSessionMessage {SessionId = args.SessionId}); - app.Run(new NeverClose()); + Dispatcher.UIThread.MainLoop(CancellationToken.None); } diff --git a/src/Avalonia.Styling/StyledElement.cs b/src/Avalonia.Styling/StyledElement.cs index f7e063dfb5..146a4c75e7 100644 --- a/src/Avalonia.Styling/StyledElement.cs +++ b/src/Avalonia.Styling/StyledElement.cs @@ -392,6 +392,7 @@ namespace Avalonia if (_initCount == 0 && !IsInitialized) { IsInitialized = true; + OnInitialized(); Initialized?.Invoke(this, EventArgs.Empty); } } @@ -608,7 +609,14 @@ namespace Avalonia protected virtual void OnDataContextEndUpdate() { } - + + /// + /// Called when the control finishes initialization. + /// + protected virtual void OnInitialized() + { + } + private static void DataContextNotifying(IAvaloniaObject o, bool updateStarted) { if (o is StyledElement element) diff --git a/src/Avalonia.X11/X11KeyTransform.cs b/src/Avalonia.X11/X11KeyTransform.cs index 26495111d1..c68cb04733 100644 --- a/src/Avalonia.X11/X11KeyTransform.cs +++ b/src/Avalonia.X11/X11KeyTransform.cs @@ -221,12 +221,8 @@ namespace Avalonia.X11 //{ X11Key.?, Key.DeadCharProcessed } }; - public static Key ConvertKey(IntPtr key) - { - var ikey = key.ToInt32(); - Key result; - return KeyDic.TryGetValue((X11Key)ikey, out result) ? result : Key.None; - } -} + public static Key ConvertKey(X11Key key) + => KeyDic.TryGetValue(key, out var result) ? result : Key.None; + } } diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index 01beebfff1..18c23aa31e 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -419,10 +419,21 @@ namespace Avalonia.X11 return; var buffer = stackalloc byte[40]; - var latinKeysym = XKeycodeToKeysym(_x11.Display, ev.KeyEvent.keycode, 0); + var index = ev.KeyEvent.state.HasFlag(XModifierMask.ShiftMask); + + // We need the latin key, since it's mainly used for hotkeys, we use a different API for text anyway + var key = (X11Key)XKeycodeToKeysym(_x11.Display, ev.KeyEvent.keycode, index ? 1 : 0).ToInt32(); + + // Manually switch the Shift index for the keypad, + // there should be a proper way to do this + if (ev.KeyEvent.state.HasFlag(XModifierMask.Mod2Mask) + && key > X11Key.Num_Lock && key <= X11Key.KP_9) + key = (X11Key)XKeycodeToKeysym(_x11.Display, ev.KeyEvent.keycode, index ? 0 : 1).ToInt32(); + + ScheduleInput(new RawKeyEventArgs(_keyboard, (ulong)ev.KeyEvent.time.ToInt64(), ev.type == XEventName.KeyPress ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp, - X11KeyTransform.ConvertKey(latinKeysym), TranslateModifiers(ev.KeyEvent.state)), ref ev); + X11KeyTransform.ConvertKey(key), TranslateModifiers(ev.KeyEvent.state)), ref ev); if (ev.type == XEventName.KeyPress) { diff --git a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj deleted file mode 100644 index 95443a364e..0000000000 --- a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - netstandard2.0 - true - $(DefineConstants);GTK3_PINVOKE - Avalonia.Gtk3 - - - - - diff --git a/src/Gtk/Avalonia.Gtk3/ClipboardImpl.cs b/src/Gtk/Avalonia.Gtk3/ClipboardImpl.cs deleted file mode 100644 index a860673732..0000000000 --- a/src/Gtk/Avalonia.Gtk3/ClipboardImpl.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.Runtime.InteropServices; -using System.Threading.Tasks; -using Avalonia.Gtk3.Interop; -using Avalonia.Input.Platform; -using Avalonia.Platform.Interop; - -namespace Avalonia.Gtk3 -{ - class ClipboardImpl : IClipboard - { - - IntPtr GetClipboard() => Native.GtkClipboardGetForDisplay(Native.GdkGetDefaultDisplay(), IntPtr.Zero); - - static void OnText(IntPtr clipboard, IntPtr utf8string, IntPtr userdata) - { - var handle = GCHandle.FromIntPtr(userdata); - - ((TaskCompletionSource) handle.Target) - .TrySetResult(Utf8Buffer.StringFromPtr(utf8string)); - handle.Free(); - } - - private static readonly Native.D.GtkClipboardTextReceivedFunc OnTextDelegate = OnText; - - static ClipboardImpl() - { - GCHandle.Alloc(OnTextDelegate); - } - - public Task GetTextAsync() - { - var tcs = new TaskCompletionSource(); - Native.GtkClipboardRequestText(GetClipboard(), OnTextDelegate, GCHandle.ToIntPtr(GCHandle.Alloc(tcs))); - return tcs.Task; - } - - public Task SetTextAsync(string text) - { - using (var buf = new Utf8Buffer(text)) - Native.GtkClipboardSetText(GetClipboard(), buf, buf.ByteLen); - return Task.FromResult(0); - } - - public Task ClearAsync() - { - Native.GtkClipboardRequestClear(GetClipboard()); - return Task.FromResult(0); - } - } -} diff --git a/src/Gtk/Avalonia.Gtk3/CursorFactory.cs b/src/Gtk/Avalonia.Gtk3/CursorFactory.cs deleted file mode 100644 index 95fa3ba9e3..0000000000 --- a/src/Gtk/Avalonia.Gtk3/CursorFactory.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System; -using System.Collections.Generic; -using Avalonia.Gtk3.Interop; -using Avalonia.Input; -using Avalonia.Platform; -using Avalonia.Platform.Interop; -using CursorType = Avalonia.Gtk3.GdkCursorType; -namespace Avalonia.Gtk3 -{ - class CursorFactory : IStandardCursorFactory - { - private static readonly Dictionary CursorTypeMapping = new Dictionary - - { - {StandardCursorType.None, CursorType.Blank}, - {StandardCursorType.AppStarting, CursorType.Watch}, - {StandardCursorType.Arrow, CursorType.LeftPtr}, - {StandardCursorType.Cross, CursorType.Cross}, - {StandardCursorType.Hand, CursorType.Hand1}, - {StandardCursorType.Ibeam, CursorType.Xterm}, - {StandardCursorType.No, "gtk-cancel"}, - {StandardCursorType.SizeAll, CursorType.Sizing}, - //{ StandardCursorType.SizeNorthEastSouthWest, 32643 }, - {StandardCursorType.SizeNorthSouth, CursorType.SbVDoubleArrow}, - //{ StandardCursorType.SizeNorthWestSouthEast, 32642 }, - {StandardCursorType.SizeWestEast, CursorType.SbHDoubleArrow}, - {StandardCursorType.UpArrow, CursorType.BasedArrowUp}, - {StandardCursorType.Wait, CursorType.Watch}, - {StandardCursorType.Help, "gtk-help"}, - {StandardCursorType.TopSide, CursorType.TopSide}, - {StandardCursorType.BottomSize, CursorType.BottomSide}, - {StandardCursorType.LeftSide, CursorType.LeftSide}, - {StandardCursorType.RightSide, CursorType.RightSide}, - {StandardCursorType.TopLeftCorner, CursorType.TopLeftCorner}, - {StandardCursorType.TopRightCorner, CursorType.TopRightCorner}, - {StandardCursorType.BottomLeftCorner, CursorType.BottomLeftCorner}, - {StandardCursorType.BottomRightCorner, CursorType.BottomRightCorner}, - {StandardCursorType.DragCopy, CursorType.CenterPtr}, - {StandardCursorType.DragMove, CursorType.Fleur}, - {StandardCursorType.DragLink, CursorType.Cross}, - }; - - private static readonly Dictionary Cache = - new Dictionary(); - - private IntPtr GetCursor(object desc) - { - IntPtr rv; - var name = desc as string; - if (name != null) - { - var theme = Native.GtkIconThemeGetDefault(); - IntPtr icon, error; - using (var u = new Utf8Buffer(name)) - icon = Native.GtkIconThemeLoadIcon(theme, u, 32, 0, out error); - rv = icon == IntPtr.Zero - ? Native.GdkCursorNew(GdkCursorType.XCursor) - : Native.GdkCursorNewFromPixbuf(Native.GdkGetDefaultDisplay(), icon, 0, 0); - } - else - { - rv = Native.GdkCursorNew((CursorType)desc); - } - - - return rv; - } - - public IPlatformHandle GetCursor(StandardCursorType cursorType) - { - IPlatformHandle rv; - if (!Cache.TryGetValue(cursorType, out rv)) - { - Cache[cursorType] = - rv = - new PlatformHandle( - GetCursor(CursorTypeMapping[cursorType]), - "GTKCURSOR"); - } - - return rv; - } - } -} diff --git a/src/Gtk/Avalonia.Gtk3/FramebufferManager.cs b/src/Gtk/Avalonia.Gtk3/FramebufferManager.cs deleted file mode 100644 index e3bbe19978..0000000000 --- a/src/Gtk/Avalonia.Gtk3/FramebufferManager.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using Avalonia.Controls.Platform.Surfaces; -using Avalonia.Platform; -using Avalonia.Threading; - -namespace Avalonia.Gtk3 -{ - class FramebufferManager : IFramebufferPlatformSurface, IDisposable - { - private readonly WindowBaseImpl _window; - public FramebufferManager(WindowBaseImpl window) - { - _window = window; - } - - public void Dispose() - { - // - } - - public ILockedFramebuffer Lock() - { - // This method may be called from non-UI thread, don't touch anything that calls back to GTK/GDK - var s = _window.ClientSize; - var width = Math.Max(1, (int) s.Width); - var height = Math.Max(1, (int) s.Height); - - - if (!Dispatcher.UIThread.CheckAccess() && Gtk3Platform.DisplayClassName.ToLower().Contains("x11")) - { - var x11 = LockX11Framebuffer(width, height); - if (x11 != null) - return x11; - } - - - return new ImageSurfaceFramebuffer(_window, width, height, _window.LastKnownScaleFactor); - } - - private static int X11ErrorHandler(IntPtr d, ref X11.XErrorEvent e) - { - return 0; - } - - private static X11.XErrorHandler X11ErrorHandlerDelegate = X11ErrorHandler; - - private static IntPtr X11Display; - private ILockedFramebuffer LockX11Framebuffer(int width, int height) - { - if (!_window.GdkWindowHandle.HasValue) - return null; - if (X11Display == IntPtr.Zero) - { - X11Display = X11.XOpenDisplay(IntPtr.Zero); - if (X11Display == IntPtr.Zero) - return null; - X11.XSetErrorHandler(X11ErrorHandlerDelegate); - } - return new X11Framebuffer(X11Display, _window.GdkWindowHandle.Value, width, height, _window.LastKnownScaleFactor); - } - } -} diff --git a/src/Gtk/Avalonia.Gtk3/GdkCursor.cs b/src/Gtk/Avalonia.Gtk3/GdkCursor.cs deleted file mode 100644 index aa0f8cde0d..0000000000 --- a/src/Gtk/Avalonia.Gtk3/GdkCursor.cs +++ /dev/null @@ -1,86 +0,0 @@ -namespace Avalonia.Gtk3 -{ - enum GdkCursorType - { - Blank = -2, - CursorIsPixmap = -1, - XCursor = 0, - Arrow = 2, - BasedArrowDown = 4, - BasedArrowUp = 6, - Boat = 8, - Bogosity = 10, - BottomLeftCorner = 12, - BottomRightCorner = 14, - BottomSide = 16, - BottomTee = 18, - BoxSpiral = 20, - CenterPtr = 22, - Circle = 24, - Clock = 26, - CoffeeMug = 28, - Cross = 30, - CrossReverse = 32, - Crosshair = 34, - DiamondCross = 36, - Dot = 38, - Dotbox = 40, - DoubleArrow = 42, - DraftLarge = 44, - DraftSmall = 46, - DrapedBox = 48, - Exchange = 50, - Fleur = 52, - Gobbler = 54, - Gumby = 56, - Hand1 = 58, - Hand2 = 60, - Heart = 62, - Icon = 64, - IronCross = 66, - LeftPtr = 68, - LeftSide = 70, - LeftTee = 72, - Leftbutton = 74, - LlAngle = 76, - LrAngle = 78, - Man = 80, - Middlebutton = 82, - Mouse = 84, - Pencil = 86, - Pirate = 88, - Plus = 90, - QuestionArrow = 92, - RightPtr = 94, - RightSide = 96, - RightTee = 98, - Rightbutton = 100, - RtlLogo = 102, - Sailboat = 104, - SbDownArrow = 106, - SbHDoubleArrow = 108, - SbLeftArrow = 110, - SbRightArrow = 112, - SbUpArrow = 114, - SbVDoubleArrow = 116, - Shuttle = 118, - Sizing = 120, - Spider = 122, - Spraycan = 124, - Star = 126, - Target = 128, - Tcross = 130, - TopLeftArrow = 132, - TopLeftCorner = 134, - TopRightCorner = 136, - TopSide = 138, - TopTee = 140, - Trek = 142, - UlAngle = 144, - Umbrella = 146, - UrAngle = 148, - Watch = 150, - Xterm = 152, - LastCursor = 153, - } -} diff --git a/src/Gtk/Avalonia.Gtk3/GdkKey.cs b/src/Gtk/Avalonia.Gtk3/GdkKey.cs deleted file mode 100644 index 7aa165a1da..0000000000 --- a/src/Gtk/Avalonia.Gtk3/GdkKey.cs +++ /dev/null @@ -1,1341 +0,0 @@ -namespace Avalonia.Gtk3 -{ - enum GdkKey - { - space = 32, - exclam = 33, - quotedbl = 34, - numbersign = 35, - dollar = 36, - percent = 37, - ampersand = 38, - apostrophe = 39, - quoteright = 39, - parenleft = 40, - parenright = 41, - asterisk = 42, - plus = 43, - comma = 44, - minus = 45, - period = 46, - slash = 47, - Key_0 = 48, - Key_1 = 49, - Key_2 = 50, - Key_3 = 51, - Key_4 = 52, - Key_5 = 53, - Key_6 = 54, - Key_7 = 55, - Key_8 = 56, - Key_9 = 57, - colon = 58, - semicolon = 59, - less = 60, - equal = 61, - greater = 62, - question = 63, - at = 64, - A = 65, - B = 66, - C = 67, - D = 68, - E = 69, - F = 70, - G = 71, - H = 72, - I = 73, - J = 74, - K = 75, - L = 76, - M = 77, - N = 78, - O = 79, - P = 80, - Q = 81, - R = 82, - S = 83, - T = 84, - U = 85, - V = 86, - W = 87, - X = 88, - Y = 89, - Z = 90, - bracketleft = 91, - backslash = 92, - bracketright = 93, - asciicircum = 94, - underscore = 95, - grave = 96, - quoteleft = 96, - a = 97, - b = 98, - c = 99, - d = 100, - e = 101, - f = 102, - g = 103, - h = 104, - i = 105, - j = 106, - k = 107, - l = 108, - m = 109, - n = 110, - o = 111, - p = 112, - q = 113, - r = 114, - s = 115, - t = 116, - u = 117, - v = 118, - w = 119, - x = 120, - y = 121, - z = 122, - braceleft = 123, - bar = 124, - braceright = 125, - asciitilde = 126, - nobreakspace = 160, - exclamdown = 161, - cent = 162, - sterling = 163, - currency = 164, - yen = 165, - brokenbar = 166, - section = 167, - diaeresis = 168, - copyright = 169, - ordfeminine = 170, - guillemotleft = 171, - notsign = 172, - hyphen = 173, - registered = 174, - macron = 175, - degree = 176, - plusminus = 177, - twosuperior = 178, - threesuperior = 179, - acute = 180, - mu = 181, - paragraph = 182, - periodcentered = 183, - cedilla = 184, - onesuperior = 185, - masculine = 186, - guillemotright = 187, - onequarter = 188, - onehalf = 189, - threequarters = 190, - questiondown = 191, - Agrave = 192, - Aacute = 193, - Acircumflex = 194, - Atilde = 195, - Adiaeresis = 196, - Aring = 197, - AE = 198, - Ccedilla = 199, - Egrave = 200, - Eacute = 201, - Ecircumflex = 202, - Ediaeresis = 203, - Igrave = 204, - Iacute = 205, - Icircumflex = 206, - Idiaeresis = 207, - ETH = 208, - Eth = 208, - Ntilde = 209, - Ograve = 210, - Oacute = 211, - Ocircumflex = 212, - Otilde = 213, - Odiaeresis = 214, - multiply = 215, - Ooblique = 216, - Ugrave = 217, - Uacute = 218, - Ucircumflex = 219, - Udiaeresis = 220, - Yacute = 221, - THORN = 222, - Thorn = 222, - ssharp = 223, - agrave = 224, - aacute = 225, - acircumflex = 226, - atilde = 227, - adiaeresis = 228, - aring = 229, - ae = 230, - ccedilla = 231, - egrave = 232, - eacute = 233, - ecircumflex = 234, - ediaeresis = 235, - igrave = 236, - iacute = 237, - icircumflex = 238, - idiaeresis = 239, - eth = 240, - ntilde = 241, - ograve = 242, - oacute = 243, - ocircumflex = 244, - otilde = 245, - odiaeresis = 246, - division = 247, - oslash = 248, - ugrave = 249, - uacute = 250, - ucircumflex = 251, - udiaeresis = 252, - yacute = 253, - thorn = 254, - ydiaeresis = 255, - Aogonek = 417, - breve = 418, - Lstroke = 419, - Lcaron = 421, - Sacute = 422, - Scaron = 425, - Scedilla = 426, - Tcaron = 427, - Zacute = 428, - Zcaron = 430, - Zabovedot = 431, - aogonek = 433, - ogonek = 434, - lstroke = 435, - lcaron = 437, - sacute = 438, - caron = 439, - scaron = 441, - scedilla = 442, - tcaron = 443, - zacute = 444, - doubleacute = 445, - zcaron = 446, - zabovedot = 447, - Racute = 448, - Abreve = 451, - Lacute = 453, - Cacute = 454, - Ccaron = 456, - Eogonek = 458, - Ecaron = 460, - Dcaron = 463, - Dstroke = 464, - Nacute = 465, - Ncaron = 466, - Odoubleacute = 469, - Rcaron = 472, - Uring = 473, - Udoubleacute = 475, - Tcedilla = 478, - racute = 480, - abreve = 483, - lacute = 485, - cacute = 486, - ccaron = 488, - eogonek = 490, - ecaron = 492, - dcaron = 495, - dstroke = 496, - nacute = 497, - ncaron = 498, - odoubleacute = 501, - rcaron = 504, - uring = 505, - udoubleacute = 507, - tcedilla = 510, - abovedot = 511, - Hstroke = 673, - Hcircumflex = 678, - Iabovedot = 681, - Gbreve = 683, - Jcircumflex = 684, - hstroke = 689, - hcircumflex = 694, - idotless = 697, - gbreve = 699, - jcircumflex = 700, - Cabovedot = 709, - Ccircumflex = 710, - Gabovedot = 725, - Gcircumflex = 728, - Ubreve = 733, - Scircumflex = 734, - cabovedot = 741, - ccircumflex = 742, - gabovedot = 757, - gcircumflex = 760, - ubreve = 765, - scircumflex = 766, - kappa = 930, - kra = 930, - Rcedilla = 931, - Itilde = 933, - Lcedilla = 934, - Emacron = 938, - Gcedilla = 939, - Tslash = 940, - rcedilla = 947, - itilde = 949, - lcedilla = 950, - emacron = 954, - gcedilla = 955, - tslash = 956, - ENG = 957, - eng = 959, - Amacron = 960, - Iogonek = 967, - Eabovedot = 972, - Imacron = 975, - Ncedilla = 977, - Omacron = 978, - Kcedilla = 979, - Uogonek = 985, - Utilde = 989, - Umacron = 990, - amacron = 992, - iogonek = 999, - eabovedot = 1004, - imacron = 1007, - ncedilla = 1009, - omacron = 1010, - kcedilla = 1011, - uogonek = 1017, - utilde = 1021, - umacron = 1022, - overline = 1150, - kana_fullstop = 1185, - kana_openingbracket = 1186, - kana_closingbracket = 1187, - kana_comma = 1188, - kana_conjunctive = 1189, - kana_middledot = 1189, - kana_WO = 1190, - kana_a = 1191, - kana_i = 1192, - kana_u = 1193, - kana_e = 1194, - kana_o = 1195, - kana_ya = 1196, - kana_yu = 1197, - kana_yo = 1198, - kana_tsu = 1199, - kana_tu = 1199, - prolongedsound = 1200, - kana_A = 1201, - kana_I = 1202, - kana_U = 1203, - kana_E = 1204, - kana_O = 1205, - kana_KA = 1206, - kana_KI = 1207, - kana_KU = 1208, - kana_KE = 1209, - kana_KO = 1210, - kana_SA = 1211, - kana_SHI = 1212, - kana_SU = 1213, - kana_SE = 1214, - kana_SO = 1215, - kana_TA = 1216, - kana_CHI = 1217, - kana_TI = 1217, - kana_TSU = 1218, - kana_TU = 1218, - kana_TE = 1219, - kana_TO = 1220, - kana_NA = 1221, - kana_NI = 1222, - kana_NU = 1223, - kana_NE = 1224, - kana_NO = 1225, - kana_HA = 1226, - kana_HI = 1227, - kana_FU = 1228, - kana_HU = 1228, - kana_HE = 1229, - kana_HO = 1230, - kana_MA = 1231, - kana_MI = 1232, - kana_MU = 1233, - kana_ME = 1234, - kana_MO = 1235, - kana_YA = 1236, - kana_YU = 1237, - kana_YO = 1238, - kana_RA = 1239, - kana_RI = 1240, - kana_RU = 1241, - kana_RE = 1242, - kana_RO = 1243, - kana_WA = 1244, - kana_N = 1245, - voicedsound = 1246, - semivoicedsound = 1247, - Arabic_comma = 1452, - Arabic_semicolon = 1467, - Arabic_question_mark = 1471, - Arabic_hamza = 1473, - Arabic_maddaonalef = 1474, - Arabic_hamzaonalef = 1475, - Arabic_hamzaonwaw = 1476, - Arabic_hamzaunderalef = 1477, - Arabic_hamzaonyeh = 1478, - Arabic_alef = 1479, - Arabic_beh = 1480, - Arabic_tehmarbuta = 1481, - Arabic_teh = 1482, - Arabic_theh = 1483, - Arabic_jeem = 1484, - Arabic_hah = 1485, - Arabic_khah = 1486, - Arabic_dal = 1487, - Arabic_thal = 1488, - Arabic_ra = 1489, - Arabic_zain = 1490, - Arabic_seen = 1491, - Arabic_sheen = 1492, - Arabic_sad = 1493, - Arabic_dad = 1494, - Arabic_tah = 1495, - Arabic_zah = 1496, - Arabic_ain = 1497, - Arabic_ghain = 1498, - Arabic_tatweel = 1504, - Arabic_feh = 1505, - Arabic_qaf = 1506, - Arabic_kaf = 1507, - Arabic_lam = 1508, - Arabic_meem = 1509, - Arabic_noon = 1510, - Arabic_ha = 1511, - Arabic_heh = 1511, - Arabic_waw = 1512, - Arabic_alefmaksura = 1513, - Arabic_yeh = 1514, - Arabic_fathatan = 1515, - Arabic_dammatan = 1516, - Arabic_kasratan = 1517, - Arabic_fatha = 1518, - Arabic_damma = 1519, - Arabic_kasra = 1520, - Arabic_shadda = 1521, - Arabic_sukun = 1522, - Serbian_dje = 1697, - Macedonia_gje = 1698, - Cyrillic_io = 1699, - Ukrainian_ie = 1700, - Ukranian_je = 1700, - Macedonia_dse = 1701, - Ukrainian_i = 1702, - Ukranian_i = 1702, - Ukrainian_yi = 1703, - Ukranian_yi = 1703, - Cyrillic_je = 1704, - Serbian_je = 1704, - Cyrillic_lje = 1705, - Serbian_lje = 1705, - Cyrillic_nje = 1706, - Serbian_nje = 1706, - Serbian_tshe = 1707, - Macedonia_kje = 1708, - Byelorussian_shortu = 1710, - Cyrillic_dzhe = 1711, - Serbian_dze = 1711, - numerosign = 1712, - Serbian_DJE = 1713, - Macedonia_GJE = 1714, - Cyrillic_IO = 1715, - Ukrainian_IE = 1716, - Ukranian_JE = 1716, - Macedonia_DSE = 1717, - Ukrainian_I = 1718, - Ukranian_I = 1718, - Ukrainian_YI = 1719, - Ukranian_YI = 1719, - Cyrillic_JE = 1720, - Serbian_JE = 1720, - Cyrillic_LJE = 1721, - Serbian_LJE = 1721, - Cyrillic_NJE = 1722, - Serbian_NJE = 1722, - Serbian_TSHE = 1723, - Macedonia_KJE = 1724, - Byelorussian_SHORTU = 1726, - Cyrillic_DZHE = 1727, - Serbian_DZE = 1727, - Cyrillic_yu = 1728, - Cyrillic_a = 1729, - Cyrillic_be = 1730, - Cyrillic_tse = 1731, - Cyrillic_de = 1732, - Cyrillic_ie = 1733, - Cyrillic_ef = 1734, - Cyrillic_ghe = 1735, - Cyrillic_ha = 1736, - Cyrillic_i = 1737, - Cyrillic_shorti = 1738, - Cyrillic_ka = 1739, - Cyrillic_el = 1740, - Cyrillic_em = 1741, - Cyrillic_en = 1742, - Cyrillic_o = 1743, - Cyrillic_pe = 1744, - Cyrillic_ya = 1745, - Cyrillic_er = 1746, - Cyrillic_es = 1747, - Cyrillic_te = 1748, - Cyrillic_u = 1749, - Cyrillic_zhe = 1750, - Cyrillic_ve = 1751, - Cyrillic_softsign = 1752, - Cyrillic_yeru = 1753, - Cyrillic_ze = 1754, - Cyrillic_sha = 1755, - Cyrillic_e = 1756, - Cyrillic_shcha = 1757, - Cyrillic_che = 1758, - Cyrillic_hardsign = 1759, - Cyrillic_YU = 1760, - Cyrillic_A = 1761, - Cyrillic_BE = 1762, - Cyrillic_TSE = 1763, - Cyrillic_DE = 1764, - Cyrillic_IE = 1765, - Cyrillic_EF = 1766, - Cyrillic_GHE = 1767, - Cyrillic_HA = 1768, - Cyrillic_I = 1769, - Cyrillic_SHORTI = 1770, - Cyrillic_KA = 1771, - Cyrillic_EL = 1772, - Cyrillic_EM = 1773, - Cyrillic_EN = 1774, - Cyrillic_O = 1775, - Cyrillic_PE = 1776, - Cyrillic_YA = 1777, - Cyrillic_ER = 1778, - Cyrillic_ES = 1779, - Cyrillic_TE = 1780, - Cyrillic_U = 1781, - Cyrillic_ZHE = 1782, - Cyrillic_VE = 1783, - Cyrillic_SOFTSIGN = 1784, - Cyrillic_YERU = 1785, - Cyrillic_ZE = 1786, - Cyrillic_SHA = 1787, - Cyrillic_E = 1788, - Cyrillic_SHCHA = 1789, - Cyrillic_CHE = 1790, - Cyrillic_HARDSIGN = 1791, - Greek_ALPHAaccent = 1953, - Greek_EPSILONaccent = 1954, - Greek_ETAaccent = 1955, - Greek_IOTAaccent = 1956, - Greek_IOTAdiaeresis = 1957, - Greek_OMICRONaccent = 1959, - Greek_UPSILONaccent = 1960, - Greek_UPSILONdieresis = 1961, - Greek_OMEGAaccent = 1963, - Greek_accentdieresis = 1966, - Greek_horizbar = 1967, - Greek_alphaaccent = 1969, - Greek_epsilonaccent = 1970, - Greek_etaaccent = 1971, - Greek_iotaaccent = 1972, - Greek_iotadieresis = 1973, - Greek_iotaaccentdieresis = 1974, - Greek_omicronaccent = 1975, - Greek_upsilonaccent = 1976, - Greek_upsilondieresis = 1977, - Greek_upsilonaccentdieresis = 1978, - Greek_omegaaccent = 1979, - Greek_ALPHA = 1985, - Greek_BETA = 1986, - Greek_GAMMA = 1987, - Greek_DELTA = 1988, - Greek_EPSILON = 1989, - Greek_ZETA = 1990, - Greek_ETA = 1991, - Greek_THETA = 1992, - Greek_IOTA = 1993, - Greek_KAPPA = 1994, - Greek_LAMBDA = 1995, - Greek_LAMDA = 1995, - Greek_MU = 1996, - Greek_NU = 1997, - Greek_XI = 1998, - Greek_OMICRON = 1999, - Greek_PI = 2000, - Greek_RHO = 2001, - Greek_SIGMA = 2002, - Greek_TAU = 2004, - Greek_UPSILON = 2005, - Greek_PHI = 2006, - Greek_CHI = 2007, - Greek_PSI = 2008, - Greek_OMEGA = 2009, - Greek_alpha = 2017, - Greek_beta = 2018, - Greek_gamma = 2019, - Greek_delta = 2020, - Greek_epsilon = 2021, - Greek_zeta = 2022, - Greek_eta = 2023, - Greek_theta = 2024, - Greek_iota = 2025, - Greek_kappa = 2026, - Greek_lambda = 2027, - Greek_lamda = 2027, - Greek_mu = 2028, - Greek_nu = 2029, - Greek_xi = 2030, - Greek_omicron = 2031, - Greek_pi = 2032, - Greek_rho = 2033, - Greek_sigma = 2034, - Greek_finalsmallsigma = 2035, - Greek_tau = 2036, - Greek_upsilon = 2037, - Greek_phi = 2038, - Greek_chi = 2039, - Greek_psi = 2040, - Greek_omega = 2041, - leftradical = 2209, - topleftradical = 2210, - horizconnector = 2211, - topintegral = 2212, - botintegral = 2213, - vertconnector = 2214, - topleftsqbracket = 2215, - botleftsqbracket = 2216, - toprightsqbracket = 2217, - botrightsqbracket = 2218, - topleftparens = 2219, - botleftparens = 2220, - toprightparens = 2221, - botrightparens = 2222, - leftmiddlecurlybrace = 2223, - rightmiddlecurlybrace = 2224, - topleftsummation = 2225, - botleftsummation = 2226, - topvertsummationconnector = 2227, - botvertsummationconnector = 2228, - toprightsummation = 2229, - botrightsummation = 2230, - rightmiddlesummation = 2231, - lessthanequal = 2236, - notequal = 2237, - greaterthanequal = 2238, - integral = 2239, - therefore = 2240, - variation = 2241, - infinity = 2242, - nabla = 2245, - approximate = 2248, - similarequal = 2249, - ifonlyif = 2253, - implies = 2254, - identical = 2255, - radical = 2262, - includedin = 2266, - includes = 2267, - intersection = 2268, - union = 2269, - logicaland = 2270, - logicalor = 2271, - partialderivative = 2287, - function = 2294, - leftarrow = 2299, - uparrow = 2300, - rightarrow = 2301, - downarrow = 2302, - blank = 2527, - soliddiamond = 2528, - checkerboard = 2529, - ht = 2530, - ff = 2531, - cr = 2532, - lf = 2533, - nl = 2536, - vt = 2537, - lowrightcorner = 2538, - uprightcorner = 2539, - upleftcorner = 2540, - lowleftcorner = 2541, - crossinglines = 2542, - horizlinescan1 = 2543, - horizlinescan3 = 2544, - horizlinescan5 = 2545, - horizlinescan7 = 2546, - horizlinescan9 = 2547, - leftt = 2548, - rightt = 2549, - bott = 2550, - topt = 2551, - vertbar = 2552, - emspace = 2721, - enspace = 2722, - em3space = 2723, - em4space = 2724, - digitspace = 2725, - punctspace = 2726, - thinspace = 2727, - hairspace = 2728, - emdash = 2729, - endash = 2730, - signifblank = 2732, - ellipsis = 2734, - doubbaselinedot = 2735, - onethird = 2736, - twothirds = 2737, - onefifth = 2738, - twofifths = 2739, - threefifths = 2740, - fourfifths = 2741, - onesixth = 2742, - fivesixths = 2743, - careof = 2744, - figdash = 2747, - leftanglebracket = 2748, - decimalpoint = 2749, - rightanglebracket = 2750, - marker = 2751, - oneeighth = 2755, - threeeighths = 2756, - fiveeighths = 2757, - seveneighths = 2758, - trademark = 2761, - signaturemark = 2762, - trademarkincircle = 2763, - leftopentriangle = 2764, - rightopentriangle = 2765, - emopencircle = 2766, - emopenrectangle = 2767, - leftsinglequotemark = 2768, - rightsinglequotemark = 2769, - leftdoublequotemark = 2770, - rightdoublequotemark = 2771, - prescription = 2772, - minutes = 2774, - seconds = 2775, - latincross = 2777, - hexagram = 2778, - filledrectbullet = 2779, - filledlefttribullet = 2780, - filledrighttribullet = 2781, - emfilledcircle = 2782, - emfilledrect = 2783, - enopencircbullet = 2784, - enopensquarebullet = 2785, - openrectbullet = 2786, - opentribulletup = 2787, - opentribulletdown = 2788, - openstar = 2789, - enfilledcircbullet = 2790, - enfilledsqbullet = 2791, - filledtribulletup = 2792, - filledtribulletdown = 2793, - leftpointer = 2794, - rightpointer = 2795, - club = 2796, - diamond = 2797, - heart = 2798, - maltesecross = 2800, - dagger = 2801, - doubledagger = 2802, - checkmark = 2803, - ballotcross = 2804, - musicalsharp = 2805, - musicalflat = 2806, - malesymbol = 2807, - femalesymbol = 2808, - telephone = 2809, - telephonerecorder = 2810, - phonographcopyright = 2811, - caret = 2812, - singlelowquotemark = 2813, - doublelowquotemark = 2814, - cursor = 2815, - leftcaret = 2979, - rightcaret = 2982, - downcaret = 2984, - upcaret = 2985, - overbar = 3008, - downtack = 3010, - upshoe = 3011, - downstile = 3012, - underbar = 3014, - jot = 3018, - quad = 3020, - uptack = 3022, - circle = 3023, - upstile = 3027, - downshoe = 3030, - rightshoe = 3032, - leftshoe = 3034, - lefttack = 3036, - righttack = 3068, - hebrew_doublelowline = 3295, - hebrew_aleph = 3296, - hebrew_bet = 3297, - hebrew_beth = 3297, - hebrew_gimel = 3298, - hebrew_gimmel = 3298, - hebrew_dalet = 3299, - hebrew_daleth = 3299, - hebrew_he = 3300, - hebrew_waw = 3301, - hebrew_zain = 3302, - hebrew_zayin = 3302, - hebrew_chet = 3303, - hebrew_het = 3303, - hebrew_tet = 3304, - hebrew_teth = 3304, - hebrew_yod = 3305, - hebrew_finalkaph = 3306, - hebrew_kaph = 3307, - hebrew_lamed = 3308, - hebrew_finalmem = 3309, - hebrew_mem = 3310, - hebrew_finalnun = 3311, - hebrew_nun = 3312, - hebrew_samech = 3313, - hebrew_samekh = 3313, - hebrew_ayin = 3314, - hebrew_finalpe = 3315, - hebrew_pe = 3316, - hebrew_finalzade = 3317, - hebrew_finalzadi = 3317, - hebrew_zade = 3318, - hebrew_zadi = 3318, - hebrew_kuf = 3319, - hebrew_qoph = 3319, - hebrew_resh = 3320, - hebrew_shin = 3321, - hebrew_taf = 3322, - hebrew_taw = 3322, - Thai_kokai = 3489, - Thai_khokhai = 3490, - Thai_khokhuat = 3491, - Thai_khokhwai = 3492, - Thai_khokhon = 3493, - Thai_khorakhang = 3494, - Thai_ngongu = 3495, - Thai_chochan = 3496, - Thai_choching = 3497, - Thai_chochang = 3498, - Thai_soso = 3499, - Thai_chochoe = 3500, - Thai_yoying = 3501, - Thai_dochada = 3502, - Thai_topatak = 3503, - Thai_thothan = 3504, - Thai_thonangmontho = 3505, - Thai_thophuthao = 3506, - Thai_nonen = 3507, - Thai_dodek = 3508, - Thai_totao = 3509, - Thai_thothung = 3510, - Thai_thothahan = 3511, - Thai_thothong = 3512, - Thai_nonu = 3513, - Thai_bobaimai = 3514, - Thai_popla = 3515, - Thai_phophung = 3516, - Thai_fofa = 3517, - Thai_phophan = 3518, - Thai_fofan = 3519, - Thai_phosamphao = 3520, - Thai_moma = 3521, - Thai_yoyak = 3522, - Thai_rorua = 3523, - Thai_ru = 3524, - Thai_loling = 3525, - Thai_lu = 3526, - Thai_wowaen = 3527, - Thai_sosala = 3528, - Thai_sorusi = 3529, - Thai_sosua = 3530, - Thai_hohip = 3531, - Thai_lochula = 3532, - Thai_oang = 3533, - Thai_honokhuk = 3534, - Thai_paiyannoi = 3535, - Thai_saraa = 3536, - Thai_maihanakat = 3537, - Thai_saraaa = 3538, - Thai_saraam = 3539, - Thai_sarai = 3540, - Thai_saraii = 3541, - Thai_saraue = 3542, - Thai_sarauee = 3543, - Thai_sarau = 3544, - Thai_sarauu = 3545, - Thai_phinthu = 3546, - Thai_maihanakat_maitho = 3550, - Thai_baht = 3551, - Thai_sarae = 3552, - Thai_saraae = 3553, - Thai_sarao = 3554, - Thai_saraaimaimuan = 3555, - Thai_saraaimaimalai = 3556, - Thai_lakkhangyao = 3557, - Thai_maiyamok = 3558, - Thai_maitaikhu = 3559, - Thai_maiek = 3560, - Thai_maitho = 3561, - Thai_maitri = 3562, - Thai_maichattawa = 3563, - Thai_thanthakhat = 3564, - Thai_nikhahit = 3565, - Thai_leksun = 3568, - Thai_leknung = 3569, - Thai_leksong = 3570, - Thai_leksam = 3571, - Thai_leksi = 3572, - Thai_lekha = 3573, - Thai_lekhok = 3574, - Thai_lekchet = 3575, - Thai_lekpaet = 3576, - Thai_lekkao = 3577, - Hangul_Kiyeog = 3745, - Hangul_SsangKiyeog = 3746, - Hangul_KiyeogSios = 3747, - Hangul_Nieun = 3748, - Hangul_NieunJieuj = 3749, - Hangul_NieunHieuh = 3750, - Hangul_Dikeud = 3751, - Hangul_SsangDikeud = 3752, - Hangul_Rieul = 3753, - Hangul_RieulKiyeog = 3754, - Hangul_RieulMieum = 3755, - Hangul_RieulPieub = 3756, - Hangul_RieulSios = 3757, - Hangul_RieulTieut = 3758, - Hangul_RieulPhieuf = 3759, - Hangul_RieulHieuh = 3760, - Hangul_Mieum = 3761, - Hangul_Pieub = 3762, - Hangul_SsangPieub = 3763, - Hangul_PieubSios = 3764, - Hangul_Sios = 3765, - Hangul_SsangSios = 3766, - Hangul_Ieung = 3767, - Hangul_Jieuj = 3768, - Hangul_SsangJieuj = 3769, - Hangul_Cieuc = 3770, - Hangul_Khieuq = 3771, - Hangul_Tieut = 3772, - Hangul_Phieuf = 3773, - Hangul_Hieuh = 3774, - Hangul_A = 3775, - Hangul_AE = 3776, - Hangul_YA = 3777, - Hangul_YAE = 3778, - Hangul_EO = 3779, - Hangul_E = 3780, - Hangul_YEO = 3781, - Hangul_YE = 3782, - Hangul_O = 3783, - Hangul_WA = 3784, - Hangul_WAE = 3785, - Hangul_OE = 3786, - Hangul_YO = 3787, - Hangul_U = 3788, - Hangul_WEO = 3789, - Hangul_WE = 3790, - Hangul_WI = 3791, - Hangul_YU = 3792, - Hangul_EU = 3793, - Hangul_YI = 3794, - Hangul_I = 3795, - Hangul_J_Kiyeog = 3796, - Hangul_J_SsangKiyeog = 3797, - Hangul_J_KiyeogSios = 3798, - Hangul_J_Nieun = 3799, - Hangul_J_NieunJieuj = 3800, - Hangul_J_NieunHieuh = 3801, - Hangul_J_Dikeud = 3802, - Hangul_J_Rieul = 3803, - Hangul_J_RieulKiyeog = 3804, - Hangul_J_RieulMieum = 3805, - Hangul_J_RieulPieub = 3806, - Hangul_J_RieulSios = 3807, - Hangul_J_RieulTieut = 3808, - Hangul_J_RieulPhieuf = 3809, - Hangul_J_RieulHieuh = 3810, - Hangul_J_Mieum = 3811, - Hangul_J_Pieub = 3812, - Hangul_J_PieubSios = 3813, - Hangul_J_Sios = 3814, - Hangul_J_SsangSios = 3815, - Hangul_J_Ieung = 3816, - Hangul_J_Jieuj = 3817, - Hangul_J_Cieuc = 3818, - Hangul_J_Khieuq = 3819, - Hangul_J_Tieut = 3820, - Hangul_J_Phieuf = 3821, - Hangul_J_Hieuh = 3822, - Hangul_RieulYeorinHieuh = 3823, - Hangul_SunkyeongeumMieum = 3824, - Hangul_SunkyeongeumPieub = 3825, - Hangul_PanSios = 3826, - Hangul_KkogjiDalrinIeung = 3827, - Hangul_SunkyeongeumPhieuf = 3828, - Hangul_YeorinHieuh = 3829, - Hangul_AraeA = 3830, - Hangul_AraeAE = 3831, - Hangul_J_PanSios = 3832, - Hangul_J_KkogjiDalrinIeung = 3833, - Hangul_J_YeorinHieuh = 3834, - Korean_Won = 3839, - OE = 5052, - oe = 5053, - Ydiaeresis = 5054, - EcuSign = 8352, - ColonSign = 8353, - CruzeiroSign = 8354, - FFrancSign = 8355, - LiraSign = 8356, - MillSign = 8357, - NairaSign = 8358, - PesetaSign = 8359, - RupeeSign = 8360, - WonSign = 8361, - NewSheqelSign = 8362, - DongSign = 8363, - EuroSign = 8364, - Key_3270_Duplicate = 64769, - Key_3270_FieldMark = 64770, - Key_3270_Right2 = 64771, - Key_3270_Left2 = 64772, - Key_3270_BackTab = 64773, - Key_3270_EraseEOF = 64774, - Key_3270_EraseInput = 64775, - Key_3270_Reset = 64776, - Key_3270_Quit = 64777, - Key_3270_PA1 = 64778, - Key_3270_PA2 = 64779, - Key_3270_PA3 = 64780, - Key_3270_Test = 64781, - Key_3270_Attn = 64782, - Key_3270_CursorBlink = 64783, - Key_3270_AltCursor = 64784, - Key_3270_KeyClick = 64785, - Key_3270_Jump = 64786, - Key_3270_Ident = 64787, - Key_3270_Rule = 64788, - Key_3270_Copy = 64789, - Key_3270_Play = 64790, - Key_3270_Setup = 64791, - Key_3270_Record = 64792, - Key_3270_ChangeScreen = 64793, - Key_3270_DeleteWord = 64794, - Key_3270_ExSelect = 64795, - Key_3270_CursorSelect = 64796, - Key_3270_PrintScreen = 64797, - Key_3270_Enter = 64798, - ISO_Lock = 65025, - ISO_Level2_Latch = 65026, - ISO_Level3_Shift = 65027, - ISO_Level3_Latch = 65028, - ISO_Level3_Lock = 65029, - ISO_Group_Latch = 65030, - ISO_Group_Lock = 65031, - ISO_Next_Group = 65032, - ISO_Next_Group_Lock = 65033, - ISO_Prev_Group = 65034, - ISO_Prev_Group_Lock = 65035, - ISO_First_Group = 65036, - ISO_First_Group_Lock = 65037, - ISO_Last_Group = 65038, - ISO_Last_Group_Lock = 65039, - ISO_Left_Tab = 65056, - ISO_Move_Line_Up = 65057, - ISO_Move_Line_Down = 65058, - ISO_Partial_Line_Up = 65059, - ISO_Partial_Line_Down = 65060, - ISO_Partial_Space_Left = 65061, - ISO_Partial_Space_Right = 65062, - ISO_Set_Margin_Left = 65063, - ISO_Set_Margin_Right = 65064, - ISO_Release_Margin_Left = 65065, - ISO_Release_Margin_Right = 65066, - ISO_Release_Both_Margins = 65067, - ISO_Fast_Cursor_Left = 65068, - ISO_Fast_Cursor_Right = 65069, - ISO_Fast_Cursor_Up = 65070, - ISO_Fast_Cursor_Down = 65071, - ISO_Continuous_Underline = 65072, - ISO_Discontinuous_Underline = 65073, - ISO_Emphasize = 65074, - ISO_Center_Object = 65075, - ISO_Enter = 65076, - dead_grave = 65104, - dead_acute = 65105, - dead_circumflex = 65106, - dead_tilde = 65107, - dead_macron = 65108, - dead_breve = 65109, - dead_abovedot = 65110, - dead_diaeresis = 65111, - dead_abovering = 65112, - dead_doubleacute = 65113, - dead_caron = 65114, - dead_cedilla = 65115, - dead_ogonek = 65116, - dead_iota = 65117, - dead_voiced_sound = 65118, - dead_semivoiced_sound = 65119, - dead_belowdot = 65120, - AccessX_Enable = 65136, - AccessX_Feedback_Enable = 65137, - RepeatKeys_Enable = 65138, - SlowKeys_Enable = 65139, - BounceKeys_Enable = 65140, - StickyKeys_Enable = 65141, - MouseKeys_Enable = 65142, - MouseKeys_Accel_Enable = 65143, - Overlay1_Enable = 65144, - Overlay2_Enable = 65145, - AudibleBell_Enable = 65146, - First_Virtual_Screen = 65232, - Prev_Virtual_Screen = 65233, - Next_Virtual_Screen = 65234, - Last_Virtual_Screen = 65236, - Terminate_Server = 65237, - Pointer_Left = 65248, - Pointer_Right = 65249, - Pointer_Up = 65250, - Pointer_Down = 65251, - Pointer_UpLeft = 65252, - Pointer_UpRight = 65253, - Pointer_DownLeft = 65254, - Pointer_DownRight = 65255, - Pointer_Button_Dflt = 65256, - Pointer_Button1 = 65257, - Pointer_Button2 = 65258, - Pointer_Button3 = 65259, - Pointer_Button4 = 65260, - Pointer_Button5 = 65261, - Pointer_DblClick_Dflt = 65262, - Pointer_DblClick1 = 65263, - Pointer_DblClick2 = 65264, - Pointer_DblClick3 = 65265, - Pointer_DblClick4 = 65266, - Pointer_DblClick5 = 65267, - Pointer_Drag_Dflt = 65268, - Pointer_Drag1 = 65269, - Pointer_Drag2 = 65270, - Pointer_Drag3 = 65271, - Pointer_Drag4 = 65272, - Pointer_EnableKeys = 65273, - Pointer_Accelerate = 65274, - Pointer_DfltBtnNext = 65275, - Pointer_DfltBtnPrev = 65276, - Pointer_Drag5 = 65277, - BackSpace = 65288, - Tab = 65289, - Linefeed = 65290, - Clear = 65291, - Return = 65293, - Pause = 65299, - Scroll_Lock = 65300, - Sys_Req = 65301, - Escape = 65307, - Multi_key = 65312, - Kanji = 65313, - Muhenkan = 65314, - Henkan = 65315, - Henkan_Mode = 65315, - Romaji = 65316, - Hiragana = 65317, - Katakana = 65318, - Hiragana_Katakana = 65319, - Zenkaku = 65320, - Hankaku = 65321, - Zenkaku_Hankaku = 65322, - Touroku = 65323, - Massyo = 65324, - Kana_Lock = 65325, - Kana_Shift = 65326, - Eisu_Shift = 65327, - Eisu_toggle = 65328, - Hangul = 65329, - Hangul_Start = 65330, - Hangul_End = 65331, - Hangul_Hanja = 65332, - Hangul_Jamo = 65333, - Hangul_Romaja = 65334, - Codeinput = 65335, - Hangul_Codeinput = 65335, - Kanji_Bangou = 65335, - Hangul_Jeonja = 65336, - Hangul_Banja = 65337, - Hangul_PreHanja = 65338, - Hangul_PostHanja = 65339, - Hangul_SingleCandidate = 65340, - SingleCandidate = 65340, - Hangul_MultipleCandidate = 65341, - MultipleCandidate = 65341, - Zen_Koho = 65341, - Hangul_PreviousCandidate = 65342, - Mae_Koho = 65342, - PreviousCandidate = 65342, - Hangul_Special = 65343, - Home = 65360, - Left = 65361, - Up = 65362, - Right = 65363, - Down = 65364, - Page_Up = 65365, - Prior = 65365, - Next = 65366, - Page_Down = 65366, - End = 65367, - Begin = 65368, - Select = 65376, - Print = 65377, - Execute = 65378, - Insert = 65379, - Undo = 65381, - Redo = 65382, - Menu = 65383, - Find = 65384, - Cancel = 65385, - Help = 65386, - Break = 65387, - Arabic_switch = 65406, - Greek_switch = 65406, - Hangul_switch = 65406, - Hebrew_switch = 65406, - ISO_Group_Shift = 65406, - Mode_switch = 65406, - kana_switch = 65406, - script_switch = 65406, - Num_Lock = 65407, - KP_Space = 65408, - KP_Tab = 65417, - KP_Enter = 65421, - KP_F1 = 65425, - KP_F2 = 65426, - KP_F3 = 65427, - KP_F4 = 65428, - KP_Home = 65429, - KP_Left = 65430, - KP_Up = 65431, - KP_Right = 65432, - KP_Down = 65433, - KP_Page_Up = 65434, - KP_Prior = 65434, - KP_Next = 65435, - KP_Page_Down = 65435, - KP_End = 65436, - KP_Begin = 65437, - KP_Insert = 65438, - KP_Delete = 65439, - KP_Multiply = 65450, - KP_Add = 65451, - KP_Separator = 65452, - KP_Subtract = 65453, - KP_Decimal = 65454, - KP_Divide = 65455, - KP_0 = 65456, - KP_1 = 65457, - KP_2 = 65458, - KP_3 = 65459, - KP_4 = 65460, - KP_5 = 65461, - KP_6 = 65462, - KP_7 = 65463, - KP_8 = 65464, - KP_9 = 65465, - KP_Equal = 65469, - F1 = 65470, - F2 = 65471, - F3 = 65472, - F4 = 65473, - F5 = 65474, - F6 = 65475, - F7 = 65476, - F8 = 65477, - F9 = 65478, - F10 = 65479, - F11 = 65480, - L1 = 65480, - F12 = 65481, - L2 = 65481, - F13 = 65482, - L3 = 65482, - F14 = 65483, - L4 = 65483, - F15 = 65484, - L5 = 65484, - F16 = 65485, - L6 = 65485, - F17 = 65486, - L7 = 65486, - F18 = 65487, - L8 = 65487, - F19 = 65488, - L9 = 65488, - F20 = 65489, - L10 = 65489, - F21 = 65490, - R1 = 65490, - F22 = 65491, - R2 = 65491, - F23 = 65492, - R3 = 65492, - F24 = 65493, - R4 = 65493, - F25 = 65494, - R5 = 65494, - F26 = 65495, - R6 = 65495, - F27 = 65496, - R7 = 65496, - F28 = 65497, - R8 = 65497, - F29 = 65498, - R9 = 65498, - F30 = 65499, - R10 = 65499, - F31 = 65500, - R11 = 65500, - F32 = 65501, - R12 = 65501, - F33 = 65502, - R13 = 65502, - F34 = 65503, - R14 = 65503, - F35 = 65504, - R15 = 65504, - Shift_L = 65505, - Shift_R = 65506, - Control_L = 65507, - Control_R = 65508, - Caps_Lock = 65509, - Shift_Lock = 65510, - Meta_L = 65511, - Meta_R = 65512, - Alt_L = 65513, - Alt_R = 65514, - Super_L = 65515, - Super_R = 65516, - Hyper_L = 65517, - Hyper_R = 65518, - Delete = 65535, - VoidSymbol = 16777215, - } -} diff --git a/src/Gtk/Avalonia.Gtk3/Gtk3ForeignX11SystemDialog.cs b/src/Gtk/Avalonia.Gtk3/Gtk3ForeignX11SystemDialog.cs deleted file mode 100644 index b80914572b..0000000000 --- a/src/Gtk/Avalonia.Gtk3/Gtk3ForeignX11SystemDialog.cs +++ /dev/null @@ -1,115 +0,0 @@ -using System; -using System.Runtime.InteropServices; -using System.Threading; -using System.Threading.Tasks; -using Avalonia.Controls; -using Avalonia.Controls.Platform; -using Avalonia.Gtk3.Interop; -using Avalonia.Platform; -using Avalonia.Platform.Interop; - -namespace Avalonia.Gtk3 -{ - public class Gtk3ForeignX11SystemDialog : ISystemDialogImpl - { - private Task _initialized; - private SystemDialogBase _inner = new SystemDialogBase(); - - - public async Task ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent) - { - await EnsureInitialized(); - var xid = parent.Handle.Handle; - return await await RunOnGtkThread( - () => _inner.ShowFileDialogAsync(dialog, GtkWindow.Null, chooser => UpdateParent(chooser, xid))); - } - - public async Task ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent) - { - await EnsureInitialized(); - var xid = parent.Handle.Handle; - return await await RunOnGtkThread( - () => _inner.ShowFolderDialogAsync(dialog, GtkWindow.Null, chooser => UpdateParent(chooser, xid))); - } - - void UpdateParent(GtkFileChooser chooser, IntPtr xid) - { - Native.GtkWidgetRealize(chooser); - var window = Native.GtkWidgetGetWindow(chooser); - var parent = Native.GdkWindowForeignNewForDisplay(GdkDisplay, xid); - if (window != IntPtr.Zero && parent != IntPtr.Zero) - Native.GdkWindowSetTransientFor(window, parent); - } - - async Task EnsureInitialized() - { - if (_initialized == null) - { - var tcs = new TaskCompletionSource(); - _initialized = tcs.Task; - new Thread(() => GtkThread(tcs)) - { - IsBackground = true - }.Start(); - } - - if (!(await _initialized)) - throw new Exception("Unable to initialize GTK on separate thread"); - - } - - Task RunOnGtkThread(Func action) - { - var tcs = new TaskCompletionSource(); - GlibTimeout.Add(0, 0, () => - { - - try - { - tcs.SetResult(action()); - } - catch (Exception e) - { - tcs.TrySetException(e); - } - - return false; - }); - return tcs.Task; - } - - - void GtkThread(TaskCompletionSource tcs) - { - try - { - X11.XInitThreads(); - }catch{} - Resolver.Resolve(); - if (Native.GdkWindowForeignNewForDisplay == null) - throw new Exception("gdk_x11_window_foreign_new_for_display is not found in your libgdk-3.so"); - using (var backends = new Utf8Buffer("x11")) - Native.GdkSetAllowedBackends?.Invoke(backends); - if (!Native.GtkInitCheck(0, IntPtr.Zero)) - { - tcs.SetResult(false); - return; - } - - using (var utf = new Utf8Buffer($"avalonia.app.a{Guid.NewGuid().ToString("N")}")) - App = Native.GtkApplicationNew(utf, 0); - if (App == IntPtr.Zero) - { - tcs.SetResult(false); - return; - } - GdkDisplay = Native.GdkGetDefaultDisplay(); - tcs.SetResult(true); - while (true) - Native.GtkMainIteration(); - } - - private IntPtr GdkDisplay { get; set; } - private IntPtr App { get; set; } - } -} diff --git a/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs b/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs deleted file mode 100644 index 965973d3cb..0000000000 --- a/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs +++ /dev/null @@ -1,168 +0,0 @@ -using System; -using System.Runtime.InteropServices; -using System.Threading; -using Avalonia.Controls; -using Avalonia.Controls.Platform; -using Avalonia.Gtk3; -using Avalonia.Gtk3.Interop; -using Avalonia.Input; -using Avalonia.Input.Platform; -using Avalonia.OpenGL; -using Avalonia.Platform; -using Avalonia.Platform.Interop; -using Avalonia.Rendering; -using Avalonia.Threading; - -namespace Avalonia.Gtk3 -{ - public class Gtk3Platform : IWindowingPlatform, IPlatformSettings, IPlatformThreadingInterface - { - internal static readonly Gtk3Platform Instance = new Gtk3Platform(); - internal static readonly MouseDevice Mouse = new MouseDevice(); - internal static readonly KeyboardDevice Keyboard = new KeyboardDevice(); - internal static IntPtr App { get; set; } - internal static string DisplayClassName; - public static bool UseDeferredRendering = true; - private static bool s_gtkInitialized; - - static bool EnvOption(string option, bool def, bool? specified) - { - bool? Parse(string env) - { - var v = Environment.GetEnvironmentVariable("AVALONIA_GTK3_" + env); - if (v == null) - return null; - if (v.ToLowerInvariant() == "false" || v == "0") - return false; - return true; - } - - var overridden = Parse(option + "_OVERRIDE"); - if (overridden.HasValue) - return overridden.Value; - if (specified.HasValue) - return specified.Value; - var envValue = Parse(option); - return envValue ?? def; - } - - public static void Initialize(Gtk3PlatformOptions options) - { - Resolver.Custom = options.CustomResolver; - UseDeferredRendering = EnvOption("USE_DEFERRED_RENDERING", true, options.UseDeferredRendering); - var useGpu = EnvOption("USE_GPU", true, options.UseGpuAcceleration); - if (!s_gtkInitialized) - { - try - { - X11.XInitThreads(); - }catch{} - Resolver.Resolve(); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - using (var backends = new Utf8Buffer("x11")) - Native.GdkSetAllowedBackends?.Invoke(backends); - Native.GtkInit(0, IntPtr.Zero); - var disp = Native.GdkGetDefaultDisplay(); - DisplayClassName = - Utf8Buffer.StringFromPtr(Native.GTypeName(Marshal.ReadIntPtr(Marshal.ReadIntPtr(disp)))); - - using (var utf = new Utf8Buffer($"avalonia.app.a{Guid.NewGuid().ToString("N")}")) - App = Native.GtkApplicationNew(utf, 0); - //Mark current thread as UI thread - s_tlsMarker = true; - s_gtkInitialized = true; - } - AvaloniaLocator.CurrentMutable.Bind().ToConstant(Instance) - .Bind().ToSingleton() - .Bind().ToConstant(new CursorFactory()) - .Bind().ToConstant(Keyboard) - .Bind().ToConstant(Instance) - .Bind().ToConstant(Instance) - .Bind().ToSingleton() - .Bind().ToConstant(new RenderLoop()) - .Bind().ToConstant(new DefaultRenderTimer(60)) - .Bind().ToSingleton() - .Bind().ToConstant(new PlatformIconLoader()); - if (useGpu) - EglGlPlatformFeature.TryInitialize(); - } - - public IWindowImpl CreateWindow() => new WindowImpl(); - - public IEmbeddableWindowImpl CreateEmbeddableWindow() - { - throw new NotImplementedException(); - } - - public IPopupImpl CreatePopup() => new PopupImpl(); - - public Size DoubleClickSize => new Size(4, 4); - - public TimeSpan DoubleClickTime => TimeSpan.FromMilliseconds(100); //STUB - public double RenderScalingFactor { get; } = 1; - public double LayoutScalingFactor { get; } = 1; - - public void RunLoop(CancellationToken cancellationToken) - { - while (!cancellationToken.IsCancellationRequested) - Native.GtkMainIteration(); - } - - public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick) - { - var msec = interval.TotalMilliseconds; - var imsec = (uint) msec; - if (imsec == 0) - imsec = 1; - return GlibTimeout.StartTimer(GlibPriority.FromDispatcherPriority(priority), imsec, tick); - } - - private bool[] _signaled = new bool[(int) DispatcherPriority.MaxValue + 1]; - object _lock = new object(); - public void Signal(DispatcherPriority prio) - { - var idx = (int) prio; - lock(_lock) - if (!_signaled[idx]) - { - _signaled[idx] = true; - GlibTimeout.Add(GlibPriority.FromDispatcherPriority(prio), 0, () => - { - lock (_lock) - { - _signaled[idx] = false; - } - Signaled?.Invoke(prio); - return false; - }); - } - } - public event Action Signaled; - - - [ThreadStatic] - private static bool s_tlsMarker; - - public bool CurrentThreadIsLoopThread => s_tlsMarker; - } - - public class Gtk3PlatformOptions - { - public bool? UseDeferredRendering { get; set; } - public bool? UseGpuAcceleration { get; set; } - public ICustomGtk3NativeLibraryResolver CustomResolver { get; set; } - } -} - -namespace Avalonia -{ - public static class Gtk3AppBuilderExtensions - { - public static T UseGtk3(this AppBuilderBase builder, Gtk3PlatformOptions options = null) - where T : AppBuilderBase, new() - { - return builder.UseWindowingSubsystem(() => Gtk3Platform.Initialize(options ?? new Gtk3PlatformOptions()), - "GTK3"); - } - } -} diff --git a/src/Gtk/Avalonia.Gtk3/GtkScreen.cs b/src/Gtk/Avalonia.Gtk3/GtkScreen.cs deleted file mode 100644 index a2b4604130..0000000000 --- a/src/Gtk/Avalonia.Gtk3/GtkScreen.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Avalonia.Platform; - -namespace Avalonia.Gtk3 -{ - public class GtkScreen : Screen - { - private readonly int _screenId; - - public GtkScreen(PixelRect bounds, PixelRect workingArea, bool primary, int screenId) : base(bounds, workingArea, primary) - { - this._screenId = screenId; - } - - public override int GetHashCode() - { - return _screenId; - } - - public override bool Equals(object obj) - { - return (obj is GtkScreen screen) ? this._screenId == screen._screenId : base.Equals(obj); - } - } -} diff --git a/src/Gtk/Avalonia.Gtk3/IDeferredRenderOperation.cs b/src/Gtk/Avalonia.Gtk3/IDeferredRenderOperation.cs deleted file mode 100644 index e16463a2ef..0000000000 --- a/src/Gtk/Avalonia.Gtk3/IDeferredRenderOperation.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace Avalonia.Gtk3 -{ - public interface IDeferredRenderOperation : IDisposable - { - void RenderNow(IntPtr? ctx); - } -} \ No newline at end of file diff --git a/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs b/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs deleted file mode 100644 index 878689442d..0000000000 --- a/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs +++ /dev/null @@ -1,144 +0,0 @@ -using System; -using Avalonia.Gtk3.Interop; -using Avalonia.Platform; -using Avalonia.Threading; - - -namespace Avalonia.Gtk3 -{ - class ImageSurfaceFramebuffer : ILockedFramebuffer - { - private readonly WindowBaseImpl _impl; - private readonly GtkWidget _widget; - private ManagedCairoSurface _surface; - private int _factor; - private object _lock = new object(); - public ImageSurfaceFramebuffer(WindowBaseImpl impl, int width, int height, int factor) - { - _impl = impl; - _widget = impl.GtkWidget; - _factor = factor; - width *= _factor; - height *= _factor; - _surface = new ManagedCairoSurface(width, height); - - Size = new PixelSize(width, height); - Address = _surface.Buffer; - RowBytes = _surface.Stride; - Native.CairoSurfaceFlush(_surface.Surface); - } - - static void Draw(IntPtr context, CairoSurface surface, double factor) - { - - Native.CairoSurfaceMarkDirty(surface); - Native.CairoScale(context, 1d / factor, 1d / factor); - Native.CairoSetSourceSurface(context, surface, 0, 0); - Native.CairoPaint(context); - - } - /* - static Stopwatch St =Stopwatch.StartNew(); - private static int _frames; - private static int _fps;*/ - static void DrawToWidget(GtkWidget widget, CairoSurface surface, int width, int height, double factor) - { - if(surface == null || widget.IsClosed) - return; - var window = Native.GtkWidgetGetWindow(widget); - if(window == IntPtr.Zero) - return; - var rc = new GdkRectangle {Width = width, Height = height}; - Native.GdkWindowBeginPaintRect(window, ref rc); - var context = Native.GdkCairoCreate(window); - Draw(context, surface, factor); - /* - _frames++; - var el = St.Elapsed; - if (el.TotalSeconds > 1) - { - _fps = (int) (_frames / el.TotalSeconds); - _frames = 0; - St = Stopwatch.StartNew(); - } - - Native.CairoSetSourceRgba(context, 1, 0, 0, 1); - Native.CairoMoveTo(context, 20, 20); - Native.CairoSetFontSize(context, 30); - using (var txt = new Utf8Buffer("FPS: " + _fps)) - Native.CairoShowText(context, txt); - */ - - Native.CairoDestroy(context); - Native.GdkWindowEndPaint(window); - } - - class RenderOp : IDeferredRenderOperation - { - private readonly GtkWidget _widget; - private ManagedCairoSurface _surface; - private readonly double _factor; - private readonly int _width; - private readonly int _height; - - public RenderOp(GtkWidget widget, ManagedCairoSurface surface, double factor, int width, int height) - { - _widget = widget; - _surface = surface ?? throw new ArgumentNullException(nameof(surface)); - _factor = factor; - _width = width; - _height = height; - } - - public void Dispose() - { - _surface?.Dispose(); - _surface = null; - } - - public void RenderNow(IntPtr? ctx) - { - if(ctx.HasValue) - Draw(ctx.Value, _surface.Surface, _factor); - else - DrawToWidget(_widget, _surface.Surface, _width, _height, _factor); - } - } - - public void Dispose() - { - lock (_lock) - { - if (Dispatcher.UIThread.CheckAccess()) - { - if (_impl.CurrentCairoContext != IntPtr.Zero) - Draw(_impl.CurrentCairoContext, _surface.Surface, _factor); - else - DrawToWidget(_widget, _surface.Surface, Size.Width, Size.Height, _factor); - _surface.Dispose(); - } - else - _impl.SetNextRenderOperation(new RenderOp(_widget, _surface, _factor, Size.Width, Size.Height)); - _surface = null; - } - } - - public IntPtr Address { get; } - public PixelSize Size { get; } - public int RowBytes { get; } - - - public Vector Dpi - { - get - { - return new Vector(96, 96) * _factor; - } - } - - public PixelFormat Format => PixelFormat.Bgra8888; - } -} - - - diff --git a/src/Gtk/Avalonia.Gtk3/Interop/CairoSurface.cs b/src/Gtk/Avalonia.Gtk3/Interop/CairoSurface.cs deleted file mode 100644 index 7838be9305..0000000000 --- a/src/Gtk/Avalonia.Gtk3/Interop/CairoSurface.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace Avalonia.Gtk3.Interop -{ - class CairoSurface : SafeHandle - { - public CairoSurface() : base(IntPtr.Zero, true) - { - } - - protected override bool ReleaseHandle() - { - Native.CairoSurfaceDestroy(handle); - return true; - } - - public override bool IsInvalid => handle == IntPtr.Zero; - } -} diff --git a/src/Gtk/Avalonia.Gtk3/Interop/GException.cs b/src/Gtk/Avalonia.Gtk3/Interop/GException.cs deleted file mode 100644 index 5ff6cbe8ed..0000000000 --- a/src/Gtk/Avalonia.Gtk3/Interop/GException.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Runtime.InteropServices; -using Avalonia.Platform.Interop; - -namespace Avalonia.Gtk3.Interop -{ - public class GException : Exception - { - [StructLayout(LayoutKind.Sequential)] - struct GError - { - UInt32 domain; - int code; - public IntPtr message; - }; - - static unsafe string GetError(IntPtr error) - { - if (error == IntPtr.Zero) - return "Unknown error"; - return Utf8Buffer.StringFromPtr(((GError*) error)->message); - } - - public GException(IntPtr error) : base(GetError(error)) - { - - } - - } -} diff --git a/src/Gtk/Avalonia.Gtk3/Interop/GObject.cs b/src/Gtk/Avalonia.Gtk3/Interop/GObject.cs deleted file mode 100644 index bb272b06d5..0000000000 --- a/src/Gtk/Avalonia.Gtk3/Interop/GObject.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System; -using System.Diagnostics; -using System.Runtime.InteropServices; - -namespace Avalonia.Gtk3.Interop -{ - class GObject : SafeHandle - { - public GObject() : base(IntPtr.Zero, true) - { - } - - public GObject(IntPtr handle, bool owned = true) : base(IntPtr.Zero, owned) - { - this.handle = handle; - } - - protected override bool ReleaseHandle() - { - if (handle != IntPtr.Zero) - { - Debug.Assert(Native.GTypeCheckInstanceIsFundamentallyA(handle, new IntPtr(Native.G_TYPE_OBJECT)), - "Handle is not a GObject"); - Native.GObjectUnref(handle); - } - - handle = IntPtr.Zero; - return true; - } - - public override bool IsInvalid => handle == IntPtr.Zero; - } - - class GInputStream : GObject - { - - } - - class GtkWidget : GObject - { - - } - - class GtkWindow : GtkWidget - { - public static GtkWindow Null { get; } = new GtkWindow(); - } - - class GtkImContext : GObject - { - } - - class GdkScreen : GObject - { - public GdkScreen() : base(IntPtr.Zero, false) - { - } - - public GdkScreen(IntPtr handle, bool owned = true) : base(handle, owned) - { - this.handle = handle; - } - } - - class UnownedGdkScreen : GdkScreen - { - public UnownedGdkScreen() : base(IntPtr.Zero, false) - { - } - - public UnownedGdkScreen(IntPtr handle, bool owned = true) : base(IntPtr.Zero, false) - { - this.handle = handle; - } - } - - class GtkDialog : GtkWindow - { - - } - - class GtkFileChooser : GtkDialog - { - - } -} - diff --git a/src/Gtk/Avalonia.Gtk3/Interop/GlibPriority.cs b/src/Gtk/Avalonia.Gtk3/Interop/GlibPriority.cs deleted file mode 100644 index 08448e30d5..0000000000 --- a/src/Gtk/Avalonia.Gtk3/Interop/GlibPriority.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using Avalonia.Threading; - -namespace Avalonia.Gtk3.Interop -{ - static class GlibPriority - { - public static int High = -100; - public static int Default = 0; - public static int HighIdle = 100; - public static int GtkResize = HighIdle + 10; - public static int GtkPaint = HighIdle + 20; - public static int DefaultIdle = 200; - public static int Low = 300; - public static int GdkEvents = Default; - public static int GdkRedraw = HighIdle + 20; - - public static int FromDispatcherPriority(DispatcherPriority prio) - { - if (prio == DispatcherPriority.Send) - return High; - if (prio == DispatcherPriority.Normal) - return Default; - if (prio == DispatcherPriority.DataBind) - return Default + 1; - if (prio == DispatcherPriority.Layout) - return Default + 2; - if (prio == DispatcherPriority.Render) - return Default + 3; - if (prio == DispatcherPriority.Loaded) - return GtkPaint + 20; - if (prio == DispatcherPriority.Input) - return GtkPaint + 21; - if (prio == DispatcherPriority.Background) - return DefaultIdle + 1; - if (prio == DispatcherPriority.ContextIdle) - return DefaultIdle + 2; - if (prio == DispatcherPriority.ApplicationIdle) - return DefaultIdle + 3; - if (prio == DispatcherPriority.SystemIdle) - return DefaultIdle + 4; - throw new ArgumentException("Unknown priority"); - - } - } -} diff --git a/src/Gtk/Avalonia.Gtk3/Interop/GlibTimeout.cs b/src/Gtk/Avalonia.Gtk3/Interop/GlibTimeout.cs deleted file mode 100644 index 6e3840914c..0000000000 --- a/src/Gtk/Avalonia.Gtk3/Interop/GlibTimeout.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace Avalonia.Gtk3.Interop -{ - static class GlibTimeout - { - static bool Handler(IntPtr data) - { - var handle = GCHandle.FromIntPtr(data); - var cb = (Func) handle.Target; - if (!cb()) - { - handle.Free(); - return false; - } - return true; - } - - private static readonly Native.D.timeout_callback PinnedHandler; - static GlibTimeout() - { - PinnedHandler = Handler; - } - - - public static void Add(int priority, uint interval, Func callback) - { - var handle = GCHandle.Alloc(callback); - Native.GTimeoutAddFull(priority, interval, PinnedHandler, GCHandle.ToIntPtr(handle), IntPtr.Zero); - } - - class Timer : IDisposable - { - public bool Stopped; - public void Dispose() - { - - Stopped = true; - } - } - - public static IDisposable StartTimer(int priority, uint interval, Action tick) - { - var timer = new Timer (); - GlibTimeout.Add(priority, interval, - () => - { - if (timer.Stopped) - return false; - tick(); - return !timer.Stopped; - }); - - return timer; - } - } -} diff --git a/src/Gtk/Avalonia.Gtk3/Interop/ICustomGtk3NativeLibraryResolver.cs b/src/Gtk/Avalonia.Gtk3/Interop/ICustomGtk3NativeLibraryResolver.cs deleted file mode 100644 index 3b78953d1b..0000000000 --- a/src/Gtk/Avalonia.Gtk3/Interop/ICustomGtk3NativeLibraryResolver.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Avalonia.Gtk3.Interop; - -namespace Avalonia.Gtk3 -{ - public interface ICustomGtk3NativeLibraryResolver - { - string GetName(GtkDll dll); - string BasePath { get; } - bool TrySystemFirst { get; } - string Lookup(GtkDll dll); - } -} diff --git a/src/Gtk/Avalonia.Gtk3/Interop/ManagedCairoSurface.cs b/src/Gtk/Avalonia.Gtk3/Interop/ManagedCairoSurface.cs deleted file mode 100644 index 2b0a7eae12..0000000000 --- a/src/Gtk/Avalonia.Gtk3/Interop/ManagedCairoSurface.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using Avalonia.Platform; - -namespace Avalonia.Gtk3.Interop -{ - class ManagedCairoSurface : IDisposable - { - public IntPtr Buffer { get; private set; } - public CairoSurface Surface { get; private set; } - public int Stride { get; private set; } - private int _size; - private IRuntimePlatform _plat; - private IUnmanagedBlob _blob; - - public ManagedCairoSurface(int width, int height) - { - _plat = AvaloniaLocator.Current.GetService(); - Stride = width * 4; - _size = height * Stride; - _blob = _plat.AllocBlob(_size * 2); - Buffer = _blob.Address; - Surface = Native.CairoImageSurfaceCreateForData(Buffer, 1, width, height, Stride); - } - - public void Dispose() - { - - if (Buffer != IntPtr.Zero) - { - Surface.Dispose(); - _blob.Dispose(); - Buffer = IntPtr.Zero; - } - } - - } -} diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs deleted file mode 100644 index 765c19a796..0000000000 --- a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs +++ /dev/null @@ -1,790 +0,0 @@ -#pragma warning disable 649 -using System; -using System.Runtime.InteropServices; -using Avalonia.Controls; -using Avalonia.Platform.Interop; -using gdouble = System.Double; -using gint = System.Int32; -using gint16 = System.Int16; -using gint8 = System.Byte; -using guint = System.UInt32; -using guint16 = System.UInt16; -using guint32 = System.UInt32; - -namespace Avalonia.Gtk3.Interop -{ - static class Native - { - public static class D - { - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] - public delegate gint16 gdk_display_get_n_screens(IntPtr display); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] - public delegate UnownedGdkScreen gdk_display_get_screen(IntPtr display, gint16 num); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] - public delegate UnownedGdkScreen gdk_display_get_default_screen (IntPtr display); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] - public delegate gint16 gdk_screen_get_n_monitors(GdkScreen screen); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] - public delegate gint16 gdk_screen_get_primary_monitor(GdkScreen screen); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] - public delegate void gdk_screen_get_monitor_geometry(GdkScreen screen, gint16 num, ref GdkRectangle rect); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] - public delegate void gdk_screen_get_monitor_workarea(GdkScreen screen, gint16 num, ref GdkRectangle rect); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate IntPtr gtk_application_new(Utf8Buffer appId, int flags); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_main_iteration(); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate GtkWindow gtk_window_new(GtkWindowType windowType); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate IntPtr gtk_init(int argc, IntPtr argv); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate bool gtk_init_check(int argc, IntPtr argv); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk, optional: true)] - public delegate IntPtr gdk_set_allowed_backends (Utf8Buffer backends); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_window_present(GtkWindow gtkWindow); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_widget_hide(GtkWidget gtkWidget); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_widget_show(GtkWidget gtkWidget); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_window_set_icon(GtkWindow window, Pixbuf pixbuf); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_window_set_modal(GtkWindow window, bool modal); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_window_set_transient_for(GtkWindow window, IntPtr parent); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] //No manual import - public delegate IntPtr gdk_get_native_handle(IntPtr gdkWindow); - - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate IntPtr gtk_widget_get_window(GtkWidget gtkWidget); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk, optional: true)] - public delegate uint gtk_widget_get_scale_factor(GtkWidget gtkWidget); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate IntPtr gtk_widget_get_screen(GtkWidget gtkWidget); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate IntPtr gtk_widget_set_double_buffered(GtkWidget gtkWidget, bool value); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate IntPtr gtk_widget_set_events(GtkWidget gtkWidget, uint flags); - - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] - public delegate int gdk_screen_get_height(IntPtr screen); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] - public delegate int gdk_screen_get_width(IntPtr screen); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] - public delegate IntPtr gdk_display_get_default(); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] - public delegate int gdk_window_get_origin(IntPtr gdkWindow, out int x, out int y); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] - public delegate void gdk_window_resize(IntPtr gtkWindow, int width, int height); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] - public delegate void gdk_window_set_override_redirect(IntPtr gdkWindow, bool value); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_widget_realize(GtkWidget gtkWidget); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_window_set_title(GtkWindow gtkWindow, Utf8Buffer title); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_window_set_resizable(GtkWindow gtkWindow, bool resizable); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_window_set_decorated(GtkWindow gtkWindow, bool decorated); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_window_set_skip_taskbar_hint(GtkWindow gtkWindow, bool setting); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate bool gtk_window_get_skip_taskbar_hint(GtkWindow gtkWindow); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_window_set_skip_pager_hint(GtkWindow gtkWindow, bool setting); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate bool gtk_window_get_skip_pager_hint(GtkWindow gtkWindow); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_window_get_size(GtkWindow gtkWindow, out int width, out int height); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_window_resize(GtkWindow gtkWindow, int width, int height); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_widget_set_size_request(GtkWidget widget, int width, int height); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_window_set_default_size(GtkWindow gtkWindow, int width, int height); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_window_get_position(GtkWindow gtkWindow, out int x, out int y); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_window_move(GtkWindow gtkWindow, int x, int y); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate GtkFileChooser gtk_file_chooser_dialog_new(Utf8Buffer title, GtkWindow parent, GtkFileChooserAction action, IntPtr ignore); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public unsafe delegate GSList* gtk_file_chooser_get_filenames(GtkFileChooser chooser); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_file_chooser_set_select_multiple(GtkFileChooser chooser, bool allow); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_file_chooser_set_filename(GtkFileChooser chooser, Utf8Buffer file); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_dialog_add_button(GtkDialog raw, Utf8Buffer button_text, GtkResponseType response_id); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] - public delegate CairoSurface cairo_image_surface_create(int format, int width, int height); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] - public delegate CairoSurface cairo_image_surface_create_for_data(IntPtr data, int format, int width, int height, int stride); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] - public delegate IntPtr cairo_image_surface_get_data(CairoSurface surface); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] - public delegate int cairo_image_surface_get_stride(CairoSurface surface); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] - public delegate void cairo_surface_mark_dirty(CairoSurface surface); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] - public delegate void cairo_surface_write_to_png(CairoSurface surface, Utf8Buffer path); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] - public delegate void cairo_surface_flush(CairoSurface surface); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] - public delegate void cairo_surface_destroy(IntPtr surface); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] - public delegate void cairo_set_source_surface(IntPtr cr, CairoSurface surface, double x, double y); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] - public delegate void cairo_set_source_rgba(IntPtr cr, double r, double g, double b, double a); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] - public delegate void cairo_scale(IntPtr context, double sx, double sy); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] - public delegate void cairo_paint(IntPtr context); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] - public delegate void cairo_show_text(IntPtr context, Utf8Buffer text); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] - public delegate void cairo_set_font_size(IntPtr context, double size); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] - public delegate void cairo_select_font_face(IntPtr context, Utf8Buffer face, int slant, int weight); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] - public delegate void cairo_move_to(IntPtr context, double x, double y); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] - public delegate void cairo_destroy(IntPtr context); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_widget_queue_draw_area(GtkWidget widget, int x, int y, int width, int height); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate uint gtk_widget_add_tick_callback(GtkWidget widget, TickCallback callback, IntPtr userData, IntPtr destroy); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate uint gtk_widget_remove_tick_callback(GtkWidget widget, uint id); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate GtkImContext gtk_im_multicontext_new(); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate IntPtr gtk_im_context_set_client_window(GtkImContext context, IntPtr window); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate bool gtk_im_context_filter_keypress(GtkImContext context, IntPtr ev); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_widget_activate(GtkWidget widget); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] - public delegate IntPtr gdk_screen_get_root_window(IntPtr screen); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] - public delegate IntPtr gdk_cursor_new(GdkCursorType type); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] - public delegate IntPtr gdk_window_get_pointer(IntPtr raw, out int x, out int y, out int mask); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] - public delegate GdkWindowState gdk_window_get_state(IntPtr window); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_window_iconify(GtkWindow window); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_window_deiconify(GtkWindow window); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_window_maximize(GtkWindow window); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_window_unmaximize(GtkWindow window); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_window_close(GtkWindow window); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_window_set_keep_above(GtkWindow gtkWindow, bool setting); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_window_set_geometry_hints(GtkWindow window, IntPtr geometry_widget, ref GdkGeometry geometry, GdkWindowHints geom_mask); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] - public delegate void gdk_window_invalidate_rect(IntPtr window, ref GdkRectangle rect, bool invalidate_children); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] - public delegate void gdk_window_begin_move_drag(IntPtr window, gint button, gint root_x, gint root_y, guint32 timestamp); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] - public delegate void gdk_window_begin_resize_drag(IntPtr window, WindowEdge edge, gint button, gint root_x, gint root_y, guint32 timestamp); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] - public delegate void gdk_window_process_updates(IntPtr window, bool updateChildren); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] - public delegate void gdk_window_begin_paint_rect(IntPtr window, ref GdkRectangle rect); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] - public delegate void gdk_window_end_paint(IntPtr window); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk, optional: true)] - public delegate IntPtr gdk_x11_window_foreign_new_for_display(IntPtr display, IntPtr xid); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] - public delegate void gdk_window_set_transient_for(IntPtr window, IntPtr parent); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] - public delegate void gdk_event_request_motions(IntPtr ev); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate IntPtr gtk_clipboard_get_for_display(IntPtr display, IntPtr atom); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_clipboard_request_text(IntPtr clipboard, GtkClipboardTextReceivedFunc callback, IntPtr user_data); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_clipboard_set_text(IntPtr clipboard, Utf8Buffer text, int len); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_clipboard_clear(IntPtr clipboard); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.GdkPixBuf)] - public delegate IntPtr gdk_pixbuf_new_from_file(Utf8Buffer filename, out IntPtr error); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate IntPtr gtk_icon_theme_get_default(); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate IntPtr gtk_icon_theme_load_icon(IntPtr icon_theme, Utf8Buffer icon_name, gint size, int flags,out IntPtr error); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] - public delegate IntPtr gdk_cursor_new_from_pixbuf(IntPtr disp, IntPtr pixbuf, int x, int y); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] - public delegate IntPtr gdk_window_set_cursor(IntPtr window, IntPtr cursor); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.GdkPixBuf)] - public delegate IntPtr gdk_pixbuf_new_from_stream(GInputStream stream, IntPtr cancel, out IntPtr error); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.GdkPixBuf)] - public delegate bool gdk_pixbuf_save_to_bufferv(Pixbuf pixbuf, out IntPtr buffer, out IntPtr buffer_size, - Utf8Buffer type, IntPtr option_keys, IntPtr option_values, out IntPtr error); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] - public delegate IntPtr gdk_cairo_create(IntPtr window); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)] - public delegate void g_object_unref(IntPtr instance); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)] - public delegate void g_object_ref(GObject instance); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)] - public delegate IntPtr g_type_name(IntPtr instance); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)] - public delegate ulong g_signal_connect_object(GObject instance, Utf8Buffer signal, IntPtr handler, IntPtr userData, int flags); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)] - public delegate ulong g_signal_handler_disconnect(GObject instance, ulong connectionId); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Glib)] - public delegate ulong g_timeout_add(uint interval, timeout_callback callback, IntPtr data); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Glib)] - public delegate ulong g_timeout_add_full(int prio, uint interval, timeout_callback callback, IntPtr data, IntPtr destroy); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Glib)] - public delegate ulong g_free(IntPtr data); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)] - public delegate bool g_type_check_instance_is_fundamentally_a(IntPtr instance, IntPtr type); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Glib)] - public unsafe delegate void g_slist_free(GSList* data); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gio)] - public delegate GInputStream g_memory_input_stream_new_from_data(IntPtr ptr, IntPtr len, IntPtr destroyCallback); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate bool signal_widget_draw(IntPtr gtkWidget, IntPtr cairoContext, IntPtr userData); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate bool signal_generic(IntPtr gtkWidget, IntPtr userData); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate bool signal_dialog_response(IntPtr gtkWidget, GtkResponseType response, IntPtr userData); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate bool signal_onevent(IntPtr gtkWidget, IntPtr ev, IntPtr userData); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void monitors_changed(IntPtr screen, IntPtr userData); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate bool signal_commit(IntPtr gtkWidget, IntPtr utf8string, IntPtr userData); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate bool timeout_callback(IntPtr data); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void GtkClipboardTextReceivedFunc(IntPtr clipboard, IntPtr utf8string, IntPtr userdata); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate bool TickCallback(IntPtr widget, IntPtr clock, IntPtr userdata); - - - } - - public static D.gdk_display_get_n_screens GdkDisplayGetNScreens; - public static D.gdk_display_get_screen GdkDisplayGetScreen; - public static D.gdk_display_get_default_screen GdkDisplayGetDefaultScreen; - public static D.gdk_screen_get_n_monitors GdkScreenGetNMonitors; - public static D.gdk_screen_get_primary_monitor GdkScreenGetPrimaryMonitor; - public static D.gdk_screen_get_monitor_geometry GdkScreenGetMonitorGeometry; - public static D.gdk_screen_get_monitor_workarea GdkScreenGetMonitorWorkarea; - public static D.gtk_window_set_decorated GtkWindowSetDecorated; - public static D.gtk_window_set_resizable GtkWindowSetResizable; - public static D.gtk_window_set_skip_taskbar_hint GtkWindowSetSkipTaskbarHint; - public static D.gtk_window_get_skip_taskbar_hint GtkWindowGetSkipTaskbarHint; - public static D.gtk_window_set_skip_pager_hint GtkWindowSetSkipPagerHint; - public static D.gtk_window_get_skip_pager_hint GtkWindowGetSkipPagerHint; - public static D.gtk_window_set_title GtkWindowSetTitle; - public static D.gtk_application_new GtkApplicationNew; - public static D.gtk_main_iteration GtkMainIteration; - public static D.gtk_window_new GtkWindowNew; - public static D.gtk_window_set_icon GtkWindowSetIcon; - public static D.gtk_window_set_modal GtkWindowSetModal; - public static D.gtk_window_set_transient_for GtkWindowSetTransientFor; - public static D.gdk_set_allowed_backends GdkSetAllowedBackends; - public static D.gtk_init GtkInit; - public static D.gtk_init_check GtkInitCheck; - public static D.gtk_window_present GtkWindowPresent; - public static D.gtk_widget_hide GtkWidgetHide; - public static D.gtk_widget_show GtkWidgetShow; - public static D.gdk_get_native_handle GetNativeGdkWindowHandle; - public static D.gtk_widget_get_window GtkWidgetGetWindow; - public static D.gtk_widget_get_scale_factor GtkWidgetGetScaleFactor; - public static D.gtk_widget_get_screen GtkWidgetGetScreen; - public static D.gtk_widget_realize GtkWidgetRealize; - public static D.gtk_window_get_size GtkWindowGetSize; - public static D.gtk_window_resize GtkWindowResize; - public static D.gdk_window_resize GdkWindowResize; - public static D.gdk_window_set_override_redirect GdkWindowSetOverrideRedirect; - public static D.gtk_widget_set_size_request GtkWindowSetSizeRequest; - public static D.gtk_window_set_default_size GtkWindowSetDefaultSize; - public static D.gtk_window_set_geometry_hints GtkWindowSetGeometryHints; - public static D.gtk_window_get_position GtkWindowGetPosition; - public static D.gtk_window_move GtkWindowMove; - public static D.gtk_file_chooser_dialog_new GtkFileChooserDialogNew; - public static D.gtk_file_chooser_set_select_multiple GtkFileChooserSetSelectMultiple; - public static D.gtk_file_chooser_set_filename GtkFileChooserSetFilename; - public static D.gtk_file_chooser_get_filenames GtkFileChooserGetFilenames; - public static D.gtk_dialog_add_button GtkDialogAddButton; - public static D.g_object_unref GObjectUnref; - public static D.g_object_ref GObjectRef; - public static D.g_type_name GTypeName; - public static D.g_signal_connect_object GSignalConnectObject; - public static D.g_signal_handler_disconnect GSignalHandlerDisconnect; - public static D.g_timeout_add GTimeoutAdd; - public static D.g_timeout_add_full GTimeoutAddFull; - public static D.g_free GFree; - public static D.g_type_check_instance_is_fundamentally_a GTypeCheckInstanceIsFundamentallyA; - public static D.g_slist_free GSlistFree; - public static D.g_memory_input_stream_new_from_data GMemoryInputStreamNewFromData; - public static D.gtk_widget_set_double_buffered GtkWidgetSetDoubleBuffered; - public static D.gtk_widget_set_events GtkWidgetSetEvents; - public static D.gdk_window_invalidate_rect GdkWindowInvalidateRect; - public static D.gtk_widget_queue_draw_area GtkWidgetQueueDrawArea; - public static D.gtk_widget_add_tick_callback GtkWidgetAddTickCallback; - public static D.gtk_widget_remove_tick_callback GtkWidgetRemoveTickCallback; - public static D.gtk_widget_activate GtkWidgetActivate; - public static D.gtk_clipboard_get_for_display GtkClipboardGetForDisplay; - public static D.gtk_clipboard_request_text GtkClipboardRequestText; - public static D.gtk_clipboard_set_text GtkClipboardSetText; - public static D.gtk_clipboard_clear GtkClipboardRequestClear; - - public static D.gtk_im_multicontext_new GtkImMulticontextNew; - public static D.gtk_im_context_filter_keypress GtkImContextFilterKeypress; - public static D.gtk_im_context_set_client_window GtkImContextSetClientWindow; - - public static D.gdk_screen_get_height GdkScreenGetHeight; - public static D.gdk_display_get_default GdkGetDefaultDisplay; - public static D.gdk_screen_get_width GdkScreenGetWidth; - public static D.gdk_screen_get_root_window GdkScreenGetRootWindow; - public static D.gdk_cursor_new GdkCursorNew; - public static D.gdk_window_get_origin GdkWindowGetOrigin; - public static D.gdk_window_get_pointer GdkWindowGetPointer; - public static D.gdk_window_get_state GdkWindowGetState; - public static D.gtk_window_iconify GtkWindowIconify; - public static D.gtk_window_deiconify GtkWindowDeiconify; - public static D.gtk_window_maximize GtkWindowMaximize; - public static D.gtk_window_unmaximize GtkWindowUnmaximize; - public static D.gtk_window_close GtkWindowClose; - public static D.gtk_window_set_keep_above GtkWindowSetKeepAbove; - public static D.gdk_window_begin_move_drag GdkWindowBeginMoveDrag; - public static D.gdk_window_begin_resize_drag GdkWindowBeginResizeDrag; - public static D.gdk_event_request_motions GdkEventRequestMotions; - public static D.gdk_window_process_updates GdkWindowProcessUpdates; - public static D.gdk_window_begin_paint_rect GdkWindowBeginPaintRect; - public static D.gdk_window_end_paint GdkWindowEndPaint; - public static D.gdk_x11_window_foreign_new_for_display GdkWindowForeignNewForDisplay; - public static D.gdk_window_set_transient_for GdkWindowSetTransientFor; - - public static D.gdk_pixbuf_new_from_file GdkPixbufNewFromFile; - public static D.gtk_icon_theme_get_default GtkIconThemeGetDefault; - public static D.gtk_icon_theme_load_icon GtkIconThemeLoadIcon; - public static D.gdk_cursor_new_from_pixbuf GdkCursorNewFromPixbuf; - public static D.gdk_window_set_cursor GdkWindowSetCursor; - public static D.gdk_pixbuf_new_from_stream GdkPixbufNewFromStream; - public static D.gdk_pixbuf_save_to_bufferv GdkPixbufSaveToBufferv; - public static D.gdk_cairo_create GdkCairoCreate; - - public static D.cairo_image_surface_create CairoImageSurfaceCreate; - public static D.cairo_image_surface_create_for_data CairoImageSurfaceCreateForData; - public static D.cairo_image_surface_get_data CairoImageSurfaceGetData; - public static D.cairo_image_surface_get_stride CairoImageSurfaceGetStride; - public static D.cairo_surface_mark_dirty CairoSurfaceMarkDirty; - public static D.cairo_surface_write_to_png CairoSurfaceWriteToPng; - public static D.cairo_surface_flush CairoSurfaceFlush; - public static D.cairo_surface_destroy CairoSurfaceDestroy; - public static D.cairo_set_source_surface CairoSetSourceSurface; - public static D.cairo_set_source_rgba CairoSetSourceRgba; - public static D.cairo_scale CairoScale; - public static D.cairo_paint CairoPaint; - public static D.cairo_show_text CairoShowText; - public static D.cairo_select_font_face CairoSelectFontFace; - public static D.cairo_set_font_size CairoSetFontSize; - public static D.cairo_move_to CairoMoveTo; - public static D.cairo_destroy CairoDestroy; - - public const int G_TYPE_OBJECT = 80; - } - - public enum GtkWindowType - { - TopLevel, - Popup - } - - [StructLayout(LayoutKind.Sequential)] - public struct GdkRectangle - { - public int X, Y, Width, Height; - - public static GdkRectangle FromRect(Rect rect) - { - return new GdkRectangle - { - X = (int) rect.X, - Y = (int) rect.Y, - Width = (int) rect.Width, - Height = (int) rect.Height - }; - } - } - - enum GdkEventType - { - Nothing = -1, - Delete = 0, - Destroy = 1, - Expose = 2, - MotionNotify = 3, - ButtonPress = 4, - TwoButtonPress = 5, - ThreeButtonPress = 6, - ButtonRelease = 7, - KeyPress = 8, - KeyRelease = 9, - EnterNotify = 10, - LeaveNotify = 11, - FocusChange = 12, - Configure = 13, - Map = 14, - Unmap = 15, - PropertyNotify = 16, - SelectionClear = 17, - SelectionRequest = 18, - SelectionNotify = 19, - ProximityIn = 20, - ProximityOut = 21, - DragEnter = 22, - DragLeave = 23, - DragMotion = 24, - DragStatus = 25, - DropStart = 26, - DropFinished = 27, - ClientEvent = 28, - VisibilityNotify = 29, - NoExpose = 30, - Scroll = 31, - WindowState = 32, - Setting = 33, - OwnerChange = 34, - GrabBroken = 35, - } - - enum GdkModifierType - { - ShiftMask = 1, - LockMask = 2, - ControlMask = 4, - Mod1Mask = 8, - Mod2Mask = 16, - Mod3Mask = 32, - Mod4Mask = 64, - Mod5Mask = 128, - Button1Mask = 256, - Button2Mask = 512, - Button3Mask = 1024, - Button4Mask = 2048, - Button5Mask = 4096, - SuperMask = 67108864, - HyperMask = 134217728, - MetaMask = 268435456, - ReleaseMask = 1073741824, - ModifierMask = ReleaseMask | Button5Mask | Button4Mask | Button3Mask | Button2Mask | Button1Mask | Mod5Mask | Mod4Mask | Mod3Mask | Mod2Mask | Mod1Mask | ControlMask | LockMask | ShiftMask, - None = 0, - } - - enum GdkScrollDirection - { - Up, - Down, - Left, - Right, - Smooth - } - - [StructLayout(LayoutKind.Sequential)] - unsafe struct GdkEventButton - { - public GdkEventType type; - public IntPtr window; - public gint8 send_event; - public guint32 time; - public gdouble x; - public gdouble y; - public gdouble* axes; - public GdkModifierType state; - public guint button; - public IntPtr device; - public gdouble x_root, y_root; - } - - [StructLayout(LayoutKind.Sequential)] - unsafe struct GdkEventMotion - { - public GdkEventType type; - public IntPtr window; - public gint8 send_event; - public guint32 time; - public gdouble x; - public gdouble y; - public gdouble* axes; - public GdkModifierType state; - public gint16 is_hint; - public IntPtr device; - public gdouble x_root, y_root; - } - - [StructLayout(LayoutKind.Sequential)] - unsafe struct GdkEventScroll - { - public GdkEventType type; - public IntPtr window; - public gint8 send_event; - public guint32 time; - public gdouble x; - public gdouble y; - public GdkModifierType state; - public GdkScrollDirection direction; - public IntPtr device; - public gdouble x_root, y_root; - public gdouble delta_x; - public gdouble delta_y; - } - - [StructLayout(LayoutKind.Sequential)] - unsafe struct GdkEventCrossing - { - public GdkEventType type; - public IntPtr window; - public gint8 send_event; - public IntPtr subwindow; - public guint32 time; - public gdouble x; - public gdouble y; - public gdouble x_root; - public gdouble y_root; - public int mode; - public int detail; - public bool focus; - public GdkModifierType state; - }; - - [StructLayout(LayoutKind.Sequential)] - unsafe struct GdkEventWindowState - { - public GdkEventType type; - public IntPtr window; - gint8 send_event; - public GdkWindowState changed_mask; - public GdkWindowState new_window_state; - } - - [StructLayout(LayoutKind.Sequential)] - unsafe struct GdkEventKey - { - public GdkEventType type; - public IntPtr window; - public gint8 send_event; - public guint32 time; - public guint state; - public guint keyval; - public gint length; - public IntPtr pstring; - public guint16 hardware_keycode; - public byte group; - public guint is_modifier; - } - - [StructLayout(LayoutKind.Sequential)] - unsafe struct GSList - { - public IntPtr Data; - public GSList* Next; - } - - [Flags] - public enum GdkWindowState - { - Withdrawn = 1, - Iconified = 2, - Maximized = 4, - Sticky = 8, - Fullscreen = 16, - Above = 32, - Below = 64, - Focused = 128, - Ttiled = 256 - } - - public enum GtkResponseType - { - Help = -11, - Apply = -10, - No = -9, - Yes = -8, - Close = -7, - Cancel = -6, - Ok = -5, - DeleteEvent = -4, - Accept = -3, - Reject = -2, - None = -1, - } - - public enum GtkFileChooserAction - { - Open, - Save, - SelectFolder, - CreateFolder, - } - - [StructLayout(LayoutKind.Sequential)] - public struct GdkGeometry - { - public gint min_width; - public gint min_height; - public gint max_width; - public gint max_height; - public gint base_width; - public gint base_height; - public gint width_inc; - public gint height_inc; - public gdouble min_aspect; - public gdouble max_aspect; - public gint win_gravity; - } - - enum GdkWindowHints - { - GDK_HINT_POS = 1 << 0, - GDK_HINT_MIN_SIZE = 1 << 1, - GDK_HINT_MAX_SIZE = 1 << 2, - GDK_HINT_BASE_SIZE = 1 << 3, - GDK_HINT_ASPECT = 1 << 4, - GDK_HINT_RESIZE_INC = 1 << 5, - GDK_HINT_WIN_GRAVITY = 1 << 6, - GDK_HINT_USER_POS = 1 << 7, - GDK_HINT_USER_SIZE = 1 << 8 - } -} diff --git a/src/Gtk/Avalonia.Gtk3/Interop/NativeException.cs b/src/Gtk/Avalonia.Gtk3/Interop/NativeException.cs deleted file mode 100644 index 64cbccbd26..0000000000 --- a/src/Gtk/Avalonia.Gtk3/Interop/NativeException.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; - -namespace Avalonia.Gtk3.Interop -{ - public class NativeException : Exception - { - public NativeException() - { - } - - public NativeException(string message) : base(message) - { - } - - public NativeException(string message, Exception inner) : base(message, inner) - { - } - - } -} diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Pixbuf.cs b/src/Gtk/Avalonia.Gtk3/Interop/Pixbuf.cs deleted file mode 100644 index 322b9bdfae..0000000000 --- a/src/Gtk/Avalonia.Gtk3/Interop/Pixbuf.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; -using System.IO; -using System.Runtime.InteropServices; -using Avalonia.Platform; -using Avalonia.Platform.Interop; - -namespace Avalonia.Gtk3.Interop -{ - internal class Pixbuf : GObject, IWindowIconImpl - { - Pixbuf(IntPtr handle) : base(handle) - { - - } - - public static Pixbuf NewFromFile(string filename) - { - using (var ub = new Utf8Buffer(filename)) - { - IntPtr err; - var rv = Native.GdkPixbufNewFromFile(ub, out err); - if(rv != IntPtr.Zero) - return new Pixbuf(rv); - throw new GException(err); - } - } - - public static unsafe Pixbuf NewFromBytes(byte[] data) - { - fixed (void* bytes = data) - { - using (var stream = Native.GMemoryInputStreamNewFromData(new IntPtr(bytes), new IntPtr(data.Length), IntPtr.Zero)) - { - IntPtr err; - var rv = Native.GdkPixbufNewFromStream(stream, IntPtr.Zero, out err); - if (rv != IntPtr.Zero) - return new Pixbuf(rv); - throw new GException(err); - } - } - } - - public static Pixbuf NewFromStream(Stream s) - { - if (s is MemoryStream) - return NewFromBytes(((MemoryStream) s).ToArray()); - var ms = new MemoryStream(); - s.CopyTo(ms); - return NewFromBytes(ms.ToArray()); - } - - public void Save(Stream outputStream) - { - IntPtr buffer, bufferLen, error; - using (var png = new Utf8Buffer("png")) - if (!Native.GdkPixbufSaveToBufferv(this, out buffer, out bufferLen, png, - IntPtr.Zero, IntPtr.Zero, out error)) - throw new GException(error); - var data = new byte[bufferLen.ToInt32()]; - Marshal.Copy(buffer, data, 0, bufferLen.ToInt32()); - Native.GFree(buffer); - outputStream.Write(data, 0, data.Length); - } - } -} diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs b/src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs deleted file mode 100644 index c39cb9e394..0000000000 --- a/src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs +++ /dev/null @@ -1,156 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Runtime.InteropServices; -using Avalonia.Platform; -using Avalonia.Platform.Interop; - -namespace Avalonia.Gtk3.Interop -{ - internal class GtkImportAttribute : Attribute - { - public GtkDll Dll { get; set; } - public string Name { get; set; } - public bool Optional { get; set; } - - public GtkImportAttribute(GtkDll dll, string name = null, bool optional = false) - { - Dll = dll; - Name = name; - Optional = optional; - } - } - - public enum GtkDll - { - Gdk, - Gtk, - Glib, - Gio, - Gobject, - Cairo, - GdkPixBuf - } - - static class Resolver - { - private static Lazy Platform = - new Lazy( - () => AvaloniaLocator.Current.GetService().GetRuntimeInfo().OperatingSystem); - - public static ICustomGtk3NativeLibraryResolver Custom { get; set; } - - - static string FormatName(string name, int version = 0) - { - if (Platform.Value == OperatingSystemType.WinNT) - return "lib" + name + "-" + version + ".dll"; - if (Platform.Value == OperatingSystemType.Linux) - return "lib" + name + ".so" + "." + version; - if (Platform.Value == OperatingSystemType.OSX) - return "lib" + name + "." + version + ".dylib"; - throw new Exception("Unknown platform, use custom name resolver"); - } - - - - static string GetDllName(GtkDll dll) - { - var name = Custom?.GetName(dll); - if (name != null) - return name; - - switch (dll) - { - case GtkDll.Cairo: - return FormatName("cairo", 2); - case GtkDll.Gdk: - return FormatName("gdk-3"); - case GtkDll.Glib: - return FormatName("glib-2.0"); - case GtkDll.Gio: - return FormatName("gio-2.0"); - case GtkDll.Gtk: - return FormatName("gtk-3"); - case GtkDll.Gobject: - return FormatName("gobject-2.0"); - case GtkDll.GdkPixBuf: - return FormatName("gdk_pixbuf-2.0"); - default: - throw new ArgumentException("Unknown lib: " + dll); - } - } - - static IntPtr LoadDll(IDynamicLibraryLoader loader, GtkDll dll) - { - - var exceptions = new List(); - - var name = GetDllName(dll); - if (Custom?.TrySystemFirst != false) - { - try - { - return loader.LoadLibrary(name); - } - catch (Exception e) - { - exceptions.Add(e); - } - } - var path = Custom?.Lookup(dll); - if (path == null && Custom?.BasePath != null) - path = Path.Combine(Custom.BasePath, name); - if (path != null) - { - try - { - return loader.LoadLibrary(path); - } - catch (Exception e) - { - exceptions.Add(e); - } - } - throw new AggregateException("Unable to load " + dll, exceptions); - } - - public static void Resolve(string basePath = null) - { - var loader = AvaloniaLocator.Current.GetService(); - - var dlls = Enum.GetValues(typeof(GtkDll)).Cast().ToDictionary(x => x, x => LoadDll(loader, x)); - - foreach (var fieldInfo in typeof(Native).GetTypeInfo().DeclaredFields) - { - var import = fieldInfo.FieldType.GetTypeInfo().GetCustomAttributes(typeof(GtkImportAttribute), true).Cast().FirstOrDefault(); - if(import == null) - continue; - IntPtr lib = dlls[import.Dll]; - - var funcPtr = loader.GetProcAddress(lib, import.Name ?? fieldInfo.FieldType.Name, import.Optional); - - if (funcPtr != IntPtr.Zero) - fieldInfo.SetValue(null, Marshal.GetDelegateForFunctionPointer(funcPtr, fieldInfo.FieldType)); - } - - var nativeHandleNames = new[] { "gdk_win32_window_get_handle", "gdk_x11_window_get_xid", "gdk_quartz_window_get_nswindow" }; - foreach (var name in nativeHandleNames) - { - var ptr = loader.GetProcAddress(dlls[GtkDll.Gdk], name, true); - if (ptr == IntPtr.Zero) - continue; - Native.GetNativeGdkWindowHandle = (Native.D.gdk_get_native_handle) Marshal - .GetDelegateForFunctionPointer(ptr, typeof(Native.D.gdk_get_native_handle)); - } - if (Native.GetNativeGdkWindowHandle == null) - throw new Exception($"Unable to locate any of [{string.Join(", ", nativeHandleNames)}] in libgdk"); - - } - - - } -} - diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Signal.cs b/src/Gtk/Avalonia.Gtk3/Interop/Signal.cs deleted file mode 100644 index 8eaca93152..0000000000 --- a/src/Gtk/Avalonia.Gtk3/Interop/Signal.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Runtime.InteropServices; -using Avalonia.Platform.Interop; - -namespace Avalonia.Gtk3.Interop -{ - class Signal - { - class ConnectedSignal : IDisposable - { - private readonly GObject _instance; - private GCHandle _handle; - private readonly ulong _id; - - public ConnectedSignal(GObject instance, GCHandle handle, ulong id) - { - _instance = instance; - Native.GObjectRef(instance); - _handle = handle; - _id = id; - } - - public void Dispose() - { - if (_handle.IsAllocated) - { - Native.GObjectUnref(_instance.DangerousGetHandle()); - Native.GSignalHandlerDisconnect(_instance, _id); - _handle.Free(); - } - } - } - - public static IDisposable Connect(GObject obj, string name, T handler) - { - var handle = GCHandle.Alloc(handler); - var ptr = Marshal.GetFunctionPointerForDelegate((Delegate)(object)handler); - using (var utf = new Utf8Buffer(name)) - { - var id = Native.GSignalConnectObject(obj, utf, ptr, IntPtr.Zero, 0); - if (id == 0) - throw new ArgumentException("Unable to connect to signal " + name); - return new ConnectedSignal(obj, handle, id); - } - } - } -} diff --git a/src/Gtk/Avalonia.Gtk3/KeyTransform.cs b/src/Gtk/Avalonia.Gtk3/KeyTransform.cs deleted file mode 100644 index 4299e07094..0000000000 --- a/src/Gtk/Avalonia.Gtk3/KeyTransform.cs +++ /dev/null @@ -1,230 +0,0 @@ -using System.Collections.Generic; -using Avalonia.Gtk3; -using Avalonia.Input; - -namespace Avalonia.Gtk.Common -{ - static class KeyTransform - { - private static readonly Dictionary KeyDic = new Dictionary - { - { GdkKey.Cancel, Key.Cancel }, - { GdkKey.BackSpace, Key.Back }, - { GdkKey.Tab, Key.Tab }, - { GdkKey.Linefeed, Key.LineFeed }, - { GdkKey.Clear, Key.Clear }, - { GdkKey.Return, Key.Return }, - { GdkKey.KP_Enter, Key.Return }, - { GdkKey.Pause, Key.Pause }, - { GdkKey.Caps_Lock, Key.CapsLock }, - //{ GdkKey.?, Key.HangulMode } - //{ GdkKey.?, Key.JunjaMode } - //{ GdkKey.?, Key.FinalMode } - //{ GdkKey.?, Key.KanjiMode } - { GdkKey.Escape, Key.Escape }, - //{ GdkKey.?, Key.ImeConvert } - //{ GdkKey.?, Key.ImeNonConvert } - //{ GdkKey.?, Key.ImeAccept } - //{ GdkKey.?, Key.ImeModeChange } - { GdkKey.space, Key.Space }, - { GdkKey.Prior, Key.Prior }, - { GdkKey.KP_Prior, Key.Prior }, - { GdkKey.Page_Down, Key.PageDown }, - { GdkKey.KP_Page_Down, Key.PageDown }, - { GdkKey.End, Key.End }, - { GdkKey.KP_End, Key.End }, - { GdkKey.Home, Key.Home }, - { GdkKey.KP_Home, Key.Home }, - { GdkKey.Left, Key.Left }, - { GdkKey.KP_Left, Key.Left }, - { GdkKey.Up, Key.Up }, - { GdkKey.KP_Up, Key.Up }, - { GdkKey.Right, Key.Right }, - { GdkKey.KP_Right, Key.Right }, - { GdkKey.Down, Key.Down }, - { GdkKey.KP_Down, Key.Down }, - { GdkKey.Select, Key.Select }, - { GdkKey.Print, Key.Print }, - { GdkKey.Execute, Key.Execute }, - //{ GdkKey.?, Key.Snapshot } - { GdkKey.Insert, Key.Insert }, - { GdkKey.KP_Insert, Key.Insert }, - { GdkKey.Delete, Key.Delete }, - { GdkKey.KP_Delete, Key.Delete }, - { GdkKey.Help, Key.Help }, - { GdkKey.Key_0, Key.D0 }, - { GdkKey.Key_1, Key.D1 }, - { GdkKey.Key_2, Key.D2 }, - { GdkKey.Key_3, Key.D3 }, - { GdkKey.Key_4, Key.D4 }, - { GdkKey.Key_5, Key.D5 }, - { GdkKey.Key_6, Key.D6 }, - { GdkKey.Key_7, Key.D7 }, - { GdkKey.Key_8, Key.D8 }, - { GdkKey.Key_9, Key.D9 }, - { GdkKey.A, Key.A }, - { GdkKey.B, Key.B }, - { GdkKey.C, Key.C }, - { GdkKey.D, Key.D }, - { GdkKey.E, Key.E }, - { GdkKey.F, Key.F }, - { GdkKey.G, Key.G }, - { GdkKey.H, Key.H }, - { GdkKey.I, Key.I }, - { GdkKey.J, Key.J }, - { GdkKey.K, Key.K }, - { GdkKey.L, Key.L }, - { GdkKey.M, Key.M }, - { GdkKey.N, Key.N }, - { GdkKey.O, Key.O }, - { GdkKey.P, Key.P }, - { GdkKey.Q, Key.Q }, - { GdkKey.R, Key.R }, - { GdkKey.S, Key.S }, - { GdkKey.T, Key.T }, - { GdkKey.U, Key.U }, - { GdkKey.V, Key.V }, - { GdkKey.W, Key.W }, - { GdkKey.X, Key.X }, - { GdkKey.Y, Key.Y }, - { GdkKey.Z, Key.Z }, - { GdkKey.a, Key.A }, - { GdkKey.b, Key.B }, - { GdkKey.c, Key.C }, - { GdkKey.d, Key.D }, - { GdkKey.e, Key.E }, - { GdkKey.f, Key.F }, - { GdkKey.g, Key.G }, - { GdkKey.h, Key.H }, - { GdkKey.i, Key.I }, - { GdkKey.j, Key.J }, - { GdkKey.k, Key.K }, - { GdkKey.l, Key.L }, - { GdkKey.m, Key.M }, - { GdkKey.n, Key.N }, - { GdkKey.o, Key.O }, - { GdkKey.p, Key.P }, - { GdkKey.q, Key.Q }, - { GdkKey.r, Key.R }, - { GdkKey.s, Key.S }, - { GdkKey.t, Key.T }, - { GdkKey.u, Key.U }, - { GdkKey.v, Key.V }, - { GdkKey.w, Key.W }, - { GdkKey.x, Key.X }, - { GdkKey.y, Key.Y }, - { GdkKey.z, Key.Z }, - //{ GdkKey.?, Key.LWin } - //{ GdkKey.?, Key.RWin } - { GdkKey.Menu, Key.Apps }, - //{ GdkKey.?, Key.Sleep } - { GdkKey.KP_0, Key.NumPad0 }, - { GdkKey.KP_1, Key.NumPad1 }, - { GdkKey.KP_2, Key.NumPad2 }, - { GdkKey.KP_3, Key.NumPad3 }, - { GdkKey.KP_4, Key.NumPad4 }, - { GdkKey.KP_5, Key.NumPad5 }, - { GdkKey.KP_6, Key.NumPad6 }, - { GdkKey.KP_7, Key.NumPad7 }, - { GdkKey.KP_8, Key.NumPad8 }, - { GdkKey.KP_9, Key.NumPad9 }, - { GdkKey.multiply, Key.Multiply }, - { GdkKey.KP_Multiply, Key.Multiply }, - { GdkKey.KP_Add, Key.Add }, - //{ GdkKey.?, Key.Separator } - { GdkKey.KP_Subtract, Key.Subtract }, - { GdkKey.KP_Decimal, Key.Decimal }, - { GdkKey.KP_Divide, Key.Divide }, - { GdkKey.F1, Key.F1 }, - { GdkKey.F2, Key.F2 }, - { GdkKey.F3, Key.F3 }, - { GdkKey.F4, Key.F4 }, - { GdkKey.F5, Key.F5 }, - { GdkKey.F6, Key.F6 }, - { GdkKey.F7, Key.F7 }, - { GdkKey.F8, Key.F8 }, - { GdkKey.F9, Key.F9 }, - { GdkKey.F10, Key.F10 }, - { GdkKey.F11, Key.F11 }, - { GdkKey.F12, Key.F12 }, - { GdkKey.L3, Key.F13 }, - { GdkKey.F14, Key.F14 }, - { GdkKey.L5, Key.F15 }, - { GdkKey.F16, Key.F16 }, - { GdkKey.F17, Key.F17 }, - { GdkKey.L8, Key.F18 }, - { GdkKey.L9, Key.F19 }, - { GdkKey.L10, Key.F20 }, - { GdkKey.R1, Key.F21 }, - { GdkKey.R2, Key.F22 }, - { GdkKey.F23, Key.F23 }, - { GdkKey.R4, Key.F24 }, - { GdkKey.Num_Lock, Key.NumLock }, - { GdkKey.Scroll_Lock, Key.Scroll }, - { GdkKey.Shift_L, Key.LeftShift }, - { GdkKey.Shift_R, Key.RightShift }, - { GdkKey.Control_L, Key.LeftCtrl }, - { GdkKey.Control_R, Key.RightCtrl }, - { GdkKey.Alt_L, Key.LeftAlt }, - { GdkKey.Alt_R, Key.RightAlt }, - //{ GdkKey.?, Key.BrowserBack } - //{ GdkKey.?, Key.BrowserForward } - //{ GdkKey.?, Key.BrowserRefresh } - //{ GdkKey.?, Key.BrowserStop } - //{ GdkKey.?, Key.BrowserSearch } - //{ GdkKey.?, Key.BrowserFavorites } - //{ GdkKey.?, Key.BrowserHome } - //{ GdkKey.?, Key.VolumeMute } - //{ GdkKey.?, Key.VolumeDown } - //{ GdkKey.?, Key.VolumeUp } - //{ GdkKey.?, Key.MediaNextTrack } - //{ GdkKey.?, Key.MediaPreviousTrack } - //{ GdkKey.?, Key.MediaStop } - //{ GdkKey.?, Key.MediaPlayPause } - //{ GdkKey.?, Key.LaunchMail } - //{ GdkKey.?, Key.SelectMedia } - //{ GdkKey.?, Key.LaunchApplication1 } - //{ GdkKey.?, Key.LaunchApplication2 } - { GdkKey.semicolon, Key.OemSemicolon }, - { GdkKey.plus, Key.OemPlus }, - { GdkKey.equal, Key.OemPlus }, - { GdkKey.comma, Key.OemComma }, - { GdkKey.minus, Key.OemMinus }, - { GdkKey.period, Key.OemPeriod }, - { GdkKey.slash, Key.Oem2 }, - { GdkKey.grave, Key.OemTilde }, - //{ GdkKey.?, Key.AbntC1 } - //{ GdkKey.?, Key.AbntC2 } - { GdkKey.bracketleft, Key.OemOpenBrackets }, - { GdkKey.backslash, Key.OemPipe }, - { GdkKey.bracketright, Key.OemCloseBrackets }, - { GdkKey.apostrophe, Key.OemQuotes }, - //{ GdkKey.?, Key.Oem8 } - //{ GdkKey.?, Key.Oem102 } - //{ GdkKey.?, Key.ImeProcessed } - //{ GdkKey.?, Key.System } - //{ GdkKey.?, Key.OemAttn } - //{ GdkKey.?, Key.OemFinish } - //{ GdkKey.?, Key.DbeHiragana } - //{ GdkKey.?, Key.OemAuto } - //{ GdkKey.?, Key.DbeDbcsChar } - //{ GdkKey.?, Key.OemBackTab } - //{ GdkKey.?, Key.Attn } - //{ GdkKey.?, Key.DbeEnterWordRegisterMode } - //{ GdkKey.?, Key.DbeEnterImeConfigureMode } - //{ GdkKey.?, Key.EraseEof } - //{ GdkKey.?, Key.Play } - //{ GdkKey.?, Key.Zoom } - //{ GdkKey.?, Key.NoName } - //{ GdkKey.?, Key.DbeEnterDialogConversionMode } - //{ GdkKey.?, Key.OemClear } - //{ GdkKey.?, Key.DeadCharProcessed } - }; - - public static Key ConvertKey(GdkKey key) - { - Key result; - return KeyDic.TryGetValue(key, out result) ? result : Key.None; - } - } -} diff --git a/src/Gtk/Avalonia.Gtk3/PlatformIconLoader.cs b/src/Gtk/Avalonia.Gtk3/PlatformIconLoader.cs deleted file mode 100644 index 6965e7c812..0000000000 --- a/src/Gtk/Avalonia.Gtk3/PlatformIconLoader.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.IO; -using Avalonia.Gtk3.Interop; -using Avalonia.Platform; - -namespace Avalonia.Gtk3 -{ - class PlatformIconLoader : IPlatformIconLoader - { - public IWindowIconImpl LoadIcon(string fileName) => Pixbuf.NewFromFile(fileName); - - public IWindowIconImpl LoadIcon(Stream stream) => Pixbuf.NewFromStream(stream); - - public IWindowIconImpl LoadIcon(IBitmapImpl bitmap) - { - var ms = new MemoryStream(); - bitmap.Save(ms); - return Pixbuf.NewFromBytes(ms.ToArray()); - } - } -} diff --git a/src/Gtk/Avalonia.Gtk3/PopupImpl.cs b/src/Gtk/Avalonia.Gtk3/PopupImpl.cs deleted file mode 100644 index ef17407dd6..0000000000 --- a/src/Gtk/Avalonia.Gtk3/PopupImpl.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Avalonia.Gtk3.Interop; -using Avalonia.Platform; - -namespace Avalonia.Gtk3 -{ - class PopupImpl : WindowBaseImpl, IPopupImpl - { - static GtkWindow CreateWindow() - { - var window = Native.GtkWindowNew(GtkWindowType.Popup); - return window; - } - - public PopupImpl() : base(CreateWindow()) - { - OverrideRedirect = true; - } - } -} diff --git a/src/Gtk/Avalonia.Gtk3/Properties/AssemblyInfo.cs b/src/Gtk/Avalonia.Gtk3/Properties/AssemblyInfo.cs deleted file mode 100644 index a27d631ee0..0000000000 --- a/src/Gtk/Avalonia.Gtk3/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Reflection; -using Avalonia.Gtk3; -using Avalonia.Platform; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: ExportWindowingSubsystem(OperatingSystemType.WinNT, 2, "GTK3", typeof(Gtk3Platform), nameof(Gtk3Platform.Initialize))] -[assembly: ExportWindowingSubsystem(OperatingSystemType.Linux, 1, "GTK3", typeof(Gtk3Platform), nameof(Gtk3Platform.Initialize))] -[assembly: ExportWindowingSubsystem(OperatingSystemType.OSX, 2, "GTK3", typeof(Gtk3Platform), nameof(Gtk3Platform.Initialize))] \ No newline at end of file diff --git a/src/Gtk/Avalonia.Gtk3/README.md b/src/Gtk/Avalonia.Gtk3/README.md deleted file mode 100644 index ea853bde75..0000000000 --- a/src/Gtk/Avalonia.Gtk3/README.md +++ /dev/null @@ -1,8 +0,0 @@ -P/Invoke based GTK3 backend -=========================== - -Code is EXPERIMENTAL at this point. It also needs Direct2D/Skia for rendering. - -Windows GTK3 binaries aren't included in the repo, you need to download them from https://sourceforge.net/projects/gtk3win/ - -On Linux it should work out of the box with system-provided GTK3. On OSX you should be able to wire GTK3 using DYLD_LIBRARY_PATH environment variable. \ No newline at end of file diff --git a/src/Gtk/Avalonia.Gtk3/ScreenImpl.cs b/src/Gtk/Avalonia.Gtk3/ScreenImpl.cs deleted file mode 100644 index 2cb8a6b127..0000000000 --- a/src/Gtk/Avalonia.Gtk3/ScreenImpl.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using System.Collections.Generic; -using Avalonia.Gtk3.Interop; -using Avalonia.Platform; - -namespace Avalonia.Gtk3 -{ - internal class ScreenImpl : IScreenImpl - { - public int ScreenCount - { - get => _allScreens.Length; - } - - private Screen[] _allScreens; - public IReadOnlyList AllScreens - { - get - { - if (_allScreens == null) - { - IntPtr display = Native.GdkGetDefaultDisplay(); - GdkScreen screen = Native.GdkDisplayGetDefaultScreen(display); - short primary = Native.GdkScreenGetPrimaryMonitor(screen); - Screen[] screens = new Screen[Native.GdkScreenGetNMonitors(screen)]; - for (short i = 0; i < screens.Length; i++) - { - GdkRectangle workArea = new GdkRectangle(), geometry = new GdkRectangle(); - Native.GdkScreenGetMonitorGeometry(screen, i, ref geometry); - Native.GdkScreenGetMonitorWorkarea(screen, i, ref workArea); - PixelRect workAreaRect = new PixelRect(workArea.X, workArea.Y, workArea.Width, workArea.Height); - PixelRect geometryRect = new PixelRect(geometry.X, geometry.Y, geometry.Width, geometry.Height); - GtkScreen s = new GtkScreen(geometryRect, workAreaRect, i == primary, i); - screens[i] = s; - } - - _allScreens = screens; - } - - return _allScreens; - } - } - - public ScreenImpl() - { - IntPtr display = Native.GdkGetDefaultDisplay(); - GdkScreen screen = Native.GdkDisplayGetDefaultScreen(display); - Signal.Connect(screen, "monitors-changed", MonitorsChanged); - } - - private unsafe void MonitorsChanged(IntPtr screen, IntPtr userData) - { - _allScreens = null; - } - } -} diff --git a/src/Gtk/Avalonia.Gtk3/SystemDialogs.cs b/src/Gtk/Avalonia.Gtk3/SystemDialogs.cs deleted file mode 100644 index 1e85eaa156..0000000000 --- a/src/Gtk/Avalonia.Gtk3/SystemDialogs.cs +++ /dev/null @@ -1,110 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Avalonia.Controls; -using Avalonia.Controls.Platform; -using Avalonia.Gtk3.Interop; -using Avalonia.Platform; -using Avalonia.Platform.Interop; - -namespace Avalonia.Gtk3 -{ - class SystemDialogBase - { - - public unsafe static Task ShowDialog(string title, GtkWindow parent, GtkFileChooserAction action, - bool multiselect, string initialFileName, Action modify) - { - GtkFileChooser dlg; - parent = parent ?? GtkWindow.Null; - using (var name = new Utf8Buffer(title)) - dlg = Native.GtkFileChooserDialogNew(name, parent, action, IntPtr.Zero); - modify?.Invoke(dlg); - if (multiselect) - Native.GtkFileChooserSetSelectMultiple(dlg, true); - - Native.GtkWindowSetModal(dlg, true); - var tcs = new TaskCompletionSource(); - List disposables = null; - Action dispose = () => - { - // ReSharper disable once PossibleNullReferenceException - foreach (var d in disposables) - d.Dispose(); - disposables.Clear(); - }; - disposables = new List - { - Signal.Connect(dlg, "close", delegate - { - tcs.TrySetResult(null); - dispose(); - return false; - }), - Signal.Connect(dlg, "response", (_, resp, __) => - { - string[] result = null; - if (resp == GtkResponseType.Accept) - { - var rlst = new List(); - var gs = Native.GtkFileChooserGetFilenames(dlg); - var cgs = gs; - while (cgs != null) - { - if (cgs->Data != IntPtr.Zero) - rlst.Add(Utf8Buffer.StringFromPtr(cgs->Data)); - cgs = cgs->Next; - } - - Native.GSlistFree(gs); - result = rlst.ToArray(); - } - - Native.GtkWidgetHide(dlg); - dispose(); - tcs.TrySetResult(result); - return false; - }), - dlg - }; - using (var open = new Utf8Buffer("Open")) - Native.GtkDialogAddButton(dlg, open, GtkResponseType.Accept); - using (var open = new Utf8Buffer("Cancel")) - Native.GtkDialogAddButton(dlg, open, GtkResponseType.Cancel); - if (initialFileName != null) - using (var fn = new Utf8Buffer(initialFileName)) - Native.GtkFileChooserSetFilename(dlg, fn); - Native.GtkWindowPresent(dlg); - return tcs.Task; - } - - public Task ShowFileDialogAsync(FileDialog dialog, GtkWindow parent, - Action modify = null) - { - return ShowDialog(dialog.Title, parent, - dialog is OpenFileDialog ? GtkFileChooserAction.Open : GtkFileChooserAction.Save, - (dialog as OpenFileDialog)?.AllowMultiple ?? false, - Path.Combine(string.IsNullOrEmpty(dialog.InitialDirectory) ? "" : dialog.InitialDirectory, - string.IsNullOrEmpty(dialog.InitialFileName) ? "" : dialog.InitialFileName), modify); - } - - public async Task ShowFolderDialogAsync(OpenFolderDialog dialog, GtkWindow parent, - Action modify = null) - { - var res = await ShowDialog(dialog.Title, parent, - GtkFileChooserAction.SelectFolder, false, dialog.InitialDirectory, modify); - return res?.FirstOrDefault(); - } - } - - class SystemDialog : SystemDialogBase, ISystemDialogImpl - { - public Task ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent) - => ShowFolderDialogAsync(dialog, ((WindowBaseImpl)parent)?.GtkWidget); - - public Task ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent) - => ShowFileDialogAsync(dialog, ((WindowBaseImpl)parent)?.GtkWidget); - } -} diff --git a/src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs b/src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs deleted file mode 100644 index bff50a979d..0000000000 --- a/src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs +++ /dev/null @@ -1,527 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.InteropServices; -using System.Threading; -using Avalonia.Controls; -using Avalonia.Gtk3.Interop; -using Avalonia.Input; -using Avalonia.Input.Raw; -using Avalonia.OpenGL; -using Avalonia.Platform; -using Avalonia.Platform.Interop; -using Avalonia.Rendering; -using Avalonia.Threading; - -namespace Avalonia.Gtk3 -{ - abstract class WindowBaseImpl : IWindowBaseImpl, IPlatformHandle, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo - { - public readonly GtkWindow GtkWidget; - private IInputRoot _inputRoot; - private readonly GtkImContext _imContext; - private readonly FramebufferManager _framebuffer; - private readonly EglGlPlatformSurface _egl; - protected readonly List Disposables = new List(); - private Size _lastSize; - private PixelPoint _lastPosition; - private double _lastScaling; - private uint _lastKbdEvent; - private uint _lastSmoothScrollEvent; - private GCHandle _gcHandle; - private object _lock = new object(); - private IDeferredRenderOperation _nextRenderOperation; - private readonly AutoResetEvent _canSetNextOperation = new AutoResetEvent(true); - internal IntPtr? GdkWindowHandle; - private bool _overrideRedirect; - private uint? _tickCallback; - public WindowBaseImpl(GtkWindow gtkWidget) - { - - GtkWidget = gtkWidget; - - var glf = AvaloniaLocator.Current.GetService() as EglGlPlatformFeature; - if (glf != null) - _egl = new EglGlPlatformSurface((EglDisplay)glf.Display, glf.DeferredContext, this); - else - _framebuffer = new FramebufferManager(this); - - _imContext = Native.GtkImMulticontextNew(); - Disposables.Add(_imContext); - Native.GtkWidgetSetEvents(gtkWidget, 0xFFFFFE); - Disposables.Add(Signal.Connect(_imContext, "commit", OnCommit)); - Connect("draw", OnDraw); - Connect("realize", OnRealized); - ConnectEvent("configure-event", OnConfigured); - ConnectEvent("button-press-event", OnButton); - ConnectEvent("button-release-event", OnButton); - ConnectEvent("motion-notify-event", OnMotion); - ConnectEvent("scroll-event", OnScroll); - ConnectEvent("window-state-event", OnStateChanged); - ConnectEvent("key-press-event", OnKeyEvent); - ConnectEvent("key-release-event", OnKeyEvent); - ConnectEvent("leave-notify-event", OnLeaveNotifyEvent); - ConnectEvent("delete-event", OnClosingEvent); - Connect("destroy", OnDestroy); - Native.GtkWidgetRealize(gtkWidget); - GdkWindowHandle = this.Handle.Handle; - _lastSize = ClientSize; - - if (_egl != null) - Native.GtkWidgetSetDoubleBuffered(gtkWidget, false); - else if (Gtk3Platform.UseDeferredRendering) - { - Native.GtkWidgetSetDoubleBuffered(gtkWidget, false); - _gcHandle = GCHandle.Alloc(this); - _tickCallback = Native.GtkWidgetAddTickCallback(GtkWidget, PinnedStaticCallback, - GCHandle.ToIntPtr(_gcHandle), IntPtr.Zero); - } - } - - private bool OnConfigured(IntPtr gtkwidget, IntPtr ev, IntPtr userdata) - { - int w, h; - if (!OverrideRedirect) - { - Native.GtkWindowGetSize(GtkWidget, out w, out h); - var size = ClientSize = new Size(w, h); - if (_lastSize != size) - { - Resized?.Invoke(size); - _lastSize = size; - } - } - var pos = Position; - if (_lastPosition != pos) - { - PositionChanged?.Invoke(pos); - _lastPosition = pos; - } - var scaling = Scaling; - if (_lastScaling != scaling) - { - ScalingChanged?.Invoke(scaling); - _lastScaling = scaling; - } - return false; - } - - private bool OnRealized(IntPtr gtkwidget, IntPtr userdata) - { - Native.GtkImContextSetClientWindow(_imContext, Native.GtkWidgetGetWindow(GtkWidget)); - return false; - } - - private bool OnDestroy(IntPtr gtkwidget, IntPtr userdata) - { - DoDispose(true); - return false; - } - - private static InputModifiers GetModifierKeys(GdkModifierType state) - { - var rv = InputModifiers.None; - if (state.HasFlag(GdkModifierType.ControlMask)) - rv |= InputModifiers.Control; - if (state.HasFlag(GdkModifierType.ShiftMask)) - rv |= InputModifiers.Shift; - if (state.HasFlag(GdkModifierType.Mod1Mask)) - rv |= InputModifiers.Alt; - if (state.HasFlag(GdkModifierType.Button1Mask)) - rv |= InputModifiers.LeftMouseButton; - if (state.HasFlag(GdkModifierType.Button2Mask)) - rv |= InputModifiers.RightMouseButton; - if (state.HasFlag(GdkModifierType.Button3Mask)) - rv |= InputModifiers.MiddleMouseButton; - return rv; - } - - private unsafe bool OnClosingEvent(IntPtr w, IntPtr ev, IntPtr userdata) - { - bool? preventClosing = Closing?.Invoke(); - return preventClosing ?? false; - } - - private unsafe bool OnButton(IntPtr w, IntPtr ev, IntPtr userdata) - { - var evnt = (GdkEventButton*)ev; - var e = new RawPointerEventArgs( - Gtk3Platform.Mouse, - evnt->time, - _inputRoot, - evnt->type == GdkEventType.ButtonRelease - ? evnt->button == 1 - ? RawPointerEventType.LeftButtonUp - : evnt->button == 3 ? RawPointerEventType.RightButtonUp : RawPointerEventType.MiddleButtonUp - : evnt->button == 1 - ? RawPointerEventType.LeftButtonDown - : evnt->button == 3 ? RawPointerEventType.RightButtonDown : RawPointerEventType.MiddleButtonDown, - new Point(evnt->x, evnt->y), GetModifierKeys(evnt->state)); - OnInput(e); - return true; - } - - protected virtual unsafe bool OnStateChanged(IntPtr w, IntPtr pev, IntPtr userData) - { - var ev = (GdkEventWindowState*) pev; - if (ev->changed_mask.HasFlag(GdkWindowState.Focused)) - { - if(ev->new_window_state.HasFlag(GdkWindowState.Focused)) - Activated?.Invoke(); - else - Deactivated?.Invoke(); - } - return true; - } - - private unsafe bool OnMotion(IntPtr w, IntPtr ev, IntPtr userdata) - { - var evnt = (GdkEventMotion*)ev; - var position = new Point(evnt->x, evnt->y); - Native.GdkEventRequestMotions(ev); - var e = new RawPointerEventArgs( - Gtk3Platform.Mouse, - evnt->time, - _inputRoot, - RawPointerEventType.Move, - position, GetModifierKeys(evnt->state)); - OnInput(e); - - return true; - } - private unsafe bool OnScroll(IntPtr w, IntPtr ev, IntPtr userdata) - { - var evnt = (GdkEventScroll*)ev; - - //Ignore duplicates - if (evnt->time - _lastSmoothScrollEvent < 10 && evnt->direction != GdkScrollDirection.Smooth) - return true; - - var delta = new Vector(); - const double step = (double) 1; - if (evnt->direction == GdkScrollDirection.Down) - delta = new Vector(0, -step); - else if (evnt->direction == GdkScrollDirection.Up) - delta = new Vector(0, step); - else if (evnt->direction == GdkScrollDirection.Right) - delta = new Vector(-step, 0); - else if (evnt->direction == GdkScrollDirection.Left) - delta = new Vector(step, 0); - else if (evnt->direction == GdkScrollDirection.Smooth) - { - delta = new Vector(-evnt->delta_x, -evnt->delta_y); - _lastSmoothScrollEvent = evnt->time; - } - var e = new RawMouseWheelEventArgs(Gtk3Platform.Mouse, evnt->time, _inputRoot, - new Point(evnt->x, evnt->y), delta, GetModifierKeys(evnt->state)); - OnInput(e); - return true; - } - - private unsafe bool OnKeyEvent(IntPtr w, IntPtr pev, IntPtr userData) - { - var evnt = (GdkEventKey*) pev; - _lastKbdEvent = evnt->time; - var e = new RawKeyEventArgs( - Gtk3Platform.Keyboard, - evnt->time, - evnt->type == GdkEventType.KeyPress ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp, - Avalonia.Gtk.Common.KeyTransform.ConvertKey((GdkKey)evnt->keyval), GetModifierKeys((GdkModifierType)evnt->state)); - OnInput(e); - if (Native.GtkImContextFilterKeypress(_imContext, pev)) - return true; - return true; - } - - private unsafe bool OnLeaveNotifyEvent(IntPtr w, IntPtr pev, IntPtr userData) - { - var evnt = (GdkEventCrossing*) pev; - var position = new Point(evnt->x, evnt->y); - OnInput(new RawPointerEventArgs(Gtk3Platform.Mouse, - evnt->time, - _inputRoot, - RawPointerEventType.Move, - position, GetModifierKeys(evnt->state))); - return true; - } - - private unsafe bool OnCommit(IntPtr gtkwidget, IntPtr utf8string, IntPtr userdata) - { - OnInput(new RawTextInputEventArgs(Gtk3Platform.Keyboard, _lastKbdEvent, Utf8Buffer.StringFromPtr(utf8string))); - return true; - } - - protected void ConnectEvent(string name, Native.D.signal_onevent handler) - => Disposables.Add(Signal.Connect(GtkWidget, name, handler)); - void Connect(string name, T handler) => Disposables.Add(Signal.Connect(GtkWidget, name, handler)); - - internal IntPtr CurrentCairoContext { get; private set; } - - private bool OnDraw(IntPtr gtkwidget, IntPtr cairocontext, IntPtr userdata) - { - if (!Gtk3Platform.UseDeferredRendering) - { - CurrentCairoContext = cairocontext; - Paint?.Invoke(new Rect(ClientSize)); - CurrentCairoContext = IntPtr.Zero; - } - else - Paint?.Invoke(new Rect(ClientSize)); - return true; - } - - private static Native.D.TickCallback PinnedStaticCallback = StaticTickCallback; - - static bool StaticTickCallback(IntPtr widget, IntPtr clock, IntPtr userData) - { - var impl = (WindowBaseImpl) GCHandle.FromIntPtr(userData).Target; - impl.OnRenderTick(); - return true; - } - - public void SetNextRenderOperation(IDeferredRenderOperation op) - { - while (true) - { - lock (_lock) - { - if (_nextRenderOperation == null) - { - _nextRenderOperation = op; - return; - } - } - _canSetNextOperation.WaitOne(); - } - - } - - private void OnRenderTick() - { - IDeferredRenderOperation op = null; - lock (_lock) - { - if (_nextRenderOperation != null) - { - op = _nextRenderOperation; - _nextRenderOperation = null; - } - _canSetNextOperation.Set(); - } - if (op != null) - { - op?.RenderNow(null); - op?.Dispose(); - } - } - - - public void Dispose() => DoDispose(false); - - void DoDispose(bool fromDestroy) - { - if (_tickCallback.HasValue) - { - if (!GtkWidget.IsClosed) - Native.GtkWidgetRemoveTickCallback(GtkWidget, _tickCallback.Value); - _tickCallback = null; - } - - //We are calling it here, since signal handler will be detached - if (!GtkWidget.IsClosed) - Closed?.Invoke(); - foreach(var d in Disposables.AsEnumerable().Reverse()) - d.Dispose(); - Disposables.Clear(); - - if (!fromDestroy && !GtkWidget.IsClosed) - Native.GtkWindowClose(GtkWidget); - GtkWidget.Dispose(); - - if (_gcHandle.IsAllocated) - { - _gcHandle.Free(); - } - } - - public Size MaxClientSize - { - get - { - var s = Native.GtkWidgetGetScreen(GtkWidget); - return new Size(Native.GdkScreenGetWidth(s), Native.GdkScreenGetHeight(s)); - } - } - - public void SetMinMaxSize(Size minSize, Size maxSize) - { - if (GtkWidget.IsClosed) - return; - - GdkGeometry geometry = new GdkGeometry(); - geometry.min_width = minSize.Width > 0 ? (int)minSize.Width : -1; - geometry.min_height = minSize.Height > 0 ? (int)minSize.Height : -1; - geometry.max_width = !Double.IsInfinity(maxSize.Width) && maxSize.Width > 0 ? (int)maxSize.Width : 999999; - geometry.max_height = !Double.IsInfinity(maxSize.Height) && maxSize.Height > 0 ? (int)maxSize.Height : 999999; - - Native.GtkWindowSetGeometryHints(GtkWidget, IntPtr.Zero, ref geometry, GdkWindowHints.GDK_HINT_MIN_SIZE | GdkWindowHints.GDK_HINT_MAX_SIZE); - } - - public IMouseDevice MouseDevice => Gtk3Platform.Mouse; - - public double Scaling => LastKnownScaleFactor = (int) (Native.GtkWidgetGetScaleFactor?.Invoke(GtkWidget) ?? 1); - - public IPlatformHandle Handle => this; - - string IPlatformHandle.HandleDescriptor => "HWND"; - - public Action Activated { get; set; } - public Func Closing { get; set; } - public Action Closed { get; set; } - public Action Deactivated { get; set; } - public Action Input { get; set; } - public Action Paint { get; set; } - public Action Resized { get; set; } - public Action ScalingChanged { get; set; } //TODO - public Action PositionChanged { get; set; } - - public void Activate() => Native.GtkWidgetActivate(GtkWidget); - - public void Invalidate(Rect rect) - { - if(GtkWidget.IsClosed) - return; - var s = ClientSize; - Native.GtkWidgetQueueDrawArea(GtkWidget, 0, 0, (int) s.Width, (int) s.Height); - } - - public void SetInputRoot(IInputRoot inputRoot) => _inputRoot = inputRoot; - - void OnInput(RawInputEventArgs args) - { - Dispatcher.UIThread.Post(() => Input?.Invoke(args), DispatcherPriority.Input); - } - - public Point PointToClient(PixelPoint point) - { - int x, y; - Native.GdkWindowGetOrigin(Native.GtkWidgetGetWindow(GtkWidget), out x, out y); - - return new Point(point.X - x, point.Y - y); - } - - public PixelPoint PointToScreen(Point point) - { - int x, y; - Native.GdkWindowGetOrigin(Native.GtkWidgetGetWindow(GtkWidget), out x, out y); - return new PixelPoint((int)(point.X + x), (int)(point.Y + y)); - } - - public void SetCursor(IPlatformHandle cursor) - { - if (GtkWidget.IsClosed) - return; - Native.GdkWindowSetCursor(Native.GtkWidgetGetWindow(GtkWidget), cursor?.Handle ?? IntPtr.Zero); - } - - public virtual void Show() => Native.GtkWindowPresent(GtkWidget); - - public void Hide() => Native.GtkWidgetHide(GtkWidget); - - public void SetTopmost(bool value) => Native.GtkWindowSetKeepAbove(GtkWidget, value); - - void GetGlobalPointer(out int x, out int y) - { - int mask; - Native.GdkWindowGetPointer(Native.GdkScreenGetRootWindow(Native.GtkWidgetGetScreen(GtkWidget)), - out x, out y, out mask); - } - - public void BeginMoveDrag() - { - int x, y; - GetGlobalPointer(out x, out y); - Native.GdkWindowBeginMoveDrag(Native.GtkWidgetGetWindow(GtkWidget), 1, x, y, 0); - } - - public void BeginResizeDrag(WindowEdge edge) - { - int x, y; - GetGlobalPointer(out x, out y); - Native.GdkWindowBeginResizeDrag(Native.GtkWidgetGetWindow(GtkWidget), edge, 1, x, y, 0); - } - - - public Size ClientSize { get; private set; } - public int LastKnownScaleFactor { get; private set; } - - public void Resize(Size value) - { - if (GtkWidget.IsClosed) - return; - - Native.GtkWindowResize(GtkWidget, (int)value.Width, (int)value.Height); - if (OverrideRedirect) - { - var size = ClientSize = value; - if (_lastSize != size) - { - Resized?.Invoke(size); - _lastSize = size; - } - } - } - - public bool OverrideRedirect - { - get => _overrideRedirect; - set - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - Native.GdkWindowSetOverrideRedirect(Native.GtkWidgetGetWindow(GtkWidget), value); - _overrideRedirect = value; - } - } - } - - public IScreenImpl Screen - { - get; - } = new ScreenImpl(); - - public PixelPoint Position - { - get - { - int x, y; - Native.GtkWindowGetPosition(GtkWidget, out x, out y); - return new PixelPoint(x, y); - } - set { Native.GtkWindowMove(GtkWidget, (int)value.X, (int)value.Y); } - } - - IntPtr IPlatformHandle.Handle => Native.GetNativeGdkWindowHandle(Native.GtkWidgetGetWindow(GtkWidget)); - public IEnumerable Surfaces => new object[] {Handle, _egl, _framebuffer}; - - public IRenderer CreateRenderer(IRenderRoot root) - { - var loop = AvaloniaLocator.Current.GetService(); - return Gtk3Platform.UseDeferredRendering - ? (IRenderer) new DeferredRenderer(root, loop) - : new ImmediateRenderer(root); - } - - PixelSize EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo.Size - { - get - { - var cs = ClientSize; - return new PixelSize((int)Math.Max(1, LastKnownScaleFactor * cs.Width), - (int)Math.Max(1, LastKnownScaleFactor * ClientSize.Height)); - } - } - double EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo.Scaling => LastKnownScaleFactor; - IntPtr EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo.Handle => Handle.Handle; - } -} diff --git a/src/Gtk/Avalonia.Gtk3/WindowImpl.cs b/src/Gtk/Avalonia.Gtk3/WindowImpl.cs deleted file mode 100644 index 1c8d800f2f..0000000000 --- a/src/Gtk/Avalonia.Gtk3/WindowImpl.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System; -using Avalonia.Controls; -using Avalonia.Gtk3.Interop; -using Avalonia.Platform; -using Avalonia.Platform.Interop; - -namespace Avalonia.Gtk3 -{ - class WindowImpl : WindowBaseImpl, IWindowImpl - { - private WindowState _lastWindowState; - - public WindowImpl() : base(Native.GtkWindowNew(GtkWindowType.TopLevel)) - { - } - - protected unsafe override bool OnStateChanged(IntPtr w, IntPtr pev, IntPtr userData) - { - var windowStateEvent = (GdkEventWindowState*)pev; - var newWindowState = windowStateEvent->new_window_state; - var windowState = newWindowState.HasFlag(GdkWindowState.Iconified) ? WindowState.Minimized - : (newWindowState.HasFlag(GdkWindowState.Maximized) ? WindowState.Maximized : WindowState.Normal); - - if (windowState != _lastWindowState) - { - _lastWindowState = windowState; - WindowStateChanged?.Invoke(windowState); - } - - return base.OnStateChanged(w, pev, userData); - } - - public void SetTitle(string title) - { - using (var t = new Utf8Buffer(title)) - Native.GtkWindowSetTitle(GtkWidget, t); - } - - public WindowState WindowState - { - get - { - var state = Native.GdkWindowGetState(Native.GtkWidgetGetWindow(GtkWidget)); - if (state.HasFlag(GdkWindowState.Iconified)) - return WindowState.Minimized; - if (state.HasFlag(GdkWindowState.Maximized)) - return WindowState.Maximized; - return WindowState.Normal; - } - set - { - if (value == WindowState.Minimized) - Native.GtkWindowIconify(GtkWidget); - else if (value == WindowState.Maximized) - Native.GtkWindowMaximize(GtkWidget); - else - { - Native.GtkWindowUnmaximize(GtkWidget); - Native.GtkWindowDeiconify(GtkWidget); - } - } - } - - public Action WindowStateChanged { get; set; } - - public void ShowDialog(IWindowImpl parent) - { - Native.GtkWindowSetModal(GtkWidget, true); - Native.GtkWindowSetTransientFor(GtkWidget, ((WindowImpl)parent).GtkWidget.DangerousGetHandle()); - Native.GtkWindowPresent(GtkWidget); - } - - public override void Show() - { - Native.GtkWindowSetModal(GtkWidget, false); - Native.GtkWindowSetTransientFor(GtkWidget, IntPtr.Zero); - Native.GtkWindowPresent(GtkWidget); - } - - public void SetSystemDecorations(bool enabled) => Native.GtkWindowSetDecorated(GtkWidget, enabled); - - public void SetIcon(IWindowIconImpl icon) => Native.GtkWindowSetIcon(GtkWidget, (Pixbuf) icon); - - public void SetCoverTaskbarWhenMaximized(bool enable) - { - //Why do we even have that? - } - - public void ShowTaskbarIcon(bool value) => Native.GtkWindowSetSkipTaskbarHint(GtkWidget, !value); - - public void CanResize(bool value) => Native.GtkWindowSetResizable(GtkWidget, value); - - - class EmptyDisposable : IDisposable - { - public void Dispose() - { - - } - } - } -} diff --git a/src/Gtk/Avalonia.Gtk3/X11.cs b/src/Gtk/Avalonia.Gtk3/X11.cs deleted file mode 100644 index 4677114bdb..0000000000 --- a/src/Gtk/Avalonia.Gtk3/X11.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace Avalonia.Gtk3 -{ - class X11 - { - [DllImport("libX11.so.6")] - public static extern IntPtr XInitThreads(); - - [DllImport("libX11.so.6")] - public static extern IntPtr XOpenDisplay(IntPtr name); - - [DllImport("libX11.so.6")] - public static extern IntPtr XLockDisplay(IntPtr display); - - [DllImport("libX11.so.6")] - public static extern IntPtr XUnlockDisplay(IntPtr display); - - [DllImport("libX11.so.6")] - public static extern IntPtr XFreeGC(IntPtr display, IntPtr gc); - - [DllImport("libX11.so.6")] - public static extern IntPtr XCreateGC(IntPtr display, IntPtr drawable, ulong valuemask, IntPtr values); - - [DllImport("libX11.so.6")] - public static extern int XInitImage(ref XImage image); - - [DllImport("libX11.so.6")] - public static extern int XDestroyImage(ref XImage image); - - [DllImport("libX11.so.6")] - public static extern IntPtr XSetErrorHandler(XErrorHandler handler); - - [DllImport("libX11.so.6")] - public static extern int XSync(IntPtr display, bool discard); - - public delegate int XErrorHandler(IntPtr display, ref XErrorEvent error); - - [DllImport("libX11.so.6")] - public static extern int XPutImage(IntPtr display, IntPtr drawable, IntPtr gc, ref XImage image, - int srcx, int srcy, int destx, int desty, uint width, uint height); - - [StructLayout(LayoutKind.Sequential)] - public unsafe struct XErrorEvent - { - public int type; - public IntPtr* display; /* Display the event was read from */ - public ulong serial; /* serial number of failed request */ - public byte error_code; /* error code of failed request */ - public byte request_code; /* Major op-code of failed request */ - public byte minor_code; /* Minor op-code of failed request */ - public IntPtr resourceid; /* resource id */ - } - - [StructLayout(LayoutKind.Sequential)] - public unsafe struct XImage - { - public int width, height; /* size of image */ - public int xoffset; /* number of pixels offset in X direction */ - public int format; /* XYBitmap, XYPixmap, ZPixmap */ - public IntPtr data; /* pointer to image data */ - public int byte_order; /* data byte order, LSBFirst, MSBFirst */ - public int bitmap_unit; /* quant. of scanline 8, 16, 32 */ - public int bitmap_bit_order; /* LSBFirst, MSBFirst */ - public int bitmap_pad; /* 8, 16, 32 either XY or ZPixmap */ - public int depth; /* depth of image */ - public int bytes_per_line; /* accelerator to next scanline */ - public int bits_per_pixel; /* bits per pixel (ZPixmap) */ - public ulong red_mask; /* bits in z arrangement */ - public ulong green_mask; - public ulong blue_mask; - private fixed byte funcs[128]; - } - - - } -} \ No newline at end of file diff --git a/src/Gtk/Avalonia.Gtk3/X11Framebuffer.cs b/src/Gtk/Avalonia.Gtk3/X11Framebuffer.cs deleted file mode 100644 index c84d7c7541..0000000000 --- a/src/Gtk/Avalonia.Gtk3/X11Framebuffer.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using Avalonia.Platform; - -namespace Avalonia.Gtk3 -{ - class X11Framebuffer : ILockedFramebuffer - { - private readonly IntPtr _display; - private readonly IntPtr _xid; - private IUnmanagedBlob _blob; - - public X11Framebuffer(IntPtr display, IntPtr xid, int width, int height, int factor) - { - _display = display; - _xid = xid; - Size = new PixelSize(width * factor, height * factor); - RowBytes = Size.Width * 4; - Dpi = new Vector(96, 96) * factor; - Format = PixelFormat.Bgra8888; - _blob = AvaloniaLocator.Current.GetService().AllocBlob(RowBytes * Size.Height); - Address = _blob.Address; - } - - public void Dispose() - { - var image = new X11.XImage(); - int bitsPerPixel = 32; - image.width = Size.Width; - image.height = Size.Height; - image.format = 2; //ZPixmap; - image.data = Address; - image.byte_order = 0;// LSBFirst; - image.bitmap_unit = bitsPerPixel; - image.bitmap_bit_order = 0;// LSBFirst; - image.bitmap_pad = bitsPerPixel; - image.depth = 24; - image.bytes_per_line = RowBytes - Size.Width * 4; - image.bits_per_pixel = bitsPerPixel; - X11.XLockDisplay(_display); - X11.XInitImage(ref image); - var gc = X11.XCreateGC(_display, _xid, 0, IntPtr.Zero); - X11.XPutImage(_display, _xid, gc, ref image, 0, 0, 0, 0, (uint)Size.Width, (uint)Size.Height); - X11.XFreeGC(_display, gc); - X11.XSync(_display, true); - X11.XUnlockDisplay(_display); - _blob.Dispose(); - } - - public IntPtr Address { get; } - public PixelSize Size { get; } - public int RowBytes { get; } - public Vector Dpi { get; } - public PixelFormat Format { get; } - } -} diff --git a/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs b/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs index 6a63014e1a..78369a3648 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs +++ b/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs @@ -18,7 +18,7 @@ namespace Avalonia.LinuxFramebuffer { _fb = fb; Invalidate(default(Rect)); - var mice = new Mice(ClientSize.Width, ClientSize.Height); + var mice = new Mice(this, ClientSize.Width, ClientSize.Height); mice.Start(); mice.Event += e => Input?.Invoke(e); } diff --git a/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebuffer.cs b/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebuffer.cs index 87b55be488..1e25bd4a8a 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebuffer.cs +++ b/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebuffer.cs @@ -43,12 +43,10 @@ namespace Avalonia.LinuxFramebuffer SetBpp(); - _varInfo.yoffset = 100; if (-1 == NativeUnsafeMethods.ioctl(_fd, FbIoCtl.FBIOPUT_VSCREENINFO, pnfo)) _varInfo.transp = new fb_bitfield(); - if (-1 == NativeUnsafeMethods.ioctl(_fd, FbIoCtl.FBIOPUT_VSCREENINFO, pnfo)) - throw new Exception("FBIOPUT_VSCREENINFO error: " + Marshal.GetLastWin32Error()); + NativeUnsafeMethods.ioctl(_fd, FbIoCtl.FBIOPUT_VSCREENINFO, pnfo); if (-1 == NativeUnsafeMethods.ioctl(_fd, FbIoCtl.FBIOGET_VSCREENINFO, pnfo)) throw new Exception("FBIOGET_VSCREENINFO error: " + Marshal.GetLastWin32Error()); diff --git a/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs b/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs index 9ed06c978d..396942c8dd 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs +++ b/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using System.Threading; using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.Embedding; using Avalonia.Controls.Platform; using Avalonia.Input; @@ -21,7 +22,6 @@ namespace Avalonia.LinuxFramebuffer private static readonly Stopwatch St = Stopwatch.StartNew(); internal static uint Timestamp => (uint)St.ElapsedTicks; public static InternalPlatformThreadingInterface Threading; - public static FramebufferToplevelImpl TopLevel; LinuxFramebufferPlatform(string fbdev = null) { _fb = new LinuxFramebuffer(fbdev); @@ -41,37 +41,73 @@ namespace Avalonia.LinuxFramebuffer .Bind().ToConstant(Threading); } - internal static TopLevel Initialize(T builder, string fbdev = null) where T : AppBuilderBase, new() + internal static LinuxFramebufferLifetime Initialize(T builder, string fbdev = null) where T : AppBuilderBase, new() { var platform = new LinuxFramebufferPlatform(fbdev); - builder.UseSkia().UseWindowingSubsystem(platform.Initialize, "fbdev") - .SetupWithoutStarting(); - var tl = new EmbeddableControlRoot(TopLevel = new FramebufferToplevelImpl(platform._fb)); - tl.Prepare(); - return tl; + builder.UseSkia().UseWindowingSubsystem(platform.Initialize, "fbdev"); + return new LinuxFramebufferLifetime(platform._fb); } } -} -public static class LinuxFramebufferPlatformExtensions -{ - class TokenClosable : ICloseable + class LinuxFramebufferLifetime : IControlledApplicationLifetime, ISingleViewApplicationLifetime { - public event EventHandler Closed; + private readonly LinuxFramebuffer _fb; + private TopLevel _topLevel; + private readonly CancellationTokenSource _cts = new CancellationTokenSource(); + public CancellationToken Token => _cts.Token; - public TokenClosable(CancellationToken token) + public LinuxFramebufferLifetime(LinuxFramebuffer fb) + { + _fb = fb; + } + + public Control MainView { - token.Register(() => Dispatcher.UIThread.Post(() => Closed?.Invoke(this, new EventArgs()))); + get => (Control)_topLevel?.Content; + set + { + if (_topLevel == null) + { + + var tl = new EmbeddableControlRoot(new FramebufferToplevelImpl(_fb)); + tl.Prepare(); + _topLevel = tl; + } + _topLevel.Content = value; + } + } + + public int ExitCode { get; private set; } + public event EventHandler Startup; + public event EventHandler Exit; + + public void Start(string[] args) + { + Startup?.Invoke(this, new ControlledApplicationLifetimeStartupEventArgs(args)); + } + + public void Shutdown(int exitCode) + { + ExitCode = exitCode; + var e = new ControlledApplicationLifetimeExitEventArgs(exitCode); + Exit?.Invoke(this, e); + ExitCode = e.ApplicationExitCode; + _cts.Cancel(); } } +} - public static void InitializeWithLinuxFramebuffer(this T builder, Action setup, - CancellationToken stop = default(CancellationToken), string fbdev = null) +public static class LinuxFramebufferPlatformExtensions +{ + public static int StartLinuxFramebuffer(this T builder, string[] args, string fbdev = null) where T : AppBuilderBase, new() { - setup(LinuxFramebufferPlatform.Initialize(builder, fbdev)); - builder.BeforeStartCallback(builder); - builder.Instance.Run(new TokenClosable(stop)); + var lifetime = LinuxFramebufferPlatform.Initialize(builder, fbdev); + builder.Instance.ApplicationLifetime = lifetime; + builder.SetupWithoutStarting(); + lifetime.Start(args); + builder.Instance.Run(lifetime.Token); + return lifetime.ExitCode; } } diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Mice.cs b/src/Linux/Avalonia.LinuxFramebuffer/Mice.cs index b982b98d38..2b82b4f4aa 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/Mice.cs +++ b/src/Linux/Avalonia.LinuxFramebuffer/Mice.cs @@ -7,8 +7,9 @@ using Avalonia.Platform; namespace Avalonia.LinuxFramebuffer { - public unsafe class Mice + unsafe class Mice { + private readonly FramebufferToplevelImpl _topLevel; private readonly double _width; private readonly double _height; private double _x; @@ -16,8 +17,9 @@ namespace Avalonia.LinuxFramebuffer public event Action Event; - public Mice(double width, double height) + public Mice(FramebufferToplevelImpl topLevel, double width, double height) { + _topLevel = topLevel; _width = width; _height = height; } @@ -78,7 +80,7 @@ namespace Avalonia.LinuxFramebuffer return; Event?.Invoke(new RawPointerEventArgs(LinuxFramebufferPlatform.MouseDevice, LinuxFramebufferPlatform.Timestamp, - LinuxFramebufferPlatform.TopLevel.InputRoot, RawPointerEventType.Move, new Point(_x, _y), + _topLevel.InputRoot, RawPointerEventType.Move, new Point(_x, _y), InputModifiers.None)); } if (ev.type ==(int) EvType.EV_ABS) @@ -91,7 +93,7 @@ namespace Avalonia.LinuxFramebuffer return; Event?.Invoke(new RawPointerEventArgs(LinuxFramebufferPlatform.MouseDevice, LinuxFramebufferPlatform.Timestamp, - LinuxFramebufferPlatform.TopLevel.InputRoot, RawPointerEventType.Move, new Point(_x, _y), + _topLevel.InputRoot, RawPointerEventType.Move, new Point(_x, _y), InputModifiers.None)); } if (ev.type == (short) EvType.EV_KEY) @@ -108,7 +110,7 @@ namespace Avalonia.LinuxFramebuffer Event?.Invoke(new RawPointerEventArgs(LinuxFramebufferPlatform.MouseDevice, LinuxFramebufferPlatform.Timestamp, - LinuxFramebufferPlatform.TopLevel.InputRoot, type.Value, new Point(_x, _y), default(InputModifiers))); + _topLevel.InputRoot, type.Value, new Point(_x, _y), default(InputModifiers))); } } } diff --git a/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs b/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs index e8f2439f46..db0b617bcd 100644 --- a/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs +++ b/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs @@ -2,18 +2,10 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; -using System.Collections.Generic; -using System.ComponentModel; using System.IO; -using System.Linq; using System.Reflection; -using System.Runtime.Serialization; using System.Text; -using System.Xml.Linq; using Avalonia.Markup.Xaml.XamlIl; -using Avalonia.Controls; -using Avalonia.Markup.Data; -using Avalonia.Markup.Xaml.PortableXaml; using Avalonia.Platform; namespace Avalonia.Markup.Xaml diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github b/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github index 610cda30c6..894b2c0282 160000 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github @@ -1 +1 @@ -Subproject commit 610cda30c69e32e83c8235060606480904c937bc +Subproject commit 894b2c02827fd5eb16a338de5d5b6c9fbc60fef5 diff --git a/src/Markup/Avalonia.Markup/Data/MultiBinding.cs b/src/Markup/Avalonia.Markup/Data/MultiBinding.cs index d1e3765dfa..19f92149ec 100644 --- a/src/Markup/Avalonia.Markup/Data/MultiBinding.cs +++ b/src/Markup/Avalonia.Markup/Data/MultiBinding.cs @@ -64,14 +64,19 @@ namespace Avalonia.Data object anchor = null, bool enableDataValidation = false) { - if (Converter == null) + var targetType = targetProperty?.PropertyType ?? typeof(object); + var converter = Converter; + // 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))) { - throw new NotSupportedException("MultiBinding without Converter not currently supported."); + converter = new StringFormatMultiValueConverter(StringFormat, converter); } - - var targetType = targetProperty?.PropertyType ?? typeof(object); + var children = Bindings.Select(x => x.Initiate(target, null)); - var input = children.Select(x => x.Observable).CombineLatest().Select(x => ConvertValue(x, targetType)); + var input = children.Select(x => x.Observable).CombineLatest().Select(x => ConvertValue(x, targetType, converter)); var mode = Mode == BindingMode.Default ? targetProperty?.GetMetadata(target.GetType()).DefaultBindingMode : Mode; @@ -87,10 +92,10 @@ namespace Avalonia.Data } } - private object ConvertValue(IList values, Type targetType) + private object ConvertValue(IList values, Type targetType, IMultiValueConverter converter) { var culture = CultureInfo.CurrentCulture; - var converted = Converter.Convert(values, targetType, ConverterParameter, culture); + var converted = converter.Convert(values, targetType, ConverterParameter, culture); if (converted == BindingOperations.DoNothing) { @@ -102,15 +107,6 @@ namespace Avalonia.Data 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; } } diff --git a/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs b/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs index b7d752dfe3..9e31cab421 100644 --- a/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs @@ -11,113 +11,6 @@ namespace Avalonia.Controls.UnitTests { public class ApplicationTests { - [Fact] - public void Should_Exit_After_MainWindow_Closed() - { - using (UnitTestApplication.Start(TestServices.StyledWindow)) - { - Application.Current.ShutdownMode = ShutdownMode.OnMainWindowClose; - - var hasExit = false; - - Application.Current.Exit += (s, e) => hasExit = true; - - var mainWindow = new Window(); - - mainWindow.Show(); - - Application.Current.MainWindow = mainWindow; - - var window = new Window(); - - window.Show(); - - mainWindow.Close(); - - Assert.True(hasExit); - } - } - - [Fact] - public void Should_Exit_After_Last_Window_Closed() - { - using (UnitTestApplication.Start(TestServices.StyledWindow)) - { - Application.Current.ShutdownMode = ShutdownMode.OnLastWindowClose; - - var hasExit = false; - - Application.Current.Exit += (s, e) => hasExit = true; - - var windowA = new Window(); - - windowA.Show(); - - var windowB = new Window(); - - windowB.Show(); - - windowA.Close(); - - Assert.False(hasExit); - - windowB.Close(); - - Assert.True(hasExit); - } - } - - [Fact] - public void Should_Only_Exit_On_Explicit_Exit() - { - using (UnitTestApplication.Start(TestServices.StyledWindow)) - { - Application.Current.ShutdownMode = ShutdownMode.OnExplicitShutdown; - - var hasExit = false; - - Application.Current.Exit += (s, e) => hasExit = true; - - var windowA = new Window(); - - windowA.Show(); - - var windowB = new Window(); - - windowB.Show(); - - windowA.Close(); - - Assert.False(hasExit); - - windowB.Close(); - - Assert.False(hasExit); - - Application.Current.Shutdown(); - - Assert.True(hasExit); - } - } - - [Fact] - public void Should_Close_All_Remaining_Open_Windows_After_Explicit_Exit_Call() - { - using (UnitTestApplication.Start(TestServices.StyledWindow)) - { - var windows = new List { new Window(), new Window(), new Window(), new Window() }; - - foreach (var window in windows) - { - window.Show(); - } - - Application.Current.Shutdown(); - - Assert.Empty(Application.Current.Windows); - } - } - [Fact] public void Throws_ArgumentNullException_On_Run_If_MainWindow_Is_Null() { @@ -142,18 +35,5 @@ namespace Avalonia.Controls.UnitTests Assert.True(raised); } } - - [Fact] - public void Should_Set_ExitCode_After_Shutdown() - { - using (UnitTestApplication.Start(TestServices.MockThreadingInterface)) - { - Application.Current.Shutdown(1337); - - var exitCode = Application.Current.Run(); - - Assert.Equal(1337, exitCode); - } - } } } diff --git a/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs b/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs index 067c66969f..6482fcb4da 100644 --- a/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs @@ -59,9 +59,7 @@ namespace Avalonia.Controls.UnitTests }; var window = new Window { Content = target }; - - Avalonia.Application.Current.MainWindow = window; - + _mouse.Click(target, MouseButton.Right); Assert.True(sut.IsOpen); diff --git a/tests/Avalonia.Controls.UnitTests/DesktopStyleApplicationLifetimeTests.cs b/tests/Avalonia.Controls.UnitTests/DesktopStyleApplicationLifetimeTests.cs new file mode 100644 index 0000000000..74523d4193 --- /dev/null +++ b/tests/Avalonia.Controls.UnitTests/DesktopStyleApplicationLifetimeTests.cs @@ -0,0 +1,213 @@ +using System; +using System.Collections.Generic; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Platform; +using Avalonia.Threading; +using Avalonia.UnitTests; +using Moq; +using Xunit; + +namespace Avalonia.Controls.UnitTests +{ + + public class DesktopStyleApplicationLifetimeTests + { + [Fact] + public void Should_Set_ExitCode_After_Shutdown() + { + using (UnitTestApplication.Start(TestServices.MockThreadingInterface)) + using(var lifetime = new ClassicDesktopStyleApplicationLifetime(Application.Current)) + { + lifetime.Shutdown(1337); + + var exitCode = lifetime.Start(Array.Empty()); + + Assert.Equal(1337, exitCode); + } + } + + + [Fact] + public void Should_Close_All_Remaining_Open_Windows_After_Explicit_Exit_Call() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + using(var lifetime = new ClassicDesktopStyleApplicationLifetime(Application.Current)) + { + var windows = new List { new Window(), new Window(), new Window(), new Window() }; + + foreach (var window in windows) + { + window.Show(); + } + Assert.Equal(4, lifetime.Windows.Count); + lifetime.Shutdown(); + + Assert.Empty(lifetime.Windows); + } + } + + [Fact] + public void Should_Only_Exit_On_Explicit_Exit() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + using(var lifetime = new ClassicDesktopStyleApplicationLifetime(Application.Current)) + { + lifetime.ShutdownMode = ShutdownMode.OnExplicitShutdown; + + var hasExit = false; + + lifetime.Exit += (s, e) => hasExit = true; + + var windowA = new Window(); + + windowA.Show(); + + var windowB = new Window(); + + windowB.Show(); + + windowA.Close(); + + Assert.False(hasExit); + + windowB.Close(); + + Assert.False(hasExit); + + lifetime.Shutdown(); + + Assert.True(hasExit); + } + } + + [Fact] + public void Should_Exit_After_MainWindow_Closed() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + using(var lifetime = new ClassicDesktopStyleApplicationLifetime(Application.Current)) + { + lifetime.ShutdownMode = ShutdownMode.OnMainWindowClose; + + var hasExit = false; + + lifetime.Exit += (s, e) => hasExit = true; + + var mainWindow = new Window(); + + mainWindow.Show(); + + lifetime.MainWindow = mainWindow; + + var window = new Window(); + + window.Show(); + + mainWindow.Close(); + + Assert.True(hasExit); + } + } + + [Fact] + public void Should_Exit_After_Last_Window_Closed() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + using(var lifetime = new ClassicDesktopStyleApplicationLifetime(Application.Current)) + { + lifetime.ShutdownMode = ShutdownMode.OnLastWindowClose; + + var hasExit = false; + + lifetime.Exit += (s, e) => hasExit = true; + + var windowA = new Window(); + + windowA.Show(); + + var windowB = new Window(); + + windowB.Show(); + + windowA.Close(); + + Assert.False(hasExit); + + windowB.Close(); + + Assert.True(hasExit); + } + } + + [Fact] + public void Show_Should_Add_Window_To_OpenWindows() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + using(var lifetime = new ClassicDesktopStyleApplicationLifetime(Application.Current)) + { + var window = new Window(); + + window.Show(); + + Assert.Equal(new[] { window }, lifetime.Windows); + } + } + + [Fact] + public void Window_Should_Be_Added_To_OpenWindows_Only_Once() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + using(var lifetime = new ClassicDesktopStyleApplicationLifetime(Application.Current)) + { + var window = new Window(); + + window.Show(); + window.Show(); + window.IsVisible = true; + + Assert.Equal(new[] { window }, lifetime.Windows); + + window.Close(); + } + } + + [Fact] + public void Close_Should_Remove_Window_From_OpenWindows() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + using(var lifetime = new ClassicDesktopStyleApplicationLifetime(Application.Current)) + { + var window = new Window(); + + window.Show(); + Assert.Equal(1, lifetime.Windows.Count); + window.Close(); + + Assert.Empty(lifetime.Windows); + } + } + + [Fact] + public void Impl_Closing_Should_Remove_Window_From_OpenWindows() + { + var windowImpl = new Mock(); + windowImpl.SetupProperty(x => x.Closed); + windowImpl.Setup(x => x.Scaling).Returns(1); + + var services = TestServices.StyledWindow.With( + windowingPlatform: new MockWindowingPlatform(() => windowImpl.Object)); + + using (UnitTestApplication.Start(services)) + using(var lifetime = new ClassicDesktopStyleApplicationLifetime(Application.Current)) + { + var window = new Window(); + + window.Show(); + Assert.Equal(1, lifetime.Windows.Count); + windowImpl.Object.Closed(); + + Assert.Empty(lifetime.Windows); + } + } + } + +} diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/RangeBaseTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/RangeBaseTests.cs index 42578c61ac..d913e3e54f 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/RangeBaseTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/RangeBaseTests.cs @@ -8,6 +8,7 @@ using Avalonia.Controls.Templates; using Avalonia.Data; using Avalonia.Markup.Data; using Avalonia.Styling; +using Avalonia.UnitTests; using Xunit; namespace Avalonia.Controls.UnitTests.Primitives @@ -22,6 +23,7 @@ namespace Avalonia.Controls.UnitTests.Primitives Minimum = 100, Maximum = 50, }; + var root = new TestRoot(target); Assert.Equal(100, target.Minimum); Assert.Equal(100, target.Maximum); @@ -36,6 +38,7 @@ namespace Avalonia.Controls.UnitTests.Primitives Maximum = 50, Value = 100, }; + var root = new TestRoot(target); Assert.Equal(0, target.Minimum); Assert.Equal(50, target.Maximum); @@ -51,6 +54,7 @@ namespace Avalonia.Controls.UnitTests.Primitives Maximum = 100, Value = 50, }; + var root = new TestRoot(target); target.Minimum = 200; @@ -68,6 +72,7 @@ namespace Avalonia.Controls.UnitTests.Primitives Maximum = 100, Value = 100, }; + var root = new TestRoot(target); target.Maximum = 50; @@ -160,6 +165,38 @@ namespace Avalonia.Controls.UnitTests.Primitives Assert.Equal(expected, track.Value); } + [Fact] + public void Coercion_Should_Not_Be_Done_During_Initialization() + { + var target = new TestRange(); + + target.BeginInit(); + + var root = new TestRoot(target); + target.Minimum = 1; + Assert.Equal(0, target.Value); + + target.Value = 50; + target.EndInit(); + + Assert.Equal(50, target.Value); + } + + [Fact] + public void Coercion_Should_Be_Done_After_Initialization() + { + var target = new TestRange(); + + target.BeginInit(); + + var root = new TestRoot(target); + target.Minimum = 1; + + target.EndInit(); + + Assert.Equal(1, target.Value); + } + private class TestRange : RangeBase { } @@ -199,4 +236,4 @@ namespace Avalonia.Controls.UnitTests.Primitives } } } -} \ No newline at end of file +} diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs index 8e421bf0a2..75cababf54 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs @@ -536,6 +536,9 @@ namespace Avalonia.Controls.UnitTests.Primitives SelectedIndex = 1, }; + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + var called = false; target.SelectionChanged += (s, e) => @@ -545,8 +548,6 @@ namespace Avalonia.Controls.UnitTests.Primitives called = true; }; - target.ApplyTemplate(); - target.Presenter.ApplyTemplate(); target.SelectedIndex = -1; Assert.True(called); @@ -783,6 +784,116 @@ namespace Avalonia.Controls.UnitTests.Primitives Assert.Equal(2, vm.Child.SelectedIndex); } + [Fact] + public void Should_Select_Correct_Item_When_Duplicate_Items_Are_Present() + { + var target = new ListBox + { + Template = Template(), + Items = new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz"}, + }; + + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + _helper.Down((Interactive)target.Presenter.Panel.Children[3]); + + Assert.Equal(3, target.SelectedIndex); + } + + [Fact] + public void Should_Apply_Selected_Pseudoclass_To_Correct_Item_When_Duplicate_Items_Are_Present() + { + var target = new ListBox + { + Template = Template(), + Items = new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" }, + }; + + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + _helper.Down((Interactive)target.Presenter.Panel.Children[3]); + + Assert.Equal(new[] { ":selected" }, target.Presenter.Panel.Children[3].Classes); + } + + [Fact] + public void Adding_Item_Before_SelectedItem_Should_Update_SelectedIndex() + { + var items = new ObservableCollection + { + "Foo", + "Bar", + "Baz" + }; + + var target = new ListBox + { + Template = Template(), + Items = items, + SelectedIndex = 1, + }; + + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + + items.Insert(0, "Qux"); + + Assert.Equal(2, target.SelectedIndex); + Assert.Equal("Bar", target.SelectedItem); + } + + [Fact] + public void Removing_Item_Before_SelectedItem_Should_Update_SelectedIndex() + { + var items = new ObservableCollection + { + "Foo", + "Bar", + "Baz" + }; + + var target = new ListBox + { + Template = Template(), + Items = items, + SelectedIndex = 1, + }; + + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + + items.RemoveAt(0); + + Assert.Equal(0, target.SelectedIndex); + Assert.Equal("Bar", target.SelectedItem); + } + + [Fact] + public void Replacing_Selected_Item_Should_Update_SelectedItem() + { + var items = new ObservableCollection + { + "Foo", + "Bar", + "Baz" + }; + + var target = new ListBox + { + Template = Template(), + Items = items, + SelectedIndex = 1, + }; + + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + + items[1] = "Qux"; + + Assert.Equal(1, target.SelectedIndex); + Assert.Equal("Qux", target.SelectedItem); + } + private FuncControlTemplate Template() { return new FuncControlTemplate(control => diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs index 9ef7d567c8..a33d97779e 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs @@ -4,12 +4,15 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Linq; using Avalonia.Collections; using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; using Avalonia.Data; +using Avalonia.Input; +using Avalonia.Interactivity; using Avalonia.Markup.Data; using Xunit; @@ -17,6 +20,8 @@ namespace Avalonia.Controls.UnitTests.Primitives { public class SelectingItemsControlTests_Multiple { + private MouseTestHelper _helper = new MouseTestHelper(); + [Fact] public void Setting_SelectedIndex_Should_Add_To_SelectedItems() { @@ -258,31 +263,25 @@ namespace Avalonia.Controls.UnitTests.Primitives } [Fact] - public void Replacing_First_SelectedItem_Should_Update_SelectedItem_SelectedIndex() + public void Setting_SelectedIndex_Should_Unmark_Previously_Selected_Containers() { - var items = new[] - { - new ListBoxItem(), - new ListBoxItem(), - new ListBoxItem(), - }; - var target = new TestSelector { - Items = items, + Items = new[] { "foo", "bar", "baz" }, Template = Template(), }; target.ApplyTemplate(); target.Presenter.ApplyTemplate(); - target.SelectedIndex = 1; - target.SelectedItems[0] = items[2]; - Assert.Equal(2, target.SelectedIndex); - Assert.Equal(items[2], target.SelectedItem); - Assert.False(items[0].IsSelected); - Assert.False(items[1].IsSelected); - Assert.True(items[2].IsSelected); + target.SelectedItems.Add("foo"); + target.SelectedItems.Add("bar"); + + Assert.Equal(new[] { 0, 1 }, SelectedContainers(target)); + + target.SelectedIndex = 2; + + Assert.Equal(new[] { 2 }, SelectedContainers(target)); } [Fact] @@ -361,6 +360,52 @@ namespace Avalonia.Controls.UnitTests.Primitives Assert.Equal(new[] { "baz", "qux", "qiz" }, target.SelectedItems.Cast().ToList()); } + [Fact] + public void Setting_SelectedIndex_After_Range_Should_Unmark_Previously_Selected_Containers() + { + var target = new TestSelector + { + Items = new[] { "foo", "bar", "baz", "qux" }, + Template = Template(), + SelectedIndex = 0, + SelectionMode = SelectionMode.Multiple, + }; + + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + + target.SelectRange(2); + + Assert.Equal(new[] { 0, 1, 2 }, SelectedContainers(target)); + + target.SelectedIndex = 3; + + Assert.Equal(new[] { 3 }, SelectedContainers(target)); + } + + [Fact] + public void Toggling_Selection_After_Range_Should_Work() + { + var target = new TestSelector + { + Items = new[] { "foo", "bar", "baz", "foo", "bar", "baz" }, + Template = Template(), + SelectedIndex = 0, + SelectionMode = SelectionMode.Multiple, + }; + + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + + target.SelectRange(3); + + Assert.Equal(new[] { 0, 1, 2, 3 }, SelectedContainers(target)); + + target.Toggle(4); + + Assert.Equal(new[] { 0, 1, 2, 3, 4 }, SelectedContainers(target)); + } + [Fact] public void Suprious_SelectedIndex_Changes_Should_Not_Be_Triggered() { @@ -382,6 +427,40 @@ namespace Avalonia.Controls.UnitTests.Primitives Assert.Equal(new[] { -1, 1, 0 }, selectedIndexes); } + [Fact] + public void Can_Set_SelectedIndex_To_Another_Selected_Item() + { + var target = new TestSelector + { + Items = new[] { "foo", "bar", "baz" }, + Template = Template(), + }; + + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + target.SelectedItems.Add("foo"); + target.SelectedItems.Add("bar"); + + Assert.Equal(0, target.SelectedIndex); + Assert.Equal(new[] { "foo", "bar" }, target.SelectedItems); + Assert.Equal(new[] { 0, 1 }, SelectedContainers(target)); + + var raised = false; + target.SelectionChanged += (s, e) => + { + raised = true; + Assert.Empty(e.AddedItems); + Assert.Equal(new[] { "foo" }, e.RemovedItems); + }; + + target.SelectedIndex = 1; + + Assert.True(raised); + Assert.Equal(1, target.SelectedIndex); + Assert.Equal(new[] { "bar" }, target.SelectedItems); + Assert.Equal(new[] { 1 }, SelectedContainers(target)); + } + /// /// Tests a problem discovered with ListBox with selection. /// @@ -471,6 +550,7 @@ namespace Avalonia.Controls.UnitTests.Primitives { DataContext = items, Template = Template(), + Items = items, }; var called = false; @@ -540,35 +620,418 @@ namespace Avalonia.Controls.UnitTests.Primitives Assert.True(called); } + + [Fact] + public void Shift_Selecting_From_No_Selection_Selects_From_Start() + { + var target = new ListBox + { + Template = Template(), + Items = new[] { "Foo", "Bar", "Baz" }, + SelectionMode = SelectionMode.Multiple, + }; + + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + _helper.Click((Interactive)target.Presenter.Panel.Children[2], modifiers: InputModifiers.Shift); + + var panel = target.Presenter.Panel; + + Assert.Equal(new[] { "Foo", "Bar", "Baz" }, target.SelectedItems); + Assert.Equal(new[] { 0, 1, 2 }, SelectedContainers(target)); + } [Fact] - public void Replacing_SelectedItems_Should_Raise_SelectionChanged_With_CorrectItems() + public void Ctrl_Selecting_SelectedItem_With_Multiple_Selection_Active_Sets_SelectedItem_To_Next_Selection() { - var items = new[] { "foo", "bar", "baz" }; + var target = new ListBox + { + Template = Template(), + Items = new[] { "Foo", "Bar", "Baz", "Qux" }, + SelectionMode = SelectionMode.Multiple, + }; + + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + _helper.Click((Interactive)target.Presenter.Panel.Children[1]); + _helper.Click((Interactive)target.Presenter.Panel.Children[2], modifiers: InputModifiers.Control); + _helper.Click((Interactive)target.Presenter.Panel.Children[3], modifiers: InputModifiers.Control); + + Assert.Equal(1, target.SelectedIndex); + Assert.Equal("Bar", target.SelectedItem); + Assert.Equal(new[] { "Bar", "Baz", "Qux" }, target.SelectedItems); + + _helper.Click((Interactive)target.Presenter.Panel.Children[1], modifiers: InputModifiers.Control); + + Assert.Equal(2, target.SelectedIndex); + Assert.Equal("Baz", target.SelectedItem); + Assert.Equal(new[] { "Baz", "Qux" }, target.SelectedItems); + } + + [Fact] + public void Ctrl_Selecting_Non_SelectedItem_With_Multiple_Selection_Active_Leaves_SelectedItem_The_Same() + { + var target = new ListBox + { + Template = Template(), + Items = new[] { "Foo", "Bar", "Baz" }, + SelectionMode = SelectionMode.Multiple, + }; + + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + _helper.Click((Interactive)target.Presenter.Panel.Children[1]); + _helper.Click((Interactive)target.Presenter.Panel.Children[2], modifiers: InputModifiers.Control); + + Assert.Equal(1, target.SelectedIndex); + Assert.Equal("Bar", target.SelectedItem); + + _helper.Click((Interactive)target.Presenter.Panel.Children[2], modifiers: InputModifiers.Control); + + Assert.Equal(1, target.SelectedIndex); + Assert.Equal("Bar", target.SelectedItem); + } + + [Fact] + public void Should_Ctrl_Select_Correct_Item_When_Duplicate_Items_Are_Present() + { + var target = new ListBox + { + Template = Template(), + Items = new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" }, + SelectionMode = SelectionMode.Multiple, + }; + + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + _helper.Click((Interactive)target.Presenter.Panel.Children[3]); + _helper.Click((Interactive)target.Presenter.Panel.Children[4], modifiers: InputModifiers.Control); + + var panel = target.Presenter.Panel; + + Assert.Equal(new[] { "Foo", "Bar" }, target.SelectedItems); + Assert.Equal(new[] { 3, 4 }, SelectedContainers(target)); + } + + [Fact] + public void Should_Shift_Select_Correct_Item_When_Duplicates_Are_Present() + { + var target = new ListBox + { + Template = Template(), + Items = new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" }, + SelectionMode = SelectionMode.Multiple, + }; + + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + _helper.Click((Interactive)target.Presenter.Panel.Children[3]); + _helper.Click((Interactive)target.Presenter.Panel.Children[5], modifiers: InputModifiers.Shift); + + var panel = target.Presenter.Panel; + Assert.Equal(new[] { "Foo", "Bar", "Baz" }, target.SelectedItems); + Assert.Equal(new[] { 3, 4, 5 }, SelectedContainers(target)); + } + + [Fact] + public void Can_Shift_Select_All_Items_When_Duplicates_Are_Present() + { + var target = new ListBox + { + Template = Template(), + Items = new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" }, + SelectionMode = SelectionMode.Multiple, + }; + + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + _helper.Click((Interactive)target.Presenter.Panel.Children[0]); + _helper.Click((Interactive)target.Presenter.Panel.Children[5], modifiers: InputModifiers.Shift); + + var panel = target.Presenter.Panel; + + Assert.Equal(new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" }, target.SelectedItems); + Assert.Equal(new[] { 0, 1, 2, 3, 4, 5 }, SelectedContainers(target)); + } + + [Fact] + public void Duplicate_Items_Are_Added_To_SelectedItems_In_Order() + { + var target = new ListBox + { + Template = Template(), + Items = new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" }, + SelectionMode = SelectionMode.Multiple, + }; + + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + _helper.Click((Interactive)target.Presenter.Panel.Children[0]); + + Assert.Equal(new[] { "Foo" }, target.SelectedItems); + + _helper.Click((Interactive)target.Presenter.Panel.Children[4], modifiers: InputModifiers.Control); + + Assert.Equal(new[] { "Foo", "Bar" }, target.SelectedItems); + + _helper.Click((Interactive)target.Presenter.Panel.Children[3], modifiers: InputModifiers.Control); + + Assert.Equal(new[] { "Foo", "Bar", "Foo" }, target.SelectedItems); + + _helper.Click((Interactive)target.Presenter.Panel.Children[1], modifiers: InputModifiers.Control); + + Assert.Equal(new[] { "Foo", "Bar", "Foo", "Bar" }, target.SelectedItems); + } + + [Fact] + public void SelectAll_Sets_SelectedIndex_And_SelectedItem() + { + var target = new TestSelector + { + Template = Template(), + Items = new[] { "Foo", "Bar", "Baz" }, + SelectionMode = SelectionMode.Multiple, + }; + + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + + target.SelectAll(); + + Assert.Equal(0, target.SelectedIndex); + Assert.Equal("Foo", target.SelectedItem); + } + + [Fact] + public void UnselectAll_Clears_SelectedIndex_And_SelectedItem() + { + var target = new TestSelector + { + Template = Template(), + Items = new[] { "Foo", "Bar", "Baz" }, + SelectionMode = SelectionMode.Multiple, + SelectedIndex = 0, + }; + + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + + target.UnselectAll(); + + Assert.Equal(-1, target.SelectedIndex); + Assert.Equal(null, target.SelectedItem); + } + + [Fact] + public void SelectAll_Handles_Duplicate_Items() + { var target = new TestSelector { + Template = Template(), + Items = new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" }, + SelectionMode = SelectionMode.Multiple, + }; + + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + target.SelectAll(); + + Assert.Equal(new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" }, target.SelectedItems); + } + + [Fact] + public void Adding_Item_Before_SelectedItems_Should_Update_Selection() + { + var items = new ObservableCollection + { + "Foo", + "Bar", + "Baz" + }; + + var target = new ListBox + { + Template = Template(), Items = items, + SelectionMode = SelectionMode.Multiple, + }; + + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + + target.SelectAll(); + items.Insert(0, "Qux"); + + Assert.Equal(1, target.SelectedIndex); + Assert.Equal("Foo", target.SelectedItem); + Assert.Equal(new[] { "Foo", "Bar", "Baz" }, target.SelectedItems); + Assert.Equal(new[] { 1, 2, 3 }, SelectedContainers(target)); + } + + [Fact] + public void Removing_Item_Before_SelectedItem_Should_Update_Selection() + { + var items = new ObservableCollection + { + "Foo", + "Bar", + "Baz" + }; + + var target = new TestSelector + { Template = Template(), - SelectedItem = "bar", + Items = items, + SelectionMode = SelectionMode.Multiple, }; - var called = false; + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); - target.SelectionChanged += (s, e) => + target.SelectedIndex = 1; + target.SelectRange(2); + + Assert.Equal(new[] { "Bar", "Baz" }, target.SelectedItems); + + items.RemoveAt(0); + + Assert.Equal(0, target.SelectedIndex); + Assert.Equal("Bar", target.SelectedItem); + Assert.Equal(new[] { "Bar", "Baz" }, target.SelectedItems); + Assert.Equal(new[] { 0, 1 }, SelectedContainers(target)); + } + + [Fact] + public void Removing_SelectedItem_With_Multiple_Selection_Active_Should_Update_Selection() + { + var items = new ObservableCollection { - Assert.Equal(new[] { "foo",}, e.AddedItems.Cast()); - Assert.Equal(new[] { "bar" }, e.RemovedItems.Cast()); - called = true; + "Foo", + "Bar", + "Baz" + }; + + var target = new ListBox + { + Template = Template(), + Items = items, + SelectionMode = SelectionMode.Multiple, }; target.ApplyTemplate(); target.Presenter.ApplyTemplate(); - target.SelectedItems[0] = "foo"; - Assert.True(called); + target.SelectAll(); + items.RemoveAt(0); + + Assert.Equal(0, target.SelectedIndex); + Assert.Equal("Bar", target.SelectedItem); + Assert.Equal(new[] { "Bar", "Baz" }, target.SelectedItems); + Assert.Equal(new[] { 0, 1 }, SelectedContainers(target)); } + [Fact] + public void Replacing_Selected_Item_Should_Update_SelectedItems() + { + var items = new ObservableCollection + { + "Foo", + "Bar", + "Baz" + }; + + var target = new ListBox + { + Template = Template(), + Items = items, + SelectionMode = SelectionMode.Multiple, + }; + + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + + target.SelectAll(); + items[1] = "Qux"; + + Assert.Equal(new[] { "Foo", "Qux", "Baz" }, target.SelectedItems); + } + + [Fact] + public void Left_Click_On_SelectedItem_Should_Clear_Existing_Selection() + { + var target = new ListBox + { + Template = Template(), + Items = new[] { "Foo", "Bar", "Baz" }, + ItemTemplate = new FuncDataTemplate(x => new TextBlock { Width = 20, Height = 10 }), + SelectionMode = SelectionMode.Multiple, + }; + + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + target.SelectAll(); + + Assert.Equal(3, target.SelectedItems.Count); + + _helper.Click((Interactive)target.Presenter.Panel.Children[0]); + + Assert.Equal(1, target.SelectedItems.Count); + Assert.Equal(new[] { "Foo", }, target.SelectedItems); + Assert.Equal(new[] { 0 }, SelectedContainers(target)); + } + + [Fact] + public void Right_Click_On_SelectedItem_Should_Not_Clear_Existing_Selection() + { + var target = new ListBox + { + Template = Template(), + Items = new[] { "Foo", "Bar", "Baz" }, + ItemTemplate = new FuncDataTemplate(x => new TextBlock { Width = 20, Height = 10 }), + SelectionMode = SelectionMode.Multiple, + }; + + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + target.SelectAll(); + + Assert.Equal(3, target.SelectedItems.Count); + + _helper.Click((Interactive)target.Presenter.Panel.Children[0], MouseButton.Right); + + Assert.Equal(3, target.SelectedItems.Count); + } + + [Fact] + public void Right_Click_On_UnselectedItem_Should_Clear_Existing_Selection() + { + var target = new ListBox + { + Template = Template(), + Items = new[] { "Foo", "Bar", "Baz" }, + ItemTemplate = new FuncDataTemplate(x => new TextBlock { Width = 20, Height = 10 }), + SelectionMode = SelectionMode.Multiple, + }; + + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + _helper.Click((Interactive)target.Presenter.Panel.Children[0]); + _helper.Click((Interactive)target.Presenter.Panel.Children[1], modifiers: InputModifiers.Shift); + + Assert.Equal(2, target.SelectedItems.Count); + + _helper.Click((Interactive)target.Presenter.Panel.Children[2], MouseButton.Right); + + Assert.Equal(1, target.SelectedItems.Count); + } + + private IEnumerable SelectedContainers(SelectingItemsControl target) + { + return target.Presenter.Panel.Children + .Select((x, i) => x.Classes.Contains(":selected") ? i : -1) + .Where(x => x != -1); + } private FuncControlTemplate Template() { @@ -598,10 +1061,10 @@ namespace Avalonia.Controls.UnitTests.Primitives set { base.SelectionMode = value; } } - public void SelectRange(int index) - { - UpdateSelection(index, true, true); - } + public new void SelectAll() => base.SelectAll(); + public new void UnselectAll() => base.UnselectAll(); + public void SelectRange(int index) => UpdateSelection(index, true, true); + public void Toggle(int index) => UpdateSelection(index, true, false, true); } private class OldDataContextViewModel diff --git a/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs b/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs index aa99d31cff..f112289460 100644 --- a/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs @@ -207,19 +207,6 @@ namespace Avalonia.Controls.UnitTests } } - [Fact] - public void Exiting_Application_Notifies_Top_Level() - { - using (UnitTestApplication.Start(TestServices.StyledWindow)) - { - var impl = new Mock(); - impl.SetupAllProperties(); - var target = new TestTopLevel(impl.Object); - UnitTestApplication.Current.Shutdown(); - Assert.True(target.IsClosed); - } - } - [Fact] public void Adding_Resource_To_Application_Should_Raise_ResourcesChanged() { @@ -259,12 +246,6 @@ namespace Avalonia.Controls.UnitTests } protected override ILayoutManager CreateLayoutManager() => _layoutManager; - - protected override void HandleApplicationExiting() - { - base.HandleApplicationExiting(); - IsClosed = true; - } } } } diff --git a/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs b/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs index 6d00409ae0..12d29f2e5b 100644 --- a/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs +++ b/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs @@ -276,12 +276,6 @@ namespace Avalonia.Controls.UnitTests : base(impl) { } - - protected override void HandleApplicationExiting() - { - base.HandleApplicationExiting(); - IsClosed = true; - } } } } diff --git a/tests/Avalonia.Controls.UnitTests/WindowTests.cs b/tests/Avalonia.Controls.UnitTests/WindowTests.cs index ee416c4cb0..f4d9a91d0c 100644 --- a/tests/Avalonia.Controls.UnitTests/WindowTests.cs +++ b/tests/Avalonia.Controls.UnitTests/WindowTests.cs @@ -121,75 +121,6 @@ namespace Avalonia.Controls.UnitTests } } - [Fact] - public void Show_Should_Add_Window_To_OpenWindows() - { - using (UnitTestApplication.Start(TestServices.StyledWindow)) - { - ClearOpenWindows(); - var window = new Window(); - - window.Show(); - - Assert.Equal(new[] { window }, Application.Current.Windows); - } - } - - [Fact] - public void Window_Should_Be_Added_To_OpenWindows_Only_Once() - { - using (UnitTestApplication.Start(TestServices.StyledWindow)) - { - ClearOpenWindows(); - var window = new Window(); - - window.Show(); - window.Show(); - window.IsVisible = true; - - Assert.Equal(new[] { window }, Application.Current.Windows); - - window.Close(); - } - } - - [Fact] - public void Close_Should_Remove_Window_From_OpenWindows() - { - using (UnitTestApplication.Start(TestServices.StyledWindow)) - { - ClearOpenWindows(); - var window = new Window(); - - window.Show(); - window.Close(); - - Assert.Empty(Application.Current.Windows); - } - } - - [Fact] - public void Impl_Closing_Should_Remove_Window_From_OpenWindows() - { - var windowImpl = new Mock(); - windowImpl.SetupProperty(x => x.Closed); - windowImpl.Setup(x => x.Scaling).Returns(1); - - var services = TestServices.StyledWindow.With( - windowingPlatform: new MockWindowingPlatform(() => windowImpl.Object)); - - using (UnitTestApplication.Start(services)) - { - ClearOpenWindows(); - var window = new Window(); - - window.Show(); - windowImpl.Object.Closed(); - - Assert.Empty(Application.Current.Windows); - } - } - [Fact] public void Closing_Should_Only_Be_Invoked_Once() { @@ -420,12 +351,5 @@ namespace Avalonia.Controls.UnitTests x.Scaling == 1 && x.CreateRenderer(It.IsAny()) == renderer.Object); } - - private void ClearOpenWindows() - { - // HACK: We really need a decent way to have "statics" that can be scoped to - // AvaloniaLocator scopes. - Application.Current.Windows.Clear(); - } } } diff --git a/tests/Avalonia.Markup.UnitTests/Data/MultiBindingTests_Converters.cs b/tests/Avalonia.Markup.UnitTests/Data/MultiBindingTests_Converters.cs index bd4b5b9d04..d3e3ce5507 100644 --- a/tests/Avalonia.Markup.UnitTests/Data/MultiBindingTests_Converters.cs +++ b/tests/Avalonia.Markup.UnitTests/Data/MultiBindingTests_Converters.cs @@ -5,11 +5,10 @@ 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 Avalonia.Layout; using Xunit; namespace Avalonia.Markup.UnitTests.Data @@ -21,7 +20,30 @@ namespace Avalonia.Markup.UnitTests.Data { var textBlock = new TextBlock { - DataContext = new MultiBindingTests_Converters.Class1(), + DataContext = new Class1(), + }; + + var target = new MultiBinding + { + StringFormat = "{0:0.0} + {1:00}", + Bindings = + { + new Binding(nameof(Class1.Foo)), + new Binding(nameof(Class1.Bar)), + } + }; + + textBlock.Bind(TextBlock.TextProperty, target); + + Assert.Equal("1.0 + 02", textBlock.Text); + } + + [Fact] + public void StringFormat_Should_Be_Applied_After_Converter() + { + var textBlock = new TextBlock + { + DataContext = new Class1(), }; var target = new MultiBinding @@ -30,8 +52,8 @@ namespace Avalonia.Markup.UnitTests.Data Converter = new SumOfDoublesConverter(), Bindings = { - new Binding(nameof(MultiBindingTests_Converters.Class1.Foo)), - new Binding(nameof(MultiBindingTests_Converters.Class1.Bar)), + new Binding(nameof(Class1.Foo)), + new Binding(nameof(Class1.Bar)), } }; @@ -45,7 +67,7 @@ namespace Avalonia.Markup.UnitTests.Data { var textBlock = new TextBlock { - DataContext = new MultiBindingTests_Converters.Class1(), + DataContext = new Class1(), }; var target = new MultiBinding @@ -54,12 +76,12 @@ namespace Avalonia.Markup.UnitTests.Data Converter = new SumOfDoublesConverter(), Bindings = { - new Binding(nameof(MultiBindingTests_Converters.Class1.Foo)), - new Binding(nameof(MultiBindingTests_Converters.Class1.Bar)), + new Binding(nameof(Class1.Foo)), + new Binding(nameof(Class1.Bar)), } }; - textBlock.Bind(TextBlock.WidthProperty, target); + textBlock.Bind(Layoutable.WidthProperty, target); Assert.Equal(3.0, textBlock.Width); } diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs index 7fe0fc4a08..720ff3b0de 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs @@ -907,6 +907,25 @@ do we need it?")] } } + [Fact] + public void Slider_Properties_Can_Be_Set_In_Any_Order() + { + using (UnitTestApplication.Start(TestServices.MockWindowingPlatform)) + { + var xaml = @" + + +"; + + var window = AvaloniaXamlLoader.Parse(xaml); + var slider = (Slider)window.Content; + + Assert.Equal(0, slider.Minimum); + Assert.Equal(1000, slider.Maximum); + Assert.Equal(500, slider.Value); + } + } + private class SelectedItemsViewModel : INotifyPropertyChanged { public string[] Items { get; set; } diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BindingTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BindingTests.cs index 14abebcdb5..3930608515 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BindingTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BindingTests.cs @@ -331,6 +331,35 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml } } + [Fact(Skip="Issue #2592")] + public void MultiBinding_To_TextBlock_Text_With_StringConverter_Works() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + + + + + + + + +"; + var loader = new AvaloniaXamlLoader(); + var window = (Window)loader.Load(xaml); + var textBlock = window.FindControl("textBlock"); + + textBlock.DataContext = new WindowViewModel(); + window.ApplyTemplate(); + + Assert.Equal("Hello World!", textBlock.Text); + } + } + [Fact] public void Binding_OneWayToSource_Works() { @@ -356,6 +385,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml private class WindowViewModel { public bool ShowInTaskbar { get; set; } + public string Greeting1 { get; set; } = "Hello"; + public string Greeting2 { get; set; } = "World"; } } } diff --git a/tests/Avalonia.UnitTests/UnitTestApplication.cs b/tests/Avalonia.UnitTests/UnitTestApplication.cs index 3578471397..a516facb92 100644 --- a/tests/Avalonia.UnitTests/UnitTestApplication.cs +++ b/tests/Avalonia.UnitTests/UnitTestApplication.cs @@ -66,8 +66,7 @@ namespace Avalonia.UnitTests .Bind().ToConstant(Services.StandardCursorFactory) .Bind().ToConstant(Services.Styler) .Bind().ToConstant(Services.WindowingPlatform) - .Bind().ToSingleton() - .Bind().ToConstant(this); + .Bind().ToSingleton(); var styles = Services.Theme?.Invoke(); if (styles != null)