Browse Source

Merge branch 'master' into refactor/properties-overhaul

pull/1499/head
Steven Kirk 8 years ago
committed by GitHub
parent
commit
1da23aa0a3
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      readme.md
  2. 1
      samples/ControlCatalog.Desktop/Program.cs
  3. 2
      samples/ControlCatalog.NetCore/Program.cs
  4. 212
      samples/ControlCatalog/ControlCatalog.csproj
  5. 1
      samples/ControlCatalog/MainView.xaml
  6. 19
      samples/ControlCatalog/Pages/DragAndDropPage.xaml
  7. 71
      samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs
  8. 36
      samples/ControlCatalog/Properties/AssemblyInfo.cs
  9. 205
      src/Avalonia.Base/Utilities/StringTokenizer.cs
  10. 9
      src/Avalonia.Controls/Application.cs
  11. 7
      src/Avalonia.Controls/ContextMenu.cs
  12. 9
      src/Avalonia.Controls/GridLength.cs
  13. 210
      src/Avalonia.Controls/Platform/InProcessDragSource.cs
  14. 66
      src/Avalonia.Controls/Primitives/Popup.cs
  15. 26
      src/Avalonia.Controls/Primitives/PopupRoot.cs
  16. 4
      src/Avalonia.Controls/TextBlock.cs
  17. 7
      src/Avalonia.Controls/TextBox.cs
  18. 3
      src/Avalonia.Controls/Utils/UndoRedoHelper.cs
  19. 2
      src/Avalonia.Diagnostics/ViewModels/DevToolsViewModel.cs
  20. 5
      src/Avalonia.Input/Cursors.cs
  21. 15
      src/Avalonia.Input/DataFormats.cs
  22. 43
      src/Avalonia.Input/DataObject.cs
  23. 54
      src/Avalonia.Input/DragDrop.cs
  24. 111
      src/Avalonia.Input/DragDropDevice.cs
  25. 13
      src/Avalonia.Input/DragDropEffects.cs
  26. 18
      src/Avalonia.Input/DragEventArgs.cs
  27. 39
      src/Avalonia.Input/IDataObject.cs
  28. 14
      src/Avalonia.Input/Platform/IPlatformDragSource.cs
  29. 8
      src/Avalonia.Input/Raw/IDragDropDevice.cs
  30. 26
      src/Avalonia.Input/Raw/RawDragEvent.cs
  31. 10
      src/Avalonia.Input/Raw/RawDragEventType.cs
  32. 6
      src/Avalonia.Themes.Default/MenuItem.xaml
  33. 24
      src/Avalonia.Visuals/Matrix.cs
  34. 23
      src/Avalonia.Visuals/Media/Brush.cs
  35. 434
      src/Avalonia.Visuals/Media/Brushes.cs
  36. 27
      src/Avalonia.Visuals/Media/Color.cs
  37. 284
      src/Avalonia.Visuals/Media/Colors.cs
  38. 224
      src/Avalonia.Visuals/Media/KnownColors.cs
  39. 16
      src/Avalonia.Visuals/Point.cs
  40. 20
      src/Avalonia.Visuals/Rect.cs
  41. 26
      src/Avalonia.Visuals/RelativePoint.cs
  42. 55
      src/Avalonia.Visuals/RelativeRect.cs
  43. 4
      src/Avalonia.Visuals/Rendering/DefaultRenderLoop.cs
  44. 15
      src/Avalonia.Visuals/Size.cs
  45. 35
      src/Avalonia.Visuals/Thickness.cs
  46. 5
      src/Gtk/Avalonia.Gtk3/CursorFactory.cs
  47. 4
      src/OSX/Avalonia.MonoMac/Avalonia.MonoMac.csproj
  48. 4
      src/OSX/Avalonia.MonoMac/Cursor.cs
  49. 124
      src/OSX/Avalonia.MonoMac/DragSource.cs
  50. 90
      src/OSX/Avalonia.MonoMac/DraggingInfo.cs
  51. 3
      src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs
  52. 47
      src/OSX/Avalonia.MonoMac/TopLevelImpl.cs
  53. 11
      src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs
  54. 80
      src/Windows/Avalonia.Win32/ClipboardFormats.cs
  55. 27
      src/Windows/Avalonia.Win32/CursorFactory.cs
  56. 361
      src/Windows/Avalonia.Win32/DataObject.cs
  57. 27
      src/Windows/Avalonia.Win32/DragSource.cs
  58. 102
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  59. 47
      src/Windows/Avalonia.Win32/OleContext.cs
  60. 171
      src/Windows/Avalonia.Win32/OleDataObject.cs
  61. 39
      src/Windows/Avalonia.Win32/OleDragSource.cs
  62. 160
      src/Windows/Avalonia.Win32/OleDropTarget.cs
  63. 7
      src/Windows/Avalonia.Win32/ScreenImpl.cs
  64. 21
      src/Windows/Avalonia.Win32/Win32Platform.cs
  65. 9
      src/Windows/Avalonia.Win32/WindowImpl.cs
  66. 18
      tests/Avalonia.Markup.UnitTests/Data/BindingExpressionTests.cs
  67. 7
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs
  68. 18
      tests/Avalonia.Visuals.UnitTests/Media/BrushTests.cs
  69. 8
      tests/Avalonia.Visuals.UnitTests/RelativeRectTests.cs

2
readme.md

@ -35,7 +35,7 @@ https://ci.appveyor.com/project/AvaloniaUI/Avalonia/branch/master/artifacts
## Documentation
As mentioned above, Avalonia is still in alpha 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).
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).
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/.

1
samples/ControlCatalog.Desktop/Program.cs

@ -10,6 +10,7 @@ namespace ControlCatalog
{
internal class Program
{
[STAThread]
static void Main(string[] args)
{
// TODO: Make this work with GTK/Skia/Cairo depending on command-line args

2
samples/ControlCatalog.NetCore/Program.cs

@ -9,8 +9,10 @@ namespace ControlCatalog.NetCore
{
static class Program
{
static void Main(string[] args)
{
Thread.CurrentThread.TrySetApartmentState(ApartmentState.STA);
if (args.Contains("--wait-for-attach"))
{
Console.WriteLine("Attach debugger and use 'Set next statement'");

212
samples/ControlCatalog/ControlCatalog.csproj

@ -1,201 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<EnableDefaultCompileItems>False</EnableDefaultCompileItems>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<None Remove="Pages\ContextMenuPage.xaml" />
</ItemGroup>
<ItemGroup>
<!-- A reference to the entire .NET Framework is automatically included -->
<EmbeddedResource Include="App.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="MainView.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="DecoratedWindow.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Pages\ButtonSpinnerPage.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Pages\DialogsPage.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Pages\AutoCompleteBoxPage.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Pages\BorderPage.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Pages\ButtonPage.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Pages\CalendarPage.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Pages\CanvasPage.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Pages\CarouselPage.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Pages\CheckBoxPage.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Pages\ContextMenuPage.xaml" />
<EmbeddedResource Include="Pages\DropDownPage.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Pages\DatePickerPage.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Pages\ExpanderPage.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Pages\ImagePage.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Pages\LayoutTransformControlPage.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Pages\MenuPage.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Pages\NumericUpDownPage.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Pages\ProgressBarPage.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Pages\RadioButtonPage.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Pages\SliderPage.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Pages\TextBoxPage.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Pages\ToolTipPage.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
</Compile>
<Compile Include="MainView.xaml.cs">
<DependentUpon>MainView.xaml</DependentUpon>
</Compile>
<Compile Include="DecoratedWindow.xaml.cs">
<DependentUpon>DecoratedWindow.xaml</DependentUpon>
</Compile>
<Compile Include="MainWindow.xaml.cs">
<DependentUpon>MainWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\DialogsPage.xaml.cs">
<DependentUpon>DialogsPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\BorderPage.xaml.cs">
<DependentUpon>BorderPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\AutoCompleteBoxPage.xaml.cs">
<DependentUpon>AutoCompleteBoxPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\ButtonPage.xaml.cs">
<DependentUpon>ButtonPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\CalendarPage.xaml.cs">
<DependentUpon>CalendarPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\CanvasPage.xaml.cs">
<DependentUpon>CanvasPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\CarouselPage.xaml.cs">
<DependentUpon>CarouselPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\ContextMenuPage.xaml.cs">
<DependentUpon>ContextMenuPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\CheckBoxPage.xaml.cs">
<DependentUpon>CheckBoxPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\DropDownPage.xaml.cs">
<DependentUpon>DropDownPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\DatePickerPage.xaml.cs">
<DependentUpon>DatePickerPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\ExpanderPage.xaml.cs">
<DependentUpon>ExpanderPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\ImagePage.xaml.cs">
<DependentUpon>ImagePage.xaml</DependentUpon>
<Compile Update="**\*.xaml.cs">
<DependentUpon>%(Filename)</DependentUpon>
</Compile>
<Compile Include="Pages\LayoutTransformControlPage.xaml.cs">
<DependentUpon>LayoutTransformControlPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\MenuPage.xaml.cs">
<DependentUpon>MenuPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\ProgressBarPage.xaml.cs">
<DependentUpon>ProgressBarPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\RadioButtonPage.xaml.cs">
<DependentUpon>RadioButtonPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\SliderPage.xaml.cs">
<DependentUpon>SliderPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\TreeViewPage.xaml.cs">
<DependentUpon>TreeViewPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\TextBoxPage.xaml.cs">
<DependentUpon>TextBoxPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\ToolTipPage.xaml.cs">
<DependentUpon>ToolTipPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\ButtonSpinnerPage.xaml.cs">
<DependentUpon>ButtonSpinnerPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\NumericUpDownPage.xaml.cs">
<DependentUpon>NumericUpDownPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\ScreenPage.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Assets\delicate-arch-896885_640.jpg" />
<EmbeddedResource Include="Assets\github_icon.png" />
<EmbeddedResource Include="Assets\hirsch-899118_640.jpg" />
<EmbeddedResource Include="Assets\maple-leaf-888807_640.jpg" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="SideBar.xaml">
<EmbeddedResource Include="**\*.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Assets\*" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" />
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup\Avalonia.Markup.csproj" />
@ -212,20 +28,6 @@
<ProjectReference Include="..\..\src\Avalonia.Styling\Avalonia.Styling.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Assets\test_icon.ico" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Pages\TreeViewPage.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="MainWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>
</SubType>
</EmbeddedResource>
</ItemGroup>
<Import Project="..\..\build\Serilog.props" />
</Project>

1
samples/ControlCatalog/MainView.xaml

@ -15,6 +15,7 @@
<TabItem Header="CheckBox"><pages:CheckBoxPage/></TabItem>
<TabItem Header="ContextMenu"><pages:ContextMenuPage/></TabItem>
<TabItem Header="DatePicker"><pages:DatePickerPage/></TabItem>
<TabItem Header="Drag+Drop"><pages:DragAndDropPage/></TabItem>
<TabItem Header="DropDown"><pages:DropDownPage/></TabItem>
<TabItem Header="Expander"><pages:ExpanderPage/></TabItem>
<TabItem Header="Image"><pages:ImagePage/></TabItem>

19
samples/ControlCatalog/Pages/DragAndDropPage.xaml

@ -0,0 +1,19 @@
<UserControl xmlns="https://github.com/avaloniaui">
<StackPanel Orientation="Vertical" Gap="4">
<TextBlock Classes="h1">Drag+Drop</TextBlock>
<TextBlock Classes="h2">Example of Drag+Drop capabilities</TextBlock>
<StackPanel Orientation="Horizontal"
Margin="0,16,0,0"
HorizontalAlignment="Center"
Gap="16">
<Border BorderBrush="{DynamicResource ThemeAccentBrush}" BorderThickness="2" Padding="16" Name="DragMe">
<TextBlock Name="DragState">Drag Me</TextBlock>
</Border>
<Border Background="{DynamicResource ThemeAccentBrush2}" Padding="16"
DragDrop.AllowDrop="True">
<TextBlock Name="DropState">Drop some text or files here</TextBlock>
</Border>
</StackPanel>
</StackPanel>
</UserControl>

71
samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs

@ -0,0 +1,71 @@
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Markup.Xaml;
using System;
using System.Collections.Generic;
using System.Text;
namespace ControlCatalog.Pages
{
public class DragAndDropPage : UserControl
{
private TextBlock _DropState;
private TextBlock _DragState;
private Border _DragMe;
private int DragCount = 0;
public DragAndDropPage()
{
this.InitializeComponent();
_DragMe.PointerPressed += DoDrag;
AddHandler(DragDrop.DropEvent, Drop);
AddHandler(DragDrop.DragOverEvent, DragOver);
}
private async void DoDrag(object sender, Avalonia.Input.PointerPressedEventArgs e)
{
DataObject dragData = new DataObject();
dragData.Set(DataFormats.Text, $"You have dragged text {++DragCount} times");
var result = await DragDrop.DoDragDrop(dragData, DragDropEffects.Copy);
switch(result)
{
case DragDropEffects.Copy:
_DragState.Text = "The text was copied"; break;
case DragDropEffects.Link:
_DragState.Text = "The text was linked"; break;
case DragDropEffects.None:
_DragState.Text = "The drag operation was canceled"; break;
}
}
private void DragOver(object sender, DragEventArgs e)
{
// Only allow Copy or Link as Drop Operations.
e.DragEffects = e.DragEffects & (DragDropEffects.Copy | DragDropEffects.Link);
// Only allow if the dragged data contains text or filenames.
if (!e.Data.Contains(DataFormats.Text) && !e.Data.Contains(DataFormats.FileNames))
e.DragEffects = DragDropEffects.None;
}
private void Drop(object sender, DragEventArgs e)
{
if (e.Data.Contains(DataFormats.Text))
_DropState.Text = e.Data.GetText();
else if (e.Data.Contains(DataFormats.FileNames))
_DropState.Text = string.Join(Environment.NewLine, e.Data.GetFileNames());
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
_DropState = this.Find<TextBlock>("DropState");
_DragState = this.Find<TextBlock>("DragState");
_DragMe = this.Find<Border>("DragMe");
}
}
}

36
samples/ControlCatalog/Properties/AssemblyInfo.cs

@ -1,36 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// 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: AssemblyTitle("ControlCatalog")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ControlCatalog")]
[assembly: AssemblyCopyright("Copyright © 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("61bec86c-f307-4295-b5b8-9428610d7d55")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

205
src/Avalonia.Base/Utilities/StringTokenizer.cs

@ -0,0 +1,205 @@
using System;
using System.Globalization;
using static System.Char;
namespace Avalonia.Utilities
{
public struct StringTokenizer : IDisposable
{
private const char DefaultSeparatorChar = ',';
private readonly string _s;
private readonly int _length;
private readonly char _separator;
private readonly string _exceptionMessage;
private readonly IFormatProvider _formatProvider;
private int _index;
private int _tokenIndex;
private int _tokenLength;
public StringTokenizer(string s, IFormatProvider formatProvider, string exceptionMessage = null)
: this(s, GetSeparatorFromFormatProvider(formatProvider), exceptionMessage)
{
_formatProvider = formatProvider;
}
public StringTokenizer(string s, char separator = DefaultSeparatorChar, string exceptionMessage = null)
{
_s = s ?? throw new ArgumentNullException(nameof(s));
_length = s?.Length ?? 0;
_separator = separator;
_exceptionMessage = exceptionMessage;
_formatProvider = CultureInfo.InvariantCulture;
_index = 0;
_tokenIndex = -1;
_tokenLength = 0;
while (_index < _length && IsWhiteSpace(_s, _index))
{
_index++;
}
}
public string CurrentToken => _tokenIndex < 0 ? null : _s.Substring(_tokenIndex, _tokenLength);
public void Dispose()
{
if (_index != _length)
{
throw GetFormatException();
}
}
public bool TryReadInt32(out Int32 result, char? separator = null)
{
var success = TryReadString(out var stringResult, separator);
result = success ? int.Parse(stringResult, _formatProvider) : 0;
return success;
}
public int ReadInt32(char? separator = null)
{
if (!TryReadInt32(out var result, separator))
{
throw GetFormatException();
}
return result;
}
public bool TryReadDouble(out double result, char? separator = null)
{
var success = TryReadString(out var stringResult, separator);
result = success ? double.Parse(stringResult, _formatProvider) : 0;
return success;
}
public double ReadDouble(char? separator = null)
{
if (!TryReadDouble(out var result, separator))
{
throw GetFormatException();
}
return result;
}
public bool TryReadString(out string result, char? separator = null)
{
var success = TryReadToken(separator ?? _separator);
result = CurrentToken;
return success;
}
public string ReadString(char? separator = null)
{
if (!TryReadString(out var result, separator))
{
throw GetFormatException();
}
return result;
}
private bool TryReadToken(char separator)
{
_tokenIndex = -1;
if (_index >= _length)
{
return false;
}
var c = _s[_index];
var index = _index;
var length = 0;
while (_index < _length)
{
c = _s[_index];
if (IsWhiteSpace(c) || c == separator)
{
break;
}
_index++;
length++;
}
SkipToNextToken(separator);
_tokenIndex = index;
_tokenLength = length;
if (_tokenLength < 1)
{
throw GetFormatException();
}
return true;
}
private void SkipToNextToken(char separator)
{
if (_index < _length)
{
var c = _s[_index];
if (c != separator && !IsWhiteSpace(c))
{
throw GetFormatException();
}
var length = 0;
while (_index < _length)
{
c = _s[_index];
if (c == separator)
{
length++;
_index++;
if (length > 1)
{
throw GetFormatException();
}
}
else
{
if (!IsWhiteSpace(c))
{
break;
}
_index++;
}
}
if (length > 0 && _index >= _length)
{
throw GetFormatException();
}
}
}
private FormatException GetFormatException() =>
_exceptionMessage != null ? new FormatException(_exceptionMessage) : new FormatException();
private static char GetSeparatorFromFormatProvider(IFormatProvider provider)
{
var c = DefaultSeparatorChar;
var formatInfo = NumberFormatInfo.GetInstance(provider);
if (formatInfo.NumberDecimalSeparator.Length > 0 && c == formatInfo.NumberDecimalSeparator[0])
{
c = ';';
}
return c;
}
}
}

9
src/Avalonia.Controls/Application.cs

@ -2,16 +2,17 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Reactive.Concurrency;
using System.Threading;
using Avalonia.Controls;
using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Input.Raw;
using Avalonia.Layout;
using Avalonia.Rendering;
using Avalonia.Platform;
using Avalonia.Styling;
using Avalonia.Threading;
using System.Reactive.Concurrency;
namespace Avalonia
{
@ -234,7 +235,9 @@ namespace Avalonia
.Bind<IStyler>().ToConstant(_styler)
.Bind<ILayoutManager>().ToSingleton<LayoutManager>()
.Bind<IApplicationLifecycle>().ToConstant(this)
.Bind<IScheduler>().ToConstant(AvaloniaScheduler.Instance);
.Bind<IScheduler>().ToConstant(AvaloniaScheduler.Instance)
.Bind<IDragDropDevice>().ToConstant(DragDropDevice.Instance)
.Bind<IPlatformDragSource>().ToTransient<InProcessDragSource>();
}
}
}

7
src/Avalonia.Controls/ContextMenu.cs

@ -19,7 +19,7 @@ namespace Avalonia.Controls
{
ContextMenuProperty.Changed.Subscribe(ContextMenuChanged);
MenuItem.ClickEvent.AddClassHandler<ContextMenu>(x => x.OnContextMenuClick, handledEventsToo: true);
MenuItem.ClickEvent.AddClassHandler<ContextMenu>(x => x.OnContextMenuClick, handledEventsToo: true);
}
/// <summary>
@ -75,13 +75,14 @@ namespace Avalonia.Controls
{
if (control != null)
{
if(_popup == null)
if (_popup == null)
{
_popup = new Popup()
{
PlacementMode = PlacementMode.Pointer,
PlacementTarget = control,
StaysOpen = false
StaysOpen = false,
ObeyScreenEdges = true
};
_popup.Closed += PopupClosed;

9
src/Avalonia.Controls/GridLength.cs

@ -1,6 +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 Avalonia.Utilities;
using System;
using System.Collections.Generic;
using System.Globalization;
@ -210,7 +211,13 @@ namespace Avalonia.Controls
/// <returns>The <see cref="GridLength"/>.</returns>
public static IEnumerable<GridLength> ParseLengths(string s, CultureInfo culture)
{
return s.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries).Select(x => Parse(x, culture));
using (var tokenizer = new StringTokenizer(s, culture))
{
while (tokenizer.TryReadString(out var item))
{
yield return Parse(item, culture);
}
}
}
}
}

210
src/Avalonia.Controls/Platform/InProcessDragSource.cs

@ -0,0 +1,210 @@
using System;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Input.Raw;
using Avalonia.Threading;
using Avalonia.VisualTree;
namespace Avalonia.Platform
{
class InProcessDragSource : IPlatformDragSource
{
private const InputModifiers MOUSE_INPUTMODIFIERS = InputModifiers.LeftMouseButton|InputModifiers.MiddleMouseButton|InputModifiers.RightMouseButton;
private readonly IDragDropDevice _dragDrop;
private readonly IInputManager _inputManager;
private readonly Subject<DragDropEffects> _result = new Subject<DragDropEffects>();
private DragDropEffects _allowedEffects;
private IDataObject _draggedData;
private IInputElement _lastRoot;
private Point _lastPosition;
private StandardCursorType _lastCursorType;
private object _originalCursor;
private InputModifiers? _initialInputModifiers;
public InProcessDragSource()
{
_inputManager = AvaloniaLocator.Current.GetService<IInputManager>();
_dragDrop = AvaloniaLocator.Current.GetService<IDragDropDevice>();
}
public async Task<DragDropEffects> DoDragDrop(IDataObject data, DragDropEffects allowedEffects)
{
Dispatcher.UIThread.VerifyAccess();
if (_draggedData == null)
{
_draggedData = data;
_lastRoot = null;
_lastPosition = default(Point);
_allowedEffects = allowedEffects;
using (_inputManager.PreProcess.OfType<RawMouseEventArgs>().Subscribe(ProcessMouseEvents))
{
using (_inputManager.PreProcess.OfType<RawKeyEventArgs>().Subscribe(ProcessKeyEvents))
{
var effect = await _result.FirstAsync();
return effect;
}
}
}
return DragDropEffects.None;
}
private DragDropEffects RaiseEventAndUpdateCursor(RawDragEventType type, IInputElement root, Point pt, InputModifiers modifiers)
{
_lastPosition = pt;
RawDragEvent rawEvent = new RawDragEvent(_dragDrop, type, root, pt, _draggedData, _allowedEffects);
var tl = root.GetSelfAndVisualAncestors().OfType<TopLevel>().FirstOrDefault();
tl.PlatformImpl.Input(rawEvent);
var effect = GetPreferredEffect(rawEvent.Effects & _allowedEffects, modifiers);
UpdateCursor(root, effect);
return effect;
}
private DragDropEffects GetPreferredEffect(DragDropEffects effect, InputModifiers modifiers)
{
if (effect == DragDropEffects.Copy || effect == DragDropEffects.Move || effect == DragDropEffects.Link || effect == DragDropEffects.None)
return effect; // No need to check for the modifiers.
if (effect.HasFlag(DragDropEffects.Link) && modifiers.HasFlag(InputModifiers.Alt))
return DragDropEffects.Link;
if (effect.HasFlag(DragDropEffects.Copy) && modifiers.HasFlag(InputModifiers.Control))
return DragDropEffects.Copy;
return DragDropEffects.Move;
}
private StandardCursorType GetCursorForDropEffect(DragDropEffects effects)
{
if (effects.HasFlag(DragDropEffects.Copy))
return StandardCursorType.DragCopy;
if (effects.HasFlag(DragDropEffects.Move))
return StandardCursorType.DragMove;
if (effects.HasFlag(DragDropEffects.Link))
return StandardCursorType.DragLink;
return StandardCursorType.No;
}
private void UpdateCursor(IInputElement root, DragDropEffects effect)
{
if (_lastRoot != root)
{
if (_lastRoot is InputElement ieLast)
{
if (_originalCursor == AvaloniaProperty.UnsetValue)
ieLast.ClearValue(InputElement.CursorProperty);
else
ieLast.Cursor = _originalCursor as Cursor;
}
if (root is InputElement ieNew)
{
if (!ieNew.IsSet(InputElement.CursorProperty))
_originalCursor = AvaloniaProperty.UnsetValue;
else
_originalCursor = root.Cursor;
}
else
_originalCursor = null;
_lastCursorType = StandardCursorType.Arrow;
_lastRoot = root;
}
if (root is InputElement ie)
{
var ct = GetCursorForDropEffect(effect);
if (ct != _lastCursorType)
{
_lastCursorType = ct;
ie.Cursor = new Cursor(ct);
}
}
}
private void CancelDragging()
{
if (_lastRoot != null)
RaiseEventAndUpdateCursor(RawDragEventType.DragLeave, _lastRoot, _lastPosition, InputModifiers.None);
UpdateCursor(null, DragDropEffects.None);
_result.OnNext(DragDropEffects.None);
}
private void ProcessKeyEvents(RawKeyEventArgs e)
{
if (e.Type == RawKeyEventType.KeyDown && e.Key == Key.Escape)
{
if (_lastRoot != null)
RaiseEventAndUpdateCursor(RawDragEventType.DragLeave, _lastRoot, _lastPosition, e.Modifiers);
UpdateCursor(null, DragDropEffects.None);
_result.OnNext(DragDropEffects.None);
e.Handled = true;
}
else if (e.Key == Key.LeftCtrl || e.Key == Key.RightCtrl || e.Key == Key.LeftAlt || e.Key == Key.RightAlt)
RaiseEventAndUpdateCursor(RawDragEventType.DragOver, _lastRoot, _lastPosition, e.Modifiers);
}
private void ProcessMouseEvents(RawMouseEventArgs e)
{
if (!_initialInputModifiers.HasValue)
_initialInputModifiers = e.InputModifiers & MOUSE_INPUTMODIFIERS;
void CheckDraggingAccepted(InputModifiers changedMouseButton)
{
if (_initialInputModifiers.Value.HasFlag(changedMouseButton))
{
var result = RaiseEventAndUpdateCursor(RawDragEventType.Drop, e.Root, e.Position, e.InputModifiers);
UpdateCursor(null, DragDropEffects.None);
_result.OnNext(result);
}
else
CancelDragging();
e.Handled = true;
}
switch (e.Type)
{
case RawMouseEventType.LeftButtonDown:
case RawMouseEventType.RightButtonDown:
case RawMouseEventType.MiddleButtonDown:
case RawMouseEventType.NonClientLeftButtonDown:
CancelDragging();
e.Handled = true;
return;
case RawMouseEventType.LeaveWindow:
RaiseEventAndUpdateCursor(RawDragEventType.DragLeave, e.Root, e.Position, e.InputModifiers); break;
case RawMouseEventType.LeftButtonUp:
CheckDraggingAccepted(InputModifiers.LeftMouseButton); break;
case RawMouseEventType.MiddleButtonUp:
CheckDraggingAccepted(InputModifiers.MiddleMouseButton); break;
case RawMouseEventType.RightButtonUp:
CheckDraggingAccepted(InputModifiers.RightMouseButton); break;
case RawMouseEventType.Move:
var mods = e.InputModifiers & MOUSE_INPUTMODIFIERS;
if (_initialInputModifiers.Value != mods)
{
CancelDragging();
e.Handled = true;
return;
}
if (e.Root != _lastRoot)
{
if (_lastRoot != null)
RaiseEventAndUpdateCursor(RawDragEventType.DragLeave, _lastRoot, _lastRoot.PointToClient(e.Root.PointToScreen(e.Position)), e.InputModifiers);
RaiseEventAndUpdateCursor(RawDragEventType.DragEnter, e.Root, e.Position, e.InputModifiers);
}
else
RaiseEventAndUpdateCursor(RawDragEventType.DragOver, e.Root, e.Position, e.InputModifiers);
break;
}
}
}
}

66
src/Avalonia.Controls/Primitives/Popup.cs

@ -40,6 +40,12 @@ namespace Avalonia.Controls.Primitives
public static readonly StyledProperty<PlacementMode> PlacementModeProperty =
AvaloniaProperty.Register<Popup, PlacementMode>(nameof(PlacementMode), defaultValue: PlacementMode.Bottom);
/// <summary>
/// Defines the <see cref="ObeyScreenEdges"/> property.
/// </summary>
public static readonly StyledProperty<bool> ObeyScreenEdgesProperty =
AvaloniaProperty.Register<Popup, bool>(nameof(ObeyScreenEdges));
/// <summary>
/// Defines the <see cref="HorizontalOffset"/> property.
/// </summary>
@ -136,6 +142,16 @@ namespace Avalonia.Controls.Primitives
set { SetValue(PlacementModeProperty, value); }
}
/// <summary>
/// Gets or sets a value indicating whether the popup positions itself within the nearest screen boundary
/// when its opened at a position where it would otherwise overlap the screen edge.
/// </summary>
public bool ObeyScreenEdges
{
get => GetValue(ObeyScreenEdgesProperty);
set => SetValue(ObeyScreenEdgesProperty, value);
}
/// <summary>
/// Gets or sets the Horizontal offset of the popup in relation to the <see cref="PlacementTarget"/>
/// </summary>
@ -215,7 +231,17 @@ namespace Avalonia.Controls.Primitives
{
var window = _topLevel as Window;
if (window != null)
{
window.Deactivated += WindowDeactivated;
}
else
{
var parentPopuproot = _topLevel as PopupRoot;
if (parentPopuproot != null && parentPopuproot.Parent != null)
{
((Popup)(parentPopuproot.Parent)).Closed += ParentClosed;
}
}
_topLevel.AddHandler(PointerPressedEvent, PointerPressedOutside, RoutingStrategies.Tunnel);
_nonClientListener = InputManager.Instance.Process.Subscribe(ListenForNonClientClick);
}
@ -224,6 +250,11 @@ namespace Avalonia.Controls.Primitives
_popupRoot.Show();
if (ObeyScreenEdges)
{
_popupRoot.SnapInsideScreenEdges();
}
_ignoreIsOpenChanged = true;
IsOpen = true;
_ignoreIsOpenChanged = false;
@ -244,6 +275,14 @@ namespace Avalonia.Controls.Primitives
var window = _topLevel as Window;
if (window != null)
window.Deactivated -= WindowDeactivated;
else
{
var parentPopuproot = _topLevel as PopupRoot;
if (parentPopuproot != null && parentPopuproot.Parent != null)
{
((Popup)parentPopuproot.Parent).Closed -= ParentClosed;
}
}
_nonClientListener?.Dispose();
_nonClientListener = null;
}
@ -328,8 +367,10 @@ namespace Avalonia.Controls.Primitives
/// <returns>The popup's position in screen coordinates.</returns>
protected virtual Point GetPosition()
{
return GetPosition(PlacementTarget ?? this.GetVisualParent<Control>(), PlacementMode, PopupRoot,
var result = GetPosition(PlacementTarget ?? this.GetVisualParent<Control>(), PlacementMode, PopupRoot,
HorizontalOffset, VerticalOffset);
return result;
}
internal static Point GetPosition(Control target, PlacementMode placement, PopupRoot popupRoot, double horizontalOffset, double verticalOffset)
@ -381,9 +422,7 @@ namespace Avalonia.Controls.Primitives
{
if (!StaysOpen)
{
var root = ((IVisual)e.Source).GetVisualRoot();
if (root != this.PopupRoot)
if (!IsChildOrThis((IVisual)e.Source))
{
Close();
e.Handled = true;
@ -391,6 +430,17 @@ namespace Avalonia.Controls.Primitives
}
}
private bool IsChildOrThis(IVisual child)
{
IVisual root = child.GetVisualRoot();
while (root is PopupRoot)
{
if (root == PopupRoot) return true;
root = ((PopupRoot)root).Parent.GetVisualRoot();
}
return false;
}
private void WindowDeactivated(object sender, EventArgs e)
{
if (!StaysOpen)
@ -398,5 +448,13 @@ namespace Avalonia.Controls.Primitives
Close();
}
}
private void ParentClosed(object sender, EventArgs e)
{
if (!StaysOpen)
{
Close();
}
}
}
}

26
src/Avalonia.Controls/Primitives/PopupRoot.cs

@ -2,10 +2,12 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Linq;
using Avalonia.Controls.Platform;
using Avalonia.Controls.Presenters;
using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Styling;
@ -75,6 +77,30 @@ namespace Avalonia.Controls.Primitives
/// <inheritdoc/>
public void Dispose() => PlatformImpl?.Dispose();
/// <summary>
/// Moves the Popups position so that it doesnt overlap screen edges.
/// This method can be called immediately after Show has been called.
/// </summary>
public void SnapInsideScreenEdges()
{
var window = this.GetSelfAndLogicalAncestors().OfType<Window>().First();
var screen = window.Screens.ScreenFromPoint(Position);
var screenX = Position.X + Bounds.Width - screen.Bounds.X;
var screenY = Position.Y + Bounds.Height - screen.Bounds.Y;
if (screenX > screen.Bounds.Width)
{
Position = Position.WithX(Position.X - (screenX - screen.Bounds.Width));
}
if (screenY > screen.Bounds.Height)
{
Position = Position.WithY(Position.Y - (screenY - screen.Bounds.Height));
}
}
/// <inheritdoc/>
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
{

4
src/Avalonia.Controls/TextBlock.cs

@ -120,6 +120,7 @@ namespace Avalonia.Controls
.Subscribe(_ =>
{
InvalidateFormattedText();
InvalidateMeasure();
});
}
@ -370,8 +371,6 @@ namespace Avalonia.Controls
_constraint = _formattedText.Constraint;
_formattedText = null;
}
InvalidateMeasure();
}
/// <summary>
@ -402,6 +401,7 @@ namespace Avalonia.Controls
{
base.OnAttachedToLogicalTree(e);
InvalidateFormattedText();
InvalidateMeasure();
}
}
}

7
src/Avalonia.Controls/TextBox.cs

@ -275,8 +275,11 @@ namespace Avalonia.Controls
protected override void OnTextInput(TextInputEventArgs e)
{
HandleTextInput(e.Text);
e.Handled = true;
if (!e.Handled)
{
HandleTextInput(e.Text);
e.Handled = true;
}
}
private void HandleTextInput(string input)

3
src/Avalonia.Controls/Utils/UndoRedoHelper.cs

@ -59,7 +59,7 @@ namespace Avalonia.Controls.Utils
public void UpdateLastState()
{
_states.Last.Value = _host.UndoRedoState;
UpdateLastState(_host.UndoRedoState);
}
public void DiscardRedo()
@ -94,6 +94,7 @@ namespace Avalonia.Controls.Utils
public void Clear()
{
_states.Clear();
_currentNode = null;
}
bool WeakTimer.IWeakTimerSubscriber.Tick()

2
src/Avalonia.Diagnostics/ViewModels/DevToolsViewModel.cs

@ -86,7 +86,7 @@ namespace Avalonia.Diagnostics.ViewModels
private void UpdateFocusedControl()
{
_focusedControl = KeyboardDevice.Instance.FocusedElement?.GetType().Name;
FocusedControl = KeyboardDevice.Instance.FocusedElement?.GetType().Name;
}
}
}

5
src/Avalonia.Input/Cursors.cs

@ -38,7 +38,10 @@ namespace Avalonia.Input
TopLeftCorner,
TopRightCorner,
BottomLeftCorner,
BottomRightCorner
BottomRightCorner,
DragMove,
DragCopy,
DragLink,
// Not available in GTK directly, see http://www.pixelbeat.org/programming/x_cursors/
// We might enable them later, preferably, by loading pixmax direclty from theme with fallback image

15
src/Avalonia.Input/DataFormats.cs

@ -0,0 +1,15 @@
namespace Avalonia.Input
{
public static class DataFormats
{
/// <summary>
/// Dataformat for plaintext
/// </summary>
public static string Text = nameof(Text);
/// <summary>
/// Dataformat for one or more filenames
/// </summary>
public static string FileNames = nameof(FileNames);
}
}

43
src/Avalonia.Input/DataObject.cs

@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Avalonia.Input
{
public class DataObject : IDataObject
{
private readonly Dictionary<string, object> _items = new Dictionary<string, object>();
public bool Contains(string dataFormat)
{
return _items.ContainsKey(dataFormat);
}
public object Get(string dataFormat)
{
if (_items.ContainsKey(dataFormat))
return _items[dataFormat];
return null;
}
public IEnumerable<string> GetDataFormats()
{
return _items.Keys;
}
public IEnumerable<string> GetFileNames()
{
return Get(DataFormats.FileNames) as IEnumerable<string>;
}
public string GetText()
{
return Get(DataFormats.Text) as string;
}
public void Set(string dataFormat, object value)
{
_items[dataFormat] = value;
}
}
}

54
src/Avalonia.Input/DragDrop.cs

@ -0,0 +1,54 @@
using System.Threading.Tasks;
using Avalonia.Interactivity;
using Avalonia.Input.Platform;
namespace Avalonia.Input
{
public static class DragDrop
{
/// <summary>
/// Event which is raised, when a drag-and-drop operation enters the element.
/// </summary>
public static RoutedEvent<DragEventArgs> DragEnterEvent = RoutedEvent.Register<DragEventArgs>("DragEnter", RoutingStrategies.Bubble, typeof(DragDrop));
/// <summary>
/// Event which is raised, when a drag-and-drop operation leaves the element.
/// </summary>
public static RoutedEvent<RoutedEventArgs> DragLeaveEvent = RoutedEvent.Register<RoutedEventArgs>("DragLeave", RoutingStrategies.Bubble, typeof(DragDrop));
/// <summary>
/// Event which is raised, when a drag-and-drop operation is updated while over the element.
/// </summary>
public static RoutedEvent<DragEventArgs> DragOverEvent = RoutedEvent.Register<DragEventArgs>("DragOver", RoutingStrategies.Bubble, typeof(DragDrop));
/// <summary>
/// Event which is raised, when a drag-and-drop operation should complete over the element.
/// </summary>
public static RoutedEvent<DragEventArgs> DropEvent = RoutedEvent.Register<DragEventArgs>("Drop", RoutingStrategies.Bubble, typeof(DragDrop));
public static AvaloniaProperty<bool> AllowDropProperty = AvaloniaProperty.RegisterAttached<Interactive, bool>("AllowDrop", typeof(DragDrop), inherits: true);
/// <summary>
/// Gets a value indicating whether the given element can be used as the target of a drag-and-drop operation.
/// </summary>
public static bool GetAllowDrop(Interactive interactive)
{
return interactive.GetValue(AllowDropProperty);
}
/// <summary>
/// Sets a value indicating whether the given interactive can be used as the target of a drag-and-drop operation.
/// </summary>
public static void SetAllowDrop(Interactive interactive, bool value)
{
interactive.SetValue(AllowDropProperty, value);
}
/// <summary>
/// Starts a dragging operation with the given <see cref="IDataObject"/> and returns the applied drop effect from the target.
/// <seealso cref="DataObject"/>
/// </summary>
public static Task<DragDropEffects> DoDragDrop(IDataObject data, DragDropEffects allowedEffects)
{
var src = AvaloniaLocator.Current.GetService<IPlatformDragSource>();
return src?.DoDragDrop(data, allowedEffects) ?? Task.FromResult(DragDropEffects.None);
}
}
}

111
src/Avalonia.Input/DragDropDevice.cs

@ -0,0 +1,111 @@
using Avalonia.Interactivity;
using Avalonia.VisualTree;
using System.Linq;
using Avalonia.Input.Raw;
namespace Avalonia.Input
{
public class DragDropDevice : IDragDropDevice
{
public static readonly DragDropDevice Instance = new DragDropDevice();
private Interactive _lastTarget = null;
private Interactive GetTarget(IInputElement root, Point local)
{
var target = root.InputHitTest(local)?.GetSelfAndVisualAncestors()?.OfType<Interactive>()?.FirstOrDefault();
if (target != null && DragDrop.GetAllowDrop(target))
return target;
return null;
}
private DragDropEffects RaiseDragEvent(Interactive target, RoutedEvent<DragEventArgs> routedEvent, DragDropEffects operation, IDataObject data)
{
if (target == null)
return DragDropEffects.None;
var args = new DragEventArgs(routedEvent, data)
{
RoutedEvent = routedEvent,
DragEffects = operation
};
target.RaiseEvent(args);
return args.DragEffects;
}
private DragDropEffects DragEnter(IInputElement inputRoot, Point point, IDataObject data, DragDropEffects effects)
{
_lastTarget = GetTarget(inputRoot, point);
return RaiseDragEvent(_lastTarget, DragDrop.DragEnterEvent, effects, data);
}
private DragDropEffects DragOver(IInputElement inputRoot, Point point, IDataObject data, DragDropEffects effects)
{
var target = GetTarget(inputRoot, point);
if (target == _lastTarget)
return RaiseDragEvent(target, DragDrop.DragOverEvent, effects, data);
try
{
if (_lastTarget != null)
_lastTarget.RaiseEvent(new RoutedEventArgs(DragDrop.DragLeaveEvent));
return RaiseDragEvent(target, DragDrop.DragEnterEvent, effects, data);
}
finally
{
_lastTarget = target;
}
}
private void DragLeave(IInputElement inputRoot)
{
if (_lastTarget == null)
return;
try
{
_lastTarget.RaiseEvent(new RoutedEventArgs(DragDrop.DragLeaveEvent));
}
finally
{
_lastTarget = null;
}
}
private DragDropEffects Drop(IInputElement inputRoot, Point point, IDataObject data, DragDropEffects effects)
{
try
{
return RaiseDragEvent(_lastTarget, DragDrop.DropEvent, effects, data);
}
finally
{
_lastTarget = null;
}
}
public void ProcessRawEvent(RawInputEventArgs e)
{
if (!e.Handled && e is RawDragEvent margs)
ProcessRawEvent(margs);
}
private void ProcessRawEvent(RawDragEvent e)
{
switch (e.Type)
{
case RawDragEventType.DragEnter:
e.Effects = DragEnter(e.InputRoot, e.Location, e.Data, e.Effects);
break;
case RawDragEventType.DragOver:
e.Effects = DragOver(e.InputRoot, e.Location, e.Data, e.Effects);
break;
case RawDragEventType.DragLeave:
DragLeave(e.InputRoot);
break;
case RawDragEventType.Drop:
e.Effects = Drop(e.InputRoot, e.Location, e.Data, e.Effects);
break;
}
}
}
}

13
src/Avalonia.Input/DragDropEffects.cs

@ -0,0 +1,13 @@
using System;
namespace Avalonia.Input
{
[Flags]
public enum DragDropEffects
{
None = 0,
Copy = 1,
Move = 2,
Link = 4,
}
}

18
src/Avalonia.Input/DragEventArgs.cs

@ -0,0 +1,18 @@
using Avalonia.Interactivity;
namespace Avalonia.Input
{
public class DragEventArgs : RoutedEventArgs
{
public DragDropEffects DragEffects { get; set; }
public IDataObject Data { get; private set; }
public DragEventArgs(RoutedEvent<DragEventArgs> routedEvent, IDataObject data)
: base(routedEvent)
{
this.Data = data;
}
}
}

39
src/Avalonia.Input/IDataObject.cs

@ -0,0 +1,39 @@
using System.Collections.Generic;
namespace Avalonia.Input
{
/// <summary>
/// Interface to access information about the data of a drag-and-drop operation.
/// </summary>
public interface IDataObject
{
/// <summary>
/// Lists all formats which are present in the DataObject.
/// <seealso cref="DataFormats"/>
/// </summary>
IEnumerable<string> GetDataFormats();
/// <summary>
/// Checks wether a given DataFormat is present in this object
/// <seealso cref="DataFormats"/>
/// </summary>
bool Contains(string dataFormat);
/// <summary>
/// Returns the dragged text if the DataObject contains any text.
/// <seealso cref="DataFormats.Text"/>
/// </summary>
string GetText();
/// <summary>
/// Returns a list of filenames if the DataObject contains filenames.
/// <seealso cref="DataFormats.FileNames"/>
/// </summary>
IEnumerable<string> GetFileNames();
/// <summary>
/// Tries to get the data of the given DataFormat.
/// </summary>
object Get(string dataFormat);
}
}

14
src/Avalonia.Input/Platform/IPlatformDragSource.cs

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Input;
using Avalonia.Interactivity;
namespace Avalonia.Input.Platform
{
public interface IPlatformDragSource
{
Task<DragDropEffects> DoDragDrop(IDataObject data, DragDropEffects allowedEffects);
}
}

8
src/Avalonia.Input/Raw/IDragDropDevice.cs

@ -0,0 +1,8 @@
using Avalonia.Input;
namespace Avalonia.Input.Raw
{
public interface IDragDropDevice : IInputDevice
{
}
}

26
src/Avalonia.Input/Raw/RawDragEvent.cs

@ -0,0 +1,26 @@
using System;
using Avalonia.Input;
using Avalonia.Input.Raw;
namespace Avalonia.Input.Raw
{
public class RawDragEvent : RawInputEventArgs
{
public IInputElement InputRoot { get; }
public Point Location { get; }
public IDataObject Data { get; }
public DragDropEffects Effects { get; set; }
public RawDragEventType Type { get; }
public RawDragEvent(IDragDropDevice inputDevice, RawDragEventType type,
IInputElement inputRoot, Point location, IDataObject data, DragDropEffects effects)
:base(inputDevice, 0)
{
Type = type;
InputRoot = inputRoot;
Location = location;
Data = data;
Effects = effects;
}
}
}

10
src/Avalonia.Input/Raw/RawDragEventType.cs

@ -0,0 +1,10 @@
namespace Avalonia.Input.Raw
{
public enum RawDragEventType
{
DragEnter,
DragOver,
DragLeave,
Drop
}
}

6
src/Avalonia.Themes.Default/MenuItem.xaml

@ -45,7 +45,8 @@
<Popup Name="PART_Popup"
PlacementMode="Right"
StaysOpen="True"
IsOpen="{TemplateBinding Path=IsSubMenuOpen, Mode=TwoWay}">
IsOpen="{TemplateBinding Path=IsSubMenuOpen, Mode=TwoWay}"
ObeyScreenEdges="True">
<Border Background="{TemplateBinding Background}"
BorderBrush="{DynamicResource ThemeBorderMidBrush}"
BorderThickness="1">
@ -92,7 +93,8 @@
</ContentPresenter>
<Popup Name="PART_Popup"
IsOpen="{TemplateBinding Path=IsSubMenuOpen, Mode=TwoWay}"
StaysOpen="True">
StaysOpen="True"
ObeyScreenEdges="True">
<Border Background="{TemplateBinding Background}"
BorderBrush="{DynamicResource ThemeBorderMidBrush}"
BorderThickness="1">

24
src/Avalonia.Visuals/Matrix.cs

@ -1,6 +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 Avalonia.Utilities;
using System;
using System.Globalization;
using System.Linq;
@ -317,23 +318,16 @@ namespace Avalonia
/// <returns>The <see cref="Matrix"/>.</returns>
public static Matrix Parse(string s, CultureInfo culture)
{
var parts = s.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries)
.Select(x => x.Trim())
.ToArray();
if (parts.Length == 6)
using (var tokenizer = new StringTokenizer(s, culture, exceptionMessage: "Invalid Matrix"))
{
return new Matrix(
double.Parse(parts[0], culture),
double.Parse(parts[1], culture),
double.Parse(parts[2], culture),
double.Parse(parts[3], culture),
double.Parse(parts[4], culture),
double.Parse(parts[5], culture));
}
else
{
throw new FormatException("Invalid Matrix.");
tokenizer.ReadDouble(),
tokenizer.ReadDouble(),
tokenizer.ReadDouble(),
tokenizer.ReadDouble(),
tokenizer.ReadDouble(),
tokenizer.ReadDouble()
);
}
}
}

23
src/Avalonia.Visuals/Media/Brush.cs

@ -34,26 +34,21 @@ namespace Avalonia.Media
/// <returns>The <see cref="Color"/>.</returns>
public static IBrush Parse(string s)
{
Contract.Requires<ArgumentNullException>(s != null);
Contract.Requires<FormatException>(s.Length > 0);
if (s[0] == '#')
{
return new SolidColorBrush(Color.Parse(s));
}
else
{
var upper = s.ToUpperInvariant();
var member = typeof(Brushes).GetTypeInfo().DeclaredProperties
.FirstOrDefault(x => x.Name.ToUpperInvariant() == upper);
if (member != null)
{
var brush = (ISolidColorBrush)member.GetValue(null);
return new SolidColorBrush(brush.Color, brush.Opacity);
}
else
{
throw new FormatException($"Invalid brush string: '{s}'.");
}
var brush = KnownColors.GetKnownBrush(s);
if (brush != null)
{
return brush;
}
throw new FormatException($"Invalid brush string: '{s}'.");
}
}
}

434
src/Avalonia.Visuals/Media/Brushes.cs

File diff suppressed because it is too large

27
src/Avalonia.Visuals/Media/Color.cs

@ -88,6 +88,9 @@ namespace Avalonia.Media
/// <returns>The <see cref="Color"/>.</returns>
public static Color Parse(string s)
{
if (s == null) throw new ArgumentNullException(nameof(s));
if (s.Length == 0) throw new FormatException();
if (s[0] == '#')
{
var or = 0u;
@ -103,21 +106,15 @@ namespace Avalonia.Media
return FromUInt32(uint.Parse(s.Substring(1), NumberStyles.HexNumber, CultureInfo.InvariantCulture) | or);
}
else
{
var upper = s.ToUpperInvariant();
var member = typeof(Colors).GetTypeInfo().DeclaredProperties
.FirstOrDefault(x => x.Name.ToUpperInvariant() == upper);
if (member != null)
{
return (Color)member.GetValue(null);
}
else
{
throw new FormatException($"Invalid color string: '{s}'.");
}
var knownColor = KnownColors.GetKnownColor(s);
if (knownColor != KnownColor.None)
{
return knownColor.ToColor();
}
throw new FormatException($"Invalid color string: '{s}'.");
}
/// <summary>
@ -128,8 +125,8 @@ namespace Avalonia.Media
/// </returns>
public override string ToString()
{
uint rgb = ((uint)A << 24) | ((uint)R << 16) | ((uint)G << 8) | (uint)B;
return $"#{rgb:x8}";
uint rgb = ToUint32();
return KnownColors.GetKnownColorName(rgb) ?? $"#{rgb:x8}";
}
/// <summary>

284
src/Avalonia.Visuals/Media/Colors.cs

@ -1,6 +1,8 @@
// 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.Linq;
namespace Avalonia.Media
{
/// <summary>
@ -11,706 +13,706 @@ namespace Avalonia.Media
/// <summary>
/// Gets a color with an ARGB value of #fff0f8ff.
/// </summary>
public static Color AliceBlue => Color.FromUInt32(0xfff0f8ff);
public static Color AliceBlue => KnownColor.AliceBlue.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #fffaebd7.
/// </summary>
public static Color AntiqueWhite => Color.FromUInt32(0xfffaebd7);
public static Color AntiqueWhite => KnownColor.AntiqueWhite.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff00ffff.
/// </summary>
public static Color Aqua => Color.FromUInt32(0xff00ffff);
public static Color Aqua => KnownColor.Aqua.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff7fffd4.
/// </summary>
public static Color Aquamarine => Color.FromUInt32(0xff7fffd4);
public static Color Aquamarine => KnownColor.Aquamarine.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #fff0ffff.
/// </summary>
public static Color Azure => Color.FromUInt32(0xfff0ffff);
public static Color Azure => KnownColor.Azure.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #fff5f5dc.
/// </summary>
public static Color Beige => Color.FromUInt32(0xfff5f5dc);
public static Color Beige => KnownColor.Beige.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffffe4c4.
/// </summary>
public static Color Bisque => Color.FromUInt32(0xffffe4c4);
public static Color Bisque => KnownColor.Bisque.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff000000.
/// </summary>
public static Color Black => Color.FromUInt32(0xff000000);
public static Color Black => KnownColor.Black.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffffebcd.
/// </summary>
public static Color BlanchedAlmond => Color.FromUInt32(0xffffebcd);
public static Color BlanchedAlmond => KnownColor.BlanchedAlmond.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff0000ff.
/// </summary>
public static Color Blue => Color.FromUInt32(0xff0000ff);
public static Color Blue => KnownColor.Blue.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff8a2be2.
/// </summary>
public static Color BlueViolet => Color.FromUInt32(0xff8a2be2);
public static Color BlueViolet => KnownColor.BlueViolet.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffa52a2a.
/// </summary>
public static Color Brown => Color.FromUInt32(0xffa52a2a);
public static Color Brown => KnownColor.Brown.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffdeb887.
/// </summary>
public static Color BurlyWood => Color.FromUInt32(0xffdeb887);
public static Color BurlyWood => KnownColor.BurlyWood.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff5f9ea0.
/// </summary>
public static Color CadetBlue => Color.FromUInt32(0xff5f9ea0);
public static Color CadetBlue => KnownColor.CadetBlue.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff7fff00.
/// </summary>
public static Color Chartreuse => Color.FromUInt32(0xff7fff00);
public static Color Chartreuse => KnownColor.Chartreuse.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffd2691e.
/// </summary>
public static Color Chocolate => Color.FromUInt32(0xffd2691e);
public static Color Chocolate => KnownColor.Chocolate.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffff7f50.
/// </summary>
public static Color Coral => Color.FromUInt32(0xffff7f50);
public static Color Coral => KnownColor.Coral.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff6495ed.
/// </summary>
public static Color CornflowerBlue => Color.FromUInt32(0xff6495ed);
public static Color CornflowerBlue => KnownColor.CornflowerBlue.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #fffff8dc.
/// </summary>
public static Color Cornsilk => Color.FromUInt32(0xfffff8dc);
public static Color Cornsilk => KnownColor.Cornsilk.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffdc143c.
/// </summary>
public static Color Crimson => Color.FromUInt32(0xffdc143c);
public static Color Crimson => KnownColor.Crimson.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff00ffff.
/// </summary>
public static Color Cyan => Color.FromUInt32(0xff00ffff);
public static Color Cyan => KnownColor.Cyan.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff00008b.
/// </summary>
public static Color DarkBlue => Color.FromUInt32(0xff00008b);
public static Color DarkBlue => KnownColor.DarkBlue.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff008b8b.
/// </summary>
public static Color DarkCyan => Color.FromUInt32(0xff008b8b);
public static Color DarkCyan => KnownColor.DarkCyan.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffb8860b.
/// </summary>
public static Color DarkGoldenrod => Color.FromUInt32(0xffb8860b);
public static Color DarkGoldenrod => KnownColor.DarkGoldenrod.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffa9a9a9.
/// </summary>
public static Color DarkGray => Color.FromUInt32(0xffa9a9a9);
public static Color DarkGray => KnownColor.DarkGray.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff006400.
/// </summary>
public static Color DarkGreen => Color.FromUInt32(0xff006400);
public static Color DarkGreen => KnownColor.DarkGreen.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffbdb76b.
/// </summary>
public static Color DarkKhaki => Color.FromUInt32(0xffbdb76b);
public static Color DarkKhaki => KnownColor.DarkKhaki.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff8b008b.
/// </summary>
public static Color DarkMagenta => Color.FromUInt32(0xff8b008b);
public static Color DarkMagenta => KnownColor.DarkMagenta.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff556b2f.
/// </summary>
public static Color DarkOliveGreen => Color.FromUInt32(0xff556b2f);
public static Color DarkOliveGreen => KnownColor.DarkOliveGreen.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffff8c00.
/// </summary>
public static Color DarkOrange => Color.FromUInt32(0xffff8c00);
public static Color DarkOrange => KnownColor.DarkOrange.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff9932cc.
/// </summary>
public static Color DarkOrchid => Color.FromUInt32(0xff9932cc);
public static Color DarkOrchid => KnownColor.DarkOrchid.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff8b0000.
/// </summary>
public static Color DarkRed => Color.FromUInt32(0xff8b0000);
public static Color DarkRed => KnownColor.DarkRed.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffe9967a.
/// </summary>
public static Color DarkSalmon => Color.FromUInt32(0xffe9967a);
public static Color DarkSalmon => KnownColor.DarkSalmon.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff8fbc8f.
/// </summary>
public static Color DarkSeaGreen => Color.FromUInt32(0xff8fbc8f);
public static Color DarkSeaGreen => KnownColor.DarkSeaGreen.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff483d8b.
/// </summary>
public static Color DarkSlateBlue => Color.FromUInt32(0xff483d8b);
public static Color DarkSlateBlue => KnownColor.DarkSlateBlue.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff2f4f4f.
/// </summary>
public static Color DarkSlateGray => Color.FromUInt32(0xff2f4f4f);
public static Color DarkSlateGray => KnownColor.DarkSlateGray.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff00ced1.
/// </summary>
public static Color DarkTurquoise => Color.FromUInt32(0xff00ced1);
public static Color DarkTurquoise => KnownColor.DarkTurquoise.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff9400d3.
/// </summary>
public static Color DarkViolet => Color.FromUInt32(0xff9400d3);
public static Color DarkViolet => KnownColor.DarkViolet.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffff1493.
/// </summary>
public static Color DeepPink => Color.FromUInt32(0xffff1493);
public static Color DeepPink => KnownColor.DeepPink.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff00bfff.
/// </summary>
public static Color DeepSkyBlue => Color.FromUInt32(0xff00bfff);
public static Color DeepSkyBlue => KnownColor.DeepSkyBlue.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff696969.
/// </summary>
public static Color DimGray => Color.FromUInt32(0xff696969);
public static Color DimGray => KnownColor.DimGray.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff1e90ff.
/// </summary>
public static Color DodgerBlue => Color.FromUInt32(0xff1e90ff);
public static Color DodgerBlue => KnownColor.DodgerBlue.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffb22222.
/// </summary>
public static Color Firebrick => Color.FromUInt32(0xffb22222);
public static Color Firebrick => KnownColor.Firebrick.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #fffffaf0.
/// </summary>
public static Color FloralWhite => Color.FromUInt32(0xfffffaf0);
public static Color FloralWhite => KnownColor.FloralWhite.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff228b22.
/// </summary>
public static Color ForestGreen => Color.FromUInt32(0xff228b22);
public static Color ForestGreen => KnownColor.ForestGreen.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffff00ff.
/// </summary>
public static Color Fuchsia => Color.FromUInt32(0xffff00ff);
public static Color Fuchsia => KnownColor.Fuchsia.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffdcdcdc.
/// </summary>
public static Color Gainsboro => Color.FromUInt32(0xffdcdcdc);
public static Color Gainsboro => KnownColor.Gainsboro.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #fff8f8ff.
/// </summary>
public static Color GhostWhite => Color.FromUInt32(0xfff8f8ff);
public static Color GhostWhite => KnownColor.GhostWhite.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffffd700.
/// </summary>
public static Color Gold => Color.FromUInt32(0xffffd700);
public static Color Gold => KnownColor.Gold.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffdaa520.
/// </summary>
public static Color Goldenrod => Color.FromUInt32(0xffdaa520);
public static Color Goldenrod => KnownColor.Goldenrod.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff808080.
/// </summary>
public static Color Gray => Color.FromUInt32(0xff808080);
public static Color Gray => KnownColor.Gray.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff008000.
/// </summary>
public static Color Green => Color.FromUInt32(0xff008000);
public static Color Green => KnownColor.Green.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffadff2f.
/// </summary>
public static Color GreenYellow => Color.FromUInt32(0xffadff2f);
public static Color GreenYellow => KnownColor.GreenYellow.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #fff0fff0.
/// </summary>
public static Color Honeydew => Color.FromUInt32(0xfff0fff0);
public static Color Honeydew => KnownColor.Honeydew.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffff69b4.
/// </summary>
public static Color HotPink => Color.FromUInt32(0xffff69b4);
public static Color HotPink => KnownColor.HotPink.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffcd5c5c.
/// </summary>
public static Color IndianRed => Color.FromUInt32(0xffcd5c5c);
public static Color IndianRed => KnownColor.IndianRed.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff4b0082.
/// </summary>
public static Color Indigo => Color.FromUInt32(0xff4b0082);
public static Color Indigo => KnownColor.Indigo.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #fffffff0.
/// </summary>
public static Color Ivory => Color.FromUInt32(0xfffffff0);
public static Color Ivory => KnownColor.Ivory.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #fff0e68c.
/// </summary>
public static Color Khaki => Color.FromUInt32(0xfff0e68c);
public static Color Khaki => KnownColor.Khaki.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffe6e6fa.
/// </summary>
public static Color Lavender => Color.FromUInt32(0xffe6e6fa);
public static Color Lavender => KnownColor.Lavender.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #fffff0f5.
/// </summary>
public static Color LavenderBlush => Color.FromUInt32(0xfffff0f5);
public static Color LavenderBlush => KnownColor.LavenderBlush.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff7cfc00.
/// </summary>
public static Color LawnGreen => Color.FromUInt32(0xff7cfc00);
public static Color LawnGreen => KnownColor.LawnGreen.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #fffffacd.
/// </summary>
public static Color LemonChiffon => Color.FromUInt32(0xfffffacd);
public static Color LemonChiffon => KnownColor.LemonChiffon.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffadd8e6.
/// </summary>
public static Color LightBlue => Color.FromUInt32(0xffadd8e6);
public static Color LightBlue => KnownColor.LightBlue.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #fff08080.
/// </summary>
public static Color LightCoral => Color.FromUInt32(0xfff08080);
public static Color LightCoral => KnownColor.LightCoral.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffe0ffff.
/// </summary>
public static Color LightCyan => Color.FromUInt32(0xffe0ffff);
public static Color LightCyan => KnownColor.LightCyan.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #fffafad2.
/// </summary>
public static Color LightGoldenrodYellow => Color.FromUInt32(0xfffafad2);
public static Color LightGoldenrodYellow => KnownColor.LightGoldenrodYellow.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffd3d3d3.
/// </summary>
public static Color LightGray => Color.FromUInt32(0xffd3d3d3);
public static Color LightGray => KnownColor.LightGray.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff90ee90.
/// </summary>
public static Color LightGreen => Color.FromUInt32(0xff90ee90);
public static Color LightGreen => KnownColor.LightGreen.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffffb6c1.
/// </summary>
public static Color LightPink => Color.FromUInt32(0xffffb6c1);
public static Color LightPink => KnownColor.LightPink.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffffa07a.
/// </summary>
public static Color LightSalmon => Color.FromUInt32(0xffffa07a);
public static Color LightSalmon => KnownColor.LightSalmon.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff20b2aa.
/// </summary>
public static Color LightSeaGreen => Color.FromUInt32(0xff20b2aa);
public static Color LightSeaGreen => KnownColor.LightSeaGreen.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff87cefa.
/// </summary>
public static Color LightSkyBlue => Color.FromUInt32(0xff87cefa);
public static Color LightSkyBlue => KnownColor.LightSkyBlue.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff778899.
/// </summary>
public static Color LightSlateGray => Color.FromUInt32(0xff778899);
public static Color LightSlateGray => KnownColor.LightSlateGray.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffb0c4de.
/// </summary>
public static Color LightSteelBlue => Color.FromUInt32(0xffb0c4de);
public static Color LightSteelBlue => KnownColor.LightSteelBlue.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffffffe0.
/// </summary>
public static Color LightYellow => Color.FromUInt32(0xffffffe0);
public static Color LightYellow => KnownColor.LightYellow.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff00ff00.
/// </summary>
public static Color Lime => Color.FromUInt32(0xff00ff00);
public static Color Lime => KnownColor.Lime.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff32cd32.
/// </summary>
public static Color LimeGreen => Color.FromUInt32(0xff32cd32);
public static Color LimeGreen => KnownColor.LimeGreen.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #fffaf0e6.
/// </summary>
public static Color Linen => Color.FromUInt32(0xfffaf0e6);
public static Color Linen => KnownColor.Linen.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffff00ff.
/// </summary>
public static Color Magenta => Color.FromUInt32(0xffff00ff);
public static Color Magenta => KnownColor.Magenta.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff800000.
/// </summary>
public static Color Maroon => Color.FromUInt32(0xff800000);
public static Color Maroon => KnownColor.Maroon.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff66cdaa.
/// </summary>
public static Color MediumAquamarine => Color.FromUInt32(0xff66cdaa);
public static Color MediumAquamarine => KnownColor.MediumAquamarine.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff0000cd.
/// </summary>
public static Color MediumBlue => Color.FromUInt32(0xff0000cd);
public static Color MediumBlue => KnownColor.MediumBlue.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffba55d3.
/// </summary>
public static Color MediumOrchid => Color.FromUInt32(0xffba55d3);
public static Color MediumOrchid => KnownColor.MediumOrchid.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff9370db.
/// </summary>
public static Color MediumPurple => Color.FromUInt32(0xff9370db);
public static Color MediumPurple => KnownColor.MediumPurple.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff3cb371.
/// </summary>
public static Color MediumSeaGreen => Color.FromUInt32(0xff3cb371);
public static Color MediumSeaGreen => KnownColor.MediumSeaGreen.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff7b68ee.
/// </summary>
public static Color MediumSlateBlue => Color.FromUInt32(0xff7b68ee);
public static Color MediumSlateBlue => KnownColor.MediumSlateBlue.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff00fa9a.
/// </summary>
public static Color MediumSpringGreen => Color.FromUInt32(0xff00fa9a);
public static Color MediumSpringGreen => KnownColor.MediumSpringGreen.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff48d1cc.
/// </summary>
public static Color MediumTurquoise => Color.FromUInt32(0xff48d1cc);
public static Color MediumTurquoise => KnownColor.MediumTurquoise.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffc71585.
/// </summary>
public static Color MediumVioletRed => Color.FromUInt32(0xffc71585);
public static Color MediumVioletRed => KnownColor.MediumVioletRed.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff191970.
/// </summary>
public static Color MidnightBlue => Color.FromUInt32(0xff191970);
public static Color MidnightBlue => KnownColor.MidnightBlue.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #fff5fffa.
/// </summary>
public static Color MintCream => Color.FromUInt32(0xfff5fffa);
public static Color MintCream => KnownColor.MintCream.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffffe4e1.
/// </summary>
public static Color MistyRose => Color.FromUInt32(0xffffe4e1);
public static Color MistyRose => KnownColor.MistyRose.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffffe4b5.
/// </summary>
public static Color Moccasin => Color.FromUInt32(0xffffe4b5);
public static Color Moccasin => KnownColor.Moccasin.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffffdead.
/// </summary>
public static Color NavajoWhite => Color.FromUInt32(0xffffdead);
public static Color NavajoWhite => KnownColor.NavajoWhite.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff000080.
/// </summary>
public static Color Navy => Color.FromUInt32(0xff000080);
public static Color Navy => KnownColor.Navy.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #fffdf5e6.
/// </summary>
public static Color OldLace => Color.FromUInt32(0xfffdf5e6);
public static Color OldLace => KnownColor.OldLace.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff808000.
/// </summary>
public static Color Olive => Color.FromUInt32(0xff808000);
public static Color Olive => KnownColor.Olive.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff6b8e23.
/// </summary>
public static Color OliveDrab => Color.FromUInt32(0xff6b8e23);
public static Color OliveDrab => KnownColor.OliveDrab.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffffa500.
/// </summary>
public static Color Orange => Color.FromUInt32(0xffffa500);
public static Color Orange => KnownColor.Orange.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffff4500.
/// </summary>
public static Color OrangeRed => Color.FromUInt32(0xffff4500);
public static Color OrangeRed => KnownColor.OrangeRed.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffda70d6.
/// </summary>
public static Color Orchid => Color.FromUInt32(0xffda70d6);
public static Color Orchid => KnownColor.Orchid.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffeee8aa.
/// </summary>
public static Color PaleGoldenrod => Color.FromUInt32(0xffeee8aa);
public static Color PaleGoldenrod => KnownColor.PaleGoldenrod.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff98fb98.
/// </summary>
public static Color PaleGreen => Color.FromUInt32(0xff98fb98);
public static Color PaleGreen => KnownColor.PaleGreen.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffafeeee.
/// </summary>
public static Color PaleTurquoise => Color.FromUInt32(0xffafeeee);
public static Color PaleTurquoise => KnownColor.PaleTurquoise.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffdb7093.
/// </summary>
public static Color PaleVioletRed => Color.FromUInt32(0xffdb7093);
public static Color PaleVioletRed => KnownColor.PaleVioletRed.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffffefd5.
/// </summary>
public static Color PapayaWhip => Color.FromUInt32(0xffffefd5);
public static Color PapayaWhip => KnownColor.PapayaWhip.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffffdab9.
/// </summary>
public static Color PeachPuff => Color.FromUInt32(0xffffdab9);
public static Color PeachPuff => KnownColor.PeachPuff.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffcd853f.
/// </summary>
public static Color Peru => Color.FromUInt32(0xffcd853f);
public static Color Peru => KnownColor.Peru.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffffc0cb.
/// </summary>
public static Color Pink => Color.FromUInt32(0xffffc0cb);
public static Color Pink => KnownColor.Pink.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffdda0dd.
/// </summary>
public static Color Plum => Color.FromUInt32(0xffdda0dd);
public static Color Plum => KnownColor.Plum.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffb0e0e6.
/// </summary>
public static Color PowderBlue => Color.FromUInt32(0xffb0e0e6);
public static Color PowderBlue => KnownColor.PowderBlue.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff800080.
/// </summary>
public static Color Purple => Color.FromUInt32(0xff800080);
public static Color Purple => KnownColor.Purple.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffff0000.
/// </summary>
public static Color Red => Color.FromUInt32(0xffff0000);
public static Color Red => KnownColor.Red.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffbc8f8f.
/// </summary>
public static Color RosyBrown => Color.FromUInt32(0xffbc8f8f);
public static Color RosyBrown => KnownColor.RosyBrown.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff4169e1.
/// </summary>
public static Color RoyalBlue => Color.FromUInt32(0xff4169e1);
public static Color RoyalBlue => KnownColor.RoyalBlue.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff8b4513.
/// </summary>
public static Color SaddleBrown => Color.FromUInt32(0xff8b4513);
public static Color SaddleBrown => KnownColor.SaddleBrown.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #fffa8072.
/// </summary>
public static Color Salmon => Color.FromUInt32(0xfffa8072);
public static Color Salmon => KnownColor.Salmon.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #fff4a460.
/// </summary>
public static Color SandyBrown => Color.FromUInt32(0xfff4a460);
public static Color SandyBrown => KnownColor.SandyBrown.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff2e8b57.
/// </summary>
public static Color SeaGreen => Color.FromUInt32(0xff2e8b57);
public static Color SeaGreen => KnownColor.SeaGreen.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #fffff5ee.
/// </summary>
public static Color SeaShell => Color.FromUInt32(0xfffff5ee);
public static Color SeaShell => KnownColor.SeaShell.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffa0522d.
/// </summary>
public static Color Sienna => Color.FromUInt32(0xffa0522d);
public static Color Sienna => KnownColor.Sienna.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffc0c0c0.
/// </summary>
public static Color Silver => Color.FromUInt32(0xffc0c0c0);
public static Color Silver => KnownColor.Silver.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff87ceeb.
/// </summary>
public static Color SkyBlue => Color.FromUInt32(0xff87ceeb);
public static Color SkyBlue => KnownColor.SkyBlue.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff6a5acd.
/// </summary>
public static Color SlateBlue => Color.FromUInt32(0xff6a5acd);
public static Color SlateBlue => KnownColor.SlateBlue.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff708090.
/// </summary>
public static Color SlateGray => Color.FromUInt32(0xff708090);
public static Color SlateGray => KnownColor.SlateGray.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #fffffafa.
/// </summary>
public static Color Snow => Color.FromUInt32(0xfffffafa);
public static Color Snow => KnownColor.Snow.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff00ff7f.
/// </summary>
public static Color SpringGreen => Color.FromUInt32(0xff00ff7f);
public static Color SpringGreen => KnownColor.SpringGreen.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff4682b4.
/// </summary>
public static Color SteelBlue => Color.FromUInt32(0xff4682b4);
public static Color SteelBlue => KnownColor.SteelBlue.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffd2b48c.
/// </summary>
public static Color Tan => Color.FromUInt32(0xffd2b48c);
public static Color Tan => KnownColor.Tan.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff008080.
/// </summary>
public static Color Teal => Color.FromUInt32(0xff008080);
public static Color Teal => KnownColor.Teal.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffd8bfd8.
/// </summary>
public static Color Thistle => Color.FromUInt32(0xffd8bfd8);
public static Color Thistle => KnownColor.Thistle.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffff6347.
/// </summary>
public static Color Tomato => Color.FromUInt32(0xffff6347);
public static Color Tomato => KnownColor.Tomato.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #00ffffff.
/// </summary>
public static Color Transparent => Color.FromUInt32(0x00ffffff);
public static Color Transparent => KnownColor.Transparent.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff40e0d0.
/// </summary>
public static Color Turquoise => Color.FromUInt32(0xff40e0d0);
public static Color Turquoise => KnownColor.Turquoise.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffee82ee.
/// </summary>
public static Color Violet => Color.FromUInt32(0xffee82ee);
public static Color Violet => KnownColor.Violet.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #fff5deb3.
/// </summary>
public static Color Wheat => Color.FromUInt32(0xfff5deb3);
public static Color Wheat => KnownColor.Wheat.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffffffff.
/// </summary>
public static Color White => Color.FromUInt32(0xffffffff);
public static Color White => KnownColor.White.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #fff5f5f5.
/// </summary>
public static Color WhiteSmoke => Color.FromUInt32(0xfff5f5f5);
public static Color WhiteSmoke => KnownColor.WhiteSmoke.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ffffff00.
/// </summary>
public static Color Yellow => Color.FromUInt32(0xffffff00);
public static Color Yellow => KnownColor.Yellow.ToColor();
/// <summary>
/// Gets a color with an ARGB value of #ff9acd32.
/// </summary>
public static Color YellowGreen => Color.FromUInt32(0xff9acd32);
public static Color YellowGreen => KnownColor.YellowGreen.ToColor();
}
}

224
src/Avalonia.Visuals/Media/KnownColors.cs

@ -0,0 +1,224 @@
using System;
using System.Reflection;
using System.Collections.Generic;
namespace Avalonia.Media
{
internal static class KnownColors
{
private static readonly IReadOnlyDictionary<string, KnownColor> _knownColorNames;
private static readonly IReadOnlyDictionary<uint, string> _knownColors;
private static readonly Dictionary<KnownColor, ISolidColorBrush> _knownBrushes;
static KnownColors()
{
var knownColorNames = new Dictionary<string, KnownColor>(StringComparer.OrdinalIgnoreCase);
var knownColors = new Dictionary<uint, string>();
foreach (var field in typeof(KnownColor).GetRuntimeFields())
{
if (field.FieldType != typeof(KnownColor)) continue;
var knownColor = (KnownColor)field.GetValue(null);
if (knownColor == KnownColor.None) continue;
knownColorNames.Add(field.Name, knownColor);
// some known colors have the same value, so use the first
if (!knownColors.ContainsKey((uint)knownColor))
{
knownColors.Add((uint)knownColor, field.Name);
}
}
_knownColorNames = knownColorNames;
_knownColors = knownColors;
_knownBrushes = new Dictionary<KnownColor, ISolidColorBrush>();
}
public static ISolidColorBrush GetKnownBrush(string s)
{
var color = GetKnownColor(s);
return color != KnownColor.None ? color.ToBrush() : null;
}
public static KnownColor GetKnownColor(string s)
{
if (_knownColorNames.TryGetValue(s, out var color))
{
return color;
}
return KnownColor.None;
}
public static string GetKnownColorName(uint rgb)
{
return _knownColors.TryGetValue(rgb, out var name) ? name : null;
}
public static Color ToColor(this KnownColor color)
{
return Color.FromUInt32((uint)color);
}
public static ISolidColorBrush ToBrush(this KnownColor color)
{
lock (_knownBrushes)
{
if (!_knownBrushes.TryGetValue(color, out var brush))
{
brush = new Immutable.ImmutableSolidColorBrush(color.ToColor());
_knownBrushes.Add(color, brush);
}
return brush;
}
}
}
internal enum KnownColor : uint
{
None,
AliceBlue = 0xfff0f8ff,
AntiqueWhite = 0xfffaebd7,
Aqua = 0xff00ffff,
Aquamarine = 0xff7fffd4,
Azure = 0xfff0ffff,
Beige = 0xfff5f5dc,
Bisque = 0xffffe4c4,
Black = 0xff000000,
BlanchedAlmond = 0xffffebcd,
Blue = 0xff0000ff,
BlueViolet = 0xff8a2be2,
Brown = 0xffa52a2a,
BurlyWood = 0xffdeb887,
CadetBlue = 0xff5f9ea0,
Chartreuse = 0xff7fff00,
Chocolate = 0xffd2691e,
Coral = 0xffff7f50,
CornflowerBlue = 0xff6495ed,
Cornsilk = 0xfffff8dc,
Crimson = 0xffdc143c,
Cyan = 0xff00ffff,
DarkBlue = 0xff00008b,
DarkCyan = 0xff008b8b,
DarkGoldenrod = 0xffb8860b,
DarkGray = 0xffa9a9a9,
DarkGreen = 0xff006400,
DarkKhaki = 0xffbdb76b,
DarkMagenta = 0xff8b008b,
DarkOliveGreen = 0xff556b2f,
DarkOrange = 0xffff8c00,
DarkOrchid = 0xff9932cc,
DarkRed = 0xff8b0000,
DarkSalmon = 0xffe9967a,
DarkSeaGreen = 0xff8fbc8f,
DarkSlateBlue = 0xff483d8b,
DarkSlateGray = 0xff2f4f4f,
DarkTurquoise = 0xff00ced1,
DarkViolet = 0xff9400d3,
DeepPink = 0xffff1493,
DeepSkyBlue = 0xff00bfff,
DimGray = 0xff696969,
DodgerBlue = 0xff1e90ff,
Firebrick = 0xffb22222,
FloralWhite = 0xfffffaf0,
ForestGreen = 0xff228b22,
Fuchsia = 0xffff00ff,
Gainsboro = 0xffdcdcdc,
GhostWhite = 0xfff8f8ff,
Gold = 0xffffd700,
Goldenrod = 0xffdaa520,
Gray = 0xff808080,
Green = 0xff008000,
GreenYellow = 0xffadff2f,
Honeydew = 0xfff0fff0,
HotPink = 0xffff69b4,
IndianRed = 0xffcd5c5c,
Indigo = 0xff4b0082,
Ivory = 0xfffffff0,
Khaki = 0xfff0e68c,
Lavender = 0xffe6e6fa,
LavenderBlush = 0xfffff0f5,
LawnGreen = 0xff7cfc00,
LemonChiffon = 0xfffffacd,
LightBlue = 0xffadd8e6,
LightCoral = 0xfff08080,
LightCyan = 0xffe0ffff,
LightGoldenrodYellow = 0xfffafad2,
LightGreen = 0xff90ee90,
LightGray = 0xffd3d3d3,
LightPink = 0xffffb6c1,
LightSalmon = 0xffffa07a,
LightSeaGreen = 0xff20b2aa,
LightSkyBlue = 0xff87cefa,
LightSlateGray = 0xff778899,
LightSteelBlue = 0xffb0c4de,
LightYellow = 0xffffffe0,
Lime = 0xff00ff00,
LimeGreen = 0xff32cd32,
Linen = 0xfffaf0e6,
Magenta = 0xffff00ff,
Maroon = 0xff800000,
MediumAquamarine = 0xff66cdaa,
MediumBlue = 0xff0000cd,
MediumOrchid = 0xffba55d3,
MediumPurple = 0xff9370db,
MediumSeaGreen = 0xff3cb371,
MediumSlateBlue = 0xff7b68ee,
MediumSpringGreen = 0xff00fa9a,
MediumTurquoise = 0xff48d1cc,
MediumVioletRed = 0xffc71585,
MidnightBlue = 0xff191970,
MintCream = 0xfff5fffa,
MistyRose = 0xffffe4e1,
Moccasin = 0xffffe4b5,
NavajoWhite = 0xffffdead,
Navy = 0xff000080,
OldLace = 0xfffdf5e6,
Olive = 0xff808000,
OliveDrab = 0xff6b8e23,
Orange = 0xffffa500,
OrangeRed = 0xffff4500,
Orchid = 0xffda70d6,
PaleGoldenrod = 0xffeee8aa,
PaleGreen = 0xff98fb98,
PaleTurquoise = 0xffafeeee,
PaleVioletRed = 0xffdb7093,
PapayaWhip = 0xffffefd5,
PeachPuff = 0xffffdab9,
Peru = 0xffcd853f,
Pink = 0xffffc0cb,
Plum = 0xffdda0dd,
PowderBlue = 0xffb0e0e6,
Purple = 0xff800080,
Red = 0xffff0000,
RosyBrown = 0xffbc8f8f,
RoyalBlue = 0xff4169e1,
SaddleBrown = 0xff8b4513,
Salmon = 0xfffa8072,
SandyBrown = 0xfff4a460,
SeaGreen = 0xff2e8b57,
SeaShell = 0xfffff5ee,
Sienna = 0xffa0522d,
Silver = 0xffc0c0c0,
SkyBlue = 0xff87ceeb,
SlateBlue = 0xff6a5acd,
SlateGray = 0xff708090,
Snow = 0xfffffafa,
SpringGreen = 0xff00ff7f,
SteelBlue = 0xff4682b4,
Tan = 0xffd2b48c,
Teal = 0xff008080,
Thistle = 0xffd8bfd8,
Tomato = 0xffff6347,
Transparent = 0x00ffffff,
Turquoise = 0xff40e0d0,
Violet = 0xffee82ee,
Wheat = 0xfff5deb3,
White = 0xffffffff,
WhiteSmoke = 0xfff5f5f5,
Yellow = 0xffffff00,
YellowGreen = 0xff9acd32
}
}

16
src/Avalonia.Visuals/Point.cs

@ -1,6 +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 Avalonia.Utilities;
using System;
using System.Globalization;
using System.Linq;
@ -173,17 +174,12 @@ namespace Avalonia
/// <returns>The <see cref="Thickness"/>.</returns>
public static Point Parse(string s, CultureInfo culture)
{
var parts = s.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries)
.Select(x => x.Trim())
.ToList();
if (parts.Count == 2)
{
return new Point(double.Parse(parts[0], culture), double.Parse(parts[1], culture));
}
else
using (var tokenizer = new StringTokenizer(s, culture, exceptionMessage: "Invalid Point"))
{
throw new FormatException("Invalid Point.");
return new Point(
tokenizer.ReadDouble(),
tokenizer.ReadDouble()
);
}
}

20
src/Avalonia.Visuals/Rect.cs

@ -1,6 +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 Avalonia.Utilities;
using System;
using System.Globalization;
using System.Linq;
@ -490,21 +491,14 @@ namespace Avalonia
/// <returns>The parsed <see cref="Rect"/>.</returns>
public static Rect Parse(string s, CultureInfo culture)
{
var parts = s.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries)
.Select(x => x.Trim())
.ToList();
if (parts.Count == 4)
using (var tokenizer = new StringTokenizer(s, culture, exceptionMessage: "Invalid Rect"))
{
return new Rect(
double.Parse(parts[0], culture),
double.Parse(parts[1], culture),
double.Parse(parts[2], culture),
double.Parse(parts[3], culture));
}
else
{
throw new FormatException("Invalid Rect.");
tokenizer.ReadDouble(),
tokenizer.ReadDouble(),
tokenizer.ReadDouble(),
tokenizer.ReadDouble()
);
}
}
}

26
src/Avalonia.Visuals/RelativePoint.cs

@ -1,6 +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 Avalonia.Utilities;
using System;
using System.Globalization;
using System.Linq;
@ -157,37 +158,32 @@ namespace Avalonia
/// <returns>The parsed <see cref="RelativePoint"/>.</returns>
public static RelativePoint Parse(string s, CultureInfo culture)
{
var parts = s.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries)
.Select(x => x.Trim())
.ToList();
if (parts.Count == 2)
using (var tokenizer = new StringTokenizer(s, culture, exceptionMessage: "Invalid RelativePoint"))
{
var x = tokenizer.ReadString();
var y = tokenizer.ReadString();
var unit = RelativeUnit.Absolute;
var scale = 1.0;
if (parts[0].EndsWith("%"))
if (x.EndsWith("%"))
{
if (!parts[1].EndsWith("%"))
if (!y.EndsWith("%"))
{
throw new FormatException("If one coordinate is relative, both must be.");
}
parts[0] = parts[0].TrimEnd('%');
parts[1] = parts[1].TrimEnd('%');
x = x.TrimEnd('%');
y = y.TrimEnd('%');
unit = RelativeUnit.Relative;
scale = 0.01;
}
return new RelativePoint(
double.Parse(parts[0], culture) * scale,
double.Parse(parts[1], culture) * scale,
double.Parse(x, culture) * scale,
double.Parse(y, culture) * scale,
unit);
}
else
{
throw new FormatException("Invalid Point.");
}
}
}
}

55
src/Avalonia.Visuals/RelativeRect.cs

@ -1,6 +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 Avalonia.Utilities;
using System;
using System.Globalization;
using System.Linq;
@ -12,6 +13,8 @@ namespace Avalonia
/// </summary>
public struct RelativeRect : IEquatable<RelativeRect>
{
private static readonly char[] PercentChar = { '%' };
/// <summary>
/// A rectangle that represents 100% of an area.
/// </summary>
@ -159,7 +162,7 @@ namespace Avalonia
Rect.Width * size.Width,
Rect.Height * size.Height);
}
/// <summary>
/// Parses a <see cref="RelativeRect"/> string.
/// </summary>
@ -168,43 +171,43 @@ namespace Avalonia
/// <returns>The parsed <see cref="RelativeRect"/>.</returns>
public static RelativeRect Parse(string s, CultureInfo culture)
{
var parts = s.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries)
.Select(x => x.Trim())
.ToList();
if (parts.Count == 4)
using (var tokenizer = new StringTokenizer(s, culture, exceptionMessage: "Invalid RelativeRect"))
{
var x = tokenizer.ReadString();
var y = tokenizer.ReadString();
var width = tokenizer.ReadString();
var height = tokenizer.ReadString();
var unit = RelativeUnit.Absolute;
var scale = 1.0;
if (parts[0].EndsWith("%"))
var xRelative = x.EndsWith("%", StringComparison.Ordinal);
var yRelative = y.EndsWith("%", StringComparison.Ordinal);
var widthRelative = width.EndsWith("%", StringComparison.Ordinal);
var heightRelative = height.EndsWith("%", StringComparison.Ordinal);
if (xRelative && yRelative && widthRelative && heightRelative)
{
if (!parts[1].EndsWith("%")
|| !parts[2].EndsWith("%")
|| !parts[3].EndsWith("%"))
{
throw new FormatException("If one coordinate is relative, all other must be too.");
}
parts[0] = parts[0].TrimEnd('%');
parts[1] = parts[1].TrimEnd('%');
parts[2] = parts[2].TrimEnd('%');
parts[3] = parts[3].TrimEnd('%');
x = x.TrimEnd(PercentChar);
y = y.TrimEnd(PercentChar);
width = width.TrimEnd(PercentChar);
height = height.TrimEnd(PercentChar);
unit = RelativeUnit.Relative;
scale = 0.01;
}
else if (xRelative || yRelative || widthRelative || heightRelative)
{
throw new FormatException("If one coordinate is relative, all must be.");
}
return new RelativeRect(
double.Parse(parts[0], culture) * scale,
double.Parse(parts[1], culture) * scale,
double.Parse(parts[2], culture) * scale,
double.Parse(parts[3], culture) * scale,
double.Parse(x, culture) * scale,
double.Parse(y, culture) * scale,
double.Parse(width, culture) * scale,
double.Parse(height, culture) * scale,
unit);
}
else
{
throw new FormatException("Invalid RelativeRect.");
}
}
}
}

4
src/Avalonia.Visuals/Rendering/DefaultRenderLoop.cs

@ -41,12 +41,12 @@ namespace Avalonia.Rendering
{
add
{
_tick += value;
if (_subscriberCount++ == 0)
{
Start();
}
_tick += value;
}
remove

15
src/Avalonia.Visuals/Size.cs

@ -1,6 +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 Avalonia.Utilities;
using System;
using System.Globalization;
using System.Linq;
@ -153,17 +154,11 @@ namespace Avalonia
/// <returns>The <see cref="Size"/>.</returns>
public static Size Parse(string s, CultureInfo culture)
{
var parts = s.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries)
.Select(x => x.Trim())
.ToList();
if (parts.Count == 2)
{
return new Size(double.Parse(parts[0], culture), double.Parse(parts[1], culture));
}
else
using (var tokenizer = new StringTokenizer(s, culture, exceptionMessage: "Invalid Size"))
{
throw new FormatException("Invalid Size.");
return new Size(
tokenizer.ReadDouble(),
tokenizer.ReadDouble());
}
}

35
src/Avalonia.Visuals/Thickness.cs

@ -1,6 +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 Avalonia.Utilities;
using System;
using System.Globalization;
using System.Linq;
@ -168,28 +169,22 @@ namespace Avalonia
/// <returns>The <see cref="Thickness"/>.</returns>
public static Thickness Parse(string s, CultureInfo culture)
{
var parts = s.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries)
.Select(x => x.Trim())
.ToList();
switch (parts.Count)
using (var tokenizer = new StringTokenizer(s, culture, exceptionMessage: "Invalid Thickness"))
{
case 1:
var uniform = double.Parse(parts[0], culture);
return new Thickness(uniform);
case 2:
var horizontal = double.Parse(parts[0], culture);
var vertical = double.Parse(parts[1], culture);
return new Thickness(horizontal, vertical);
case 4:
var left = double.Parse(parts[0], culture);
var top = double.Parse(parts[1], culture);
var right = double.Parse(parts[2], culture);
var bottom = double.Parse(parts[3], culture);
return new Thickness(left, top, right, bottom);
var a = tokenizer.ReadDouble();
if (tokenizer.TryReadDouble(out var b))
{
if (tokenizer.TryReadDouble(out var c))
{
return new Thickness(a, b, c, tokenizer.ReadDouble());
}
return new Thickness(a, b);
}
return new Thickness(a);
}
throw new FormatException("Invalid Thickness.");
}
/// <summary>

5
src/Gtk/Avalonia.Gtk3/CursorFactory.cs

@ -32,7 +32,10 @@ namespace Avalonia.Gtk3
{StandardCursorType.TopLeftCorner, CursorType.TopLeftCorner},
{StandardCursorType.TopRightCorner, CursorType.TopRightCorner},
{StandardCursorType.BottomLeftCorner, CursorType.BottomLeftCorner},
{StandardCursorType.BottomRightCorner, CursorType.BottomRightCorner}
{StandardCursorType.BottomRightCorner, CursorType.BottomRightCorner},
{StandardCursorType.DragCopy, CursorType.CenterPtr},
{StandardCursorType.DragMove, CursorType.Fleur},
{StandardCursorType.DragLink, CursorType.Cross},
};
private static readonly Dictionary<StandardCursorType, IPlatformHandle> Cache =

4
src/OSX/Avalonia.MonoMac/Avalonia.MonoMac.csproj

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
@ -19,4 +19,4 @@
<ProjectReference Include="..\..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
</ItemGroup>
<Import Project="..\..\..\build\MonoMac.props" />
</Project>
</Project>

4
src/OSX/Avalonia.MonoMac/Cursor.cs

@ -51,6 +51,10 @@ namespace Avalonia.MonoMac
[StandardCursorType.TopSide] = NSCursor.ResizeUpCursor,
[StandardCursorType.UpArrow] = NSCursor.ResizeUpCursor,
[StandardCursorType.Wait] = NSCursor.ArrowCursor, //TODO
[StandardCursorType.DragMove] = NSCursor.DragCopyCursor, // TODO
[StandardCursorType.DragCopy] = NSCursor.DragCopyCursor,
[StandardCursorType.DragLink] = NSCursor.DragLinkCursor,
};
}

124
src/OSX/Avalonia.MonoMac/DragSource.cs

@ -0,0 +1,124 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Runtime.InteropServices;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Input.Raw;
using MonoMac;
using MonoMac.AppKit;
using MonoMac.CoreGraphics;
using MonoMac.Foundation;
using MonoMac.OpenGL;
namespace Avalonia.MonoMac
{
public class DragSource : NSDraggingSource, IPlatformDragSource
{
private const string NSPasteboardTypeString = "public.utf8-plain-text";
private const string NSPasteboardTypeFileUrl = "public.file-url";
private readonly Subject<DragDropEffects> _result = new Subject<DragDropEffects>();
private readonly IInputManager _inputManager;
private DragDropEffects _allowedEffects;
public override bool IgnoreModifierKeysWhileDragging => false;
public DragSource()
{
_inputManager = AvaloniaLocator.Current.GetService<IInputManager>();
}
private string DataFormatToUTI(string s)
{
if (s == DataFormats.FileNames)
return NSPasteboardTypeFileUrl;
if (s == DataFormats.Text)
return NSPasteboardTypeString;
return s;
}
private NSDraggingItem CreateDraggingItem(string format, object data)
{
var pasteboardItem = new NSPasteboardItem();
NSData nsData;
if (data is string s)
{
if (format == DataFormats.FileNames)
s = new Uri(s).AbsoluteUri; // Ensure file uris...
nsData = NSData.FromString(s);
}
else if (data is Stream strm)
nsData = NSData.FromStream(strm);
else if (data is byte[] bytes)
nsData = NSData.FromArray(bytes);
else
{
BinaryFormatter bf = new BinaryFormatter();
using (var ms = new MemoryStream())
{
bf.Serialize(ms, data);
ms.Position = 0;
nsData = NSData.FromStream(ms);
}
}
pasteboardItem.SetDataForType(nsData, DataFormatToUTI(format));
NSPasteboardWriting writing = new NSPasteboardWriting(pasteboardItem.Handle);
return new NSDraggingItem(writing);
}
public IEnumerable<NSDraggingItem> CreateDraggingItems(string format, object data)
{
if (format == DataFormats.FileNames && data is IEnumerable<string> files)
{
foreach (var file in files)
yield return CreateDraggingItem(format, file);
yield break;
}
yield return CreateDraggingItem(format, data);
}
public async Task<DragDropEffects> DoDragDrop(IDataObject data, DragDropEffects allowedEffects)
{
// We need the TopLevelImpl + a mouse location so we just wait for the next event.
var mouseEv = await _inputManager.PreProcess.OfType<RawMouseEventArgs>().FirstAsync();
var view = ((mouseEv.Root as TopLevel)?.PlatformImpl as TopLevelImpl)?.View;
if (view == null)
return DragDropEffects.None;
// Prepare the source event:
var pt = view.TranslateLocalPoint(mouseEv.Position).ToMonoMacPoint();
var ev = NSEvent.MouseEvent(NSEventType.LeftMouseDown, pt, 0, 0, 0, null, 0, 0, 0);
_allowedEffects = allowedEffects;
var items = data.GetDataFormats().SelectMany(fmt => CreateDraggingItems(fmt, data.Get(fmt))).ToArray();
view.BeginDraggingSession(items ,ev, this);
return await _result;
}
public override NSDragOperation DraggingSourceOperationMaskForLocal(bool flag)
{
return DraggingInfo.ConvertDragOperation(_allowedEffects);
}
public override void DraggedImageEndedAtOperation(NSImage image, CGPoint screenPoint, NSDragOperation operation)
{
_result.OnNext(DraggingInfo.ConvertDragOperation(operation));
_result.OnCompleted();
}
}
}

90
src/OSX/Avalonia.MonoMac/DraggingInfo.cs

@ -0,0 +1,90 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Input;
using MonoMac.AppKit;
using MonoMac.Foundation;
namespace Avalonia.MonoMac
{
class DraggingInfo : IDataObject
{
private readonly NSDraggingInfo _info;
public DraggingInfo(NSDraggingInfo info)
{
_info = info;
}
internal static NSDragOperation ConvertDragOperation(DragDropEffects d)
{
NSDragOperation result = NSDragOperation.None;
if (d.HasFlag(DragDropEffects.Copy))
result |= NSDragOperation.Copy;
if (d.HasFlag(DragDropEffects.Link))
result |= NSDragOperation.Link;
if (d.HasFlag(DragDropEffects.Move))
result |= NSDragOperation.Move;
return result;
}
internal static DragDropEffects ConvertDragOperation(NSDragOperation d)
{
DragDropEffects result = DragDropEffects.None;
if (d.HasFlag(NSDragOperation.Copy))
result |= DragDropEffects.Copy;
if (d.HasFlag(NSDragOperation.Link))
result |= DragDropEffects.Link;
if (d.HasFlag(NSDragOperation.Move))
result |= DragDropEffects.Move;
return result;
}
public Point Location => new Point(_info.DraggingLocation.X, _info.DraggingLocation.Y);
public IEnumerable<string> GetDataFormats()
{
return _info.DraggingPasteboard.Types.Select(NSTypeToWellknownType);
}
private string NSTypeToWellknownType(string type)
{
if (type == NSPasteboard.NSStringType)
return DataFormats.Text;
if (type == NSPasteboard.NSFilenamesType)
return DataFormats.FileNames;
return type;
}
public string GetText()
{
return _info.DraggingPasteboard.GetStringForType(NSPasteboard.NSStringType);
}
public IEnumerable<string> GetFileNames()
{
using(var fileNames = (NSArray)_info.DraggingPasteboard.GetPropertyListForType(NSPasteboard.NSFilenamesType))
{
if (fileNames != null)
return NSArray.StringArrayFromHandle(fileNames.Handle);
}
return Enumerable.Empty<string>();
}
public bool Contains(string dataFormat)
{
return GetDataFormats().Any(f => f == dataFormat);
}
public object Get(string dataFormat)
{
if (dataFormat == DataFormats.Text)
return GetText();
if (dataFormat == DataFormats.FileNames)
return GetFileNames();
return _info.DraggingPasteboard.GetDataForType(dataFormat).ToArray();
}
}
}

3
src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs

@ -35,7 +35,8 @@ namespace Avalonia.MonoMac
.Bind<ISystemDialogImpl>().ToSingleton<SystemDialogsImpl>()
.Bind<IClipboard>().ToSingleton<ClipboardImpl>()
.Bind<IRenderLoop>().ToConstant(s_renderLoop)
.Bind<IPlatformThreadingInterface>().ToConstant(PlatformThreadingInterface.Instance);
.Bind<IPlatformThreadingInterface>().ToConstant(PlatformThreadingInterface.Instance)
/*.Bind<IPlatformDragSource>().ToTransient<DragSource>()*/;
}
public static void Initialize()

47
src/OSX/Avalonia.MonoMac/TopLevelImpl.cs

@ -18,6 +18,7 @@ namespace Avalonia.MonoMac
{
public TopLevelView View { get; }
private readonly IMouseDevice _mouse = AvaloniaLocator.Current.GetService<IMouseDevice>();
private readonly IDragDropDevice _dragDevice = AvaloniaLocator.Current.GetService<IDragDropDevice>();
protected TopLevelImpl()
{
View = new TopLevelView(this);
@ -53,6 +54,10 @@ namespace Avalonia.MonoMac
_tl = tl;
_mouse = AvaloniaLocator.Current.GetService<IMouseDevice>();
_keyboard = AvaloniaLocator.Current.GetService<IKeyboardDevice>();
RegisterForDraggedTypes(new string[] {
"public.data" // register for any kind of data.
});
}
protected override void Dispose(bool disposing)
@ -149,6 +154,48 @@ namespace Avalonia.MonoMac
UpdateCursor();
}
private NSDragOperation SendRawDragEvent(NSDraggingInfo sender, RawDragEventType type)
{
Action<RawInputEventArgs> input = _tl.Input;
IDragDropDevice dragDevice = _tl._dragDevice;
IInputRoot root = _tl?.InputRoot;
if (root == null || dragDevice == null || input == null)
return NSDragOperation.None;
var dragOp = DraggingInfo.ConvertDragOperation(sender.DraggingSourceOperationMask);
DraggingInfo info = new DraggingInfo(sender);
var pt = TranslateLocalPoint(info.Location);
var args = new RawDragEvent(dragDevice, type, root, pt, info, dragOp);
input(args);
return DraggingInfo.ConvertDragOperation(args.Effects);
}
public override NSDragOperation DraggingEntered(NSDraggingInfo sender)
{
return SendRawDragEvent(sender, RawDragEventType.DragEnter);
}
public override NSDragOperation DraggingUpdated(NSDraggingInfo sender)
{
return SendRawDragEvent(sender, RawDragEventType.DragOver);
}
public override void DraggingExited(NSDraggingInfo sender)
{
SendRawDragEvent(sender, RawDragEventType.DragLeave);
}
public override bool PrepareForDragOperation(NSDraggingInfo sender)
{
return SendRawDragEvent(sender, RawDragEventType.DragOver) != NSDragOperation.None;
}
public override bool PerformDragOperation(NSDraggingInfo sender)
{
return SendRawDragEvent(sender, RawDragEventType.Drop) != NSDragOperation.None;
}
public override void SetFrameSize(CGSize newSize)
{
lock (SyncRoot)

11
src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs

@ -103,7 +103,7 @@ namespace Avalonia.Direct2D1
/// Converts a pen to a Direct2D stroke style.
/// </summary>
/// <param name="pen">The pen to convert.</param>
/// <param name="target">The render target.</param>
/// <param name="renderTarget">The render target.</param>
/// <returns>The Direct2D brush.</returns>
public static StrokeStyle ToDirect2DStrokeStyle(this Avalonia.Media.Pen pen, SharpDX.Direct2D1.RenderTarget renderTarget)
{
@ -114,7 +114,7 @@ namespace Avalonia.Direct2D1
/// Converts a pen to a Direct2D stroke style.
/// </summary>
/// <param name="pen">The pen to convert.</param>
/// <param name="target">The render target.</param>
/// <param name="factory">The factory associated with this resource.</param>
/// <returns>The Direct2D brush.</returns>
public static StrokeStyle ToDirect2DStrokeStyle(this Avalonia.Media.Pen pen, Factory factory)
{
@ -127,13 +127,16 @@ namespace Avalonia.Direct2D1
EndCap = pen.EndLineCap.ToDirect2D(),
DashCap = pen.DashCap.ToDirect2D()
};
var dashes = new float[0];
float[] dashes = null;
if (pen.DashStyle?.Dashes != null && pen.DashStyle.Dashes.Count > 0)
{
properties.DashStyle = DashStyle.Custom;
properties.DashOffset = (float)pen.DashStyle.Offset;
dashes = pen.DashStyle?.Dashes.Select(x => (float)x).ToArray();
dashes = pen.DashStyle.Dashes.Select(x => (float)x).ToArray();
}
dashes = dashes ?? Array.Empty<float>();
return new StrokeStyle(factory, properties, dashes);
}

80
src/Windows/Avalonia.Win32/ClipboardFormats.cs

@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using Avalonia.Input;
using Avalonia.Win32.Interop;
namespace Avalonia.Win32
{
static class ClipboardFormats
{
private const int MAX_FORMAT_NAME_LENGTH = 260;
class ClipboardFormat
{
public short Format { get; private set; }
public string Name { get; private set; }
public short[] Synthesized { get; private set; }
public ClipboardFormat(string name, short format, params short[] synthesized)
{
Format = format;
Name = name;
Synthesized = synthesized;
}
}
private static readonly List<ClipboardFormat> FormatList = new List<ClipboardFormat>()
{
new ClipboardFormat(DataFormats.Text, (short)UnmanagedMethods.ClipboardFormat.CF_UNICODETEXT, (short)UnmanagedMethods.ClipboardFormat.CF_TEXT),
new ClipboardFormat(DataFormats.FileNames, (short)UnmanagedMethods.ClipboardFormat.CF_HDROP),
};
private static string QueryFormatName(short format)
{
StringBuilder sb = new StringBuilder(MAX_FORMAT_NAME_LENGTH);
if (UnmanagedMethods.GetClipboardFormatName(format, sb, sb.Capacity) > 0)
return sb.ToString();
return null;
}
public static string GetFormat(short format)
{
lock (FormatList)
{
var pd = FormatList.FirstOrDefault(f => f.Format == format || Array.IndexOf(f.Synthesized, format) >= 0);
if (pd == null)
{
string name = QueryFormatName(format);
if (string.IsNullOrEmpty(name))
name = string.Format("Unknown_Format_{0}", format);
pd = new ClipboardFormat(name, format);
FormatList.Add(pd);
}
return pd.Name;
}
}
public static short GetFormat(string format)
{
lock (FormatList)
{
var pd = FormatList.FirstOrDefault(f => StringComparer.OrdinalIgnoreCase.Equals(f.Name, format));
if (pd == null)
{
int id = UnmanagedMethods.RegisterClipboardFormat(format);
if (id == 0)
throw new Win32Exception();
pd = new ClipboardFormat(format, (short)id);
FormatList.Add(pd);
}
return pd.Format;
}
}
}
}

27
src/Windows/Avalonia.Win32/CursorFactory.cs

@ -9,6 +9,7 @@ using System.Text;
using System.Threading.Tasks;
using Avalonia.Input;
using Avalonia.Platform;
using System.Runtime.InteropServices;
namespace Avalonia.Win32
{
@ -20,6 +21,27 @@ namespace Avalonia.Win32
{
}
static CursorFactory()
{
LoadModuleCursor(StandardCursorType.DragMove, "ole32.dll", 2);
LoadModuleCursor(StandardCursorType.DragCopy, "ole32.dll", 3);
LoadModuleCursor(StandardCursorType.DragLink, "ole32.dll", 4);
}
private static void LoadModuleCursor(StandardCursorType cursorType, string module, int id)
{
IntPtr mh = UnmanagedMethods.GetModuleHandle(module);
if (mh != IntPtr.Zero)
{
IntPtr cursor = UnmanagedMethods.LoadCursor(mh, new IntPtr(id));
if (cursor != IntPtr.Zero)
{
PlatformHandle phCursor = new PlatformHandle(cursor, PlatformConstants.CursorHandleType);
Cache.Add(cursorType, phCursor);
}
}
}
private static readonly Dictionary<StandardCursorType, int> CursorTypeMapping = new Dictionary
<StandardCursorType, int>
{
@ -47,6 +69,11 @@ namespace Avalonia.Win32
//Using SizeNorthEastSouthWest
{StandardCursorType.TopRightCorner, 32643},
{StandardCursorType.BottomLeftCorner, 32643},
// Fallback, should have been loaded from ole32.dll
{StandardCursorType.DragMove, 32516},
{StandardCursorType.DragCopy, 32516},
{StandardCursorType.DragLink, 32516},
};
private static readonly Dictionary<StandardCursorType, IPlatformHandle> Cache =

361
src/Windows/Avalonia.Win32/DataObject.cs

@ -0,0 +1,361 @@
using System;
using System.Linq;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using Avalonia.Input;
using Avalonia.Win32.Interop;
using IDataObject = Avalonia.Input.IDataObject;
using IOleDataObject = System.Runtime.InteropServices.ComTypes.IDataObject;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace Avalonia.Win32
{
class DataObject : IDataObject, IOleDataObject
{
// Compatibility with WinForms + WPF...
internal static readonly byte[] SerializedObjectGUID = new Guid("FD9EA796-3B13-4370-A679-56106BB288FB").ToByteArray();
class FormatEnumerator : IEnumFORMATETC
{
private FORMATETC[] _formats;
private int _current;
private FormatEnumerator(FORMATETC[] formats, int current)
{
_formats = formats;
_current = current;
}
public FormatEnumerator(IDataObject dataobj)
{
_formats = dataobj.GetDataFormats().Select(ConvertToFormatEtc).ToArray();
_current = 0;
}
private FORMATETC ConvertToFormatEtc(string aFormatName)
{
FORMATETC result = default(FORMATETC);
result.cfFormat = ClipboardFormats.GetFormat(aFormatName);
result.dwAspect = DVASPECT.DVASPECT_CONTENT;
result.ptd = IntPtr.Zero;
result.lindex = -1;
result.tymed = TYMED.TYMED_HGLOBAL;
return result;
}
public void Clone(out IEnumFORMATETC newEnum)
{
newEnum = new FormatEnumerator(_formats, _current);
}
public int Next(int celt, FORMATETC[] rgelt, int[] pceltFetched)
{
if (rgelt == null)
return unchecked((int)UnmanagedMethods.HRESULT.E_INVALIDARG);
int i = 0;
while (i < celt && _current < _formats.Length)
{
rgelt[i] = _formats[_current];
_current++;
i++;
}
if (pceltFetched != null)
pceltFetched[0] = i;
if (i != celt)
return unchecked((int)UnmanagedMethods.HRESULT.S_FALSE);
return unchecked((int)UnmanagedMethods.HRESULT.S_OK);
}
public int Reset()
{
_current = 0;
return unchecked((int)UnmanagedMethods.HRESULT.S_OK);
}
public int Skip(int celt)
{
_current += Math.Min(celt, int.MaxValue - _current);
if (_current >= _formats.Length)
return unchecked((int)UnmanagedMethods.HRESULT.S_FALSE);
return unchecked((int)UnmanagedMethods.HRESULT.S_OK);
}
}
private const int DV_E_TYMED = unchecked((int)0x80040069);
private const int DV_E_DVASPECT = unchecked((int)0x8004006B);
private const int DV_E_FORMATETC = unchecked((int)0x80040064);
private const int OLE_E_ADVISENOTSUPPORTED = unchecked((int)0x80040003);
private const int STG_E_MEDIUMFULL = unchecked((int)0x80030070);
private const int GMEM_ZEROINIT = 0x0040;
private const int GMEM_MOVEABLE = 0x0002;
IDataObject _wrapped;
public DataObject(IDataObject wrapped)
{
_wrapped = wrapped;
}
#region IDataObject
bool IDataObject.Contains(string dataFormat)
{
return _wrapped.Contains(dataFormat);
}
IEnumerable<string> IDataObject.GetDataFormats()
{
return _wrapped.GetDataFormats();
}
IEnumerable<string> IDataObject.GetFileNames()
{
return _wrapped.GetFileNames();
}
string IDataObject.GetText()
{
return _wrapped.GetText();
}
object IDataObject.Get(string dataFormat)
{
return _wrapped.Get(dataFormat);
}
#endregion
#region IOleDataObject
int IOleDataObject.DAdvise(ref FORMATETC pFormatetc, ADVF advf, IAdviseSink adviseSink, out int connection)
{
if (_wrapped is IOleDataObject ole)
return ole.DAdvise(ref pFormatetc, advf, adviseSink, out connection);
connection = 0;
return OLE_E_ADVISENOTSUPPORTED;
}
void IOleDataObject.DUnadvise(int connection)
{
if (_wrapped is IOleDataObject ole)
ole.DUnadvise(connection);
Marshal.ThrowExceptionForHR(OLE_E_ADVISENOTSUPPORTED);
}
int IOleDataObject.EnumDAdvise(out IEnumSTATDATA enumAdvise)
{
if (_wrapped is IOleDataObject ole)
return ole.EnumDAdvise(out enumAdvise);
enumAdvise = null;
return OLE_E_ADVISENOTSUPPORTED;
}
IEnumFORMATETC IOleDataObject.EnumFormatEtc(DATADIR direction)
{
if (_wrapped is IOleDataObject ole)
return ole.EnumFormatEtc(direction);
if (direction == DATADIR.DATADIR_GET)
return new FormatEnumerator(_wrapped);
throw new NotSupportedException();
}
int IOleDataObject.GetCanonicalFormatEtc(ref FORMATETC formatIn, out FORMATETC formatOut)
{
if (_wrapped is IOleDataObject ole)
return ole.GetCanonicalFormatEtc(ref formatIn, out formatOut);
formatOut = new FORMATETC();
formatOut.ptd = IntPtr.Zero;
return unchecked((int)UnmanagedMethods.HRESULT.E_NOTIMPL);
}
void IOleDataObject.GetData(ref FORMATETC format, out STGMEDIUM medium)
{
if (_wrapped is IOleDataObject ole)
{
ole.GetData(ref format, out medium);
return;
}
if(!format.tymed.HasFlag(TYMED.TYMED_HGLOBAL))
Marshal.ThrowExceptionForHR(DV_E_TYMED);
if (format.dwAspect != DVASPECT.DVASPECT_CONTENT)
Marshal.ThrowExceptionForHR(DV_E_DVASPECT);
string fmt = ClipboardFormats.GetFormat(format.cfFormat);
if (string.IsNullOrEmpty(fmt) || !_wrapped.Contains(fmt))
Marshal.ThrowExceptionForHR(DV_E_FORMATETC);
medium = default(STGMEDIUM);
medium.tymed = TYMED.TYMED_HGLOBAL;
int result = WriteDataToHGlobal(fmt, ref medium.unionmember);
Marshal.ThrowExceptionForHR(result);
}
void IOleDataObject.GetDataHere(ref FORMATETC format, ref STGMEDIUM medium)
{
if (_wrapped is IOleDataObject ole)
{
ole.GetDataHere(ref format, ref medium);
return;
}
if (medium.tymed != TYMED.TYMED_HGLOBAL || !format.tymed.HasFlag(TYMED.TYMED_HGLOBAL))
Marshal.ThrowExceptionForHR(DV_E_TYMED);
if (format.dwAspect != DVASPECT.DVASPECT_CONTENT)
Marshal.ThrowExceptionForHR(DV_E_DVASPECT);
string fmt = ClipboardFormats.GetFormat(format.cfFormat);
if (string.IsNullOrEmpty(fmt) || !_wrapped.Contains(fmt))
Marshal.ThrowExceptionForHR(DV_E_FORMATETC);
if (medium.unionmember == IntPtr.Zero)
Marshal.ThrowExceptionForHR(STG_E_MEDIUMFULL);
int result = WriteDataToHGlobal(fmt, ref medium.unionmember);
Marshal.ThrowExceptionForHR(result);
}
int IOleDataObject.QueryGetData(ref FORMATETC format)
{
if (_wrapped is IOleDataObject ole)
return ole.QueryGetData(ref format);
if (format.dwAspect != DVASPECT.DVASPECT_CONTENT)
return DV_E_DVASPECT;
if (!format.tymed.HasFlag(TYMED.TYMED_HGLOBAL))
return DV_E_TYMED;
string dataFormat = ClipboardFormats.GetFormat(format.cfFormat);
if (!string.IsNullOrEmpty(dataFormat) && _wrapped.Contains(dataFormat))
return unchecked((int)UnmanagedMethods.HRESULT.S_OK);
return DV_E_FORMATETC;
}
void IOleDataObject.SetData(ref FORMATETC formatIn, ref STGMEDIUM medium, bool release)
{
if (_wrapped is IOleDataObject ole)
{
ole.SetData(ref formatIn, ref medium, release);
return;
}
Marshal.ThrowExceptionForHR(unchecked((int)UnmanagedMethods.HRESULT.E_NOTIMPL));
}
private int WriteDataToHGlobal(string dataFormat, ref IntPtr hGlobal)
{
object data = _wrapped.Get(dataFormat);
if (dataFormat == DataFormats.Text || data is string)
return WriteStringToHGlobal(ref hGlobal, Convert.ToString(data));
if (dataFormat == DataFormats.FileNames && data is IEnumerable<string> files)
return WriteFileListToHGlobal(ref hGlobal, files);
if (data is Stream stream)
{
byte[] buffer = new byte[stream.Length - stream.Position];
stream.Read(buffer, 0, buffer.Length);
return WriteBytesToHGlobal(ref hGlobal, buffer);
}
if (data is IEnumerable<byte> bytes)
{
var byteArr = bytes is byte[] ? (byte[])bytes : bytes.ToArray();
return WriteBytesToHGlobal(ref hGlobal, byteArr);
}
return WriteBytesToHGlobal(ref hGlobal, SerializeObject(data));
}
private byte[] SerializeObject(object data)
{
using (var ms = new MemoryStream())
{
ms.Write(SerializedObjectGUID, 0, SerializedObjectGUID.Length);
BinaryFormatter binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(ms, data);
return ms.ToArray();
}
}
private int WriteBytesToHGlobal(ref IntPtr hGlobal, byte[] data)
{
int required = data.Length;
if (hGlobal == IntPtr.Zero)
hGlobal = UnmanagedMethods.GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, required);
long available = UnmanagedMethods.GlobalSize(hGlobal).ToInt64();
if (required > available)
return STG_E_MEDIUMFULL;
IntPtr ptr = UnmanagedMethods.GlobalLock(hGlobal);
try
{
Marshal.Copy(data, 0, ptr, data.Length);
return unchecked((int)UnmanagedMethods.HRESULT.S_OK);
}
finally
{
UnmanagedMethods.GlobalUnlock(hGlobal);
}
}
private int WriteFileListToHGlobal(ref IntPtr hGlobal, IEnumerable<string> files)
{
if (!files?.Any() ?? false)
return unchecked((int)UnmanagedMethods.HRESULT.S_OK);
char[] filesStr = (string.Join("\0", files) + "\0\0").ToCharArray();
_DROPFILES df = new _DROPFILES();
df.pFiles = Marshal.SizeOf<_DROPFILES>();
df.fWide = true;
int required = (filesStr.Length * sizeof(char)) + Marshal.SizeOf<_DROPFILES>();
if (hGlobal == IntPtr.Zero)
hGlobal = UnmanagedMethods.GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, required);
long available = UnmanagedMethods.GlobalSize(hGlobal).ToInt64();
if (required > available)
return STG_E_MEDIUMFULL;
IntPtr ptr = UnmanagedMethods.GlobalLock(hGlobal);
try
{
Marshal.StructureToPtr(df, ptr, false);
Marshal.Copy(filesStr, 0, ptr + Marshal.SizeOf<_DROPFILES>(), filesStr.Length);
return unchecked((int)UnmanagedMethods.HRESULT.S_OK);
}
finally
{
UnmanagedMethods.GlobalUnlock(hGlobal);
}
}
private int WriteStringToHGlobal(ref IntPtr hGlobal, string data)
{
int required = (data.Length + 1) * sizeof(char);
if (hGlobal == IntPtr.Zero)
hGlobal = UnmanagedMethods.GlobalAlloc(GMEM_MOVEABLE|GMEM_ZEROINIT, required);
long available = UnmanagedMethods.GlobalSize(hGlobal).ToInt64();
if (required > available)
return STG_E_MEDIUMFULL;
IntPtr ptr = UnmanagedMethods.GlobalLock(hGlobal);
try
{
char[] chars = (data + '\0').ToCharArray();
Marshal.Copy(chars, 0, ptr, chars.Length);
return unchecked((int)UnmanagedMethods.HRESULT.S_OK);
}
finally
{
UnmanagedMethods.GlobalUnlock(hGlobal);
}
}
#endregion
}
}

27
src/Windows/Avalonia.Win32/DragSource.cs

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Threading;
using Avalonia.Win32.Interop;
namespace Avalonia.Win32
{
class DragSource : IPlatformDragSource
{
public Task<DragDropEffects> DoDragDrop(IDataObject data, DragDropEffects allowedEffects)
{
Dispatcher.UIThread.VerifyAccess();
OleDragSource src = new OleDragSource();
DataObject dataObject = new DataObject(data);
int allowed = (int)OleDropTarget.ConvertDropEffect(allowedEffects);
int[] finalEffect = new int[1];
UnmanagedMethods.DoDragDrop(dataObject, src, allowed, finalEffect);
return Task.FromResult(OleDropTarget.ConvertDropEffect((DropEffect)finalEffect[0]));}
}
}

102
src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs

@ -5,6 +5,7 @@ using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
// ReSharper disable InconsistentNaming
@ -951,6 +952,32 @@ namespace Avalonia.Win32.Interop
[DllImport("msvcrt.dll", EntryPoint="memcpy", SetLastError = false, CallingConvention=CallingConvention.Cdecl)]
public static extern IntPtr CopyMemory(IntPtr dest, IntPtr src, UIntPtr count);
[DllImport("ole32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern HRESULT RegisterDragDrop(IntPtr hwnd, IDropTarget target);
[DllImport("ole32.dll", EntryPoint = "OleInitialize")]
public static extern HRESULT OleInitialize(IntPtr val);
[DllImport("ole32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
internal static extern void ReleaseStgMedium(ref STGMEDIUM medium);
[DllImport("user32.dll", BestFitMapping = false, CharSet = CharSet.Auto, SetLastError = true)]
public static extern int GetClipboardFormatName(int format, StringBuilder lpString, int cchMax);
[DllImport("user32.dll", BestFitMapping = false, CharSet = CharSet.Auto, SetLastError = true)]
public static extern int RegisterClipboardFormat(string format);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = true, SetLastError = true)]
public static extern IntPtr GlobalSize(IntPtr hGlobal);
[DllImport("shell32.dll", BestFitMapping = false, CharSet = CharSet.Auto)]
public static extern int DragQueryFile(IntPtr hDrop, int iFile, StringBuilder lpszFile, int cch);
[DllImport("ole32.dll", CharSet = CharSet.Auto, ExactSpelling = true, PreserveSig = false)]
public static extern void DoDragDrop(IDataObject dataObject, IDropSource dropSource, int allowedEffects, int[] finalEffect);
public enum MONITOR
{
MONITOR_DEFAULTTONULL = 0x00000000,
@ -990,10 +1017,28 @@ namespace Avalonia.Win32.Interop
MDT_DEFAULT = MDT_EFFECTIVE_DPI
}
public enum ClipboardFormat
public enum ClipboardFormat
{
/// <summary>
/// Text format. Each line ends with a carriage return/linefeed (CR-LF) combination. A null character signals the end of the data. Use this format for ANSI text.
/// </summary>
CF_TEXT = 1,
CF_UNICODETEXT = 13
/// <summary>
/// A handle to a bitmap
/// </summary>
CF_BITMAP = 2,
/// <summary>
/// A memory object containing a BITMAPINFO structure followed by the bitmap bits.
/// </summary>
CF_DIB = 3,
/// <summary>
/// Unicode text format. Each line ends with a carriage return/linefeed (CR-LF) combination. A null character signals the end of the data.
/// </summary>
CF_UNICODETEXT = 13,
/// <summary>
/// A handle to type HDROP that identifies a list of files.
/// </summary>
CF_HDROP = 15,
}
public struct MSG
@ -1136,7 +1181,9 @@ namespace Avalonia.Win32.Interop
S_FALSE = 0x0001,
S_OK = 0x0000,
E_INVALIDARG = 0x80070057,
E_OUTOFMEMORY = 0x8007000E
E_OUTOFMEMORY = 0x8007000E,
E_NOTIMPL = 0x80004001,
E_UNEXPECTED = 0x8000FFFF,
}
public enum Icons
@ -1300,4 +1347,53 @@ namespace Avalonia.Win32.Interop
uint Compare([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi, [In] uint hint, out int piOrder);
}
[Flags]
internal enum DropEffect : int
{
None = 0,
Copy = 1,
Move = 2,
Link = 4,
Scroll = -2147483648,
}
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("00000122-0000-0000-C000-000000000046")]
internal interface IDropTarget
{
[PreserveSig]
UnmanagedMethods.HRESULT DragEnter([MarshalAs(UnmanagedType.Interface)] [In] IDataObject pDataObj, [MarshalAs(UnmanagedType.U4)] [In] int grfKeyState, [MarshalAs(UnmanagedType.U8)] [In] long pt, [In] [Out] ref DropEffect pdwEffect);
[PreserveSig]
UnmanagedMethods.HRESULT DragOver([MarshalAs(UnmanagedType.U4)] [In] int grfKeyState, [MarshalAs(UnmanagedType.U8)] [In] long pt, [In] [Out] ref DropEffect pdwEffect);
[PreserveSig]
UnmanagedMethods.HRESULT DragLeave();
[PreserveSig]
UnmanagedMethods.HRESULT Drop([MarshalAs(UnmanagedType.Interface)] [In] IDataObject pDataObj, [MarshalAs(UnmanagedType.U4)] [In] int grfKeyState, [MarshalAs(UnmanagedType.U8)] [In] long pt, [In] [Out] ref DropEffect pdwEffect);
}
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("00000121-0000-0000-C000-000000000046")]
internal interface IDropSource
{
[PreserveSig]
int QueryContinueDrag(int fEscapePressed, [MarshalAs(UnmanagedType.U4)] [In] int grfKeyState);
[PreserveSig]
int GiveFeedback([MarshalAs(UnmanagedType.U4)] [In] int dwEffect);
}
[StructLayoutAttribute(LayoutKind.Sequential)]
internal struct _DROPFILES
{
public Int32 pFiles;
public Int32 X;
public Int32 Y;
public bool fNC;
public bool fWide;
}
}

47
src/Windows/Avalonia.Win32/OleContext.cs

@ -0,0 +1,47 @@
using System;
using System.Threading;
using Avalonia.Platform;
using Avalonia.Threading;
using Avalonia.Win32.Interop;
namespace Avalonia.Win32
{
class OleContext
{
private static OleContext fCurrent;
internal static OleContext Current
{
get
{
if (!IsValidOleThread())
return null;
if (fCurrent == null)
fCurrent = new OleContext();
return fCurrent;
}
}
private OleContext()
{
if (UnmanagedMethods.OleInitialize(IntPtr.Zero) != UnmanagedMethods.HRESULT.S_OK)
throw new SystemException("Failed to initialize OLE");
}
private static bool IsValidOleThread()
{
return Dispatcher.UIThread.CheckAccess() &&
Thread.CurrentThread.GetApartmentState() == ApartmentState.STA;
}
internal bool RegisterDragDrop(IPlatformHandle hwnd, IDropTarget target)
{
if (hwnd?.HandleDescriptor != "HWND" || target == null)
return false;
return UnmanagedMethods.RegisterDragDrop(hwnd.Handle, target) == UnmanagedMethods.HRESULT.S_OK;
}
}
}

171
src/Windows/Avalonia.Win32/OleDataObject.cs

@ -0,0 +1,171 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using Avalonia.Input;
using Avalonia.Win32.Interop;
using IDataObject = System.Runtime.InteropServices.ComTypes.IDataObject;
namespace Avalonia.Win32
{
class OleDataObject : Avalonia.Input.IDataObject
{
private IDataObject _wrapped;
public OleDataObject(IDataObject wrapped)
{
_wrapped = wrapped;
}
public bool Contains(string dataFormat)
{
return GetDataFormatsCore().Any(df => StringComparer.OrdinalIgnoreCase.Equals(df, dataFormat));
}
public IEnumerable<string> GetDataFormats()
{
return GetDataFormatsCore().Distinct();
}
public string GetText()
{
return GetDataFromOleHGLOBAL(DataFormats.Text, DVASPECT.DVASPECT_CONTENT) as string;
}
public IEnumerable<string> GetFileNames()
{
return GetDataFromOleHGLOBAL(DataFormats.FileNames, DVASPECT.DVASPECT_CONTENT) as IEnumerable<string>;
}
public object Get(string dataFormat)
{
return GetDataFromOleHGLOBAL(dataFormat, DVASPECT.DVASPECT_CONTENT);
}
private object GetDataFromOleHGLOBAL(string format, DVASPECT aspect)
{
FORMATETC formatEtc = new FORMATETC();
formatEtc.cfFormat = ClipboardFormats.GetFormat(format);
formatEtc.dwAspect = aspect;
formatEtc.lindex = -1;
formatEtc.tymed = TYMED.TYMED_HGLOBAL;
if (_wrapped.QueryGetData(ref formatEtc) == 0)
{
_wrapped.GetData(ref formatEtc, out STGMEDIUM medium);
try
{
if (medium.unionmember != IntPtr.Zero && medium.tymed == TYMED.TYMED_HGLOBAL)
{
if (format == DataFormats.Text)
return ReadStringFromHGlobal(medium.unionmember);
if (format == DataFormats.FileNames)
return ReadFileNamesFromHGlobal(medium.unionmember);
byte[] data = ReadBytesFromHGlobal(medium.unionmember);
if (IsSerializedObject(data))
{
using (var ms = new MemoryStream(data))
{
ms.Position = DataObject.SerializedObjectGUID.Length;
BinaryFormatter binaryFormatter = new BinaryFormatter();
return binaryFormatter.Deserialize(ms);
}
}
return data;
}
}
finally
{
UnmanagedMethods.ReleaseStgMedium(ref medium);
}
}
return null;
}
private bool IsSerializedObject(byte[] data)
{
if (data.Length < DataObject.SerializedObjectGUID.Length)
return false;
for (int i = 0; i < DataObject.SerializedObjectGUID.Length; i++)
if (data[i] != DataObject.SerializedObjectGUID[i])
return false;
return true;
}
private static IEnumerable<string> ReadFileNamesFromHGlobal(IntPtr hGlobal)
{
List<string> files = new List<string>();
int fileCount = UnmanagedMethods.DragQueryFile(hGlobal, -1, null, 0);
if (fileCount > 0)
{
for (int i = 0; i < fileCount; i++)
{
int pathLen = UnmanagedMethods.DragQueryFile(hGlobal, i, null, 0);
StringBuilder sb = new StringBuilder(pathLen+1);
if (UnmanagedMethods.DragQueryFile(hGlobal, i, sb, sb.Capacity) == pathLen)
{
files.Add(sb.ToString());
}
}
}
return files;
}
private static string ReadStringFromHGlobal(IntPtr hGlobal)
{
IntPtr ptr = UnmanagedMethods.GlobalLock(hGlobal);
try
{
return Marshal.PtrToStringAuto(ptr);
}
finally
{
UnmanagedMethods.GlobalUnlock(hGlobal);
}
}
private static byte[] ReadBytesFromHGlobal(IntPtr hGlobal)
{
IntPtr source = UnmanagedMethods.GlobalLock(hGlobal);
try
{
int size = (int)UnmanagedMethods.GlobalSize(hGlobal).ToInt64();
byte[] data = new byte[size];
Marshal.Copy(source, data, 0, size);
return data;
}
finally
{
UnmanagedMethods.GlobalUnlock(hGlobal);
}
}
private IEnumerable<string> GetDataFormatsCore()
{
var enumFormat = _wrapped.EnumFormatEtc(DATADIR.DATADIR_GET);
if (enumFormat != null)
{
enumFormat.Reset();
FORMATETC[] formats = new FORMATETC[1];
int[] fetched = { 1 };
while (fetched[0] > 0)
{
fetched[0] = 0;
if (enumFormat.Next(1, formats, fetched) == 0 && fetched[0] > 0)
{
if (formats[0].ptd != IntPtr.Zero)
Marshal.FreeCoTaskMem(formats[0].ptd);
yield return ClipboardFormats.GetFormat(formats[0].cfFormat);
}
}
}
}
}
}

39
src/Windows/Avalonia.Win32/OleDragSource.cs

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Win32.Interop;
namespace Avalonia.Win32
{
class OleDragSource : IDropSource
{
private const int DRAGDROP_S_USEDEFAULTCURSORS = 0x00040102;
private const int DRAGDROP_S_DROP = 0x00040100;
private const int DRAGDROP_S_CANCEL = 0x00040101;
private const int KEYSTATE_LEFTMB = 1;
private const int KEYSTATE_MIDDLEMB = 16;
private const int KEYSTATE_RIGHTMB = 2;
private static readonly int[] MOUSE_BUTTONS = new int[] { KEYSTATE_LEFTMB, KEYSTATE_MIDDLEMB, KEYSTATE_RIGHTMB };
public int QueryContinueDrag(int fEscapePressed, int grfKeyState)
{
if (fEscapePressed != 0)
return DRAGDROP_S_CANCEL;
int pressedMouseButtons = MOUSE_BUTTONS.Where(mb => (grfKeyState & mb) == mb).Count();
if (pressedMouseButtons >= 2)
return DRAGDROP_S_CANCEL;
if (pressedMouseButtons == 0)
return DRAGDROP_S_DROP;
return unchecked((int)UnmanagedMethods.HRESULT.S_OK);
}
public int GiveFeedback(int dwEffect)
{
return DRAGDROP_S_USEDEFAULTCURSORS;
}
}
}

160
src/Windows/Avalonia.Win32/OleDropTarget.cs

@ -0,0 +1,160 @@
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Platform;
using Avalonia.Win32.Interop;
using IDataObject = Avalonia.Input.IDataObject;
using IOleDataObject = System.Runtime.InteropServices.ComTypes.IDataObject;
namespace Avalonia.Win32
{
class OleDropTarget : IDropTarget
{
private readonly IInputElement _target;
private readonly ITopLevelImpl _tl;
private readonly IDragDropDevice _dragDevice;
private IDataObject _currentDrag = null;
public OleDropTarget(ITopLevelImpl tl, IInputElement target)
{
_dragDevice = AvaloniaLocator.Current.GetService<IDragDropDevice>();
_tl = tl;
_target = target;
}
public static DropEffect ConvertDropEffect(DragDropEffects operation)
{
DropEffect result = DropEffect.None;
if (operation.HasFlag(DragDropEffects.Copy))
result |= DropEffect.Copy;
if (operation.HasFlag(DragDropEffects.Move))
result |= DropEffect.Move;
if (operation.HasFlag(DragDropEffects.Link))
result |= DropEffect.Link;
return result;
}
public static DragDropEffects ConvertDropEffect(DropEffect effect)
{
DragDropEffects result = DragDropEffects.None;
if (effect.HasFlag(DropEffect.Copy))
result |= DragDropEffects.Copy;
if (effect.HasFlag(DropEffect.Move))
result |= DragDropEffects.Move;
if (effect.HasFlag(DropEffect.Link))
result |= DragDropEffects.Link;
return result;
}
UnmanagedMethods.HRESULT IDropTarget.DragEnter(IOleDataObject pDataObj, int grfKeyState, long pt, ref DropEffect pdwEffect)
{
var dispatch = _tl?.Input;
if (dispatch == null)
{
pdwEffect = DropEffect.None;
return UnmanagedMethods.HRESULT.S_OK;
}
_currentDrag = pDataObj as IDataObject;
if (_currentDrag == null)
_currentDrag = new OleDataObject(pDataObj);
var args = new RawDragEvent(
_dragDevice,
RawDragEventType.DragEnter,
_target,
GetDragLocation(pt),
_currentDrag,
ConvertDropEffect(pdwEffect)
);
dispatch(args);
pdwEffect = ConvertDropEffect(args.Effects);
return UnmanagedMethods.HRESULT.S_OK;
}
UnmanagedMethods.HRESULT IDropTarget.DragOver(int grfKeyState, long pt, ref DropEffect pdwEffect)
{
var dispatch = _tl?.Input;
if (dispatch == null)
{
pdwEffect = DropEffect.None;
return UnmanagedMethods.HRESULT.S_OK;
}
var args = new RawDragEvent(
_dragDevice,
RawDragEventType.DragOver,
_target,
GetDragLocation(pt),
_currentDrag,
ConvertDropEffect(pdwEffect)
);
dispatch(args);
pdwEffect = ConvertDropEffect(args.Effects);
return UnmanagedMethods.HRESULT.S_OK;
}
UnmanagedMethods.HRESULT IDropTarget.DragLeave()
{
try
{
_tl?.Input(new RawDragEvent(
_dragDevice,
RawDragEventType.DragLeave,
_target,
default(Point),
null,
DragDropEffects.None
));
return UnmanagedMethods.HRESULT.S_OK;
}
finally
{
_currentDrag = null;
}
}
UnmanagedMethods.HRESULT IDropTarget.Drop(IOleDataObject pDataObj, int grfKeyState, long pt, ref DropEffect pdwEffect)
{
try
{
var dispatch = _tl?.Input;
if (dispatch == null)
{
pdwEffect = DropEffect.None;
return UnmanagedMethods.HRESULT.S_OK;
}
_currentDrag = pDataObj as IDataObject;
if (_currentDrag == null)
_currentDrag= new OleDataObject(pDataObj);
var args = new RawDragEvent(
_dragDevice,
RawDragEventType.Drop,
_target,
GetDragLocation(pt),
_currentDrag,
ConvertDropEffect(pdwEffect)
);
dispatch(args);
pdwEffect = ConvertDropEffect(args.Effects);
return UnmanagedMethods.HRESULT.S_OK;
}
finally
{
_currentDrag = null;
}
}
private Point GetDragLocation(long dragPoint)
{
int x = (int)dragPoint;
int y = (int)(dragPoint >> 32);
Point screenPt = new Point(x, y);
return _target.PointToClient(screenPt);
}
}
}

7
src/Windows/Avalonia.Win32/ScreenImpl.cs

@ -2,16 +2,9 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Linq;
using Avalonia.Controls;
using Avalonia.Platform;
using Avalonia.Utilities;
using static Avalonia.Win32.Interop.UnmanagedMethods;
#if NETSTANDARD
using Win32Exception = Avalonia.Win32.NetStandard.AvaloniaWin32Exception;
#endif
namespace Avalonia.Win32
{
public class ScreenImpl : IScreenImpl

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

@ -1,27 +1,23 @@
// 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 Avalonia.Input.Platform;
using System;
using System.Collections.Generic;
using System.Reactive.Disposables;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Reactive.Disposables;
using System.Runtime.InteropServices;
using System.Threading;
using Avalonia.Controls;
using Avalonia.Controls.Platform;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Platform;
using Avalonia.Win32.Input;
using Avalonia.Win32.Interop;
using Avalonia.Controls;
using Avalonia.Rendering;
using Avalonia.Threading;
using System.IO;
#if NETSTANDARD
using Win32Exception = Avalonia.Win32.NetStandard.AvaloniaWin32Exception;
#else
using System.ComponentModel;
#endif
using Avalonia.Win32.Input;
using Avalonia.Win32.Interop;
namespace Avalonia
{
@ -86,6 +82,9 @@ namespace Avalonia.Win32
.Bind<IWindowingPlatform>().ToConstant(s_instance)
.Bind<IPlatformIconLoader>().ToConstant(s_instance);
if (OleContext.Current != null)
AvaloniaLocator.CurrentMutable.Bind<IPlatformDragSource>().ToSingleton<DragSource>();
UseDeferredRendering = deferredRendering;
_uiThread = UnmanagedMethods.GetCurrentThreadId();
}

9
src/Windows/Avalonia.Win32/WindowImpl.cs

@ -34,6 +34,7 @@ namespace Avalonia.Win32
private double _scaling = 1;
private WindowState _showWindowState;
private FramebufferManager _framebuffer;
private OleDropTarget _dropTarget;
#if USE_MANAGED_DRAG
private readonly ManagedWindowResizeDragHelper _managedDrag;
#endif
@ -310,6 +311,7 @@ namespace Avalonia.Win32
public void SetInputRoot(IInputRoot inputRoot)
{
_owner = inputRoot;
CreateDropTarget();
}
public void SetTitle(string title)
@ -699,6 +701,13 @@ namespace Avalonia.Win32
}
}
private void CreateDropTarget()
{
OleDropTarget odt = new OleDropTarget(this, _owner);
if (OleContext.Current?.RegisterDragDrop(Handle, odt) ?? false)
_dropTarget = odt;
}
private Point DipFromLParam(IntPtr lParam)
{
return new Point((short)(ToInt32(lParam) & 0xffff), (short)(ToInt32(lParam) >> 16)) / Scaling;

18
tests/Avalonia.Markup.UnitTests/Data/BindingExpressionTests.cs

@ -58,7 +58,7 @@ namespace Avalonia.Markup.UnitTests.Data
[Fact]
public async Task Should_Convert_Get_String_To_Double()
{
var data = new Class1 { StringValue = "5.6" };
var data = new Class1 { StringValue = $"{5.6}" };
var target = new BindingExpression(new ExpressionObserver(data, "StringValue"), typeof(double));
var result = await target.Take(1);
@ -94,12 +94,12 @@ namespace Avalonia.Markup.UnitTests.Data
[Fact]
public void Should_Convert_Set_String_To_Double()
{
var data = new Class1 { StringValue = (5.6).ToString() };
var data = new Class1 { StringValue = $"{5.6}" };
var target = new BindingExpression(new ExpressionObserver(data, "StringValue"), typeof(double));
target.OnNext(6.7);
Assert.Equal((6.7).ToString(), data.StringValue);
Assert.Equal($"{6.7}", data.StringValue);
GC.KeepAlive(data);
}
@ -111,7 +111,7 @@ namespace Avalonia.Markup.UnitTests.Data
var target = new BindingExpression(new ExpressionObserver(data, "DoubleValue"), typeof(string));
var result = await target.Take(1);
Assert.Equal((5.6).ToString(), result);
Assert.Equal($"{5.6}", result);
GC.KeepAlive(data);
}
@ -122,7 +122,7 @@ namespace Avalonia.Markup.UnitTests.Data
var data = new Class1 { DoubleValue = 5.6 };
var target = new BindingExpression(new ExpressionObserver(data, "DoubleValue"), typeof(string));
target.OnNext("6.7");
target.OnNext($"{6.7}");
Assert.Equal(6.7, data.DoubleValue);
@ -318,15 +318,15 @@ namespace Avalonia.Markup.UnitTests.Data
target.Subscribe(x => result.Add(x));
target.OnNext(1.2);
target.OnNext("3.4");
target.OnNext($"{3.4}");
target.OnNext("bar");
Assert.Equal(
new[]
{
new BindingNotification("5.6"),
new BindingNotification("1.2"),
new BindingNotification("3.4"),
new BindingNotification($"{5.6}"),
new BindingNotification($"{1.2}"),
new BindingNotification($"{3.4}"),
new BindingNotification(
new InvalidCastException("'bar' is not a valid number."),
BindingErrorType.Error)

7
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs

@ -8,6 +8,7 @@ using Avalonia.Markup.Xaml.Data;
using Avalonia.Markup.Xaml.Styling;
using Avalonia.Markup.Xaml.Templates;
using Avalonia.Media;
using Avalonia.Media.Immutable;
using Avalonia.Styling;
using Avalonia.UnitTests;
using Portable.Xaml;
@ -378,8 +379,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
var control = AvaloniaXamlLoader.Parse<UserControl>(xaml);
var bk = control.Background;
Assert.IsType<SolidColorBrush>(bk);
Assert.Equal(Colors.White, (bk as SolidColorBrush).Color);
Assert.IsType<ImmutableSolidColorBrush>(bk);
Assert.Equal(Colors.White, (bk as ISolidColorBrush).Color);
}
[Fact]
@ -515,7 +516,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
Assert.NotNull(brush);
Assert.Equal(Colors.White, ((SolidColorBrush)brush).Color);
Assert.Equal(Colors.White, ((ISolidColorBrush)brush).Color);
style.TryGetResource("Double", out var d);

18
tests/Avalonia.Visuals.UnitTests/Media/BrushTests.cs

@ -12,7 +12,7 @@ namespace Avalonia.Visuals.UnitTests.Media
[Fact]
public void Parse_Parses_RGB_Hash_Brush()
{
var result = (SolidColorBrush)Brush.Parse("#ff8844");
var result = (ISolidColorBrush)Brush.Parse("#ff8844");
Assert.Equal(0xff, result.Color.R);
Assert.Equal(0x88, result.Color.G);
@ -23,7 +23,7 @@ namespace Avalonia.Visuals.UnitTests.Media
[Fact]
public void Parse_Parses_ARGB_Hash_Brush()
{
var result = (SolidColorBrush)Brush.Parse("#40ff8844");
var result = (ISolidColorBrush)Brush.Parse("#40ff8844");
Assert.Equal(0xff, result.Color.R);
Assert.Equal(0x88, result.Color.G);
@ -34,7 +34,7 @@ namespace Avalonia.Visuals.UnitTests.Media
[Fact]
public void Parse_Parses_Named_Brush_Lowercase()
{
var result = (SolidColorBrush)Brush.Parse("red");
var result = (ISolidColorBrush)Brush.Parse("red");
Assert.Equal(0xff, result.Color.R);
Assert.Equal(0x00, result.Color.G);
@ -45,7 +45,7 @@ namespace Avalonia.Visuals.UnitTests.Media
[Fact]
public void Parse_Parses_Named_Brush_Uppercase()
{
var result = (SolidColorBrush)Brush.Parse("RED");
var result = (ISolidColorBrush)Brush.Parse("RED");
Assert.Equal(0xff, result.Color.R);
Assert.Equal(0x00, result.Color.G);
@ -53,6 +53,16 @@ namespace Avalonia.Visuals.UnitTests.Media
Assert.Equal(0xff, result.Color.A);
}
[Fact]
public void Parse_ToString_Named_Brush_Roundtrip()
{
const string expectedName = "Red";
var brush = (ISolidColorBrush)Brush.Parse(expectedName);
var name = brush.ToString();
Assert.Equal(expectedName, name);
}
[Fact]
public void Parse_Hex_Value_Doesnt_Accept_Too_Few_Chars()
{

8
tests/Avalonia.Visuals.UnitTests/RelativeRectTests.cs

@ -1,6 +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;
using System.Globalization;
using Xunit;
@ -25,5 +26,12 @@ namespace Avalonia.Visuals.UnitTests
Assert.Equal(new RelativeRect(0.1, 0.2, 0.4, 0.7, RelativeUnit.Relative), result, Compare);
}
[Fact]
public void Parse_Should_Throw_Mixed_Values()
{
Assert.Throws<FormatException>(() =>
RelativeRect.Parse("10%, 20%, 40, 70%", CultureInfo.InvariantCulture));
}
}
}

Loading…
Cancel
Save