From 4f549c16fc35d527da751167b19467f5bd51918a Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Fri, 31 Aug 2018 14:43:48 +0200 Subject: [PATCH 001/374] Initial --- samples/ControlCatalog/ControlCatalog.csproj | 15 ++ samples/ControlCatalog/MainView.xaml | 6 +- .../ControlCatalog/Pages/TabControlPage.xaml | 124 ++++++++++ .../Pages/TabControlPage.xaml.cs | 80 +++++++ samples/ControlCatalog/SideBar.xaml | 104 +++++---- src/Avalonia.Controls/ContentControl.cs | 4 +- .../Generators/TabItemContainerGenerator.cs | 60 +++++ .../Primitives/HeaderedContentControl.cs | 21 +- src/Avalonia.Controls/TabControl.cs | 176 ++++++++------ src/Avalonia.Controls/TabItem.cs | 73 ++++++ .../Avalonia.Themes.Default.csproj | 8 + src/Avalonia.Themes.Default/DefaultTheme.xaml | 1 + src/Avalonia.Themes.Default/TabControl.xaml | 220 ++++++++++++++---- src/Avalonia.Themes.Default/TabItem.xaml | 45 ++++ .../TabControlTests.cs | 145 ++++++------ 15 files changed, 830 insertions(+), 252 deletions(-) create mode 100644 samples/ControlCatalog/Pages/TabControlPage.xaml create mode 100644 samples/ControlCatalog/Pages/TabControlPage.xaml.cs create mode 100644 src/Avalonia.Controls/Generators/TabItemContainerGenerator.cs create mode 100644 src/Avalonia.Themes.Default/TabItem.xaml diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj index dea9b35e24..8eab683049 100644 --- a/samples/ControlCatalog/ControlCatalog.csproj +++ b/samples/ControlCatalog/ControlCatalog.csproj @@ -11,6 +11,9 @@ + + + @@ -33,6 +36,18 @@ + + + + TabControlPage.xaml + + + + + + MSBuild:Compile + + \ No newline at end of file diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml index 87cb5e9c5c..1b613aee9a 100644 --- a/samples/ControlCatalog/MainView.xaml +++ b/samples/ControlCatalog/MainView.xaml @@ -2,9 +2,6 @@ xmlns:pages="clr-namespace:ControlCatalog.Pages" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> - - - @@ -21,12 +18,13 @@ - + + diff --git a/samples/ControlCatalog/Pages/TabControlPage.xaml b/samples/ControlCatalog/Pages/TabControlPage.xaml new file mode 100644 index 0000000000..5b10e7d790 --- /dev/null +++ b/samples/ControlCatalog/Pages/TabControlPage.xaml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + This is the first page in the TabControl. + + + + + + + + + + This is the second page in the TabControl. + + + + + + + + + You should not see this. + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tab Placement: + + Left + Bottom + Right + Top + + + + + diff --git a/samples/ControlCatalog/Pages/TabControlPage.xaml.cs b/samples/ControlCatalog/Pages/TabControlPage.xaml.cs new file mode 100644 index 0000000000..808d90a49c --- /dev/null +++ b/samples/ControlCatalog/Pages/TabControlPage.xaml.cs @@ -0,0 +1,80 @@ +using System; + +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using Avalonia.Media.Imaging; +using Avalonia.Platform; + +using ReactiveUI; + +namespace ControlCatalog.Pages +{ + using System.Collections.Generic; + + public class TabControlPage : UserControl + { + public TabControlPage() + { + InitializeComponent(); + + DataContext = new PageViewModel + { + Tabs = new[] + { + new TabItemViewModel + { + Header = "Arch", + Text = "This is the first templated tab page.", + Image = LoadBitmap("resm:ControlCatalog.Assets.delicate-arch-896885_640.jpg?assembly=ControlCatalog"), + }, + new TabItemViewModel + { + Header = "Leaf", + Text = "This is the second templated tab page.", + Image = LoadBitmap("resm:ControlCatalog.Assets.maple-leaf-888807_640.jpg?assembly=ControlCatalog"), + }, + new TabItemViewModel + { + Header = "Disabled", + Text = "You should not see this.", + IsEnabled = false, + }, + }, + TabPlacement = Dock.Top, + }; + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + + private IBitmap LoadBitmap(string uri) + { + var assets = AvaloniaLocator.Current.GetService(); + return new Bitmap(assets.Open(new Uri(uri))); + } + + private class PageViewModel : ReactiveObject + { + private Dock _tabPlacement; + + public TabItemViewModel[] Tabs { get; set; } + + public Dock TabPlacement + { + get { return _tabPlacement; } + set { this.RaiseAndSetIfChanged(ref _tabPlacement, value); } + } + } + + private class TabItemViewModel + { + public string Header { get; set; } + public string Text { get; set; } + public IBitmap Image { get; set; } + public bool IsEnabled { get; set; } = true; + } + } +} diff --git a/samples/ControlCatalog/SideBar.xaml b/samples/ControlCatalog/SideBar.xaml index 7d72d1821b..dbab4e4a27 100644 --- a/samples/ControlCatalog/SideBar.xaml +++ b/samples/ControlCatalog/SideBar.xaml @@ -1,52 +1,64 @@ - + - + - + - + diff --git a/src/Avalonia.Controls/ContentControl.cs b/src/Avalonia.Controls/ContentControl.cs index 6da6da54a5..4621524bdc 100644 --- a/src/Avalonia.Controls/ContentControl.cs +++ b/src/Avalonia.Controls/ContentControl.cs @@ -45,7 +45,7 @@ namespace Avalonia.Controls static ContentControl() { ContentControlMixin.Attach(ContentProperty, x => x.LogicalChildren); - } + } /// /// Gets or sets the content to display. @@ -65,7 +65,7 @@ namespace Avalonia.Controls { get { return GetValue(ContentTemplateProperty); } set { SetValue(ContentTemplateProperty, value); } - } + } /// /// Gets the presenter from the control's template. diff --git a/src/Avalonia.Controls/Generators/TabItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/TabItemContainerGenerator.cs new file mode 100644 index 0000000000..75788e393f --- /dev/null +++ b/src/Avalonia.Controls/Generators/TabItemContainerGenerator.cs @@ -0,0 +1,60 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +namespace Avalonia.Controls.Generators +{ + using Avalonia.Controls.Primitives; + + public class TabItemContainerGenerator : ItemContainerGenerator + { + public TabItemContainerGenerator(TabControl owner) + : base(owner, ContentControl.ContentProperty, ContentControl.ContentTemplateProperty) + { + Owner = owner; + } + + public new TabControl Owner { get; } + + protected override IControl CreateContainer(object item) + { + var tabItem = (TabItem)base.CreateContainer(item); + + tabItem.ParentTabControl = Owner; + + if (tabItem.HeaderTemplate == null) + { + tabItem[~HeaderedContentControl.HeaderTemplateProperty] = Owner[~ItemsControl.ItemTemplateProperty]; + } + + if (tabItem.Header == null) + { + if (item is IHeadered headered) + { + if (tabItem.Header != headered.Header) + { + tabItem.Header = headered.Header; + } + } + else + { + if (!(tabItem.DataContext is IControl)) + { + tabItem.Header = tabItem.DataContext; + } + } + } + + if (!(tabItem.Content is IControl)) + { + tabItem[~ContentControl.ContentTemplateProperty] = Owner[~TabControl.ContentTemplateProperty]; + } + + if (tabItem.Content == null) + { + tabItem[~ContentControl.ContentProperty] = tabItem[~StyledElement.DataContextProperty]; + } + + return tabItem; + } + } +} diff --git a/src/Avalonia.Controls/Primitives/HeaderedContentControl.cs b/src/Avalonia.Controls/Primitives/HeaderedContentControl.cs index d67ebfd489..b0517c23f1 100644 --- a/src/Avalonia.Controls/Primitives/HeaderedContentControl.cs +++ b/src/Avalonia.Controls/Primitives/HeaderedContentControl.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 Avalonia.Controls.Templates; + namespace Avalonia.Controls.Primitives { /// @@ -12,7 +14,13 @@ namespace Avalonia.Controls.Primitives /// Defines the property. /// public static readonly StyledProperty HeaderProperty = - AvaloniaProperty.Register(nameof(Header)); + AvaloniaProperty.Register(nameof(Header)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty HeaderTemplateProperty = + AvaloniaProperty.Register(nameof(HeaderTemplate)); /// /// Gets or sets the header content. @@ -21,6 +29,15 @@ namespace Avalonia.Controls.Primitives { get { return GetValue(HeaderProperty); } set { SetValue(HeaderProperty, value); } + } + + /// + /// Gets or sets the data template used to display the content of the control. + /// + public IDataTemplate HeaderTemplate + { + get { return GetValue(HeaderTemplateProperty); } + set { SetValue(HeaderTemplateProperty, value); } } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Controls/TabControl.cs b/src/Avalonia.Controls/TabControl.cs index 70cf8b4e05..043242543b 100644 --- a/src/Avalonia.Controls/TabControl.cs +++ b/src/Avalonia.Controls/TabControl.cs @@ -1,10 +1,14 @@ // 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.Animation; +using System; + using Avalonia.Controls.Generators; +using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; +using Avalonia.Input; +using Avalonia.Layout; namespace Avalonia.Controls { @@ -14,28 +18,50 @@ namespace Avalonia.Controls public class TabControl : SelectingItemsControl { /// - /// Defines the property. + /// Defines the property. /// - public static readonly StyledProperty PageTransitionProperty = - Avalonia.Controls.Carousel.PageTransitionProperty.AddOwner(); + public static readonly StyledProperty TabStripPlacementProperty = + AvaloniaProperty.Register(nameof(TabStripPlacement), defaultValue: Dock.Top); /// - /// Defines an that selects the content of a . + /// Defines the property. /// - public static readonly IMemberSelector ContentSelector = - new FuncMemberSelector(SelectContent); + public static readonly StyledProperty HorizontalContentAlignmentProperty = + ContentControl.HorizontalContentAlignmentProperty.AddOwner(); /// - /// Defines an that selects the header of a . + /// Defines the property. /// - public static readonly IMemberSelector HeaderSelector = - new FuncMemberSelector(SelectHeader); + public static readonly StyledProperty VerticalContentAlignmentProperty = + ContentControl.VerticalContentAlignmentProperty.AddOwner(); /// - /// Defines the property. + /// Defines the property. /// - public static readonly StyledProperty TabStripPlacementProperty = - AvaloniaProperty.Register(nameof(TabStripPlacement), defaultValue: Dock.Top); + public static readonly StyledProperty ContentTemplateProperty = + ContentControl.ContentTemplateProperty.AddOwner(); + + /// + /// The selected content property + /// + public static readonly StyledProperty SelectedContentProperty = + AvaloniaProperty.Register(nameof(SelectedContent)); + + /// + /// The selected content template property + /// + public static readonly StyledProperty SelectedContentTemplateProperty = + AvaloniaProperty.Register(nameof(SelectedContentTemplate)); + + /// + /// The default value for the property. + /// + private static readonly FuncTemplate DefaultPanel = + new FuncTemplate(() => new WrapPanel { Orientation = Orientation.Horizontal }); + + internal ItemsPresenter ItemsPresenterPart { get; private set; } + + internal ContentPresenter ContentPart { get; private set; } /// /// Initializes static members of the class. @@ -43,35 +69,26 @@ namespace Avalonia.Controls static TabControl() { SelectionModeProperty.OverrideDefaultValue(SelectionMode.AlwaysSelected); - FocusableProperty.OverrideDefaultValue(false); + ItemsPanelProperty.OverrideDefaultValue(DefaultPanel); AffectsMeasure(TabStripPlacementProperty); } /// - /// Gets the pages portion of the 's template. + /// Gets or sets the horizontal alignment of the content within the control. /// - public IControl Pages + public HorizontalAlignment HorizontalContentAlignment { - get; - private set; + get { return GetValue(HorizontalContentAlignmentProperty); } + set { SetValue(HorizontalContentAlignmentProperty, value); } } /// - /// Gets the tab strip portion of the 's template. + /// Gets or sets the vertical alignment of the content within the control. /// - public IControl TabStrip + public VerticalAlignment VerticalContentAlignment { - get; - private set; - } - - /// - /// Gets or sets the transition to use when switching tabs. - /// - public IPageTransition PageTransition - { - get { return GetValue(PageTransitionProperty); } - set { SetValue(PageTransitionProperty, value); } + get { return GetValue(VerticalContentAlignmentProperty); } + set { SetValue(VerticalContentAlignmentProperty, value); } } /// @@ -83,67 +100,82 @@ namespace Avalonia.Controls set { SetValue(TabStripPlacementProperty, value); } } - protected override IItemContainerGenerator CreateItemContainerGenerator() + /// + /// Gets or sets the data template used to display the content of the control. + /// + public IDataTemplate ContentTemplate { - // TabControl doesn't actually create items - instead its TabStrip and Carousel - // children create the items. However we want it to be a SelectingItemsControl - // so that it has the Items/SelectedItem etc properties. In this case, we can - // return a null ItemContainerGenerator to disable the creation of item containers. - return null; + get { return GetValue(ContentTemplateProperty); } + set { SetValue(ContentTemplateProperty, value); } } - protected override void OnTemplateApplied(TemplateAppliedEventArgs e) + /// + /// Gets or sets the currently selected content. + /// + /// + /// The content of the selected. + /// + public object SelectedContent { - base.OnTemplateApplied(e); - - TabStrip = e.NameScope.Find("PART_TabStrip"); - Pages = e.NameScope.Find("PART_Content"); + get { return GetValue(SelectedContentProperty); } + set { SetValue(SelectedContentProperty, value); } } /// - /// Selects the content of a tab item. + /// Gets or sets the template for the currently selected content. /// - /// The tab item. - /// The content. - private static object SelectContent(object o) + /// + /// The selected content template. + /// + public IDataTemplate SelectedContentTemplate + { + get { return GetValue(SelectedContentTemplateProperty); } + set { SetValue(SelectedContentTemplateProperty, value); } + } + + protected override IItemContainerGenerator CreateItemContainerGenerator() + { + return new TabItemContainerGenerator(this); + } + + protected override void OnTemplateApplied(TemplateAppliedEventArgs e) { - var content = o as IContentControl; + base.OnTemplateApplied(e); + + ItemsPresenterPart = e.NameScope.Find("PART_ItemsPresenter"); - if (content != null) + if (ItemsPresenterPart == null) { - return content.Content; + throw new NotSupportedException("ItemsPresenter not found."); } - else + + ContentPart = e.NameScope.Find("PART_Content"); + + if (ContentPart == null) { - return o; - } + throw new NotSupportedException("ContentPresenter not found."); + } } - /// - /// Selects the header of a tab item. - /// - /// The tab item. - /// The content. - private static object SelectHeader(object o) + /// + protected override void OnGotFocus(GotFocusEventArgs e) { - var headered = o as IHeadered; - var control = o as IControl; + base.OnGotFocus(e); - if (headered != null) + if (e.NavigationMethod == NavigationMethod.Directional) { - return headered.Header ?? string.Empty; + e.Handled = UpdateSelectionFromEventSource(e.Source); } - else if (control != null) - { - // Non-headered control items should result in TabStripItems with empty content. - // If a TabStrip is created with non IHeadered controls as its items, don't try to - // display the control in the TabStripItem: the content portion will also try to - // display this control, resulting in dual-parentage breakage. - return string.Empty; - } - else + } + + /// + protected override void OnPointerPressed(PointerPressedEventArgs e) + { + base.OnPointerPressed(e); + + if (e.MouseButton == MouseButton.Left) { - return o; + e.Handled = UpdateSelectionFromEventSource(e.Source); } } } diff --git a/src/Avalonia.Controls/TabItem.cs b/src/Avalonia.Controls/TabItem.cs index 4fb68c8b6f..80a3846ab2 100644 --- a/src/Avalonia.Controls/TabItem.cs +++ b/src/Avalonia.Controls/TabItem.cs @@ -11,12 +11,20 @@ namespace Avalonia.Controls /// public class TabItem : HeaderedContentControl, ISelectable { + /// + /// Defines the property. + /// + public static readonly StyledProperty TabStripPlacementProperty = + TabControl.TabStripPlacementProperty.AddOwner(); + /// /// Defines the property. /// public static readonly StyledProperty IsSelectedProperty = ListBoxItem.IsSelectedProperty.AddOwner(); + private TabControl _parentTabControl; + /// /// Initializes static members of the class. /// @@ -24,6 +32,19 @@ namespace Avalonia.Controls { SelectableMixin.Attach(IsSelectedProperty); FocusableProperty.OverrideDefaultValue(typeof(TabItem), true); + IsSelectedProperty.Changed.AddClassHandler(x => x.UpdateSelectedContent); + DataContextProperty.Changed.AddClassHandler(x => x.UpdateHeader); + } + + /// + /// Gets the tab strip placement. + /// + /// + /// The tab strip placement. + /// + public Dock TabStripPlacement + { + get { return GetValue(TabStripPlacementProperty); } } /// @@ -34,5 +55,57 @@ namespace Avalonia.Controls get { return GetValue(IsSelectedProperty); } set { SetValue(IsSelectedProperty, value); } } + + internal TabControl ParentTabControl + { + get => _parentTabControl; + set => _parentTabControl = value; + } + + private void UpdateHeader(AvaloniaPropertyChangedEventArgs obj) + { + if (Header == null) + { + if (obj.NewValue is IHeadered headered) + { + if (Header != headered.Header) + { + Header = headered.Header; + } + } + else + { + if (!(obj.NewValue is IControl)) + { + Header = obj.NewValue; + } + } + } + else + { + if (Header == obj.OldValue) + { + Header = obj.NewValue; + } + } + } + + private void UpdateSelectedContent(AvaloniaPropertyChangedEventArgs e) + { + if (!IsSelected) + { + return; + } + + if (ParentTabControl.SelectedContentTemplate != ContentTemplate) + { + ParentTabControl.SelectedContentTemplate = ContentTemplate; + } + + if (ParentTabControl.SelectedContent != Content) + { + ParentTabControl.SelectedContent = Content; + } + } } } diff --git a/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj b/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj index eceafc2371..4e73afb755 100644 --- a/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj +++ b/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj @@ -2,6 +2,9 @@ netstandard2.0 + + + @@ -15,4 +18,9 @@ + + + MSBuild:Compile + + \ No newline at end of file diff --git a/src/Avalonia.Themes.Default/DefaultTheme.xaml b/src/Avalonia.Themes.Default/DefaultTheme.xaml index 2b9132ee56..7b8710866a 100644 --- a/src/Avalonia.Themes.Default/DefaultTheme.xaml +++ b/src/Avalonia.Themes.Default/DefaultTheme.xaml @@ -30,6 +30,7 @@ + diff --git a/src/Avalonia.Themes.Default/TabControl.xaml b/src/Avalonia.Themes.Default/TabControl.xaml index 76cf190151..43265764ec 100644 --- a/src/Avalonia.Themes.Default/TabControl.xaml +++ b/src/Avalonia.Themes.Default/TabControl.xaml @@ -1,50 +1,172 @@ - - - - - - \ No newline at end of file + + + + + diff --git a/src/Avalonia.Themes.Default/TabItem.xaml b/src/Avalonia.Themes.Default/TabItem.xaml new file mode 100644 index 0000000000..311fb2b973 --- /dev/null +++ b/src/Avalonia.Themes.Default/TabItem.xaml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + diff --git a/tests/Avalonia.Controls.UnitTests/TabControlTests.cs b/tests/Avalonia.Controls.UnitTests/TabControlTests.cs index 322c14c6bd..a5c3881d37 100644 --- a/tests/Avalonia.Controls.UnitTests/TabControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TabControlTests.cs @@ -22,7 +22,7 @@ namespace Avalonia.Controls.UnitTests TabItem selected; var target = new TabControl { - Template = new FuncControlTemplate(CreateTabControlTemplate), + Template = TabControlTemplate(), Items = new[] { (selected = new TabItem @@ -61,7 +61,7 @@ namespace Avalonia.Controls.UnitTests var target = new TabControl { - Template = new FuncControlTemplate(CreateTabControlTemplate), + Template = TabControlTemplate(), Items = items, }; @@ -94,7 +94,7 @@ namespace Avalonia.Controls.UnitTests var target = new TabControl { - Template = new FuncControlTemplate(CreateTabControlTemplate), + Template = TabControlTemplate(), Items = collection, }; @@ -147,7 +147,7 @@ namespace Avalonia.Controls.UnitTests }, Child = new TabControl { - Template = new FuncControlTemplate(CreateTabControlTemplate), + Template = TabControlTemplate(), Items = collection, } }; @@ -172,7 +172,7 @@ namespace Avalonia.Controls.UnitTests var target = new TabControl { - Template = new FuncControlTemplate(CreateTabControlTemplate), + Template = TabControlTemplate(), DataContext = "Base", DataTemplates = { @@ -182,41 +182,39 @@ namespace Avalonia.Controls.UnitTests }; ApplyTemplate(target); - var carousel = (Carousel)target.Pages; - var container = (ContentPresenter)carousel.Presenter.Panel.Children.Single(); - container.UpdateChild(); - var dataContext = ((TextBlock)container.Child).DataContext; + target.ContentPart.UpdateChild(); + var dataContext = ((TextBlock)target.ContentPart.Child).DataContext; Assert.Equal(items[0], dataContext); target.SelectedIndex = 1; - container = (ContentPresenter)carousel.Presenter.Panel.Children.Single(); - container.UpdateChild(); - dataContext = ((Button)container.Child).DataContext; + target.ContentPart.UpdateChild(); + dataContext = ((Button)target.ContentPart.Child).DataContext; Assert.Equal(items[1], dataContext); target.SelectedIndex = 2; - dataContext = ((TextBlock)carousel.Presenter.Panel.Children.Single()).DataContext; + target.ContentPart.UpdateChild(); + dataContext = ((TextBlock)target.ContentPart.Child).DataContext; Assert.Equal("Base", dataContext); target.SelectedIndex = 3; - container = (ContentPresenter)carousel.Presenter.Panel.Children[0]; - container.UpdateChild(); - dataContext = ((TextBlock)container.Child).DataContext; + target.ContentPart.UpdateChild(); + dataContext = ((TextBlock)target.ContentPart.Child).DataContext; Assert.Equal("Qux", dataContext); target.SelectedIndex = 4; - dataContext = ((TextBlock)carousel.Presenter.Panel.Children.Single()).DataContext; + target.ContentPart.UpdateChild(); + dataContext = target.ContentPart.DataContext; Assert.Equal("Base", dataContext); } /// - /// Non-headered control items should result in TabStripItems with empty content. + /// Non-headered control items should result in TabItems with empty header. /// /// - /// If a TabStrip is created with non IHeadered controls as its items, don't try to - /// display the control in the TabStripItem: if the TabStrip is part of a TabControl - /// then *that* will also try to display the control, resulting in dual-parentage + /// If a TabControl is created with non IHeadered controls as its items, don't try to + /// display the control in the header: if the control is part of the header then + /// *that* control would also end up in the content region, resulting in dual-parentage /// breakage. /// [Fact] @@ -230,18 +228,20 @@ namespace Avalonia.Controls.UnitTests var target = new TabControl { - Template = new FuncControlTemplate(CreateTabControlTemplate), + Template = TabControlTemplate(), Items = items, }; ApplyTemplate(target); - var result = target.TabStrip.GetLogicalChildren() - .OfType() - .Select(x => x.Content) + var logicalChildren = target.ItemsPresenterPart.Panel.GetLogicalChildren(); + + var result = logicalChildren + .OfType() + .Select(x => x.Header) .ToList(); - Assert.Equal(new object[] { string.Empty, string.Empty }, result); + Assert.Equal(new object[] { null, null }, result); } [Fact] @@ -249,7 +249,7 @@ namespace Avalonia.Controls.UnitTests { TabControl target = new TabControl { - Template = new FuncControlTemplate(CreateTabControlTemplate), + Template = TabControlTemplate(), Items = new[] { new TabItem { Header = "Foo" }, @@ -262,70 +262,61 @@ namespace Avalonia.Controls.UnitTests target.SelectedIndex = 2; - var carousel = (Carousel)target.Pages; - var page = (TabItem)carousel.SelectedItem; + var page = (TabItem)target.SelectedItem; Assert.Null(page.Content); } - private Control CreateTabControlTemplate(TabControl parent) + private IControlTemplate TabControlTemplate() { - return new StackPanel - { - Children = - { - new TabStrip - { - Name = "PART_TabStrip", - Template = new FuncControlTemplate(CreateTabStripTemplate), - MemberSelector = TabControl.HeaderSelector, - [!TabStrip.ItemsProperty] = parent[!TabControl.ItemsProperty], - [!!TabStrip.SelectedIndexProperty] = parent[!!TabControl.SelectedIndexProperty] - }, - new Carousel - { - Name = "PART_Content", - Template = new FuncControlTemplate(CreateCarouselTemplate), - MemberSelector = TabControl.ContentSelector, - [!Carousel.ItemsProperty] = parent[!TabControl.ItemsProperty], - [!Carousel.SelectedItemProperty] = parent[!TabControl.SelectedItemProperty], - } - } - }; - } + return new FuncControlTemplate(parent => - private Control CreateTabStripTemplate(TabStrip parent) - { - return new ItemsPresenter - { - Name = "PART_ItemsPresenter", - [~ItemsPresenter.ItemsProperty] = parent[~ItemsControl.ItemsProperty], - [!CarouselPresenter.MemberSelectorProperty] = parent[!ItemsControl.MemberSelectorProperty], - }; + new StackPanel + { + Children = { + new ItemsPresenter + { + Name = "PART_ItemsPresenter", + [!TabStrip.ItemsProperty] = parent[!TabControl.ItemsProperty], + [!TabStrip.ItemTemplateProperty] = parent[!TabControl.ItemTemplateProperty], + }, + new ContentPresenter + { + Name = "PART_Content", + [!ContentPresenter.ContentProperty] = parent[!TabControl.SelectedContentProperty], + [!ContentPresenter.ContentTemplateProperty] = parent[!TabControl.SelectedContentTemplateProperty], + } + } + }); } - private Control CreateCarouselTemplate(Carousel control) + private IControlTemplate TabItemTemplate() { - return new CarouselPresenter - { - Name = "PART_ItemsPresenter", - [!CarouselPresenter.ItemsProperty] = control[!ItemsControl.ItemsProperty], - [!CarouselPresenter.ItemsPanelProperty] = control[!ItemsControl.ItemsPanelProperty], - [!CarouselPresenter.MemberSelectorProperty] = control[!ItemsControl.MemberSelectorProperty], - [!CarouselPresenter.SelectedIndexProperty] = control[!SelectingItemsControl.SelectedIndexProperty], - [~CarouselPresenter.PageTransitionProperty] = control[~Carousel.PageTransitionProperty], - }; + return new FuncControlTemplate(parent => + new ContentPresenter + { + Name = "PART_ContentPresenter", + [!ContentPresenter.ContentProperty] = parent[!TabItem.HeaderProperty], + [!ContentPresenter.ContentTemplateProperty] = parent[!TabItem.HeaderTemplateProperty] + }); } private void ApplyTemplate(TabControl target) { target.ApplyTemplate(); - var carousel = (Carousel)target.Pages; - carousel.ApplyTemplate(); - carousel.Presenter.ApplyTemplate(); - var tabStrip = (TabStrip)target.TabStrip; - tabStrip.ApplyTemplate(); - tabStrip.Presenter.ApplyTemplate(); + + target.Presenter.ApplyTemplate(); + + foreach (var tabItem in target.GetLogicalChildren().OfType()) + { + tabItem.Template = TabItemTemplate(); + + tabItem.ApplyTemplate(); + + ((ContentPresenter)tabItem.Presenter).UpdateChild(); + } + + target.ContentPart.ApplyTemplate(); } private class Item From 63de9f3ddf60ce26f6eadf75f228c8f166012bcb Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Thu, 6 Sep 2018 16:22:01 +0200 Subject: [PATCH 002/374] Rework default style --- src/Avalonia.Themes.Default/TabControl.xaml | 114 ++------------------ 1 file changed, 8 insertions(+), 106 deletions(-) diff --git a/src/Avalonia.Themes.Default/TabControl.xaml b/src/Avalonia.Themes.Default/TabControl.xaml index 43265764ec..1e5b5c8841 100644 --- a/src/Avalonia.Themes.Default/TabControl.xaml +++ b/src/Avalonia.Themes.Default/TabControl.xaml @@ -44,79 +44,10 @@ - + + From ab97dbb12c87f2188eebd8bd22add64c6464ef81 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Thu, 6 Sep 2018 20:28:20 +0200 Subject: [PATCH 003/374] Selector rework --- samples/ControlCatalog/SideBar.xaml | 5 ++--- src/Avalonia.Themes.Default/TabItem.xaml | 16 +++++++--------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/samples/ControlCatalog/SideBar.xaml b/samples/ControlCatalog/SideBar.xaml index dbab4e4a27..3552f224fc 100644 --- a/samples/ControlCatalog/SideBar.xaml +++ b/samples/ControlCatalog/SideBar.xaml @@ -39,7 +39,7 @@ - - - - - - - - - - - - From 00cc08f979b38625393bd87dd06fe0070fff1faa Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Thu, 6 Sep 2018 23:44:04 +0200 Subject: [PATCH 004/374] New selectors --- samples/ControlCatalog/SideBar.xaml | 4 ++-- src/Avalonia.Themes.Default/TabItem.xaml | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/samples/ControlCatalog/SideBar.xaml b/samples/ControlCatalog/SideBar.xaml index 3552f224fc..62c200d8d6 100644 --- a/samples/ControlCatalog/SideBar.xaml +++ b/samples/ControlCatalog/SideBar.xaml @@ -52,11 +52,11 @@ - - diff --git a/src/Avalonia.Themes.Default/TabItem.xaml b/src/Avalonia.Themes.Default/TabItem.xaml index b51d1426ef..8b30d40d7e 100644 --- a/src/Avalonia.Themes.Default/TabItem.xaml +++ b/src/Avalonia.Themes.Default/TabItem.xaml @@ -25,19 +25,19 @@ - - - - - From 8985af21ba685036740e8d75d3a8b57b46c0e53d Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Fri, 7 Sep 2018 00:24:24 +0200 Subject: [PATCH 005/374] Selector fix --- samples/ControlCatalog/SideBar.xaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/samples/ControlCatalog/SideBar.xaml b/samples/ControlCatalog/SideBar.xaml index 62c200d8d6..3ec8e43b07 100644 --- a/samples/ControlCatalog/SideBar.xaml +++ b/samples/ControlCatalog/SideBar.xaml @@ -52,12 +52,16 @@ + + From fc709c3a0fc9b72b6eec7038b2be3adf8917c036 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 7 Sep 2018 00:51:25 +0200 Subject: [PATCH 006/374] Partially fix tab strip orientation. --- src/Avalonia.Controls/TabControl.cs | 2 +- src/Avalonia.Themes.Default/TabControl.xaml | 30 +++++++-------------- 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/src/Avalonia.Controls/TabControl.cs b/src/Avalonia.Controls/TabControl.cs index 043242543b..f81e1560c2 100644 --- a/src/Avalonia.Controls/TabControl.cs +++ b/src/Avalonia.Controls/TabControl.cs @@ -57,7 +57,7 @@ namespace Avalonia.Controls /// The default value for the property. /// private static readonly FuncTemplate DefaultPanel = - new FuncTemplate(() => new WrapPanel { Orientation = Orientation.Horizontal }); + new FuncTemplate(() => new WrapPanel()); internal ItemsPresenter ItemsPresenterPart { get; private set; } diff --git a/src/Avalonia.Themes.Default/TabControl.xaml b/src/Avalonia.Themes.Default/TabControl.xaml index 1e5b5c8841..e551b2e841 100644 --- a/src/Avalonia.Themes.Default/TabControl.xaml +++ b/src/Avalonia.Themes.Default/TabControl.xaml @@ -24,7 +24,6 @@ + - - + From ed460795716b3df233fec43ed791a5a114ebf498 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Fri, 7 Sep 2018 01:30:49 +0200 Subject: [PATCH 007/374] Share the same style between TabItem and TabStripItem --- src/Avalonia.Themes.Default/TabItem.xaml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Avalonia.Themes.Default/TabItem.xaml b/src/Avalonia.Themes.Default/TabItem.xaml index 8b30d40d7e..6d7cdea1fd 100644 --- a/src/Avalonia.Themes.Default/TabItem.xaml +++ b/src/Avalonia.Themes.Default/TabItem.xaml @@ -3,12 +3,9 @@ - - - - From 16ea1f37621b0400c96e5f4085195ce4a4573b53 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sun, 7 Oct 2018 22:05:00 +0300 Subject: [PATCH 008/374] [X11] Platform threading interface and platform sanity checks --- Avalonia.sln | 54 ++++ samples/PlatformSanityChecks/App.xaml | 6 + samples/PlatformSanityChecks/App.xaml.cs | 13 + .../PlatformSanityChecks.csproj | 24 ++ samples/PlatformSanityChecks/Program.cs | 140 ++++++++++ src/Avalonia.X11/Avalonia.X11.csproj | 12 + src/Avalonia.X11/X11Exception.cs | 12 + src/Avalonia.X11/X11Platform.cs | 39 +++ src/Avalonia.X11/X11PlatformThreading.cs | 251 ++++++++++++++++++ src/Avalonia.X11/XLib.cs | 105 ++++++++ 10 files changed, 656 insertions(+) create mode 100644 samples/PlatformSanityChecks/App.xaml create mode 100644 samples/PlatformSanityChecks/App.xaml.cs create mode 100644 samples/PlatformSanityChecks/PlatformSanityChecks.csproj create mode 100644 samples/PlatformSanityChecks/Program.cs create mode 100644 src/Avalonia.X11/Avalonia.X11.csproj create mode 100644 src/Avalonia.X11/X11Exception.cs create mode 100644 src/Avalonia.X11/X11Platform.cs create mode 100644 src/Avalonia.X11/X11PlatformThreading.cs create mode 100644 src/Avalonia.X11/XLib.cs diff --git a/Avalonia.sln b/Avalonia.sln index f71a94888d..388dbb6e6a 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -188,6 +188,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Skia.UnitTests", " EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.OpenGL", "src\Avalonia.OpenGL\Avalonia.OpenGL.csproj", "{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.X11", "src\Avalonia.X11\Avalonia.X11.csproj", "{212D02D5-C873-469A-8E78-4A6350EC4A1A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlatformSanityChecks", "samples\PlatformSanityChecks\PlatformSanityChecks.csproj", "{8B5768BB-71F9-4E23-89B5-DDBA6458B856}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13 @@ -1689,6 +1693,54 @@ Global {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Release|iPhone.Build.0 = Release|Any CPU {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.AppStore|iPhone.Build.0 = Debug|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.Debug|iPhone.Build.0 = Debug|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.Release|Any CPU.Build.0 = Release|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.Release|iPhone.ActiveCfg = Release|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.Release|iPhone.Build.0 = Release|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {212D02D5-C873-469A-8E78-4A6350EC4A1A}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.AppStore|iPhone.Build.0 = Debug|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Debug|iPhone.Build.0 = Debug|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Release|Any CPU.Build.0 = Release|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Release|iPhone.ActiveCfg = Release|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Release|iPhone.Build.0 = Release|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Release|iPhoneSimulator.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1742,6 +1794,8 @@ Global {CBFD5788-567D-401B-9DFA-74E4224025A0} = {A59C4C0A-64DF-4621-B450-2BA00D6F61E2} {4ADA61C8-D191-428D-9066-EF4F0D86520F} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637} {E1240B49-7B4B-4371-A00E-068778C5CF0B} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} + {212D02D5-C873-469A-8E78-4A6350EC4A1A} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B} + {8B5768BB-71F9-4E23-89B5-DDBA6458B856} = {9B9E3891-2366-4253-A952-D08BCEB71098} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A} diff --git a/samples/PlatformSanityChecks/App.xaml b/samples/PlatformSanityChecks/App.xaml new file mode 100644 index 0000000000..25bab6ae35 --- /dev/null +++ b/samples/PlatformSanityChecks/App.xaml @@ -0,0 +1,6 @@ + + + + + + diff --git a/samples/PlatformSanityChecks/App.xaml.cs b/samples/PlatformSanityChecks/App.xaml.cs new file mode 100644 index 0000000000..508fc1e34b --- /dev/null +++ b/samples/PlatformSanityChecks/App.xaml.cs @@ -0,0 +1,13 @@ +using Avalonia; +using Avalonia.Markup.Xaml; + +namespace PlatformSanityChecks +{ + public class App : Application + { + public override void Initialize() + { + AvaloniaXamlLoader.Load(this); + } + } +} diff --git a/samples/PlatformSanityChecks/PlatformSanityChecks.csproj b/samples/PlatformSanityChecks/PlatformSanityChecks.csproj new file mode 100644 index 0000000000..00b5b10106 --- /dev/null +++ b/samples/PlatformSanityChecks/PlatformSanityChecks.csproj @@ -0,0 +1,24 @@ + + + + Exe + netcoreapp2.0 + + + + + + + + + + + + + + + + + + + diff --git a/samples/PlatformSanityChecks/Program.cs b/samples/PlatformSanityChecks/Program.cs new file mode 100644 index 0000000000..8a3aa82981 --- /dev/null +++ b/samples/PlatformSanityChecks/Program.cs @@ -0,0 +1,140 @@ +using System; +using System.Diagnostics; +using System.Reactive.Disposables; +using System.Runtime.CompilerServices; +using System.Threading; +using Avalonia; +using Avalonia.Platform; +using Avalonia.Threading; +using Avalonia.X11; + +namespace PlatformSanityChecks +{ + public class Program + { + static Thread UiThread; + + static void Main(string[] args) + { + UiThread = Thread.CurrentThread; + AppBuilder.Configure().RuntimePlatformServicesInitializer(); + var app = new App(); + + new AvaloniaX11Platform().Initialize(); + + CheckPlatformThreading(); + + + + } + + static bool CheckAccess() => UiThread == Thread.CurrentThread; + + static void VerifyAccess() + { + if (!CheckAccess()) + Die("Call from invalid thread"); + } + + static Exception Die(string error) + { + Console.Error.WriteLine(error); + Console.Error.WriteLine(Environment.StackTrace); + Process.GetCurrentProcess().Kill(); + throw new Exception(error); + } + + static IDisposable Enter([CallerMemberName] string caller = null) + { + Console.WriteLine("Entering " + caller); + return Disposable.Create(() => { Console.WriteLine("Leaving " + caller); }); + } + + static void EnterLoop(Action cb, [CallerMemberName] string caller = null) + { + using (Enter(caller)) + { + var cts = new CancellationTokenSource(); + cb(cts); + Dispatcher.UIThread.MainLoop(cts.Token); + if (!cts.IsCancellationRequested) + Die("Unexpected loop exit"); + } + } + + static void CheckTimerOrdering() => EnterLoop(cts => + { + bool firstFired = false, secondFired = false; + DispatcherTimer.Run(() => + { + Console.WriteLine("Second tick"); + VerifyAccess(); + if (!firstFired) + throw Die("Invalid timer ordering"); + if (secondFired) + throw Die("Invocation of finished timer"); + secondFired = true; + cts.Cancel(); + return false; + }, TimeSpan.FromSeconds(2)); + DispatcherTimer.Run(() => + { + Console.WriteLine("First tick"); + VerifyAccess(); + if (secondFired) + throw Die("Invalid timer ordering"); + if (firstFired) + throw Die("Invocation of finished timer"); + firstFired = true; + return false; + }, TimeSpan.FromSeconds(1)); + }); + + static void CheckTimerTicking() => EnterLoop(cts => + { + int ticks = 0; + var st = Stopwatch.StartNew(); + DispatcherTimer.Run(() => + { + ticks++; + Console.WriteLine($"Tick {ticks} at {st.Elapsed}"); + if (ticks == 5) + { + if (st.Elapsed.TotalSeconds < 4.5) + Die("Timer is too fast"); + if (st.Elapsed.TotalSeconds > 6) + Die("Timer is too slow"); + cts.Cancel(); + return false; + } + + return true; + }, TimeSpan.FromSeconds(1)); + + + }); + + + static void CheckSignaling() => EnterLoop(cts => + { + ThreadPool.QueueUserWorkItem(_ => + { + Thread.Sleep(100); + Dispatcher.UIThread.Post(() => + { + VerifyAccess(); + cts.Cancel(); + }); + }); + }); + + static void CheckPlatformThreading() + { + CheckSignaling(); + CheckTimerOrdering(); + CheckTimerTicking(); + + } + + } +} diff --git a/src/Avalonia.X11/Avalonia.X11.csproj b/src/Avalonia.X11/Avalonia.X11.csproj new file mode 100644 index 0000000000..a035febe5b --- /dev/null +++ b/src/Avalonia.X11/Avalonia.X11.csproj @@ -0,0 +1,12 @@ + + + + netstandard2.0 + true + + + + + + + diff --git a/src/Avalonia.X11/X11Exception.cs b/src/Avalonia.X11/X11Exception.cs new file mode 100644 index 0000000000..2ac5a31d9b --- /dev/null +++ b/src/Avalonia.X11/X11Exception.cs @@ -0,0 +1,12 @@ +using System; + +namespace Avalonia.X11 +{ + public class X11Exception : Exception + { + public X11Exception(string message) : base(message) + { + + } + } +} diff --git a/src/Avalonia.X11/X11Platform.cs b/src/Avalonia.X11/X11Platform.cs new file mode 100644 index 0000000000..c266433eef --- /dev/null +++ b/src/Avalonia.X11/X11Platform.cs @@ -0,0 +1,39 @@ +using System; +using Avalonia.Controls; +using Avalonia.Platform; +using Avalonia.X11; +using static Avalonia.X11.XLib; +namespace Avalonia.X11 +{ + public class AvaloniaX11Platform + { + public void Initialize() + { + Display = XOpenDisplay(IntPtr.Zero); + DeferredDisplay = XOpenDisplay(IntPtr.Zero); + if (Display == IntPtr.Zero) + throw new Exception("XOpenDisplay failed"); + + + AvaloniaLocator.CurrentMutable.BindToSelf(this) + .Bind().ToConstant(new X11PlatformThreading(Display)); + + } + + public IntPtr DeferredDisplay { get; set; } + public IntPtr Display { get; set; } + } +} + +namespace Avalonia +{ + public static class AvaloniaX11PlatformExtensions + { + public static T UseX11(this T builder) where T : AppBuilderBase, new() + { + builder.UseWindowingSubsystem(() => new AvaloniaX11Platform().Initialize()); + return builder; + } + } + +} diff --git a/src/Avalonia.X11/X11PlatformThreading.cs b/src/Avalonia.X11/X11PlatformThreading.cs new file mode 100644 index 0000000000..18b08085e3 --- /dev/null +++ b/src/Avalonia.X11/X11PlatformThreading.cs @@ -0,0 +1,251 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; +using Avalonia.Platform; +using Avalonia.Threading; +using static Avalonia.X11.XLib; + +namespace Avalonia.X11 +{ + public unsafe class X11PlatformThreading : IPlatformThreadingInterface + { + private readonly IntPtr _display; + private Thread _mainThread; + + [StructLayout(LayoutKind.Explicit)] + struct epoll_data + { + [FieldOffset(0)] + public IntPtr ptr; + [FieldOffset(0)] + public int fd; + [FieldOffset(0)] + public uint u32; + [FieldOffset(0)] + public ulong u64; + } + + private const int EPOLLIN = 1; + private const int EPOLL_CTL_ADD = 1; + private const int O_NONBLOCK = 2048; + + [StructLayout(LayoutKind.Sequential)] + struct epoll_event + { + public uint events; + public epoll_data data; + } + + [DllImport("libc")] + extern static int epoll_create1(int size); + + [DllImport("libc")] + extern static int epoll_ctl(int epfd, int op, int fd, ref epoll_event __event); + + [DllImport("libc")] + extern static int epoll_wait(int epfd, epoll_event* events, int maxevents, int timeout); + + [DllImport("libc")] + extern static int pipe2(int* fds, int flags); + [DllImport("libc")] + extern static IntPtr write(int fd, void* buf, IntPtr count); + + [DllImport("libc")] + extern static IntPtr read(int fd, void* buf, IntPtr count); + + enum EventCodes + { + X11 = 1, + Signal =2 + } + + private int _sigread, _sigwrite; + private object _lock = new object(); + private bool _signaled; + private DispatcherPriority _signaledPriority; + private int _epoll; + private Stopwatch _clock = Stopwatch.StartNew(); + + class X11Timer : IDisposable + { + private readonly X11PlatformThreading _parent; + + public X11Timer(X11PlatformThreading parent, DispatcherPriority prio, TimeSpan interval, Action tick) + { + _parent = parent; + Priority = prio; + Tick = tick; + Interval = interval; + Reschedule(); + } + + public DispatcherPriority Priority { get; } + public TimeSpan NextTick { get; private set; } + public TimeSpan Interval { get; } + public Action Tick { get; } + public bool Disposed { get; private set; } + + public void Reschedule() + { + NextTick = _parent._clock.Elapsed + Interval; + } + + public void Dispose() + { + Disposed = true; + lock (_parent._lock) + _parent._timers.Remove(this); + } + } + + List _timers = new List(); + + public X11PlatformThreading(IntPtr display) + { + _display = display; + _mainThread = Thread.CurrentThread; + var fd = XLib.XConnectionNumber(display); + var ev = new epoll_event() + { + events = EPOLLIN, + data = {u32 = (int)EventCodes.X11} + }; + _epoll = epoll_create1(0); + if (_epoll == -1) + throw new X11Exception("epoll_create1 failed"); + + if (epoll_ctl(_epoll, EPOLL_CTL_ADD, fd, ref ev) == -1) + throw new X11Exception("Unable to attach X11 connection handle to epoll"); + + var fds = stackalloc int[2]; + pipe2(fds, O_NONBLOCK); + _sigread = fds[0]; + _sigwrite = fds[1]; + + ev = new epoll_event + { + events = EPOLLIN, + data = {u32 = (int)EventCodes.Signal} + }; + if (epoll_ctl(_epoll, EPOLL_CTL_ADD, _sigread, ref ev) == -1) + throw new X11Exception("Unable to attach signal pipe to epoll"); + } + + int TimerComparer(X11Timer t1, X11Timer t2) + { + return t2.Priority - t1.Priority; + } + + public void RunLoop(CancellationToken cancellationToken) + { + var readyTimers = new List(); + while (!cancellationToken.IsCancellationRequested) + { + var now = _clock.Elapsed; + TimeSpan? nextTick = null; + readyTimers.Clear(); + lock(_timers) + foreach (var t in _timers) + { + if (nextTick == null || t.NextTick < nextTick.Value) + nextTick = t.NextTick; + if (t.NextTick < now) + readyTimers.Add(t); + } + + readyTimers.Sort(TimerComparer); + + foreach (var t in readyTimers) + { + if (cancellationToken.IsCancellationRequested) + return; + t.Tick(); + if(!t.Disposed) + { + t.Reschedule(); + if (nextTick == null || t.NextTick < nextTick.Value) + nextTick = t.NextTick; + } + } + + if (cancellationToken.IsCancellationRequested) + return; + epoll_event ev; + var len = epoll_wait(_epoll, &ev, 1, + nextTick == null ? -1 : Math.Max(1, (int)(nextTick.Value - _clock.Elapsed).TotalMilliseconds)); + if (cancellationToken.IsCancellationRequested) + return; + if (len == 0) + { + // We handle timer-related stuff at the beginning of the loop + continue; + } + else + { + if (ev.data.u32 == (int)EventCodes.Signal) + { + int buf = 0; + while (read(_sigread, &buf, new IntPtr(4)).ToInt64() > 0) + { + } + + DispatcherPriority prio; + lock (_lock) + { + _signaled = false; + prio = _signaledPriority; + _signaledPriority = DispatcherPriority.MinValue; + } + Signaled?.Invoke(prio); + } + else + { + while (XPending(_display)) + { + if (cancellationToken.IsCancellationRequested) + return; + XNextEvent(_display, out var xev); + } + } + } + } + } + + + + public void Signal(DispatcherPriority priority) + { + lock (_lock) + { + if (priority > _signaledPriority) + _signaledPriority = priority; + + if(_signaled) + return; + _signaled = true; + int buf = 0; + write(_sigwrite, &buf, new IntPtr(1)); + } + } + + public bool CurrentThreadIsLoopThread => Thread.CurrentThread == _mainThread; + public event Action Signaled; + + public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick) + { + if (_mainThread != Thread.CurrentThread) + throw new InvalidOperationException("StartTimer can be only called from UI thread"); + if (interval <= TimeSpan.Zero) + throw new ArgumentException("Interval must be positive", nameof(interval)); + + // We assume that we are on the main thread and outside of epoll_wait, so there is no need for wakeup signal + + var timer = new X11Timer(this, priority, interval, tick); + lock(_timers) + _timers.Add(timer); + return timer; + } + } +} diff --git a/src/Avalonia.X11/XLib.cs b/src/Avalonia.X11/XLib.cs new file mode 100644 index 0000000000..88d36091ca --- /dev/null +++ b/src/Avalonia.X11/XLib.cs @@ -0,0 +1,105 @@ +using System; +using System.Runtime.InteropServices; + +namespace Avalonia.X11 +{ + public static class XLib + { + + [DllImport("libX11.so.6")] + public static extern IntPtr XInitThreads(); + + [DllImport("libX11.so.6")] + public static extern IntPtr XOpenDisplay(IntPtr name); + + [DllImport("libX11.so.6")] + public static extern int XConnectionNumber(IntPtr display); + + [DllImport("libX11.so.6")] + public static extern IntPtr XLockDisplay(IntPtr display); + + [DllImport("libX11.so.6")] + public static extern IntPtr XUnlockDisplay(IntPtr display); + + [DllImport("libX11.so.6")] + public static extern IntPtr XFreeGC(IntPtr display, IntPtr gc); + + [DllImport("libX11.so.6")] + public static extern IntPtr XCreateGC(IntPtr display, IntPtr drawable, ulong valuemask, IntPtr values); + + [DllImport("libX11.so.6")] + public static extern int XInitImage(ref XImage image); + + [DllImport("libX11.so.6")] + public static extern int XDestroyImage(ref XImage image); + + [DllImport("libX11.so.6")] + public static extern IntPtr XSetErrorHandler(XErrorHandler handler); + + [DllImport("libX11.so.6")] + public static extern int XSync(IntPtr display, bool discard); + + [DllImport("libX11.so.6")] + public static extern int XNextEvent(IntPtr display, out XEvent ev); + + [DllImport("libX11.so.6")] + public static extern bool XPending(IntPtr display); + + [StructLayout(LayoutKind.Sequential)] + public struct XAnyEvent + { + + public int Type; + public ulong Serial; /* # of last request processed by server */ + public bool send_event; /* true if this came from a SendEvent request */ + public IntPtr display; /* Display the event was read from */ + public IntPtr window; /* window on which event was requested in event mask */ + } + + [StructLayout(LayoutKind.Explicit)] + public unsafe struct XEvent + { + [FieldOffset(0)] public int Type; + [FieldOffset(0)] public XAnyEvent XAny; + [FieldOffset(0)] private fixed int pad[40]; + } + + public delegate int XErrorHandler(IntPtr display, ref XErrorEvent error); + + [DllImport("libX11.so.6")] + public static extern int XPutImage(IntPtr display, IntPtr drawable, IntPtr gc, ref XImage image, + int srcx, int srcy, int destx, int desty, uint width, uint height); + + [StructLayout(LayoutKind.Sequential)] + public unsafe struct XErrorEvent + { + public int type; + public IntPtr* display; /* Display the event was read from */ + public ulong serial; /* serial number of failed request */ + public byte error_code; /* error code of failed request */ + public byte request_code; /* Major op-code of failed request */ + public byte minor_code; /* Minor op-code of failed request */ + public IntPtr resourceid; /* resource id */ + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe struct XImage + { + public int width, height; /* size of image */ + public int xoffset; /* number of pixels offset in X direction */ + public int format; /* XYBitmap, XYPixmap, ZPixmap */ + public IntPtr data; /* pointer to image data */ + public int byte_order; /* data byte order, LSBFirst, MSBFirst */ + public int bitmap_unit; /* quant. of scanline 8, 16, 32 */ + public int bitmap_bit_order; /* LSBFirst, MSBFirst */ + public int bitmap_pad; /* 8, 16, 32 either XY or ZPixmap */ + public int depth; /* depth of image */ + public int bytes_per_line; /* accelerator to next scanline */ + public int bits_per_pixel; /* bits per pixel (ZPixmap) */ + public ulong red_mask; /* bits in z arrangement */ + public ulong green_mask; + public ulong blue_mask; + private fixed byte funcs[128]; + } + } +} From 666efd311b4db57b2648725be64847230d5973c5 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 23 Oct 2018 18:06:26 +0300 Subject: [PATCH 009/374] [X11] Initial windowing and mouse events --- samples/PlatformSanityChecks/Program.cs | 2 +- src/Avalonia.Input/MouseDevice.cs | 4 +- src/Avalonia.Input/Raw/RawInputEventArgs.cs | 4 +- src/Avalonia.Input/Raw/RawKeyEventArgs.cs | 2 +- src/Avalonia.Input/Raw/RawMouseEventArgs.cs | 2 +- .../Raw/RawMouseWheelEventArgs.cs | 2 +- .../Raw/RawTextInputEventArgs.cs | 2 +- src/Avalonia.X11/Avalonia.X11.csproj | 1 + src/Avalonia.X11/Stubs.cs | 103 + src/Avalonia.X11/X11Atoms.cs | 341 ++++ src/Avalonia.X11/X11Enums.cs | 110 + src/Avalonia.X11/X11Framebuffer.cs | 57 + src/Avalonia.X11/X11FramebufferSurface.cs | 27 + src/Avalonia.X11/X11Info.cs | 35 + src/Avalonia.X11/X11Platform.cs | 47 +- src/Avalonia.X11/X11PlatformThreading.cs | 24 +- src/Avalonia.X11/X11Structs.cs | 1815 +++++++++++++++++ src/Avalonia.X11/X11Window.cs | 492 +++++ src/Avalonia.X11/XError.cs | 30 + src/Avalonia.X11/XLib.cs | 468 ++++- 20 files changed, 3474 insertions(+), 94 deletions(-) create mode 100644 src/Avalonia.X11/Stubs.cs create mode 100644 src/Avalonia.X11/X11Atoms.cs create mode 100644 src/Avalonia.X11/X11Enums.cs create mode 100644 src/Avalonia.X11/X11Framebuffer.cs create mode 100644 src/Avalonia.X11/X11FramebufferSurface.cs create mode 100644 src/Avalonia.X11/X11Info.cs create mode 100644 src/Avalonia.X11/X11Structs.cs create mode 100644 src/Avalonia.X11/X11Window.cs create mode 100644 src/Avalonia.X11/XError.cs diff --git a/samples/PlatformSanityChecks/Program.cs b/samples/PlatformSanityChecks/Program.cs index 8a3aa82981..3fdc9b1e77 100644 --- a/samples/PlatformSanityChecks/Program.cs +++ b/samples/PlatformSanityChecks/Program.cs @@ -20,7 +20,7 @@ namespace PlatformSanityChecks AppBuilder.Configure().RuntimePlatformServicesInitializer(); var app = new App(); - new AvaloniaX11Platform().Initialize(); + AvaloniaX11PlatformExtensions.InitializeX11Platform(); CheckPlatformThreading(); diff --git a/src/Avalonia.Input/MouseDevice.cs b/src/Avalonia.Input/MouseDevice.cs index e581772978..e01dedeede 100644 --- a/src/Avalonia.Input/MouseDevice.cs +++ b/src/Avalonia.Input/MouseDevice.cs @@ -18,7 +18,7 @@ namespace Avalonia.Input { private int _clickCount; private Rect _lastClickRect; - private uint _lastClickTime; + private ulong _lastClickTime; private IInputElement _captured; private IDisposable _capturedSubscription; @@ -152,7 +152,7 @@ namespace Avalonia.Input ClearPointerOver(this, root); } - private bool MouseDown(IMouseDevice device, uint timestamp, IInputElement root, Point p, MouseButton button, InputModifiers inputModifiers) + private bool MouseDown(IMouseDevice device, ulong timestamp, IInputElement root, Point p, MouseButton button, InputModifiers inputModifiers) { Contract.Requires(device != null); Contract.Requires(root != null); diff --git a/src/Avalonia.Input/Raw/RawInputEventArgs.cs b/src/Avalonia.Input/Raw/RawInputEventArgs.cs index 041c361d30..78c1b58624 100644 --- a/src/Avalonia.Input/Raw/RawInputEventArgs.cs +++ b/src/Avalonia.Input/Raw/RawInputEventArgs.cs @@ -21,7 +21,7 @@ namespace Avalonia.Input.Raw /// /// The associated device. /// The event timestamp. - public RawInputEventArgs(IInputDevice device, uint timestamp) + public RawInputEventArgs(IInputDevice device, ulong timestamp) { Contract.Requires(device != null); @@ -47,6 +47,6 @@ namespace Avalonia.Input.Raw /// /// Gets the timestamp associated with the event. /// - public uint Timestamp { get; private set; } + public ulong Timestamp { get; private set; } } } diff --git a/src/Avalonia.Input/Raw/RawKeyEventArgs.cs b/src/Avalonia.Input/Raw/RawKeyEventArgs.cs index 0c208b4596..044f244138 100644 --- a/src/Avalonia.Input/Raw/RawKeyEventArgs.cs +++ b/src/Avalonia.Input/Raw/RawKeyEventArgs.cs @@ -13,7 +13,7 @@ namespace Avalonia.Input.Raw { public RawKeyEventArgs( IKeyboardDevice device, - uint timestamp, + ulong timestamp, RawKeyEventType type, Key key, InputModifiers modifiers) : base(device, timestamp) diff --git a/src/Avalonia.Input/Raw/RawMouseEventArgs.cs b/src/Avalonia.Input/Raw/RawMouseEventArgs.cs index 914833624f..c5637d66cc 100644 --- a/src/Avalonia.Input/Raw/RawMouseEventArgs.cs +++ b/src/Avalonia.Input/Raw/RawMouseEventArgs.cs @@ -35,7 +35,7 @@ namespace Avalonia.Input.Raw /// The input modifiers. public RawMouseEventArgs( IInputDevice device, - uint timestamp, + ulong timestamp, IInputRoot root, RawMouseEventType type, Point position, diff --git a/src/Avalonia.Input/Raw/RawMouseWheelEventArgs.cs b/src/Avalonia.Input/Raw/RawMouseWheelEventArgs.cs index f4078af9e1..d2e5faab6c 100644 --- a/src/Avalonia.Input/Raw/RawMouseWheelEventArgs.cs +++ b/src/Avalonia.Input/Raw/RawMouseWheelEventArgs.cs @@ -8,7 +8,7 @@ namespace Avalonia.Input.Raw { public RawMouseWheelEventArgs( IInputDevice device, - uint timestamp, + ulong timestamp, IInputRoot root, Point position, Vector delta, InputModifiers inputModifiers) diff --git a/src/Avalonia.Input/Raw/RawTextInputEventArgs.cs b/src/Avalonia.Input/Raw/RawTextInputEventArgs.cs index 6e427c3751..0d1e5d2422 100644 --- a/src/Avalonia.Input/Raw/RawTextInputEventArgs.cs +++ b/src/Avalonia.Input/Raw/RawTextInputEventArgs.cs @@ -7,7 +7,7 @@ namespace Avalonia.Input.Raw { public string Text { get; set; } - public RawTextInputEventArgs(IKeyboardDevice device, uint timestamp, string text) : base(device, timestamp) + public RawTextInputEventArgs(IKeyboardDevice device, ulong timestamp, string text) : base(device, timestamp) { Text = text; } diff --git a/src/Avalonia.X11/Avalonia.X11.csproj b/src/Avalonia.X11/Avalonia.X11.csproj index a035febe5b..3f61961571 100644 --- a/src/Avalonia.X11/Avalonia.X11.csproj +++ b/src/Avalonia.X11/Avalonia.X11.csproj @@ -7,6 +7,7 @@ + diff --git a/src/Avalonia.X11/Stubs.cs b/src/Avalonia.X11/Stubs.cs new file mode 100644 index 0000000000..ed14ab7878 --- /dev/null +++ b/src/Avalonia.X11/Stubs.cs @@ -0,0 +1,103 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Avalonia.Controls; +using Avalonia.Controls.Platform; +using Avalonia.Input; +using Avalonia.Input.Platform; +using Avalonia.Platform; + +namespace Avalonia.X11 +{ + class CursorFactoryStub : IStandardCursorFactory + { + public IPlatformHandle GetCursor(StandardCursorType cursorType) + { + return new PlatformHandle(IntPtr.Zero, "FAKE"); + } + } + + class ClipboardStub : IClipboard + { + private string _text; + public Task GetTextAsync() + { + return Task.FromResult(_text); + } + + public Task SetTextAsync(string text) + { + _text = text; + return Task.CompletedTask; + } + + public Task ClearAsync() + { + _text = null; + return Task.CompletedTask; + } + } + + class PlatformSettingsStub : IPlatformSettings + { + public Size DoubleClickSize { get; } = new Size(2, 2); + public TimeSpan DoubleClickTime { get; } = TimeSpan.FromMilliseconds(500); + } + + class SystemDialogsStub : ISystemDialogImpl + { + public Task ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent) + { + return Task.FromResult((string[])null); + } + + public Task ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent) + { + return Task.FromResult((string)null); + } + } + + class IconLoaderStub : IPlatformIconLoader + { + class FakeIcon : IWindowIconImpl + { + private readonly byte[] _data; + + public FakeIcon(byte[] data) + { + _data = data; + } + public void Save(Stream outputStream) + { + outputStream.Write(_data, 0, _data.Length); + } + } + + public IWindowIconImpl LoadIcon(string fileName) + { + return new FakeIcon(File.ReadAllBytes(fileName)); + } + + public IWindowIconImpl LoadIcon(Stream stream) + { + var ms = new MemoryStream(); + stream.CopyTo(ms); + return new FakeIcon(ms.ToArray()); + } + + public IWindowIconImpl LoadIcon(IBitmapImpl bitmap) + { + var ms = new MemoryStream(); + bitmap.Save(ms); + return new FakeIcon(ms.ToArray()); + } + } + + class ScreenStub : IScreenImpl + { + public int ScreenCount { get; } = 1; + + public Screen[] AllScreens { get; } = + {new Screen(new Rect(0, 0, 1920, 1280), new Rect(0, 0, 1920, 1280), true)}; + } +} diff --git a/src/Avalonia.X11/X11Atoms.cs b/src/Avalonia.X11/X11Atoms.cs new file mode 100644 index 0000000000..e7b83f59d3 --- /dev/null +++ b/src/Avalonia.X11/X11Atoms.cs @@ -0,0 +1,341 @@ +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Copyright (c) 2006 Novell, Inc. (http://www.novell.com) +// +// + +using System; +using static Avalonia.X11.XLib; +// ReSharper disable FieldCanBeMadeReadOnly.Global +// ReSharper disable IdentifierTypo +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable CommentTypo +// ReSharper disable ArrangeThisQualifier +// ReSharper disable NotAccessedField.Global +// ReSharper disable InconsistentNaming +// ReSharper disable StringLiteralTypo +#pragma warning disable 649 + +namespace Avalonia.X11 { + + internal class X11Atoms { + + // Our atoms + public readonly IntPtr AnyPropertyType = (IntPtr)0; + public readonly IntPtr XA_PRIMARY = (IntPtr)1; + public readonly IntPtr XA_SECONDARY = (IntPtr)2; + public readonly IntPtr XA_ARC = (IntPtr)3; + public readonly IntPtr XA_ATOM = (IntPtr)4; + public readonly IntPtr XA_BITMAP = (IntPtr)5; + public readonly IntPtr XA_CARDINAL = (IntPtr)6; + public readonly IntPtr XA_COLORMAP = (IntPtr)7; + public readonly IntPtr XA_CURSOR = (IntPtr)8; + public readonly IntPtr XA_CUT_BUFFER0 = (IntPtr)9; + public readonly IntPtr XA_CUT_BUFFER1 = (IntPtr)10; + public readonly IntPtr XA_CUT_BUFFER2 = (IntPtr)11; + public readonly IntPtr XA_CUT_BUFFER3 = (IntPtr)12; + public readonly IntPtr XA_CUT_BUFFER4 = (IntPtr)13; + public readonly IntPtr XA_CUT_BUFFER5 = (IntPtr)14; + public readonly IntPtr XA_CUT_BUFFER6 = (IntPtr)15; + public readonly IntPtr XA_CUT_BUFFER7 = (IntPtr)16; + public readonly IntPtr XA_DRAWABLE = (IntPtr)17; + public readonly IntPtr XA_FONT = (IntPtr)18; + public readonly IntPtr XA_INTEGER = (IntPtr)19; + public readonly IntPtr XA_PIXMAP = (IntPtr)20; + public readonly IntPtr XA_POINT = (IntPtr)21; + public readonly IntPtr XA_RECTANGLE = (IntPtr)22; + public readonly IntPtr XA_RESOURCE_MANAGER = (IntPtr)23; + public readonly IntPtr XA_RGB_COLOR_MAP = (IntPtr)24; + public readonly IntPtr XA_RGB_BEST_MAP = (IntPtr)25; + public readonly IntPtr XA_RGB_BLUE_MAP = (IntPtr)26; + public readonly IntPtr XA_RGB_DEFAULT_MAP = (IntPtr)27; + public readonly IntPtr XA_RGB_GRAY_MAP = (IntPtr)28; + public readonly IntPtr XA_RGB_GREEN_MAP = (IntPtr)29; + public readonly IntPtr XA_RGB_RED_MAP = (IntPtr)30; + public readonly IntPtr XA_STRING = (IntPtr)31; + public readonly IntPtr XA_VISUALID = (IntPtr)32; + public readonly IntPtr XA_WINDOW = (IntPtr)33; + public readonly IntPtr XA_WM_COMMAND = (IntPtr)34; + public readonly IntPtr XA_WM_HINTS = (IntPtr)35; + public readonly IntPtr XA_WM_CLIENT_MACHINE = (IntPtr)36; + public readonly IntPtr XA_WM_ICON_NAME = (IntPtr)37; + public readonly IntPtr XA_WM_ICON_SIZE = (IntPtr)38; + public readonly IntPtr XA_WM_NAME = (IntPtr)39; + public readonly IntPtr XA_WM_NORMAL_HINTS = (IntPtr)40; + public readonly IntPtr XA_WM_SIZE_HINTS = (IntPtr)41; + public readonly IntPtr XA_WM_ZOOM_HINTS = (IntPtr)42; + public readonly IntPtr XA_MIN_SPACE = (IntPtr)43; + public readonly IntPtr XA_NORM_SPACE = (IntPtr)44; + public readonly IntPtr XA_MAX_SPACE = (IntPtr)45; + public readonly IntPtr XA_END_SPACE = (IntPtr)46; + public readonly IntPtr XA_SUPERSCRIPT_X = (IntPtr)47; + public readonly IntPtr XA_SUPERSCRIPT_Y = (IntPtr)48; + public readonly IntPtr XA_SUBSCRIPT_X = (IntPtr)49; + public readonly IntPtr XA_SUBSCRIPT_Y = (IntPtr)50; + public readonly IntPtr XA_UNDERLINE_POSITION = (IntPtr)51; + public readonly IntPtr XA_UNDERLINE_THICKNESS = (IntPtr)52; + public readonly IntPtr XA_STRIKEOUT_ASCENT = (IntPtr)53; + public readonly IntPtr XA_STRIKEOUT_DESCENT = (IntPtr)54; + public readonly IntPtr XA_ITALIC_ANGLE = (IntPtr)55; + public readonly IntPtr XA_X_HEIGHT = (IntPtr)56; + public readonly IntPtr XA_QUAD_WIDTH = (IntPtr)57; + public readonly IntPtr XA_WEIGHT = (IntPtr)58; + public readonly IntPtr XA_POINT_SIZE = (IntPtr)59; + public readonly IntPtr XA_RESOLUTION = (IntPtr)60; + public readonly IntPtr XA_COPYRIGHT = (IntPtr)61; + public readonly IntPtr XA_NOTICE = (IntPtr)62; + public readonly IntPtr XA_FONT_NAME = (IntPtr)63; + public readonly IntPtr XA_FAMILY_NAME = (IntPtr)64; + public readonly IntPtr XA_FULL_NAME = (IntPtr)65; + public readonly IntPtr XA_CAP_HEIGHT = (IntPtr)66; + public readonly IntPtr XA_WM_CLASS = (IntPtr)67; + public readonly IntPtr XA_WM_TRANSIENT_FOR = (IntPtr)68; + + public readonly IntPtr WM_PROTOCOLS; + public readonly IntPtr WM_DELETE_WINDOW; + public readonly IntPtr WM_TAKE_FOCUS; + public readonly IntPtr _NET_SUPPORTED; + public readonly IntPtr _NET_CLIENT_LIST; + public readonly IntPtr _NET_NUMBER_OF_DESKTOPS; + public readonly IntPtr _NET_DESKTOP_GEOMETRY; + public readonly IntPtr _NET_DESKTOP_VIEWPORT; + public readonly IntPtr _NET_CURRENT_DESKTOP; + public readonly IntPtr _NET_DESKTOP_NAMES; + public readonly IntPtr _NET_ACTIVE_WINDOW; + public readonly IntPtr _NET_WORKAREA; + public readonly IntPtr _NET_SUPPORTING_WM_CHECK; + public readonly IntPtr _NET_VIRTUAL_ROOTS; + public readonly IntPtr _NET_DESKTOP_LAYOUT; + public readonly IntPtr _NET_SHOWING_DESKTOP; + public readonly IntPtr _NET_CLOSE_WINDOW; + public readonly IntPtr _NET_MOVERESIZE_WINDOW; + public readonly IntPtr _NET_WM_MOVERESIZE; + public readonly IntPtr _NET_RESTACK_WINDOW; + public readonly IntPtr _NET_REQUEST_FRAME_EXTENTS; + public readonly IntPtr _NET_WM_NAME; + public readonly IntPtr _NET_WM_VISIBLE_NAME; + public readonly IntPtr _NET_WM_ICON_NAME; + public readonly IntPtr _NET_WM_VISIBLE_ICON_NAME; + public readonly IntPtr _NET_WM_DESKTOP; + public readonly IntPtr _NET_WM_WINDOW_TYPE; + public readonly IntPtr _NET_WM_STATE; + public readonly IntPtr _NET_WM_ALLOWED_ACTIONS; + public readonly IntPtr _NET_WM_STRUT; + public readonly IntPtr _NET_WM_STRUT_PARTIAL; + public readonly IntPtr _NET_WM_ICON_GEOMETRY; + public readonly IntPtr _NET_WM_ICON; + public readonly IntPtr _NET_WM_PID; + public readonly IntPtr _NET_WM_HANDLED_ICONS; + public readonly IntPtr _NET_WM_USER_TIME; + public readonly IntPtr _NET_FRAME_EXTENTS; + public readonly IntPtr _NET_WM_PING; + public readonly IntPtr _NET_WM_SYNC_REQUEST; + public readonly IntPtr _NET_SYSTEM_TRAY_S; + public readonly IntPtr _NET_SYSTEM_TRAY_ORIENTATION; + public readonly IntPtr _NET_SYSTEM_TRAY_OPCODE; + public readonly IntPtr _NET_WM_STATE_MAXIMIZED_HORZ; + public readonly IntPtr _NET_WM_STATE_MAXIMIZED_VERT; + public readonly IntPtr _XEMBED; + public readonly IntPtr _XEMBED_INFO; + public readonly IntPtr _MOTIF_WM_HINTS; + public readonly IntPtr _NET_WM_STATE_SKIP_TASKBAR; + public readonly IntPtr _NET_WM_STATE_ABOVE; + public readonly IntPtr _NET_WM_STATE_MODAL; + public readonly IntPtr _NET_WM_STATE_HIDDEN; + public readonly IntPtr _NET_WM_CONTEXT_HELP; + public readonly IntPtr _NET_WM_WINDOW_OPACITY; + public readonly IntPtr _NET_WM_WINDOW_TYPE_DESKTOP; + public readonly IntPtr _NET_WM_WINDOW_TYPE_DOCK; + public readonly IntPtr _NET_WM_WINDOW_TYPE_TOOLBAR; + public readonly IntPtr _NET_WM_WINDOW_TYPE_MENU; + public readonly IntPtr _NET_WM_WINDOW_TYPE_UTILITY; + public readonly IntPtr _NET_WM_WINDOW_TYPE_SPLASH; + public readonly IntPtr _NET_WM_WINDOW_TYPE_DIALOG; + public readonly IntPtr _NET_WM_WINDOW_TYPE_NORMAL; + public readonly IntPtr CLIPBOARD; + public readonly IntPtr PRIMARY; + public readonly IntPtr DIB; + public readonly IntPtr OEMTEXT; + public readonly IntPtr UNICODETEXT; + public readonly IntPtr TARGETS; + public readonly IntPtr PostAtom; + public readonly IntPtr HoverState; + public readonly IntPtr AsyncAtom; + + + public X11Atoms (IntPtr display) { + + // make sure this array stays in sync with the statements below + string [] atom_names = new string[] { + "WM_PROTOCOLS", + "WM_DELETE_WINDOW", + "WM_TAKE_FOCUS", + "_NET_SUPPORTED", + "_NET_CLIENT_LIST", + "_NET_NUMBER_OF_DESKTOPS", + "_NET_DESKTOP_GEOMETRY", + "_NET_DESKTOP_VIEWPORT", + "_NET_CURRENT_DESKTOP", + "_NET_DESKTOP_NAMES", + "_NET_ACTIVE_WINDOW", + "_NET_WORKAREA", + "_NET_SUPPORTING_WM_CHECK", + "_NET_VIRTUAL_ROOTS", + "_NET_DESKTOP_LAYOUT", + "_NET_SHOWING_DESKTOP", + "_NET_CLOSE_WINDOW", + "_NET_MOVERESIZE_WINDOW", + "_NET_WM_MOVERESIZE", + "_NET_RESTACK_WINDOW", + "_NET_REQUEST_FRAME_EXTENTS", + "_NET_WM_NAME", + "_NET_WM_VISIBLE_NAME", + "_NET_WM_ICON_NAME", + "_NET_WM_VISIBLE_ICON_NAME", + "_NET_WM_DESKTOP", + "_NET_WM_WINDOW_TYPE", + "_NET_WM_STATE", + "_NET_WM_ALLOWED_ACTIONS", + "_NET_WM_STRUT", + "_NET_WM_STRUT_PARTIAL", + "_NET_WM_ICON_GEOMETRY", + "_NET_WM_ICON", + "_NET_WM_PID", + "_NET_WM_HANDLED_ICONS", + "_NET_WM_USER_TIME", + "_NET_FRAME_EXTENTS", + "_NET_WM_PING", + "_NET_WM_SYNC_REQUEST", + "_NET_SYSTEM_TRAY_OPCODE", + "_NET_SYSTEM_TRAY_ORIENTATION", + "_NET_WM_STATE_MAXIMIZED_HORZ", + "_NET_WM_STATE_MAXIMIZED_VERT", + "_NET_WM_STATE_HIDDEN", + "_XEMBED", + "_XEMBED_INFO", + "_MOTIF_WM_HINTS", + "_NET_WM_STATE_SKIP_TASKBAR", + "_NET_WM_STATE_ABOVE", + "_NET_WM_STATE_MODAL", + "_NET_WM_CONTEXT_HELP", + "_NET_WM_WINDOW_OPACITY", + "_NET_WM_WINDOW_TYPE_DESKTOP", + "_NET_WM_WINDOW_TYPE_DOCK", + "_NET_WM_WINDOW_TYPE_TOOLBAR", + "_NET_WM_WINDOW_TYPE_MENU", + "_NET_WM_WINDOW_TYPE_UTILITY", + "_NET_WM_WINDOW_TYPE_DIALOG", + "_NET_WM_WINDOW_TYPE_SPLASH", + "_NET_WM_WINDOW_TYPE_NORMAL", + "CLIPBOARD", + "PRIMARY", + "COMPOUND_TEXT", + "UTF8_STRING", + "TARGETS", + "_SWF_AsyncAtom", + "_SWF_PostMessageAtom", + "_SWF_HoverAtom" }; + + IntPtr[] atoms = new IntPtr [atom_names.Length];; + + XInternAtoms (display, atom_names, atom_names.Length, false, atoms); + + int off = 0; + WM_PROTOCOLS = atoms [off++]; + WM_DELETE_WINDOW = atoms [off++]; + WM_TAKE_FOCUS = atoms [off++]; + _NET_SUPPORTED = atoms [off++]; + _NET_CLIENT_LIST = atoms [off++]; + _NET_NUMBER_OF_DESKTOPS = atoms [off++]; + _NET_DESKTOP_GEOMETRY = atoms [off++]; + _NET_DESKTOP_VIEWPORT = atoms [off++]; + _NET_CURRENT_DESKTOP = atoms [off++]; + _NET_DESKTOP_NAMES = atoms [off++]; + _NET_ACTIVE_WINDOW = atoms [off++]; + _NET_WORKAREA = atoms [off++]; + _NET_SUPPORTING_WM_CHECK = atoms [off++]; + _NET_VIRTUAL_ROOTS = atoms [off++]; + _NET_DESKTOP_LAYOUT = atoms [off++]; + _NET_SHOWING_DESKTOP = atoms [off++]; + _NET_CLOSE_WINDOW = atoms [off++]; + _NET_MOVERESIZE_WINDOW = atoms [off++]; + _NET_WM_MOVERESIZE = atoms [off++]; + _NET_RESTACK_WINDOW = atoms [off++]; + _NET_REQUEST_FRAME_EXTENTS = atoms [off++]; + _NET_WM_NAME = atoms [off++]; + _NET_WM_VISIBLE_NAME = atoms [off++]; + _NET_WM_ICON_NAME = atoms [off++]; + _NET_WM_VISIBLE_ICON_NAME = atoms [off++]; + _NET_WM_DESKTOP = atoms [off++]; + _NET_WM_WINDOW_TYPE = atoms [off++]; + _NET_WM_STATE = atoms [off++]; + _NET_WM_ALLOWED_ACTIONS = atoms [off++]; + _NET_WM_STRUT = atoms [off++]; + _NET_WM_STRUT_PARTIAL = atoms [off++]; + _NET_WM_ICON_GEOMETRY = atoms [off++]; + _NET_WM_ICON = atoms [off++]; + _NET_WM_PID = atoms [off++]; + _NET_WM_HANDLED_ICONS = atoms [off++]; + _NET_WM_USER_TIME = atoms [off++]; + _NET_FRAME_EXTENTS = atoms [off++]; + _NET_WM_PING = atoms [off++]; + _NET_WM_SYNC_REQUEST = atoms [off++]; + _NET_SYSTEM_TRAY_OPCODE = atoms [off++]; + _NET_SYSTEM_TRAY_ORIENTATION = atoms [off++]; + _NET_WM_STATE_MAXIMIZED_HORZ = atoms [off++]; + _NET_WM_STATE_MAXIMIZED_VERT = atoms [off++]; + _NET_WM_STATE_HIDDEN = atoms [off++]; + _XEMBED = atoms [off++]; + _XEMBED_INFO = atoms [off++]; + _MOTIF_WM_HINTS = atoms [off++]; + _NET_WM_STATE_SKIP_TASKBAR = atoms [off++]; + _NET_WM_STATE_ABOVE = atoms [off++]; + _NET_WM_STATE_MODAL = atoms [off++]; + _NET_WM_CONTEXT_HELP = atoms [off++]; + _NET_WM_WINDOW_OPACITY = atoms [off++]; + _NET_WM_WINDOW_TYPE_DESKTOP = atoms [off++]; + _NET_WM_WINDOW_TYPE_DOCK = atoms [off++]; + _NET_WM_WINDOW_TYPE_TOOLBAR = atoms [off++]; + _NET_WM_WINDOW_TYPE_MENU = atoms [off++]; + _NET_WM_WINDOW_TYPE_UTILITY = atoms [off++]; + _NET_WM_WINDOW_TYPE_DIALOG = atoms [off++]; + _NET_WM_WINDOW_TYPE_SPLASH = atoms [off++]; + _NET_WM_WINDOW_TYPE_NORMAL = atoms [off++]; + CLIPBOARD = atoms [off++]; + PRIMARY = atoms [off++]; + OEMTEXT = atoms [off++]; + UNICODETEXT = atoms [off++]; + TARGETS = atoms [off++]; + AsyncAtom = atoms [off++]; + PostAtom = atoms [off++]; + HoverState = atoms [off++]; + + DIB = XA_PIXMAP; + + var defScreen = XDefaultScreen(display); + // XXX multi screen stuff here + _NET_SYSTEM_TRAY_S = XInternAtom (display, "_NET_SYSTEM_TRAY_S" + defScreen.ToString(), false); + } + + } + +} + diff --git a/src/Avalonia.X11/X11Enums.cs b/src/Avalonia.X11/X11Enums.cs new file mode 100644 index 0000000000..d97e1c42bb --- /dev/null +++ b/src/Avalonia.X11/X11Enums.cs @@ -0,0 +1,110 @@ +using System; + +namespace Avalonia.X11 +{ + + public enum Status + { + Success = 0, /* everything's okay */ + BadRequest = 1, /* bad request code */ + BadValue = 2, /* int parameter out of range */ + BadWindow = 3, /* parameter not a Window */ + BadPixmap = 4, /* parameter not a Pixmap */ + BadAtom = 5, /* parameter not an Atom */ + BadCursor = 6, /* parameter not a Cursor */ + BadFont = 7, /* parameter not a Font */ + BadMatch = 8, /* parameter mismatch */ + BadDrawable = 9, /* parameter not a Pixmap or Window */ + BadAccess = 10, /* depending on context: + - key/button already grabbed + - attempt to free an illegal + cmap entry + - attempt to store into a read-only + color map entry. + - attempt to modify the access control + list from other than the local host. + */ + BadAlloc = 11, /* insufficient resources */ + BadColor = 12, /* no such colormap */ + BadGC = 13, /* parameter not a GC */ + BadIDChoice = 14, /* choice not in range or already used */ + BadName = 15, /* font or color name doesn't exist */ + BadLength = 16, /* Request length incorrect */ + BadImplementation = 17, /* server is defective */ + + FirstExtensionError = 128, + LastExtensionError = 255, + + } + + [Flags] + public enum XEventMask : int + { + NoEventMask = 0, + KeyPressMask = (1 << 0), + KeyReleaseMask = (1 << 1), + ButtonPressMask = (1 << 2), + ButtonReleaseMask = (1 << 3), + EnterWindowMask = (1 << 4), + LeaveWindowMask = (1 << 5), + PointerMotionMask = (1 << 6), + PointerMotionHintMask = (1 << 7), + Button1MotionMask = (1 << 8), + Button2MotionMask = (1 << 9), + Button3MotionMask = (1 << 10), + Button4MotionMask = (1 << 11), + Button5MotionMask = (1 << 12), + ButtonMotionMask = (1 << 13), + KeymapStateMask = (1 << 14), + ExposureMask = (1 << 15), + VisibilityChangeMask = (1 << 16), + StructureNotifyMask = (1 << 17), + ResizeRedirectMask = (1 << 18), + SubstructureNotifyMask = (1 << 19), + SubstructureRedirectMask = (1 << 20), + FocusChangeMask = (1 << 21), + PropertyChangeMask = (1 << 22), + ColormapChangeMask = (1 << 23), + OwnerGrabButtonMask = (1 << 24) + } + + [Flags] + public enum XModifierMask + { + ShiftMask = (1 << 0), + LockMask = (1 << 1), + ControlMask = (1 << 2), + Mod1Mask = (1 << 3), + Mod2Mask = (1 << 4), + Mod3Mask = (1 << 5), + Mod4Mask = (1 << 6), + Mod5Mask = (1 << 7), + Button1Mask = (1 << 8), + Button2Mask = (1 << 9), + Button3Mask = (1 << 10), + Button4Mask = (1 << 11), + Button5Mask = (1 << 12), + AnyModifier = (1 << 15) + + } + + [Flags] + public enum XCreateWindowFlags + { + CWBackPixmap = (1 << 0), + CWBackPixel = (1 << 1), + CWBorderPixmap = (1 << 2), + CWBorderPixel = (1 << 3), + CWBitGravity = (1 << 4), + CWWinGravity = (1 << 5), + CWBackingStore = (1 << 6), + CWBackingPlanes = (1 << 7), + CWBackingPixel = (1 << 8), + CWOverrideRedirect = (1 << 9), + CWSaveUnder = (1 << 10), + CWEventMask = (1 << 11), + CWDontPropagate = (1 << 12), + CWColormap = (1 << 13), + CWCursor = (1 << 14), + } +} diff --git a/src/Avalonia.X11/X11Framebuffer.cs b/src/Avalonia.X11/X11Framebuffer.cs new file mode 100644 index 0000000000..ef93a7347b --- /dev/null +++ b/src/Avalonia.X11/X11Framebuffer.cs @@ -0,0 +1,57 @@ +using System; +using Avalonia.Platform; +using static Avalonia.X11.XLib; +namespace Avalonia.X11 +{ + class X11Framebuffer : ILockedFramebuffer + { + private readonly IntPtr _display; + private readonly IntPtr _xid; + private IUnmanagedBlob _blob; + + public X11Framebuffer(IntPtr display, IntPtr xid, int width, int height, int factor) + { + _display = display; + _xid = xid; + Width = width*factor; + Height = height*factor; + RowBytes = Width * 4; + Dpi = new Vector(96, 96) * factor; + Format = PixelFormat.Bgra8888; + _blob = AvaloniaLocator.Current.GetService().AllocBlob(RowBytes * Height); + Address = _blob.Address; + } + + public void Dispose() + { + var image = new XImage(); + int bitsPerPixel = 32; + image.width = Width; + image.height = Height; + image.format = 2; //ZPixmap; + image.data = Address; + image.byte_order = 0;// LSBFirst; + image.bitmap_unit = bitsPerPixel; + image.bitmap_bit_order = 0;// LSBFirst; + image.bitmap_pad = bitsPerPixel; + image.depth = 32; + image.bytes_per_line = RowBytes - Width * 4; + image.bits_per_pixel = bitsPerPixel; + XLockDisplay(_display); + XInitImage(ref image); + var gc = XCreateGC(_display, _xid, 0, IntPtr.Zero); + XPutImage(_display, _xid, gc, ref image, 0, 0, 0, 0, (uint) Width, (uint) Height); + XFreeGC(_display, gc); + XSync(_display, true); + XUnlockDisplay(_display); + _blob.Dispose(); + } + + public IntPtr Address { get; } + public int Width { get; } + public int Height { get; } + public int RowBytes { get; } + public Vector Dpi { get; } + public PixelFormat Format { get; } + } +} diff --git a/src/Avalonia.X11/X11FramebufferSurface.cs b/src/Avalonia.X11/X11FramebufferSurface.cs new file mode 100644 index 0000000000..05b21efb0c --- /dev/null +++ b/src/Avalonia.X11/X11FramebufferSurface.cs @@ -0,0 +1,27 @@ +using System; +using Avalonia.Controls.Platform.Surfaces; +using Avalonia.Platform; +using static Avalonia.X11.XLib; +namespace Avalonia.X11 +{ + public class X11FramebufferSurface : IFramebufferPlatformSurface + { + private readonly IntPtr _display; + private readonly IntPtr _xid; + + public X11FramebufferSurface(IntPtr display, IntPtr xid) + { + _display = display; + _xid = xid; + } + + public ILockedFramebuffer Lock() + { + XLockDisplay(_display); + XGetGeometry(_display, _xid, out var root, out var x, out var y, out var width, out var height, + out var bw, out var d); + XUnlockDisplay(_display); + return new X11Framebuffer(_display, _xid, width, height, 1); + } + } +} diff --git a/src/Avalonia.X11/X11Info.cs b/src/Avalonia.X11/X11Info.cs new file mode 100644 index 0000000000..6ad9faffbc --- /dev/null +++ b/src/Avalonia.X11/X11Info.cs @@ -0,0 +1,35 @@ +using System; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using static Avalonia.X11.XLib; +// ReSharper disable UnusedAutoPropertyAccessor.Local +namespace Avalonia.X11 +{ + class X11Info + { + public IntPtr Display { get; } + public IntPtr DeferredDisplay { get; } + public int DefaultScreen { get; } + public IntPtr BlackPixel { get; } + public IntPtr RootWindow { get; } + public IntPtr DefaultRootWindow { get; } + public XVisualInfo MatchedVisual { get; } + public X11Atoms Atoms { get; } + + public IntPtr LastActivityTimestamp { get; set; } + + public unsafe X11Info(IntPtr display, IntPtr deferredDisplay) + { + Display = display; + DeferredDisplay = deferredDisplay; + DefaultScreen = XDefaultScreen(display); + BlackPixel = XBlackPixel(display, DefaultScreen); + RootWindow = XRootWindow(display, DefaultScreen); + DefaultRootWindow = XDefaultRootWindow(display); + Atoms = new X11Atoms(display); + XMatchVisualInfo(display, DefaultScreen, 32, 4, out var info); + MatchedVisual = info; + } + } +} diff --git a/src/Avalonia.X11/X11Platform.cs b/src/Avalonia.X11/X11Platform.cs index c266433eef..33497be411 100644 --- a/src/Avalonia.X11/X11Platform.cs +++ b/src/Avalonia.X11/X11Platform.cs @@ -1,27 +1,66 @@ using System; +using System.Collections.Generic; using Avalonia.Controls; +using Avalonia.Controls.Platform; +using Avalonia.Input; +using Avalonia.Input.Platform; +using Avalonia.OpenGL; using Avalonia.Platform; +using Avalonia.Rendering; using Avalonia.X11; using static Avalonia.X11.XLib; namespace Avalonia.X11 { - public class AvaloniaX11Platform + class AvaloniaX11Platform : IWindowingPlatform { + private Lazy _keyboardDevice = new Lazy(() => new KeyboardDevice()); + private Lazy _mouseDevice = new Lazy(() => new MouseDevice()); + public KeyboardDevice KeyboardDevice => _keyboardDevice.Value; + public MouseDevice MouseDevice => _mouseDevice.Value; + public Dictionary> Windows = new Dictionary>(); + public X11Info Info { get; private set; } public void Initialize() { + XInitThreads(); Display = XOpenDisplay(IntPtr.Zero); DeferredDisplay = XOpenDisplay(IntPtr.Zero); if (Display == IntPtr.Zero) throw new Exception("XOpenDisplay failed"); - + XError.Init(); + Info = new X11Info(Display, DeferredDisplay); AvaloniaLocator.CurrentMutable.BindToSelf(this) - .Bind().ToConstant(new X11PlatformThreading(Display)); + .Bind().ToConstant(this) + .Bind().ToConstant(new X11PlatformThreading(Display, Windows)) + .Bind().ToConstant(new DefaultRenderTimer(60)) + .Bind().ToConstant(new RenderLoop()) + .Bind().ToConstant(new PlatformHotkeyConfiguration(InputModifiers.Control)) + .Bind().ToFunc(() => KeyboardDevice) + .Bind().ToConstant(new CursorFactoryStub()) + .Bind().ToSingleton() + .Bind().ToConstant(new PlatformSettingsStub()) + .Bind().ToConstant(new SystemDialogsStub()) + .Bind().ToConstant(new IconLoaderStub()); + EglGlPlatformFeature.TryInitialize(); } public IntPtr DeferredDisplay { get; set; } public IntPtr Display { get; set; } + public IWindowImpl CreateWindow() + { + return new X11Window(this, false); + } + + public IEmbeddableWindowImpl CreateEmbeddableWindow() + { + throw new NotSupportedException(); + } + + public IPopupImpl CreatePopup() + { + return new X11Window(this, true); + } } } @@ -34,6 +73,8 @@ namespace Avalonia builder.UseWindowingSubsystem(() => new AvaloniaX11Platform().Initialize()); return builder; } + + public static void InitializeX11Platform() => new AvaloniaX11Platform().Initialize(); } } diff --git a/src/Avalonia.X11/X11PlatformThreading.cs b/src/Avalonia.X11/X11PlatformThreading.cs index 18b08085e3..298b4da19c 100644 --- a/src/Avalonia.X11/X11PlatformThreading.cs +++ b/src/Avalonia.X11/X11PlatformThreading.cs @@ -9,9 +9,10 @@ using static Avalonia.X11.XLib; namespace Avalonia.X11 { - public unsafe class X11PlatformThreading : IPlatformThreadingInterface + unsafe class X11PlatformThreading : IPlatformThreadingInterface { private readonly IntPtr _display; + private readonly Dictionary> _eventHandlers; private Thread _mainThread; [StructLayout(LayoutKind.Explicit)] @@ -102,9 +103,10 @@ namespace Avalonia.X11 List _timers = new List(); - public X11PlatformThreading(IntPtr display) + public X11PlatformThreading(IntPtr display, Dictionary> eventHandlers) { _display = display; + _eventHandlers = eventHandlers; _mainThread = Thread.CurrentThread; var fd = XLib.XConnectionNumber(display); var ev = new epoll_event() @@ -202,12 +204,22 @@ namespace Avalonia.X11 } else { - while (XPending(_display)) + while (true) { - if (cancellationToken.IsCancellationRequested) - return; - XNextEvent(_display, out var xev); + var pending = XPending(_display); + if (pending == 0) + break; + while (pending > 0) + { + if (cancellationToken.IsCancellationRequested) + return; + XNextEvent(_display, out var xev); + pending--; + if (_eventHandlers.TryGetValue(xev.AnyEvent.window, out var handler)) + handler(xev); + } } + Dispatcher.UIThread.RunJobs(); } } } diff --git a/src/Avalonia.X11/X11Structs.cs b/src/Avalonia.X11/X11Structs.cs new file mode 100644 index 0000000000..a18bd4be74 --- /dev/null +++ b/src/Avalonia.X11/X11Structs.cs @@ -0,0 +1,1815 @@ +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software",, to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Copyright (c) 2004 Novell, Inc. +// +// Authors: +// Peter Bartok pbartok@novell.com +// + + +// NOT COMPLETE + +using System; +using System.ComponentModel; +using System.Collections; +using System.Drawing; +using System.Diagnostics; +using System.Reflection; +using System.Runtime.InteropServices; +// ReSharper disable FieldCanBeMadeReadOnly.Global +// ReSharper disable IdentifierTypo +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable CommentTypo +// ReSharper disable ArrangeThisQualifier +// ReSharper disable NotAccessedField.Global +#pragma warning disable 649 + +namespace Avalonia.X11 { + // + // In the structures below, fields of type long are mapped to IntPtr. + // This will work on all platforms where sizeof(long)==sizeof(void*), which + // is almost all platforms except WIN64. + // + + [StructLayout(LayoutKind.Sequential)] + internal struct XAnyEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr window; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XKeyEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr window; + internal IntPtr root; + internal IntPtr subwindow; + internal IntPtr time; + internal int x; + internal int y; + internal int x_root; + internal int y_root; + internal int state; + internal int keycode; + internal bool same_screen; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XButtonEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr window; + internal IntPtr root; + internal IntPtr subwindow; + internal IntPtr time; + internal int x; + internal int y; + internal int x_root; + internal int y_root; + internal XModifierMask state; + internal int button; + internal bool same_screen; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XMotionEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr window; + internal IntPtr root; + internal IntPtr subwindow; + internal IntPtr time; + internal int x; + internal int y; + internal int x_root; + internal int y_root; + internal XModifierMask state; + internal byte is_hint; + internal bool same_screen; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XCrossingEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr window; + internal IntPtr root; + internal IntPtr subwindow; + internal IntPtr time; + internal int x; + internal int y; + internal int x_root; + internal int y_root; + internal NotifyMode mode; + internal NotifyDetail detail; + internal bool same_screen; + internal bool focus; + internal int state; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XFocusChangeEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr window; + internal int mode; + internal NotifyDetail detail; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XKeymapEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr window; + internal byte key_vector0; + internal byte key_vector1; + internal byte key_vector2; + internal byte key_vector3; + internal byte key_vector4; + internal byte key_vector5; + internal byte key_vector6; + internal byte key_vector7; + internal byte key_vector8; + internal byte key_vector9; + internal byte key_vector10; + internal byte key_vector11; + internal byte key_vector12; + internal byte key_vector13; + internal byte key_vector14; + internal byte key_vector15; + internal byte key_vector16; + internal byte key_vector17; + internal byte key_vector18; + internal byte key_vector19; + internal byte key_vector20; + internal byte key_vector21; + internal byte key_vector22; + internal byte key_vector23; + internal byte key_vector24; + internal byte key_vector25; + internal byte key_vector26; + internal byte key_vector27; + internal byte key_vector28; + internal byte key_vector29; + internal byte key_vector30; + internal byte key_vector31; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XExposeEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr window; + internal int x; + internal int y; + internal int width; + internal int height; + internal int count; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XGraphicsExposeEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr drawable; + internal int x; + internal int y; + internal int width; + internal int height; + internal int count; + internal int major_code; + internal int minor_code; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XNoExposeEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr drawable; + internal int major_code; + internal int minor_code; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XVisibilityEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr window; + internal int state; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XCreateWindowEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr parent; + internal IntPtr window; + internal int x; + internal int y; + internal int width; + internal int height; + internal int border_width; + internal bool override_redirect; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XDestroyWindowEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr xevent; + internal IntPtr window; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XUnmapEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr xevent; + internal IntPtr window; + internal bool from_configure; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XMapEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr xevent; + internal IntPtr window; + internal bool override_redirect; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XMapRequestEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr parent; + internal IntPtr window; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XReparentEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr xevent; + internal IntPtr window; + internal IntPtr parent; + internal int x; + internal int y; + internal bool override_redirect; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XConfigureEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr xevent; + internal IntPtr window; + internal int x; + internal int y; + internal int width; + internal int height; + internal int border_width; + internal IntPtr above; + internal bool override_redirect; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XGravityEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr xevent; + internal IntPtr window; + internal int x; + internal int y; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XResizeRequestEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr window; + internal int width; + internal int height; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XConfigureRequestEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr parent; + internal IntPtr window; + internal int x; + internal int y; + internal int width; + internal int height; + internal int border_width; + internal IntPtr above; + internal int detail; + internal IntPtr value_mask; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XCirculateEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr xevent; + internal IntPtr window; + internal int place; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XCirculateRequestEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr parent; + internal IntPtr window; + internal int place; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XPropertyEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr window; + internal IntPtr atom; + internal IntPtr time; + internal int state; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XSelectionClearEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr window; + internal IntPtr selection; + internal IntPtr time; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XSelectionRequestEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr owner; + internal IntPtr requestor; + internal IntPtr selection; + internal IntPtr target; + internal IntPtr property; + internal IntPtr time; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XSelectionEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr requestor; + internal IntPtr selection; + internal IntPtr target; + internal IntPtr property; + internal IntPtr time; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XColormapEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr window; + internal IntPtr colormap; + internal bool c_new; + internal int state; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XClientMessageEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr window; + internal IntPtr message_type; + internal int format; + internal IntPtr ptr1; + internal IntPtr ptr2; + internal IntPtr ptr3; + internal IntPtr ptr4; + internal IntPtr ptr5; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XMappingEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr window; + internal int request; + internal int first_keycode; + internal int count; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XErrorEvent { + internal XEventName type; + internal IntPtr display; + internal IntPtr resourceid; + internal IntPtr serial; + internal byte error_code; + internal XRequest request_code; + internal byte minor_code; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XEventPad { + internal IntPtr pad0; + internal IntPtr pad1; + internal IntPtr pad2; + internal IntPtr pad3; + internal IntPtr pad4; + internal IntPtr pad5; + internal IntPtr pad6; + internal IntPtr pad7; + internal IntPtr pad8; + internal IntPtr pad9; + internal IntPtr pad10; + internal IntPtr pad11; + internal IntPtr pad12; + internal IntPtr pad13; + internal IntPtr pad14; + internal IntPtr pad15; + internal IntPtr pad16; + internal IntPtr pad17; + internal IntPtr pad18; + internal IntPtr pad19; + internal IntPtr pad20; + internal IntPtr pad21; + internal IntPtr pad22; + internal IntPtr pad23; + } + + [StructLayout(LayoutKind.Explicit)] + internal struct XEvent { + [ FieldOffset(0) ] internal XEventName type; + [ FieldOffset(0) ] internal XAnyEvent AnyEvent; + [ FieldOffset(0) ] internal XKeyEvent KeyEvent; + [ FieldOffset(0) ] internal XButtonEvent ButtonEvent; + [ FieldOffset(0) ] internal XMotionEvent MotionEvent; + [ FieldOffset(0) ] internal XCrossingEvent CrossingEvent; + [ FieldOffset(0) ] internal XFocusChangeEvent FocusChangeEvent; + [ FieldOffset(0) ] internal XExposeEvent ExposeEvent; + [ FieldOffset(0) ] internal XGraphicsExposeEvent GraphicsExposeEvent; + [ FieldOffset(0) ] internal XNoExposeEvent NoExposeEvent; + [ FieldOffset(0) ] internal XVisibilityEvent VisibilityEvent; + [ FieldOffset(0) ] internal XCreateWindowEvent CreateWindowEvent; + [ FieldOffset(0) ] internal XDestroyWindowEvent DestroyWindowEvent; + [ FieldOffset(0) ] internal XUnmapEvent UnmapEvent; + [ FieldOffset(0) ] internal XMapEvent MapEvent; + [ FieldOffset(0) ] internal XMapRequestEvent MapRequestEvent; + [ FieldOffset(0) ] internal XReparentEvent ReparentEvent; + [ FieldOffset(0) ] internal XConfigureEvent ConfigureEvent; + [ FieldOffset(0) ] internal XGravityEvent GravityEvent; + [ FieldOffset(0) ] internal XResizeRequestEvent ResizeRequestEvent; + [ FieldOffset(0) ] internal XConfigureRequestEvent ConfigureRequestEvent; + [ FieldOffset(0) ] internal XCirculateEvent CirculateEvent; + [ FieldOffset(0) ] internal XCirculateRequestEvent CirculateRequestEvent; + [ FieldOffset(0) ] internal XPropertyEvent PropertyEvent; + [ FieldOffset(0) ] internal XSelectionClearEvent SelectionClearEvent; + [ FieldOffset(0) ] internal XSelectionRequestEvent SelectionRequestEvent; + [ FieldOffset(0) ] internal XSelectionEvent SelectionEvent; + [ FieldOffset(0) ] internal XColormapEvent ColormapEvent; + [ FieldOffset(0) ] internal XClientMessageEvent ClientMessageEvent; + [ FieldOffset(0) ] internal XMappingEvent MappingEvent; + [ FieldOffset(0) ] internal XErrorEvent ErrorEvent; + [ FieldOffset(0) ] internal XKeymapEvent KeymapEvent; + + //[MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=24)] + //[ FieldOffset(0) ] internal int[] pad; + [ FieldOffset(0) ] internal XEventPad Pad; + public override string ToString() { + switch (type) + { + case XEventName.ButtonPress: + case XEventName.ButtonRelease: + return ToString (ButtonEvent); + case XEventName.CirculateNotify: + case XEventName.CirculateRequest: + return ToString (CirculateEvent); + case XEventName.ClientMessage: + return ToString (ClientMessageEvent); + case XEventName.ColormapNotify: + return ToString (ColormapEvent); + case XEventName.ConfigureNotify: + return ToString (ConfigureEvent); + case XEventName.ConfigureRequest: + return ToString (ConfigureRequestEvent); + case XEventName.CreateNotify: + return ToString (CreateWindowEvent); + case XEventName.DestroyNotify: + return ToString (DestroyWindowEvent); + case XEventName.Expose: + return ToString (ExposeEvent); + case XEventName.FocusIn: + case XEventName.FocusOut: + return ToString (FocusChangeEvent); + case XEventName.GraphicsExpose: + return ToString (GraphicsExposeEvent); + case XEventName.GravityNotify: + return ToString (GravityEvent); + case XEventName.KeymapNotify: + return ToString (KeymapEvent); + case XEventName.MapNotify: + return ToString (MapEvent); + case XEventName.MappingNotify: + return ToString (MappingEvent); + case XEventName.MapRequest: + return ToString (MapRequestEvent); + case XEventName.MotionNotify: + return ToString (MotionEvent); + case XEventName.NoExpose: + return ToString (NoExposeEvent); + case XEventName.PropertyNotify: + return ToString (PropertyEvent); + case XEventName.ReparentNotify: + return ToString (ReparentEvent); + case XEventName.ResizeRequest: + return ToString (ResizeRequestEvent); + case XEventName.SelectionClear: + return ToString (SelectionClearEvent); + case XEventName.SelectionNotify: + return ToString (SelectionEvent); + case XEventName.SelectionRequest: + return ToString (SelectionRequestEvent); + case XEventName.UnmapNotify: + return ToString (UnmapEvent); + case XEventName.VisibilityNotify: + return ToString (VisibilityEvent); + case XEventName.EnterNotify: + case XEventName.LeaveNotify: + return ToString (CrossingEvent); + default: + return type.ToString (); + } + } + + public static string ToString (object ev) + { + string result = string.Empty; + Type type = ev.GetType (); + FieldInfo [] fields = type.GetFields (System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Instance); + for (int i = 0; i < fields.Length; i++) { + if (result != string.Empty) { + result += ", "; + } + object value = fields [i].GetValue (ev); + result += fields [i].Name + "=" + (value == null ? "" : value.ToString ()); + } + return type.Name + " (" + result + ")"; + } + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XSetWindowAttributes { + internal IntPtr background_pixmap; + internal IntPtr background_pixel; + internal IntPtr border_pixmap; + internal IntPtr border_pixel; + internal Gravity bit_gravity; + internal Gravity win_gravity; + internal int backing_store; + internal IntPtr backing_planes; + internal IntPtr backing_pixel; + internal bool save_under; + internal IntPtr event_mask; + internal IntPtr do_not_propagate_mask; + internal bool override_redirect; + internal IntPtr colormap; + internal IntPtr cursor; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XWindowAttributes { + internal int x; + internal int y; + internal int width; + internal int height; + internal int border_width; + internal int depth; + internal IntPtr visual; + internal IntPtr root; + internal int c_class; + internal Gravity bit_gravity; + internal Gravity win_gravity; + internal int backing_store; + internal IntPtr backing_planes; + internal IntPtr backing_pixel; + internal bool save_under; + internal IntPtr colormap; + internal bool map_installed; + internal MapState map_state; + internal IntPtr all_event_masks; + internal IntPtr your_event_mask; + internal IntPtr do_not_propagate_mask; + internal bool override_direct; + internal IntPtr screen; + + public override string ToString () + { + return XEvent.ToString (this); + } + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XTextProperty { + internal string value; + internal IntPtr encoding; + internal int format; + internal IntPtr nitems; + } + + internal enum XWindowClass { + InputOutput = 1, + InputOnly = 2 + } + + internal enum XEventName { + KeyPress = 2, + KeyRelease = 3, + ButtonPress = 4, + ButtonRelease = 5, + MotionNotify = 6, + EnterNotify = 7, + LeaveNotify = 8, + FocusIn = 9, + FocusOut = 10, + KeymapNotify = 11, + Expose = 12, + GraphicsExpose = 13, + NoExpose = 14, + VisibilityNotify = 15, + CreateNotify = 16, + DestroyNotify = 17, + UnmapNotify = 18, + MapNotify = 19, + MapRequest = 20, + ReparentNotify = 21, + ConfigureNotify = 22, + ConfigureRequest = 23, + GravityNotify = 24, + ResizeRequest = 25, + CirculateNotify = 26, + CirculateRequest = 27, + PropertyNotify = 28, + SelectionClear = 29, + SelectionRequest = 30, + SelectionNotify = 31, + ColormapNotify = 32, + ClientMessage = 33, + MappingNotify = 34, + + LASTEvent + } + + [Flags] + internal enum SetWindowValuemask { + Nothing = 0, + BackPixmap = 1, + BackPixel = 2, + BorderPixmap = 4, + BorderPixel = 8, + BitGravity = 16, + WinGravity = 32, + BackingStore = 64, + BackingPlanes = 128, + BackingPixel = 256, + OverrideRedirect = 512, + SaveUnder = 1024, + EventMask = 2048, + DontPropagate = 4096, + ColorMap = 8192, + Cursor = 16384 + } + + internal enum SendEventValues { + PointerWindow = 0, + InputFocus = 1 + } + + internal enum CreateWindowArgs { + CopyFromParent = 0, + ParentRelative = 1, + InputOutput = 1, + InputOnly = 2 + } + + internal enum Gravity { + ForgetGravity = 0, + NorthWestGravity= 1, + NorthGravity = 2, + NorthEastGravity= 3, + WestGravity = 4, + CenterGravity = 5, + EastGravity = 6, + SouthWestGravity= 7, + SouthGravity = 8, + SouthEastGravity= 9, + StaticGravity = 10 + } + + internal enum XKeySym : uint { + XK_BackSpace = 0xFF08, + XK_Tab = 0xFF09, + XK_Clear = 0xFF0B, + XK_Return = 0xFF0D, + XK_Home = 0xFF50, + XK_Left = 0xFF51, + XK_Up = 0xFF52, + XK_Right = 0xFF53, + XK_Down = 0xFF54, + XK_Page_Up = 0xFF55, + XK_Page_Down = 0xFF56, + XK_End = 0xFF57, + XK_Begin = 0xFF58, + XK_Menu = 0xFF67, + XK_Shift_L = 0xFFE1, + XK_Shift_R = 0xFFE2, + XK_Control_L = 0xFFE3, + XK_Control_R = 0xFFE4, + XK_Caps_Lock = 0xFFE5, + XK_Shift_Lock = 0xFFE6, + XK_Meta_L = 0xFFE7, + XK_Meta_R = 0xFFE8, + XK_Alt_L = 0xFFE9, + XK_Alt_R = 0xFFEA, + XK_Super_L = 0xFFEB, + XK_Super_R = 0xFFEC, + XK_Hyper_L = 0xFFED, + XK_Hyper_R = 0xFFEE, + } + + [Flags] + internal enum EventMask { + NoEventMask = 0, + KeyPressMask = 1<<0, + KeyReleaseMask = 1<<1, + ButtonPressMask = 1<<2, + ButtonReleaseMask = 1<<3, + EnterWindowMask = 1<<4, + LeaveWindowMask = 1<<5, + PointerMotionMask = 1<<6, + PointerMotionHintMask = 1<<7, + Button1MotionMask = 1<<8, + Button2MotionMask = 1<<9, + Button3MotionMask = 1<<10, + Button4MotionMask = 1<<11, + Button5MotionMask = 1<<12, + ButtonMotionMask = 1<<13, + KeymapStateMask = 1<<14, + ExposureMask = 1<<15, + VisibilityChangeMask = 1<<16, + StructureNotifyMask = 1<<17, + ResizeRedirectMask = 1<<18, + SubstructureNotifyMask = 1<<19, + SubstructureRedirectMask= 1<<20, + FocusChangeMask = 1<<21, + PropertyChangeMask = 1<<22, + ColormapChangeMask = 1<<23, + OwnerGrabButtonMask = 1<<24 + } + + internal enum GrabMode { + GrabModeSync = 0, + GrabModeAsync = 1 + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XStandardColormap { + internal IntPtr colormap; + internal IntPtr red_max; + internal IntPtr red_mult; + internal IntPtr green_max; + internal IntPtr green_mult; + internal IntPtr blue_max; + internal IntPtr blue_mult; + internal IntPtr base_pixel; + internal IntPtr visualid; + internal IntPtr killid; + } + + [StructLayout(LayoutKind.Sequential, Pack=2)] + internal struct XColor { + internal IntPtr pixel; + internal ushort red; + internal ushort green; + internal ushort blue; + internal byte flags; + internal byte pad; + } + + internal enum Atom { + AnyPropertyType = 0, + XA_PRIMARY = 1, + XA_SECONDARY = 2, + XA_ARC = 3, + XA_ATOM = 4, + XA_BITMAP = 5, + XA_CARDINAL = 6, + XA_COLORMAP = 7, + XA_CURSOR = 8, + XA_CUT_BUFFER0 = 9, + XA_CUT_BUFFER1 = 10, + XA_CUT_BUFFER2 = 11, + XA_CUT_BUFFER3 = 12, + XA_CUT_BUFFER4 = 13, + XA_CUT_BUFFER5 = 14, + XA_CUT_BUFFER6 = 15, + XA_CUT_BUFFER7 = 16, + XA_DRAWABLE = 17, + XA_FONT = 18, + XA_INTEGER = 19, + XA_PIXMAP = 20, + XA_POINT = 21, + XA_RECTANGLE = 22, + XA_RESOURCE_MANAGER = 23, + XA_RGB_COLOR_MAP = 24, + XA_RGB_BEST_MAP = 25, + XA_RGB_BLUE_MAP = 26, + XA_RGB_DEFAULT_MAP = 27, + XA_RGB_GRAY_MAP = 28, + XA_RGB_GREEN_MAP = 29, + XA_RGB_RED_MAP = 30, + XA_STRING = 31, + XA_VISUALID = 32, + XA_WINDOW = 33, + XA_WM_COMMAND = 34, + XA_WM_HINTS = 35, + XA_WM_CLIENT_MACHINE = 36, + XA_WM_ICON_NAME = 37, + XA_WM_ICON_SIZE = 38, + XA_WM_NAME = 39, + XA_WM_NORMAL_HINTS = 40, + XA_WM_SIZE_HINTS = 41, + XA_WM_ZOOM_HINTS = 42, + XA_MIN_SPACE = 43, + XA_NORM_SPACE = 44, + XA_MAX_SPACE = 45, + XA_END_SPACE = 46, + XA_SUPERSCRIPT_X = 47, + XA_SUPERSCRIPT_Y = 48, + XA_SUBSCRIPT_X = 49, + XA_SUBSCRIPT_Y = 50, + XA_UNDERLINE_POSITION = 51, + XA_UNDERLINE_THICKNESS = 52, + XA_STRIKEOUT_ASCENT = 53, + XA_STRIKEOUT_DESCENT = 54, + XA_ITALIC_ANGLE = 55, + XA_X_HEIGHT = 56, + XA_QUAD_WIDTH = 57, + XA_WEIGHT = 58, + XA_POINT_SIZE = 59, + XA_RESOLUTION = 60, + XA_COPYRIGHT = 61, + XA_NOTICE = 62, + XA_FONT_NAME = 63, + XA_FAMILY_NAME = 64, + XA_FULL_NAME = 65, + XA_CAP_HEIGHT = 66, + XA_WM_CLASS = 67, + XA_WM_TRANSIENT_FOR = 68, + + XA_LAST_PREDEFINED = 68 + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XScreen { + internal IntPtr ext_data; + internal IntPtr display; + internal IntPtr root; + internal int width; + internal int height; + internal int mwidth; + internal int mheight; + internal int ndepths; + internal IntPtr depths; + internal int root_depth; + internal IntPtr root_visual; + internal IntPtr default_gc; + internal IntPtr cmap; + internal IntPtr white_pixel; + internal IntPtr black_pixel; + internal int max_maps; + internal int min_maps; + internal int backing_store; + internal bool save_unders; + internal IntPtr root_input_mask; + } + + [Flags] + internal enum ChangeWindowFlags { + CWX = 1<<0, + CWY = 1<<1, + CWWidth = 1<<2, + CWHeight = 1<<3, + CWBorderWidth = 1<<4, + CWSibling = 1<<5, + CWStackMode = 1<<6 + } + + internal enum StackMode { + Above = 0, + Below = 1, + TopIf = 2, + BottomIf = 3, + Opposite = 4 + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XWindowChanges { + internal int x; + internal int y; + internal int width; + internal int height; + internal int border_width; + internal IntPtr sibling; + internal StackMode stack_mode; + } + + [Flags] + internal enum ColorFlags { + DoRed = 1<<0, + DoGreen = 1<<1, + DoBlue = 1<<2 + } + + internal enum NotifyMode { + NotifyNormal = 0, + NotifyGrab = 1, + NotifyUngrab = 2 + } + + internal enum NotifyDetail { + NotifyAncestor = 0, + NotifyVirtual = 1, + NotifyInferior = 2, + NotifyNonlinear = 3, + NotifyNonlinearVirtual = 4, + NotifyPointer = 5, + NotifyPointerRoot = 6, + NotifyDetailNone = 7 + } + + [StructLayout(LayoutKind.Sequential)] + internal struct MotifWmHints { + internal IntPtr flags; + internal IntPtr functions; + internal IntPtr decorations; + internal IntPtr input_mode; + internal IntPtr status; + + public override string ToString () + { + return string.Format("MotifWmHints _eventHandler; + private bool _invalidated; + private XConfigureEvent? _configure; + private IInputRoot _inputRoot; + private IMouseDevice _mouse; + private Point _position; + private IntPtr _handle; + private bool _mapped; + + class InputEventContainer + { + public RawInputEventArgs Event; + } + private Queue _inputQueue = new Queue(); + private InputEventContainer _lastEvent; + + public X11Window(AvaloniaX11Platform platform, bool popup) + { + _platform = platform; + _popup = popup; + _x11 = platform.Info; + _mouse = platform.MouseDevice; + + + XSetWindowAttributes attr = new XSetWindowAttributes(); + var valueMask = default(SetWindowValuemask); + /* + _handle = XCreateSimpleWindow(_x11.Display, _x11.DefaultRootWindow, 10, 10, 100, 100, 0, IntPtr.Zero, + IntPtr.Zero);*/ + var vinfo = _x11.MatchedVisual; + attr.colormap = XCreateColormap(_x11.Display, _x11.RootWindow, vinfo.visual, 0); + attr.backing_store = 2; + attr.bit_gravity = Gravity.NorthWestGravity; + attr.win_gravity = Gravity.NorthWestGravity; + valueMask |= SetWindowValuemask.ColorMap | SetWindowValuemask.BackPixel | SetWindowValuemask.BorderPixel + | SetWindowValuemask.BackPixmap | SetWindowValuemask.BackingStore + | SetWindowValuemask.BitGravity | SetWindowValuemask.WinGravity; + + if (popup) + { + attr.override_redirect = true; + valueMask |= SetWindowValuemask.OverrideRedirect; + } + + _handle = XCreateWindow(_x11.Display, _x11.RootWindow, 10, 10, 300, 200, 0, + (int)vinfo.depth, + (int)CreateWindowArgs.InputOutput, vinfo.visual, + new UIntPtr((uint)valueMask), ref attr); + + + Handle = new PlatformHandle(_handle, "XID"); + ClientSize = new Size(400, 400); + _eventHandler = OnEvent; + platform.Windows[_handle] = _eventHandler; + XSelectInput(_x11.Display, _handle, + new IntPtr(0xffffff + ^ (int)XEventMask.SubstructureRedirectMask + ^ (int)XEventMask.ResizeRedirectMask + ^ (int)XEventMask.PointerMotionHintMask)); + var protocols = new[] + { + _x11.Atoms.WM_DELETE_WINDOW + }; + XSetWMProtocols(_x11.Display, _handle, protocols, protocols.Length); + var feature = (EglGlPlatformFeature)AvaloniaLocator.Current.GetService(); + var surfaces = new List + { + new X11FramebufferSurface(_x11.DeferredDisplay, _handle) + }; + if (feature != null) + surfaces.Insert(0, + new EglGlPlatformSurface((EglDisplay)feature.Display, feature.DeferredContext, + new SurfaceInfo(_x11.DeferredDisplay, _handle))); + Surfaces = surfaces.ToArray(); + UpdateWmHits(); + XFlush(_x11.Display); + } + + class SurfaceInfo : EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo + { + private readonly IntPtr _display; + + public SurfaceInfo(IntPtr display, IntPtr xid) + { + _display = display; + Handle = xid; + } + public IntPtr Handle { get; } + + public System.Drawing.Size PixelSize + { + get + { + XLockDisplay(_display); + + XGetGeometry(_display, Handle, out var geo); + XUnlockDisplay(_display); + return new System.Drawing.Size(geo.width, geo.height); + } + } + + public double Scaling { get; } = 1; + } + + void UpdateWmHits() + { + var functions = MotifFunctions.All; + var decorations = MotifDecorations.All; + + if (_popup || !_systemDecorations) + { + functions = 0; + decorations = 0; + } + + if (!_canResize) + { + functions ^= MotifFunctions.Resize | MotifFunctions.Maximize; + decorations ^= MotifDecorations.Maximize | MotifDecorations.ResizeH; + } + + var hints = new MotifWmHints(); + hints.flags = new IntPtr((int)(MotifFlags.Decorations | MotifFlags.Functions)); + + hints.decorations = new IntPtr((int)decorations); + hints.functions = new IntPtr((int)functions); + + XChangeProperty(_x11.Display, _handle, + _x11.Atoms._MOTIF_WM_HINTS, _x11.Atoms._MOTIF_WM_HINTS, 32, + PropertyMode.Replace, ref hints, 5); + } + + public Size ClientSize { get; private set; } + public double Scaling { get; } = 1; + public IEnumerable Surfaces { get; } + public Action Input { get; set; } + public Action Paint { get; set; } + public Action Resized { get; set; } + public Action ScalingChanged { get; set; } + public Action Deactivated { get; set; } + public Action Activated { get; set; } + public Func Closing { get; set; } + public WindowState WindowState { get; set; } + public Action WindowStateChanged { get; set; } + public Action Closed { get; set; } + public Action PositionChanged { get; set; } + + public IRenderer CreateRenderer(IRenderRoot root) => + new DeferredRenderer(root, AvaloniaLocator.Current.GetService()); + + unsafe void OnEvent(XEvent ev) + { + if (ev.type == XEventName.MapNotify) + _mapped = true; + else if (ev.type == XEventName.UnmapNotify) + _mapped = false; + else if (ev.type == XEventName.Expose) + DoPaint(); + else if (ev.type == XEventName.FocusIn) + Activated?.Invoke(); + else if (ev.type == XEventName.FocusOut) + Deactivated?.Invoke(); + else if (ev.type == XEventName.MotionNotify) + MouseEvent(RawMouseEventType.Move, ev, ev.MotionEvent.state); + + else if (ev.type == XEventName.ButtonPress) + { + if (ev.ButtonEvent.button < 4) + MouseEvent(ev.ButtonEvent.button == 1 ? RawMouseEventType.LeftButtonDown + : ev.ButtonEvent.button == 2 ? RawMouseEventType.MiddleButtonDown + : RawMouseEventType.RightButtonDown, ev, ev.ButtonEvent.state); + else + { + var delta = ev.ButtonEvent.button == 4 + ? new Vector(0, 1) + : ev.ButtonEvent.button == 5 + ? new Vector(0, -1) + : ev.ButtonEvent.button == 6 + ? new Vector(1, 0) + : new Vector(-1, 0); + ScheduleInput(new RawMouseWheelEventArgs(_mouse, (ulong)ev.ButtonEvent.time.ToInt64(), + _inputRoot, new Point(ev.ButtonEvent.x, ev.ButtonEvent.y), delta, + TranslateModifiers(ev.ButtonEvent.state))); + } + + } + else if (ev.type == XEventName.ButtonRelease) + { + if (ev.ButtonEvent.button < 4) + MouseEvent(ev.ButtonEvent.button == 1 ? RawMouseEventType.LeftButtonUp + : ev.ButtonEvent.button == 2 ? RawMouseEventType.MiddleButtonUp + : RawMouseEventType.RightButtonUp, ev, ev.ButtonEvent.state); + } + else if (ev.type == XEventName.ConfigureNotify) + { + var needEnqueue = (_configure == null); + _configure = ev.ConfigureEvent; + if (needEnqueue) + Dispatcher.UIThread.Post(() => + { + if (_configure == null) + return; + var cev = _configure.Value; + _configure = null; + var nsize = new Size(cev.width, cev.height); + var npos = new Point(cev.x, cev.y); + var changedSize = ClientSize != nsize; + var changedPos = npos != _position; + ClientSize = nsize; + _position = npos; + if (changedSize) + Resized?.Invoke(nsize); + if (changedPos) + PositionChanged?.Invoke(npos); + }); + } + else if (ev.type == XEventName.DestroyNotify) + { + if (_handle != IntPtr.Zero) + { + _platform.Windows.Remove(_handle); + _handle = IntPtr.Zero; + Closed?.Invoke(); + } + } + else if (ev.type == XEventName.ClientMessage) + { + if (ev.ClientMessageEvent.message_type == _x11.Atoms.WM_PROTOCOLS) + { + if (ev.ClientMessageEvent.ptr1 == _x11.Atoms.WM_DELETE_WINDOW) + { + if (Closing?.Invoke() != true) + Dispose(); + } + + } + } + } + + + + InputModifiers TranslateModifiers(XModifierMask state) + { + var rv = default(InputModifiers); + if (state.HasFlag(XModifierMask.Button1Mask)) + rv |= InputModifiers.LeftMouseButton; + if (state.HasFlag(XModifierMask.Button2Mask)) + rv |= InputModifiers.RightMouseButton; + if (state.HasFlag(XModifierMask.Button2Mask)) + rv |= InputModifiers.MiddleMouseButton; + if (state.HasFlag(XModifierMask.ShiftMask)) + rv |= InputModifiers.Shift; + if (state.HasFlag(XModifierMask.ControlMask)) + rv |= InputModifiers.Control; + if (state.HasFlag(XModifierMask.Mod1Mask)) + rv |= InputModifiers.Alt; + if (state.HasFlag(XModifierMask.Mod4Mask)) + rv |= InputModifiers.Windows; + return rv; + } + static Stopwatch St = Stopwatch.StartNew(); + private bool _systemDecorations = true; + private bool _canResize = true; + + void ScheduleInput(RawInputEventArgs args) + { + _lastEvent = new InputEventContainer() {Event = args}; + _inputQueue.Enqueue(_lastEvent); + if (_inputQueue.Count == 1) + { + Dispatcher.UIThread.Post(() => + { + while (_inputQueue.Count > 0) + { + Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1); + var ev = _inputQueue.Dequeue(); + Input?.Invoke(ev.Event); + } + }, DispatcherPriority.Input); + } + } + + void MouseEvent(RawMouseEventType type, XEvent ev, XModifierMask mods) + { + _x11.LastActivityTimestamp = ev.ButtonEvent.time; + var mev = new RawMouseEventArgs( + _mouse, (ulong)ev.ButtonEvent.time.ToInt64(), _inputRoot, + type, new Point(ev.ButtonEvent.x, ev.ButtonEvent.y), TranslateModifiers(mods)); + if(type == RawMouseEventType.Move && _inputQueue.Count>0 && _lastEvent.Event is RawMouseEventArgs ma) + if (ma.Type == RawMouseEventType.Move) + { + _lastEvent.Event = mev; + return; + } + ScheduleInput(mev); + } + + void DoPaint() + { + _invalidated = false; + Paint?.Invoke(new Rect()); + } + + public void Invalidate(Rect rect) + { + if(_invalidated) + return; + _invalidated = true; + Dispatcher.UIThread.InvokeAsync(() => + { + if (_mapped) + DoPaint(); + }); + } + + public void SetInputRoot(IInputRoot inputRoot) + { + _inputRoot = inputRoot; + } + + public void Dispose() + { + if (_handle != IntPtr.Zero) + { + XDestroyWindow(_x11.Display, _handle); + _platform.Windows.Remove(_handle); + _handle = IntPtr.Zero; + Closed?.Invoke(); + + } + } + + public void Show() => XMapWindow(_x11.Display, _handle); + + public void Hide() => XUnmapWindow(_x11.Display, _handle); + + + public Point PointToClient(Point point) => new Point(point.X - _position.X, point.Y - _position.Y); + + public Point PointToScreen(Point point) => new Point(point.X + _position.X, point.Y + _position.Y); + + public void SetSystemDecorations(bool enabled) + { + _systemDecorations = enabled; + UpdateWmHits(); + } + + + public void Resize(Size clientSize) + { + if (clientSize == ClientSize) + return; + var changes = new XWindowChanges(); + changes.width = (int)clientSize.Width; + changes.height = (int)clientSize.Height; + var needResize = clientSize != ClientSize; + XConfigureWindow(_x11.Display, _handle, ChangeWindowFlags.CWHeight | ChangeWindowFlags.CWWidth, + ref changes); + XFlush(_x11.Display); + + if (_popup && needResize) + { + ClientSize = clientSize; + Resized?.Invoke(clientSize); + } + } + + public void CanResize(bool value) + { + _canResize = value; + UpdateWmHits(); + } + + public void SetCursor(IPlatformHandle cursor) + { + } + + public IPlatformHandle Handle { get; } + + public Point Position + { + get => _position; + set + { + var changes = new XWindowChanges(); + changes.x = (int)value.X; + changes.y = (int)value.Y; + XConfigureWindow(_x11.Display, _handle, ChangeWindowFlags.CWX | ChangeWindowFlags.CWY, + ref changes); + XFlush(_x11.Display); + + } + } + + public IMouseDevice MouseDevice => _mouse; + + public unsafe void Activate() + { + if (_x11.Atoms._NET_ACTIVE_WINDOW != IntPtr.Zero) + { + + var ev = new XEvent + { + AnyEvent = + { + type = XEventName.ClientMessage, + window = _handle, + }, + ClientMessageEvent = { + message_type = _x11.Atoms._NET_ACTIVE_WINDOW, + format = 32, + ptr1 = new IntPtr(1), + ptr2 = _x11.LastActivityTimestamp + + } + }; + + XSendEvent(_x11.Display, _x11.RootWindow, false, + new IntPtr((int)(XEventMask.SubstructureRedirectMask | XEventMask.StructureNotifyMask)), ref ev); + } + else + { + XRaiseWindow(_x11.Display, _handle); + XSetInputFocus(_x11.Display, _handle, 0, IntPtr.Zero); + } + } + + + public IScreenImpl Screen { get; } = new ScreenStub(); + public Size MaxClientSize { get; } = new Size(1920, 1280); + + + public void BeginMoveDrag() + { + } + + public void BeginResizeDrag(WindowEdge edge) + { + } + + public void SetTitle(string title) + { + } + + public void SetMinMaxSize(Size minSize, Size maxSize) + { + + } + + public void SetTopmost(bool value) + { + + } + + public IDisposable ShowDialog() + { + return Disposable.Empty; + } + + public void SetIcon(IWindowIconImpl icon) + { + } + + public void ShowTaskbarIcon(bool value) + { + } + + + + + } +} diff --git a/src/Avalonia.X11/XError.cs b/src/Avalonia.X11/XError.cs new file mode 100644 index 0000000000..2cc8f63c96 --- /dev/null +++ b/src/Avalonia.X11/XError.cs @@ -0,0 +1,30 @@ +using System; + +namespace Avalonia.X11 +{ + static class XError + { + private static readonly XErrorHandler s_errorHandlerDelegate = Handler; + public static XErrorEvent LastError; + static int Handler(IntPtr display, ref XErrorEvent error) + { + LastError = error; + return 0; + } + + public static void ThrowLastError(string desc) + { + var err = LastError; + LastError = new XErrorEvent(); + if (err.error_code == 0) + throw new X11Exception(desc); + throw new X11Exception(desc + ": " + err.error_code); + + } + + public static void Init() + { + XLib.XSetErrorHandler(s_errorHandlerDelegate); + } + } +} diff --git a/src/Avalonia.X11/XLib.cs b/src/Avalonia.X11/XLib.cs index 88d36091ca..593e6b438f 100644 --- a/src/Avalonia.X11/XLib.cs +++ b/src/Avalonia.X11/XLib.cs @@ -1,105 +1,421 @@ using System; using System.Runtime.InteropServices; +using System.Text; + +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable FieldCanBeMadeReadOnly.Global +// ReSharper disable CommentTypo +// ReSharper disable UnusedMember.Global +// ReSharper disable IdentifierTypo +// ReSharper disable NotAccessedField.Global +// ReSharper disable UnusedMethodReturnValue.Global namespace Avalonia.X11 { - public static class XLib + internal static class XLib { + const string libX11 = "X11"; + + [DllImport(libX11)] + public static extern IntPtr XOpenDisplay(IntPtr display); + + [DllImport(libX11)] + public static extern int XCloseDisplay(IntPtr display); + + [DllImport(libX11)] + public static extern IntPtr XSynchronize(IntPtr display, bool onoff); + + [DllImport(libX11)] + public static extern IntPtr XCreateWindow(IntPtr display, IntPtr parent, int x, int y, int width, int height, + int border_width, int depth, int xclass, IntPtr visual, UIntPtr valuemask, + ref XSetWindowAttributes attributes); + + [DllImport(libX11)] + public static extern IntPtr XCreateSimpleWindow(IntPtr display, IntPtr parent, int x, int y, int width, + int height, int border_width, IntPtr border, IntPtr background); + + [DllImport(libX11)] + public static extern int XMapWindow(IntPtr display, IntPtr window); + + [DllImport(libX11)] + public static extern int XUnmapWindow(IntPtr display, IntPtr window); + + [DllImport(libX11)] + public static extern int XMapSubindows(IntPtr display, IntPtr window); + + [DllImport(libX11)] + public static extern int XUnmapSubwindows(IntPtr display, IntPtr window); + + [DllImport(libX11)] + public static extern IntPtr XRootWindow(IntPtr display, int screen_number); + [DllImport(libX11)] + public static extern IntPtr XDefaultRootWindow(IntPtr display); + + [DllImport(libX11)] + public static extern IntPtr XNextEvent(IntPtr display, out XEvent xevent); + + [DllImport(libX11)] + public static extern int XConnectionNumber(IntPtr diplay); + + [DllImport(libX11)] + public static extern int XPending(IntPtr diplay); + + [DllImport(libX11)] + public static extern IntPtr XSelectInput(IntPtr display, IntPtr window, IntPtr mask); + + [DllImport(libX11)] + public static extern int XDestroyWindow(IntPtr display, IntPtr window); + + [DllImport(libX11)] + public static extern int XReparentWindow(IntPtr display, IntPtr window, IntPtr parent, int x, int y); + + [DllImport(libX11)] + public static extern int XMoveResizeWindow(IntPtr display, IntPtr window, int x, int y, int width, int height); + + [DllImport(libX11)] + public static extern int XResizeWindow(IntPtr display, IntPtr window, int width, int height); + + [DllImport(libX11)] + public static extern int XGetWindowAttributes(IntPtr display, IntPtr window, ref XWindowAttributes attributes); + + [DllImport(libX11)] + public static extern int XFlush(IntPtr display); + + [DllImport(libX11)] + public static extern int XSetWMName(IntPtr display, IntPtr window, ref XTextProperty text_prop); + + [DllImport(libX11)] + public static extern int XStoreName(IntPtr display, IntPtr window, string window_name); + + [DllImport(libX11)] + public static extern int XFetchName(IntPtr display, IntPtr window, ref IntPtr window_name); + + [DllImport(libX11)] + public static extern int XSendEvent(IntPtr display, IntPtr window, bool propagate, IntPtr event_mask, + ref XEvent send_event); + + [DllImport(libX11)] + public static extern int XQueryTree(IntPtr display, IntPtr window, out IntPtr root_return, + out IntPtr parent_return, out IntPtr children_return, out int nchildren_return); + + [DllImport(libX11)] + public static extern int XFree(IntPtr data); + + [DllImport(libX11)] + public static extern int XRaiseWindow(IntPtr display, IntPtr window); + + [DllImport(libX11)] + public static extern uint XLowerWindow(IntPtr display, IntPtr window); + + [DllImport(libX11)] + public static extern uint XConfigureWindow(IntPtr display, IntPtr window, ChangeWindowFlags value_mask, + ref XWindowChanges values); + + [DllImport(libX11)] + public static extern IntPtr XInternAtom(IntPtr display, string atom_name, bool only_if_exists); + + [DllImport(libX11)] + public static extern int XInternAtoms(IntPtr display, string[] atom_names, int atom_count, bool only_if_exists, + IntPtr[] atoms); + + [DllImport(libX11)] + public static extern int XSetWMProtocols(IntPtr display, IntPtr window, IntPtr[] protocols, int count); + + [DllImport(libX11)] + public static extern int XGrabPointer(IntPtr display, IntPtr window, bool owner_events, EventMask event_mask, + GrabMode pointer_mode, GrabMode keyboard_mode, IntPtr confine_to, IntPtr cursor, IntPtr timestamp); + + [DllImport(libX11)] + public static extern int XUngrabPointer(IntPtr display, IntPtr timestamp); + + [DllImport(libX11)] + public static extern bool XQueryPointer(IntPtr display, IntPtr window, out IntPtr root, out IntPtr child, + out int root_x, out int root_y, out int win_x, out int win_y, out int keys_buttons); + + [DllImport(libX11)] + public static extern bool XTranslateCoordinates(IntPtr display, IntPtr src_w, IntPtr dest_w, int src_x, + int src_y, out int intdest_x_return, out int dest_y_return, out IntPtr child_return); + + [DllImport(libX11)] + public static extern bool XGetGeometry(IntPtr display, IntPtr window, out IntPtr root, out int x, out int y, + out int width, out int height, out int border_width, out int depth); + + [DllImport(libX11)] + public static extern bool XGetGeometry(IntPtr display, IntPtr window, IntPtr root, out int x, out int y, + out int width, out int height, IntPtr border_width, IntPtr depth); + + [DllImport(libX11)] + public static extern bool XGetGeometry(IntPtr display, IntPtr window, IntPtr root, out int x, out int y, + IntPtr width, IntPtr height, IntPtr border_width, IntPtr depth); + + [DllImport(libX11)] + public static extern bool XGetGeometry(IntPtr display, IntPtr window, IntPtr root, IntPtr x, IntPtr y, + out int width, out int height, IntPtr border_width, IntPtr depth); + + [DllImport(libX11)] + public static extern uint XWarpPointer(IntPtr display, IntPtr src_w, IntPtr dest_w, int src_x, int src_y, + uint src_width, uint src_height, int dest_x, int dest_y); + + [DllImport(libX11)] + public static extern int XClearWindow(IntPtr display, IntPtr window); + + [DllImport(libX11)] + public static extern int XClearArea(IntPtr display, IntPtr window, int x, int y, int width, int height, + bool exposures); + + // Colormaps + [DllImport(libX11)] + public static extern IntPtr XDefaultScreenOfDisplay(IntPtr display); + + [DllImport(libX11)] + public static extern int XScreenNumberOfScreen(IntPtr display, IntPtr Screen); + + [DllImport(libX11)] + public static extern IntPtr XDefaultVisual(IntPtr display, int screen_number); + + [DllImport(libX11)] + public static extern uint XDefaultDepth(IntPtr display, int screen_number); + + [DllImport(libX11)] + public static extern int XDefaultScreen(IntPtr display); + + [DllImport(libX11)] + public static extern IntPtr XDefaultColormap(IntPtr display, int screen_number); + + [DllImport(libX11)] + public static extern int XLookupColor(IntPtr display, IntPtr Colormap, string Coloranem, + ref XColor exact_def_color, ref XColor screen_def_color); + + [DllImport(libX11)] + public static extern int XAllocColor(IntPtr display, IntPtr Colormap, ref XColor colorcell_def); + + [DllImport(libX11)] + public static extern int XSetTransientForHint(IntPtr display, IntPtr window, IntPtr prop_window); + + [DllImport(libX11)] + public static extern int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, + int format, PropertyMode mode, ref MotifWmHints data, int nelements); + + [DllImport(libX11)] + public static extern int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, + int format, PropertyMode mode, ref uint value, int nelements); + + [DllImport(libX11)] + public static extern int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, + int format, PropertyMode mode, ref IntPtr value, int nelements); + + [DllImport(libX11)] + public static extern int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, + int format, PropertyMode mode, uint[] data, int nelements); + + [DllImport(libX11)] + public static extern int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, + int format, PropertyMode mode, int[] data, int nelements); + + [DllImport(libX11)] + public static extern int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, + int format, PropertyMode mode, IntPtr[] data, int nelements); + + [DllImport(libX11)] + public static extern int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, + int format, PropertyMode mode, IntPtr atoms, int nelements); + + [DllImport(libX11, CharSet = CharSet.Ansi)] + public static extern int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, + int format, PropertyMode mode, string text, int text_length); + + [DllImport(libX11)] + public static extern int XDeleteProperty(IntPtr display, IntPtr window, IntPtr property); + + // Drawing + [DllImport(libX11)] + public static extern IntPtr XCreateGC(IntPtr display, IntPtr window, IntPtr valuemask, ref XGCValues values); + + [DllImport(libX11)] + public static extern int XFreeGC(IntPtr display, IntPtr gc); + + [DllImport(libX11)] + public static extern int XSetFunction(IntPtr display, IntPtr gc, GXFunction function); + + [DllImport(libX11)] + internal static extern int XSetLineAttributes(IntPtr display, IntPtr gc, int line_width, GCLineStyle line_style, + GCCapStyle cap_style, GCJoinStyle join_style); + + [DllImport(libX11)] + public static extern int XDrawLine(IntPtr display, IntPtr drawable, IntPtr gc, int x1, int y1, int x2, int y2); + + [DllImport(libX11)] + public static extern int XDrawRectangle(IntPtr display, IntPtr drawable, IntPtr gc, int x1, int y1, int width, + int height); + + [DllImport(libX11)] + public static extern int XFillRectangle(IntPtr display, IntPtr drawable, IntPtr gc, int x1, int y1, int width, + int height); + + [DllImport(libX11)] + public static extern int XSetWindowBackground(IntPtr display, IntPtr window, IntPtr background); + + [DllImport(libX11)] + public static extern int XCopyArea(IntPtr display, IntPtr src, IntPtr dest, IntPtr gc, int src_x, int src_y, + int width, int height, int dest_x, int dest_y); + + [DllImport(libX11)] + public static extern int XGetWindowProperty(IntPtr display, IntPtr window, IntPtr atom, IntPtr long_offset, + IntPtr long_length, bool delete, IntPtr req_type, out IntPtr actual_type, out int actual_format, + out IntPtr nitems, out IntPtr bytes_after, ref IntPtr prop); + + [DllImport(libX11)] + public static extern int XSetInputFocus(IntPtr display, IntPtr window, RevertTo revert_to, IntPtr time); + + [DllImport(libX11)] + public static extern int XIconifyWindow(IntPtr display, IntPtr window, int screen_number); + + [DllImport(libX11)] + public static extern int XDefineCursor(IntPtr display, IntPtr window, IntPtr cursor); + + [DllImport(libX11)] + public static extern int XUndefineCursor(IntPtr display, IntPtr window); + + [DllImport(libX11)] + public static extern int XFreeCursor(IntPtr display, IntPtr cursor); + + [DllImport(libX11)] + public static extern IntPtr XCreateFontCursor(IntPtr display, CursorFontShape shape); + + [DllImport(libX11)] + public static extern IntPtr XCreatePixmapCursor(IntPtr display, IntPtr source, IntPtr mask, + ref XColor foreground_color, ref XColor background_color, int x_hot, int y_hot); + + [DllImport(libX11)] + public static extern IntPtr XCreatePixmapFromBitmapData(IntPtr display, IntPtr drawable, byte[] data, int width, + int height, IntPtr fg, IntPtr bg, int depth); + + [DllImport(libX11)] + public static extern IntPtr XCreatePixmap(IntPtr display, IntPtr d, int width, int height, int depth); + + [DllImport(libX11)] + public static extern IntPtr XFreePixmap(IntPtr display, IntPtr pixmap); + + [DllImport(libX11)] + public static extern int XQueryBestCursor(IntPtr display, IntPtr drawable, int width, int height, + out int best_width, out int best_height); + + [DllImport(libX11)] + public static extern IntPtr XWhitePixel(IntPtr display, int screen_no); + + [DllImport(libX11)] + public static extern IntPtr XBlackPixel(IntPtr display, int screen_no); + + [DllImport(libX11)] + public static extern void XGrabServer(IntPtr display); + + [DllImport(libX11)] + public static extern void XUngrabServer(IntPtr display); + + [DllImport(libX11)] + public static extern void XGetWMNormalHints(IntPtr display, IntPtr window, ref XSizeHints hints, + out IntPtr supplied_return); + + [DllImport(libX11)] + public static extern void XSetWMNormalHints(IntPtr display, IntPtr window, ref XSizeHints hints); + + [DllImport(libX11)] + public static extern void XSetZoomHints(IntPtr display, IntPtr window, ref XSizeHints hints); + + [DllImport(libX11)] + public static extern void XSetWMHints(IntPtr display, IntPtr window, ref XWMHints wmhints); + + [DllImport(libX11)] + public static extern int XGetIconSizes(IntPtr display, IntPtr window, out IntPtr size_list, out int count); + + [DllImport(libX11)] + public static extern IntPtr XSetErrorHandler(XErrorHandler error_handler); + + [DllImport(libX11)] + public static extern IntPtr XGetErrorText(IntPtr display, byte code, StringBuilder buffer, int length); + + [DllImport(libX11)] + public static extern int XInitThreads(); + + [DllImport(libX11)] + public static extern int XConvertSelection(IntPtr display, IntPtr selection, IntPtr target, IntPtr property, + IntPtr requestor, IntPtr time); + + [DllImport(libX11)] + public static extern IntPtr XGetSelectionOwner(IntPtr display, IntPtr selection); + + [DllImport(libX11)] + public static extern int XSetSelectionOwner(IntPtr display, IntPtr selection, IntPtr owner, IntPtr time); + + [DllImport(libX11)] + public static extern int XSetPlaneMask(IntPtr display, IntPtr gc, IntPtr mask); + + [DllImport(libX11)] + public static extern int XSetForeground(IntPtr display, IntPtr gc, UIntPtr foreground); + + [DllImport(libX11)] + public static extern int XSetBackground(IntPtr display, IntPtr gc, UIntPtr background); + + [DllImport(libX11)] + public static extern int XBell(IntPtr display, int percent); + + [DllImport(libX11)] + public static extern int XChangeActivePointerGrab(IntPtr display, EventMask event_mask, IntPtr cursor, + IntPtr time); + + [DllImport(libX11)] + public static extern bool XFilterEvent(ref XEvent xevent, IntPtr window); + + [DllImport(libX11)] + public static extern void XkbSetDetectableAutoRepeat(IntPtr display, bool detectable, IntPtr supported); + + [DllImport(libX11)] + public static extern void XPeekEvent(IntPtr display, out XEvent xevent); - [DllImport("libX11.so.6")] - public static extern IntPtr XInitThreads(); - - [DllImport("libX11.so.6")] - public static extern IntPtr XOpenDisplay(IntPtr name); - - [DllImport("libX11.so.6")] - public static extern int XConnectionNumber(IntPtr display); + [DllImport(libX11)] + public static extern void XMatchVisualInfo(IntPtr display, int screen, int depth, int klass, out XVisualInfo info); - [DllImport("libX11.so.6")] + [DllImport(libX11)] public static extern IntPtr XLockDisplay(IntPtr display); - [DllImport("libX11.so.6")] + [DllImport(libX11)] public static extern IntPtr XUnlockDisplay(IntPtr display); - [DllImport("libX11.so.6")] - public static extern IntPtr XFreeGC(IntPtr display, IntPtr gc); - - [DllImport("libX11.so.6")] + [DllImport(libX11)] public static extern IntPtr XCreateGC(IntPtr display, IntPtr drawable, ulong valuemask, IntPtr values); - [DllImport("libX11.so.6")] + [DllImport(libX11)] public static extern int XInitImage(ref XImage image); - [DllImport("libX11.so.6")] + [DllImport(libX11)] public static extern int XDestroyImage(ref XImage image); - - [DllImport("libX11.so.6")] - public static extern IntPtr XSetErrorHandler(XErrorHandler handler); - [DllImport("libX11.so.6")] + [DllImport(libX11)] + public static extern int XPutImage(IntPtr display, IntPtr drawable, IntPtr gc, ref XImage image, + int srcx, int srcy, int destx, int desty, uint width, uint height); + [DllImport(libX11)] public static extern int XSync(IntPtr display, bool discard); - [DllImport("libX11.so.6")] - public static extern int XNextEvent(IntPtr display, out XEvent ev); + [DllImport(libX11)] + public static extern IntPtr XCreateColormap(IntPtr display, IntPtr window, IntPtr visual, int create); - [DllImport("libX11.so.6")] - public static extern bool XPending(IntPtr display); - - [StructLayout(LayoutKind.Sequential)] - public struct XAnyEvent + public struct XGeometry { - - public int Type; - public ulong Serial; /* # of last request processed by server */ - public bool send_event; /* true if this came from a SendEvent request */ - public IntPtr display; /* Display the event was read from */ - public IntPtr window; /* window on which event was requested in event mask */ + public IntPtr root; + public int x; + public int y; + public int width; + public int height; + public int bw; + public int d; } - [StructLayout(LayoutKind.Explicit)] - public unsafe struct XEvent + public static bool XGetGeometry(IntPtr display, IntPtr window, out XGeometry geo) { - [FieldOffset(0)] public int Type; - [FieldOffset(0)] public XAnyEvent XAny; - [FieldOffset(0)] private fixed int pad[40]; + geo = new XGeometry(); + return XGetGeometry(display, window, out geo.root, out geo.x, out geo.y, out geo.width, out geo.height, + out geo.bw, out geo.d); } - public delegate int XErrorHandler(IntPtr display, ref XErrorEvent error); - - [DllImport("libX11.so.6")] - public static extern int XPutImage(IntPtr display, IntPtr drawable, IntPtr gc, ref XImage image, - int srcx, int srcy, int destx, int desty, uint width, uint height); - - [StructLayout(LayoutKind.Sequential)] - public unsafe struct XErrorEvent - { - public int type; - public IntPtr* display; /* Display the event was read from */ - public ulong serial; /* serial number of failed request */ - public byte error_code; /* error code of failed request */ - public byte request_code; /* Major op-code of failed request */ - public byte minor_code; /* Minor op-code of failed request */ - public IntPtr resourceid; /* resource id */ - } - - [StructLayout(LayoutKind.Sequential)] - public unsafe struct XImage - { - public int width, height; /* size of image */ - public int xoffset; /* number of pixels offset in X direction */ - public int format; /* XYBitmap, XYPixmap, ZPixmap */ - public IntPtr data; /* pointer to image data */ - public int byte_order; /* data byte order, LSBFirst, MSBFirst */ - public int bitmap_unit; /* quant. of scanline 8, 16, 32 */ - public int bitmap_bit_order; /* LSBFirst, MSBFirst */ - public int bitmap_pad; /* 8, 16, 32 either XY or ZPixmap */ - public int depth; /* depth of image */ - public int bytes_per_line; /* accelerator to next scanline */ - public int bits_per_pixel; /* bits per pixel (ZPixmap) */ - public ulong red_mask; /* bits in z arrangement */ - public ulong green_mask; - public ulong blue_mask; - private fixed byte funcs[128]; - } } } From 80950d0eb17136903799b56edf83b503d32aa3d3 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 24 Oct 2018 11:11:56 +0300 Subject: [PATCH 010/374] [X11] Use 24-bit visuals so GL drawing works on nVidia cards --- src/Avalonia.X11/X11Framebuffer.cs | 2 +- src/Avalonia.X11/X11Info.cs | 3 --- src/Avalonia.X11/X11Window.cs | 16 ++++++---------- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/src/Avalonia.X11/X11Framebuffer.cs b/src/Avalonia.X11/X11Framebuffer.cs index ef93a7347b..2d6955f47a 100644 --- a/src/Avalonia.X11/X11Framebuffer.cs +++ b/src/Avalonia.X11/X11Framebuffer.cs @@ -34,7 +34,7 @@ namespace Avalonia.X11 image.bitmap_unit = bitsPerPixel; image.bitmap_bit_order = 0;// LSBFirst; image.bitmap_pad = bitsPerPixel; - image.depth = 32; + image.depth = 24; image.bytes_per_line = RowBytes - Width * 4; image.bits_per_pixel = bitsPerPixel; XLockDisplay(_display); diff --git a/src/Avalonia.X11/X11Info.cs b/src/Avalonia.X11/X11Info.cs index 6ad9faffbc..ddad739dd0 100644 --- a/src/Avalonia.X11/X11Info.cs +++ b/src/Avalonia.X11/X11Info.cs @@ -14,7 +14,6 @@ namespace Avalonia.X11 public IntPtr BlackPixel { get; } public IntPtr RootWindow { get; } public IntPtr DefaultRootWindow { get; } - public XVisualInfo MatchedVisual { get; } public X11Atoms Atoms { get; } public IntPtr LastActivityTimestamp { get; set; } @@ -28,8 +27,6 @@ namespace Avalonia.X11 RootWindow = XRootWindow(display, DefaultScreen); DefaultRootWindow = XDefaultRootWindow(display); Atoms = new X11Atoms(display); - XMatchVisualInfo(display, DefaultScreen, 32, 4, out var info); - MatchedVisual = info; } } } diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index 944044cfa8..6bf59866f1 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -44,15 +44,11 @@ namespace Avalonia.X11 XSetWindowAttributes attr = new XSetWindowAttributes(); var valueMask = default(SetWindowValuemask); - /* - _handle = XCreateSimpleWindow(_x11.Display, _x11.DefaultRootWindow, 10, 10, 100, 100, 0, IntPtr.Zero, - IntPtr.Zero);*/ - var vinfo = _x11.MatchedVisual; - attr.colormap = XCreateColormap(_x11.Display, _x11.RootWindow, vinfo.visual, 0); - attr.backing_store = 2; + + attr.backing_store = 1; attr.bit_gravity = Gravity.NorthWestGravity; attr.win_gravity = Gravity.NorthWestGravity; - valueMask |= SetWindowValuemask.ColorMap | SetWindowValuemask.BackPixel | SetWindowValuemask.BorderPixel + valueMask |= SetWindowValuemask.BackPixel | SetWindowValuemask.BorderPixel | SetWindowValuemask.BackPixmap | SetWindowValuemask.BackingStore | SetWindowValuemask.BitGravity | SetWindowValuemask.WinGravity; @@ -63,8 +59,8 @@ namespace Avalonia.X11 } _handle = XCreateWindow(_x11.Display, _x11.RootWindow, 10, 10, 300, 200, 0, - (int)vinfo.depth, - (int)CreateWindowArgs.InputOutput, vinfo.visual, + 24, + (int)CreateWindowArgs.InputOutput, IntPtr.Zero, new UIntPtr((uint)valueMask), ref attr); @@ -278,7 +274,7 @@ namespace Avalonia.X11 rv |= InputModifiers.Windows; return rv; } - static Stopwatch St = Stopwatch.StartNew(); + private bool _systemDecorations = true; private bool _canResize = true; From 9ee771b425d3f4aa0d0a951fc861144c86096bd4 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 24 Oct 2018 11:12:14 +0300 Subject: [PATCH 011/374] [X11] Reference X11 backend from ControlCatalog --- samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj index ec841db5b2..8410a197ab 100644 --- a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj +++ b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj @@ -8,6 +8,7 @@ + From 9b7f8b5f35a1d748158b43c55de44eea80d92e31 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 24 Oct 2018 11:55:48 +0300 Subject: [PATCH 012/374] [X11] Use a child window for OpenGL rendering. Fixes tons of issues on nVidia cards --- src/Avalonia.X11/X11Window.cs | 48 +++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index 6bf59866f1..f102a6abff 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -25,6 +25,7 @@ namespace Avalonia.X11 private IMouseDevice _mouse; private Point _position; private IntPtr _handle; + private IntPtr _renderHandle; private bool _mapped; class InputEventContainer @@ -62,8 +63,12 @@ namespace Avalonia.X11 24, (int)CreateWindowArgs.InputOutput, IntPtr.Zero, new UIntPtr((uint)valueMask), ref attr); - - + _renderHandle = XCreateWindow(_x11.Display, _handle, 0, 0, 300, 200, 0, 24, + (int)CreateWindowArgs.InputOutput, + IntPtr.Zero, + new UIntPtr((uint)(SetWindowValuemask.BorderPixel | SetWindowValuemask.BitGravity | + SetWindowValuemask.WinGravity | SetWindowValuemask.BackingStore)), ref attr); + Handle = new PlatformHandle(_handle, "XID"); ClientSize = new Size(400, 400); _eventHandler = OnEvent; @@ -86,7 +91,7 @@ namespace Avalonia.X11 if (feature != null) surfaces.Insert(0, new EglGlPlatformSurface((EglDisplay)feature.Display, feature.DeferredContext, - new SurfaceInfo(_x11.DeferredDisplay, _handle))); + new SurfaceInfo(_x11.DeferredDisplay, _handle, _renderHandle))); Surfaces = surfaces.ToArray(); UpdateWmHits(); XFlush(_x11.Display); @@ -95,10 +100,12 @@ namespace Avalonia.X11 class SurfaceInfo : EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo { private readonly IntPtr _display; + private readonly IntPtr _parent; - public SurfaceInfo(IntPtr display, IntPtr xid) + public SurfaceInfo(IntPtr display, IntPtr parent, IntPtr xid) { _display = display; + _parent = parent; Handle = xid; } public IntPtr Handle { get; } @@ -108,8 +115,10 @@ namespace Avalonia.X11 get { XLockDisplay(_display); - - XGetGeometry(_display, Handle, out var geo); + XGetGeometry(_display, _parent, out var geo); + XResizeWindow(_display, Handle, geo.width, geo.height); + XFlush(_display); + XSync(_display, true); XUnlockDisplay(_display); return new System.Drawing.Size(geo.width, geo.height); } @@ -167,7 +176,10 @@ namespace Avalonia.X11 unsafe void OnEvent(XEvent ev) { if (ev.type == XEventName.MapNotify) + { _mapped = true; + XMapWindow(_x11.Display, _renderHandle); + } else if (ev.type == XEventName.UnmapNotify) _mapped = false; else if (ev.type == XEventName.Expose) @@ -232,12 +244,7 @@ namespace Avalonia.X11 } else if (ev.type == XEventName.DestroyNotify) { - if (_handle != IntPtr.Zero) - { - _platform.Windows.Remove(_handle); - _handle = IntPtr.Zero; - Closed?.Invoke(); - } + Cleanup(); } else if (ev.type == XEventName.ClientMessage) { @@ -335,6 +342,15 @@ namespace Avalonia.X11 } public void Dispose() + { + if (_handle != IntPtr.Zero) + { + XDestroyWindow(_x11.Display, _handle); + Cleanup(); + } + } + + void Cleanup() { if (_handle != IntPtr.Zero) { @@ -342,10 +358,16 @@ namespace Avalonia.X11 _platform.Windows.Remove(_handle); _handle = IntPtr.Zero; Closed?.Invoke(); - + } + + if (_renderHandle != IntPtr.Zero) + { + XDestroyWindow(_x11.Display, _renderHandle); + _renderHandle = IntPtr.Zero; } } + public void Show() => XMapWindow(_x11.Display, _handle); public void Hide() => XUnmapWindow(_x11.Display, _handle); From 074d8949a6900b4d8ae2cd89a7fe85dbf4483709 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 24 Oct 2018 12:37:01 +0300 Subject: [PATCH 013/374] [X11] Enforce layout priority --- src/Avalonia.X11/X11Window.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index f102a6abff..deb0491168 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -240,7 +240,8 @@ namespace Avalonia.X11 Resized?.Invoke(nsize); if (changedPos) PositionChanged?.Invoke(npos); - }); + Dispatcher.UIThread.RunJobs(DispatcherPriority.Layout); + }, DispatcherPriority.Layout); } else if (ev.type == XEventName.DestroyNotify) { From 52a0514ee112565a5e3f2d0c7e2d1e799aa7f1a0 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Fri, 26 Oct 2018 22:32:21 +0300 Subject: [PATCH 014/374] [X11] BeginMove/ResizeDrag and WindowState --- src/Avalonia.X11/X11Window.cs | 157 ++++++++++++++++++++++++++++------ src/Avalonia.X11/XLib.cs | 61 ++++++++++++- 2 files changed, 189 insertions(+), 29 deletions(-) diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index deb0491168..036c7d0f34 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Diagnostics; +using System.Linq; using System.Reactive.Disposables; using Avalonia.Controls; using Avalonia.Input; @@ -13,7 +14,7 @@ using Avalonia.Threading; using static Avalonia.X11.XLib; namespace Avalonia.X11 { - class X11Window : IWindowImpl, IPopupImpl + unsafe class X11Window : IWindowImpl, IPopupImpl { private readonly AvaloniaX11Platform _platform; private readonly bool _popup; @@ -134,7 +135,6 @@ namespace Avalonia.X11 if (_popup || !_systemDecorations) { - functions = 0; decorations = 0; } @@ -165,7 +165,6 @@ namespace Avalonia.X11 public Action Deactivated { get; set; } public Action Activated { get; set; } public Func Closing { get; set; } - public WindowState WindowState { get; set; } public Action WindowStateChanged { get; set; } public Action Closed { get; set; } public Action PositionChanged { get; set; } @@ -190,7 +189,10 @@ namespace Avalonia.X11 Deactivated?.Invoke(); else if (ev.type == XEventName.MotionNotify) MouseEvent(RawMouseEventType.Move, ev, ev.MotionEvent.state); - + else if (ev.type == XEventName.PropertyNotify) + { + OnPropertyChange(ev.PropertyEvent.atom, ev.PropertyEvent.state == 0); + } else if (ev.type == XEventName.ButtonPress) { if (ev.ButtonEvent.button < 4) @@ -260,8 +262,76 @@ namespace Avalonia.X11 } } } - - + + private WindowState _lastWindowState; + public WindowState WindowState + { + get { return _lastWindowState; } + set + { + _lastWindowState = value; + if (value == WindowState.Minimized) + { + XIconifyWindow(_x11.Display, _handle, _x11.DefaultScreen); + } + else if (value == WindowState.Maximized) + { + SendNetWMMessage(_x11.Atoms._NET_WM_STATE, (IntPtr)0, _x11.Atoms._NET_WM_STATE_HIDDEN, IntPtr.Zero); + SendNetWMMessage(_x11.Atoms._NET_WM_STATE, (IntPtr)1, _x11.Atoms._NET_WM_STATE_MAXIMIZED_VERT, + _x11.Atoms._NET_WM_STATE_MAXIMIZED_HORZ); + } + else + { + SendNetWMMessage(_x11.Atoms._NET_WM_STATE, (IntPtr)0, _x11.Atoms._NET_WM_STATE_HIDDEN, IntPtr.Zero); + SendNetWMMessage(_x11.Atoms._NET_WM_STATE, (IntPtr)0, _x11.Atoms._NET_WM_STATE_MAXIMIZED_VERT, + _x11.Atoms._NET_WM_STATE_MAXIMIZED_HORZ); + } + } + } + + private void OnPropertyChange(IntPtr atom, bool hasValue) + { + if (atom == _x11.Atoms._NET_WM_STATE) + { + WindowState state = WindowState.Normal; + if(hasValue) + { + + XGetWindowProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_STATE, IntPtr.Zero, new IntPtr(256), + false, (IntPtr)Atom.XA_ATOM, out var actualAtom, out var actualFormat, out var nitems, out var bytesAfter, + out var prop); + int maximized = 0; + var pitems = (IntPtr*)prop.ToPointer(); + for (var c = 0; c < nitems.ToInt32(); c++) + { + if (pitems[c] == _x11.Atoms._NET_WM_STATE_HIDDEN) + { + state = WindowState.Minimized; + break; + } + + if (pitems[c] == _x11.Atoms._NET_WM_STATE_MAXIMIZED_HORZ || + pitems[c] == _x11.Atoms._NET_WM_STATE_MAXIMIZED_VERT) + { + maximized++; + if (maximized == 2) + { + state = WindowState.Maximized; + break; + } + } + } + XFree(prop); + } + if (_lastWindowState != state) + { + _lastWindowState = state; + WindowStateChanged?.Invoke(state); + } + } + + } + InputModifiers TranslateModifiers(XModifierMask state) { @@ -437,25 +507,8 @@ namespace Avalonia.X11 { if (_x11.Atoms._NET_ACTIVE_WINDOW != IntPtr.Zero) { - - var ev = new XEvent - { - AnyEvent = - { - type = XEventName.ClientMessage, - window = _handle, - }, - ClientMessageEvent = { - message_type = _x11.Atoms._NET_ACTIVE_WINDOW, - format = 32, - ptr1 = new IntPtr(1), - ptr2 = _x11.LastActivityTimestamp - - } - }; - - XSendEvent(_x11.Display, _x11.RootWindow, false, - new IntPtr((int)(XEventMask.SubstructureRedirectMask | XEventMask.StructureNotifyMask)), ref ev); + SendNetWMMessage(_x11.Atoms._NET_ACTIVE_WINDOW, (IntPtr)1, _x11.LastActivityTimestamp, + IntPtr.Zero); } else { @@ -467,14 +520,64 @@ namespace Avalonia.X11 public IScreenImpl Screen { get; } = new ScreenStub(); public Size MaxClientSize { get; } = new Size(1920, 1280); - - + + + + void SendNetWMMessage(IntPtr message_type, IntPtr l0, + IntPtr? l1 = null, IntPtr? l2 = null, IntPtr? l3 = null, IntPtr? l4 = null) + { + XEvent xev; + + xev = new XEvent(); + xev.ClientMessageEvent.type = XEventName.ClientMessage; + xev.ClientMessageEvent.send_event = true; + xev.ClientMessageEvent.window = _handle; + xev.ClientMessageEvent.message_type = message_type; + xev.ClientMessageEvent.format = 32; + xev.ClientMessageEvent.ptr1 = l0; + xev.ClientMessageEvent.ptr2 = l1 ?? IntPtr.Zero; + xev.ClientMessageEvent.ptr3 = l2 ?? IntPtr.Zero; + xev.ClientMessageEvent.ptr4 = l3 ?? IntPtr.Zero; + xev.ClientMessageEvent.ptr4 = l4 ?? IntPtr.Zero; + XSendEvent(_x11.Display, _x11.RootWindow, false, + new IntPtr((int)(EventMask.SubstructureRedirectMask | EventMask.SubstructureNotifyMask)), ref xev); + + } + + void BeginMoveResize(NetWmMoveResize side) + { + var pos = GetCursorPos(_x11); + XUngrabPointer(_x11.Display, _x11.LastActivityTimestamp); + SendNetWMMessage (_x11.Atoms._NET_WM_MOVERESIZE, (IntPtr) pos.x, (IntPtr) pos.y, + (IntPtr) side, + (IntPtr) 1, (IntPtr)1); // left button + } + public void BeginMoveDrag() { + BeginMoveResize(NetWmMoveResize._NET_WM_MOVERESIZE_MOVE); } public void BeginResizeDrag(WindowEdge edge) { + var side = NetWmMoveResize._NET_WM_MOVERESIZE_CANCEL; + if (edge == WindowEdge.East) + side = NetWmMoveResize._NET_WM_MOVERESIZE_SIZE_RIGHT; + if (edge == WindowEdge.North) + side = NetWmMoveResize._NET_WM_MOVERESIZE_SIZE_TOP; + if (edge == WindowEdge.South) + side = NetWmMoveResize._NET_WM_MOVERESIZE_SIZE_BOTTOM; + if (edge == WindowEdge.West) + side = NetWmMoveResize._NET_WM_MOVERESIZE_SIZE_LEFT; + if (edge == WindowEdge.NorthEast) + side = NetWmMoveResize._NET_WM_MOVERESIZE_SIZE_TOPRIGHT; + if (edge == WindowEdge.NorthWest) + side = NetWmMoveResize._NET_WM_MOVERESIZE_SIZE_TOPLEFT; + if (edge == WindowEdge.SouthEast) + side = NetWmMoveResize._NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT; + if (edge == WindowEdge.SouthWest) + side = NetWmMoveResize._NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT; + BeginMoveResize(side); } public void SetTitle(string title) diff --git a/src/Avalonia.X11/XLib.cs b/src/Avalonia.X11/XLib.cs index 593e6b438f..d6c4ec6593 100644 --- a/src/Avalonia.X11/XLib.cs +++ b/src/Avalonia.X11/XLib.cs @@ -12,7 +12,7 @@ using System.Text; namespace Avalonia.X11 { - internal static class XLib + internal unsafe static class XLib { const string libX11 = "X11"; @@ -262,7 +262,7 @@ namespace Avalonia.X11 [DllImport(libX11)] public static extern int XGetWindowProperty(IntPtr display, IntPtr window, IntPtr atom, IntPtr long_offset, IntPtr long_length, bool delete, IntPtr req_type, out IntPtr actual_type, out int actual_format, - out IntPtr nitems, out IntPtr bytes_after, ref IntPtr prop); + out IntPtr nitems, out IntPtr bytes_after, out IntPtr prop); [DllImport(libX11)] public static extern int XSetInputFocus(IntPtr display, IntPtr window, RevertTo revert_to, IntPtr time); @@ -416,6 +416,63 @@ namespace Avalonia.X11 return XGetGeometry(display, window, out geo.root, out geo.x, out geo.y, out geo.width, out geo.height, out geo.bw, out geo.d); } + + public static void QueryPointer (IntPtr display, IntPtr w, out IntPtr root, out IntPtr child, + out int root_x, out int root_y, out int child_x, out int child_y, + out int mask) + { + + IntPtr c; + + XGrabServer (display); + + XQueryPointer(display, w, out root, out c, + out root_x, out root_y, out child_x, out child_y, + out mask); + + if (root != w) + c = root; + + IntPtr child_last = IntPtr.Zero; + while (c != IntPtr.Zero) { + child_last = c; + XQueryPointer(display, c, out root, out c, + out root_x, out root_y, out child_x, out child_y, + out mask); + } + XUngrabServer (display); + XFlush (display); + + child = child_last; + } + + public static (int x, int y) GetCursorPos(X11Info x11, IntPtr? handle = null) + { + IntPtr root; + IntPtr child; + int root_x; + int root_y; + int win_x; + int win_y; + int keys_buttons; + + + + QueryPointer(x11.Display, handle ?? x11.RootWindow, out root, out child, out root_x, out root_y, out win_x, out win_y, + out keys_buttons); + + + if (handle != null) + { + return (win_x, win_y); + } + else + { + return (root_x, root_y); + } + } + + } } From 8f80351bcce3229ebdf6b1730fa4bce1440700de Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Fri, 26 Oct 2018 22:54:01 +0300 Subject: [PATCH 015/374] [X11] Implemented cursor support --- src/Avalonia.X11/Stubs.cs | 8 ---- src/Avalonia.X11/X11Atoms.cs | 11 +----- src/Avalonia.X11/X11CursorFactory.cs | 57 ++++++++++++++++++++++++++++ src/Avalonia.X11/X11Info.cs | 2 + src/Avalonia.X11/X11Platform.cs | 2 +- src/Avalonia.X11/X11Window.cs | 8 ++++ 6 files changed, 69 insertions(+), 19 deletions(-) create mode 100644 src/Avalonia.X11/X11CursorFactory.cs diff --git a/src/Avalonia.X11/Stubs.cs b/src/Avalonia.X11/Stubs.cs index ed14ab7878..6dda7b81ca 100644 --- a/src/Avalonia.X11/Stubs.cs +++ b/src/Avalonia.X11/Stubs.cs @@ -9,14 +9,6 @@ using Avalonia.Platform; namespace Avalonia.X11 { - class CursorFactoryStub : IStandardCursorFactory - { - public IPlatformHandle GetCursor(StandardCursorType cursorType) - { - return new PlatformHandle(IntPtr.Zero, "FAKE"); - } - } - class ClipboardStub : IClipboard { private string _text; diff --git a/src/Avalonia.X11/X11Atoms.cs b/src/Avalonia.X11/X11Atoms.cs index e7b83f59d3..50a735b2ef 100644 --- a/src/Avalonia.X11/X11Atoms.cs +++ b/src/Avalonia.X11/X11Atoms.cs @@ -176,9 +176,6 @@ namespace Avalonia.X11 { public readonly IntPtr OEMTEXT; public readonly IntPtr UNICODETEXT; public readonly IntPtr TARGETS; - public readonly IntPtr PostAtom; - public readonly IntPtr HoverState; - public readonly IntPtr AsyncAtom; public X11Atoms (IntPtr display) { @@ -249,10 +246,7 @@ namespace Avalonia.X11 { "PRIMARY", "COMPOUND_TEXT", "UTF8_STRING", - "TARGETS", - "_SWF_AsyncAtom", - "_SWF_PostMessageAtom", - "_SWF_HoverAtom" }; + "TARGETS"}; IntPtr[] atoms = new IntPtr [atom_names.Length];; @@ -324,9 +318,6 @@ namespace Avalonia.X11 { OEMTEXT = atoms [off++]; UNICODETEXT = atoms [off++]; TARGETS = atoms [off++]; - AsyncAtom = atoms [off++]; - PostAtom = atoms [off++]; - HoverState = atoms [off++]; DIB = XA_PIXMAP; diff --git a/src/Avalonia.X11/X11CursorFactory.cs b/src/Avalonia.X11/X11CursorFactory.cs new file mode 100644 index 0000000000..d25a0da0a2 --- /dev/null +++ b/src/Avalonia.X11/X11CursorFactory.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Avalonia.Input; +using Avalonia.Platform; + +namespace Avalonia.X11 +{ + class X11CursorFactory : IStandardCursorFactory + { + private readonly IntPtr _display; + private Dictionary _cursors; + + private static readonly Dictionary s_mapping = + new Dictionary + { + {StandardCursorType.Arrow, CursorFontShape.XC_arrow}, + {StandardCursorType.Cross, CursorFontShape.XC_cross}, + {StandardCursorType.Hand, CursorFontShape.XC_hand1}, + {StandardCursorType.Help, CursorFontShape.XC_question_arrow}, + {StandardCursorType.Ibeam, CursorFontShape.XC_xterm}, + {StandardCursorType.No, CursorFontShape.XC_X_cursor}, + {StandardCursorType.Wait, CursorFontShape.XC_watch}, + {StandardCursorType.AppStarting, CursorFontShape.XC_watch}, + {StandardCursorType.BottomSize, CursorFontShape.XC_bottom_side}, + {StandardCursorType.DragCopy, CursorFontShape.XC_center_ptr}, + {StandardCursorType.DragLink, CursorFontShape.XC_fleur}, + {StandardCursorType.DragMove, CursorFontShape.XC_diamond_cross}, + {StandardCursorType.LeftSide, CursorFontShape.XC_left_side}, + {StandardCursorType.RightSide, CursorFontShape.XC_right_side}, + {StandardCursorType.SizeAll, CursorFontShape.XC_sizing}, + {StandardCursorType.TopSide, CursorFontShape.XC_top_side}, + {StandardCursorType.UpArrow, CursorFontShape.XC_sb_up_arrow}, + {StandardCursorType.BottomLeftCorner, CursorFontShape.XC_bottom_left_corner}, + {StandardCursorType.BottomRightCorner, CursorFontShape.XC_bottom_right_corner}, + {StandardCursorType.SizeNorthSouth, CursorFontShape.XC_sb_v_double_arrow}, + {StandardCursorType.SizeWestEast, CursorFontShape.XC_sb_h_double_arrow}, + {StandardCursorType.TopLeftCorner, CursorFontShape.XC_top_left_corner}, + {StandardCursorType.TopRightCorner, CursorFontShape.XC_top_right_corner}, + }; + + public X11CursorFactory(IntPtr display) + { + _display = display; + _cursors = Enum.GetValues(typeof(CursorFontShape)).Cast() + .ToDictionary(id => id, id => XLib.XCreateFontCursor(_display, id)); + } + + public IPlatformHandle GetCursor(StandardCursorType cursorType) + { + var handle = s_mapping.TryGetValue(cursorType, out var shape) + ? _cursors[shape] + : _cursors[CursorFontShape.XC_arrow]; + return new PlatformHandle(handle, "XCURSOR"); + } + } +} diff --git a/src/Avalonia.X11/X11Info.cs b/src/Avalonia.X11/X11Info.cs index ddad739dd0..527cdc31ef 100644 --- a/src/Avalonia.X11/X11Info.cs +++ b/src/Avalonia.X11/X11Info.cs @@ -14,6 +14,7 @@ namespace Avalonia.X11 public IntPtr BlackPixel { get; } public IntPtr RootWindow { get; } public IntPtr DefaultRootWindow { get; } + public IntPtr DefaultCursor { get; } public X11Atoms Atoms { get; } public IntPtr LastActivityTimestamp { get; set; } @@ -25,6 +26,7 @@ namespace Avalonia.X11 DefaultScreen = XDefaultScreen(display); BlackPixel = XBlackPixel(display, DefaultScreen); RootWindow = XRootWindow(display, DefaultScreen); + DefaultCursor = XCreateFontCursor(display, CursorFontShape.XC_arrow); DefaultRootWindow = XDefaultRootWindow(display); Atoms = new X11Atoms(display); } diff --git a/src/Avalonia.X11/X11Platform.cs b/src/Avalonia.X11/X11Platform.cs index 33497be411..b3ffc2a30b 100644 --- a/src/Avalonia.X11/X11Platform.cs +++ b/src/Avalonia.X11/X11Platform.cs @@ -36,7 +36,7 @@ namespace Avalonia.X11 .Bind().ToConstant(new RenderLoop()) .Bind().ToConstant(new PlatformHotkeyConfiguration(InputModifiers.Control)) .Bind().ToFunc(() => KeyboardDevice) - .Bind().ToConstant(new CursorFactoryStub()) + .Bind().ToConstant(new X11CursorFactory(Display)) .Bind().ToSingleton() .Bind().ToConstant(new PlatformSettingsStub()) .Bind().ToConstant(new SystemDialogsStub()) diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index 036c7d0f34..82564e98e0 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -482,6 +482,14 @@ namespace Avalonia.X11 public void SetCursor(IPlatformHandle cursor) { + if (cursor == null) + XDefineCursor(_x11.Display, _handle, _x11.DefaultCursor); + else + { + if (cursor.HandleDescriptor != "XCURSOR") + throw new ArgumentException("Expected XCURSOR handle type"); + XDefineCursor(_x11.Display, _handle, cursor.Handle); + } } public IPlatformHandle Handle { get; } From 2ad8e3162bd671ad02b0dff4700f83df995e8846 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sat, 27 Oct 2018 13:34:11 +0300 Subject: [PATCH 016/374] [X11] MinMax size/SetTopmost/ShowTaskbarIcon/Title --- src/Avalonia.X11/X11Atoms.cs | 150 +++------------------------------- src/Avalonia.X11/X11Window.cs | 56 ++++++++++--- src/Avalonia.X11/XLib.cs | 3 + 3 files changed, 58 insertions(+), 151 deletions(-) diff --git a/src/Avalonia.X11/X11Atoms.cs b/src/Avalonia.X11/X11Atoms.cs index 50a735b2ef..51a4fc52f0 100644 --- a/src/Avalonia.X11/X11Atoms.cs +++ b/src/Avalonia.X11/X11Atoms.cs @@ -22,6 +22,7 @@ // using System; +using System.Linq; using static Avalonia.X11.XLib; // ReSharper disable FieldCanBeMadeReadOnly.Global // ReSharper disable IdentifierTypo @@ -172,158 +173,27 @@ namespace Avalonia.X11 { public readonly IntPtr _NET_WM_WINDOW_TYPE_NORMAL; public readonly IntPtr CLIPBOARD; public readonly IntPtr PRIMARY; - public readonly IntPtr DIB; public readonly IntPtr OEMTEXT; public readonly IntPtr UNICODETEXT; public readonly IntPtr TARGETS; + public readonly IntPtr UTF8_STRING; public X11Atoms (IntPtr display) { // make sure this array stays in sync with the statements below - string [] atom_names = new string[] { - "WM_PROTOCOLS", - "WM_DELETE_WINDOW", - "WM_TAKE_FOCUS", - "_NET_SUPPORTED", - "_NET_CLIENT_LIST", - "_NET_NUMBER_OF_DESKTOPS", - "_NET_DESKTOP_GEOMETRY", - "_NET_DESKTOP_VIEWPORT", - "_NET_CURRENT_DESKTOP", - "_NET_DESKTOP_NAMES", - "_NET_ACTIVE_WINDOW", - "_NET_WORKAREA", - "_NET_SUPPORTING_WM_CHECK", - "_NET_VIRTUAL_ROOTS", - "_NET_DESKTOP_LAYOUT", - "_NET_SHOWING_DESKTOP", - "_NET_CLOSE_WINDOW", - "_NET_MOVERESIZE_WINDOW", - "_NET_WM_MOVERESIZE", - "_NET_RESTACK_WINDOW", - "_NET_REQUEST_FRAME_EXTENTS", - "_NET_WM_NAME", - "_NET_WM_VISIBLE_NAME", - "_NET_WM_ICON_NAME", - "_NET_WM_VISIBLE_ICON_NAME", - "_NET_WM_DESKTOP", - "_NET_WM_WINDOW_TYPE", - "_NET_WM_STATE", - "_NET_WM_ALLOWED_ACTIONS", - "_NET_WM_STRUT", - "_NET_WM_STRUT_PARTIAL", - "_NET_WM_ICON_GEOMETRY", - "_NET_WM_ICON", - "_NET_WM_PID", - "_NET_WM_HANDLED_ICONS", - "_NET_WM_USER_TIME", - "_NET_FRAME_EXTENTS", - "_NET_WM_PING", - "_NET_WM_SYNC_REQUEST", - "_NET_SYSTEM_TRAY_OPCODE", - "_NET_SYSTEM_TRAY_ORIENTATION", - "_NET_WM_STATE_MAXIMIZED_HORZ", - "_NET_WM_STATE_MAXIMIZED_VERT", - "_NET_WM_STATE_HIDDEN", - "_XEMBED", - "_XEMBED_INFO", - "_MOTIF_WM_HINTS", - "_NET_WM_STATE_SKIP_TASKBAR", - "_NET_WM_STATE_ABOVE", - "_NET_WM_STATE_MODAL", - "_NET_WM_CONTEXT_HELP", - "_NET_WM_WINDOW_OPACITY", - "_NET_WM_WINDOW_TYPE_DESKTOP", - "_NET_WM_WINDOW_TYPE_DOCK", - "_NET_WM_WINDOW_TYPE_TOOLBAR", - "_NET_WM_WINDOW_TYPE_MENU", - "_NET_WM_WINDOW_TYPE_UTILITY", - "_NET_WM_WINDOW_TYPE_DIALOG", - "_NET_WM_WINDOW_TYPE_SPLASH", - "_NET_WM_WINDOW_TYPE_NORMAL", - "CLIPBOARD", - "PRIMARY", - "COMPOUND_TEXT", - "UTF8_STRING", - "TARGETS"}; - IntPtr[] atoms = new IntPtr [atom_names.Length];; + var fields = typeof(X11Atoms).GetFields() + .Where(f => f.FieldType == typeof(IntPtr) && (IntPtr)f.GetValue(this) == IntPtr.Zero).ToArray(); + var atomNames = fields.Select(f => f.Name).ToArray(); + + IntPtr[] atoms = new IntPtr [atomNames.Length];; - XInternAtoms (display, atom_names, atom_names.Length, false, atoms); + XInternAtoms (display, atomNames, atomNames.Length, true, atoms); - int off = 0; - WM_PROTOCOLS = atoms [off++]; - WM_DELETE_WINDOW = atoms [off++]; - WM_TAKE_FOCUS = atoms [off++]; - _NET_SUPPORTED = atoms [off++]; - _NET_CLIENT_LIST = atoms [off++]; - _NET_NUMBER_OF_DESKTOPS = atoms [off++]; - _NET_DESKTOP_GEOMETRY = atoms [off++]; - _NET_DESKTOP_VIEWPORT = atoms [off++]; - _NET_CURRENT_DESKTOP = atoms [off++]; - _NET_DESKTOP_NAMES = atoms [off++]; - _NET_ACTIVE_WINDOW = atoms [off++]; - _NET_WORKAREA = atoms [off++]; - _NET_SUPPORTING_WM_CHECK = atoms [off++]; - _NET_VIRTUAL_ROOTS = atoms [off++]; - _NET_DESKTOP_LAYOUT = atoms [off++]; - _NET_SHOWING_DESKTOP = atoms [off++]; - _NET_CLOSE_WINDOW = atoms [off++]; - _NET_MOVERESIZE_WINDOW = atoms [off++]; - _NET_WM_MOVERESIZE = atoms [off++]; - _NET_RESTACK_WINDOW = atoms [off++]; - _NET_REQUEST_FRAME_EXTENTS = atoms [off++]; - _NET_WM_NAME = atoms [off++]; - _NET_WM_VISIBLE_NAME = atoms [off++]; - _NET_WM_ICON_NAME = atoms [off++]; - _NET_WM_VISIBLE_ICON_NAME = atoms [off++]; - _NET_WM_DESKTOP = atoms [off++]; - _NET_WM_WINDOW_TYPE = atoms [off++]; - _NET_WM_STATE = atoms [off++]; - _NET_WM_ALLOWED_ACTIONS = atoms [off++]; - _NET_WM_STRUT = atoms [off++]; - _NET_WM_STRUT_PARTIAL = atoms [off++]; - _NET_WM_ICON_GEOMETRY = atoms [off++]; - _NET_WM_ICON = atoms [off++]; - _NET_WM_PID = atoms [off++]; - _NET_WM_HANDLED_ICONS = atoms [off++]; - _NET_WM_USER_TIME = atoms [off++]; - _NET_FRAME_EXTENTS = atoms [off++]; - _NET_WM_PING = atoms [off++]; - _NET_WM_SYNC_REQUEST = atoms [off++]; - _NET_SYSTEM_TRAY_OPCODE = atoms [off++]; - _NET_SYSTEM_TRAY_ORIENTATION = atoms [off++]; - _NET_WM_STATE_MAXIMIZED_HORZ = atoms [off++]; - _NET_WM_STATE_MAXIMIZED_VERT = atoms [off++]; - _NET_WM_STATE_HIDDEN = atoms [off++]; - _XEMBED = atoms [off++]; - _XEMBED_INFO = atoms [off++]; - _MOTIF_WM_HINTS = atoms [off++]; - _NET_WM_STATE_SKIP_TASKBAR = atoms [off++]; - _NET_WM_STATE_ABOVE = atoms [off++]; - _NET_WM_STATE_MODAL = atoms [off++]; - _NET_WM_CONTEXT_HELP = atoms [off++]; - _NET_WM_WINDOW_OPACITY = atoms [off++]; - _NET_WM_WINDOW_TYPE_DESKTOP = atoms [off++]; - _NET_WM_WINDOW_TYPE_DOCK = atoms [off++]; - _NET_WM_WINDOW_TYPE_TOOLBAR = atoms [off++]; - _NET_WM_WINDOW_TYPE_MENU = atoms [off++]; - _NET_WM_WINDOW_TYPE_UTILITY = atoms [off++]; - _NET_WM_WINDOW_TYPE_DIALOG = atoms [off++]; - _NET_WM_WINDOW_TYPE_SPLASH = atoms [off++]; - _NET_WM_WINDOW_TYPE_NORMAL = atoms [off++]; - CLIPBOARD = atoms [off++]; - PRIMARY = atoms [off++]; - OEMTEXT = atoms [off++]; - UNICODETEXT = atoms [off++]; - TARGETS = atoms [off++]; + for (var c = 0; c < fields.Length; c++) + fields[c].SetValue(this, atoms[c]); - DIB = XA_PIXMAP; - - var defScreen = XDefaultScreen(display); - // XXX multi screen stuff here - _NET_SYSTEM_TRAY_S = XInternAtom (display, "_NET_SYSTEM_TRAY_S" + defScreen.ToString(), false); } } diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index 82564e98e0..523d4dae29 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -4,6 +4,7 @@ using System.ComponentModel.DataAnnotations; using System.Diagnostics; using System.Linq; using System.Reactive.Disposables; +using System.Text; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Input.Raw; @@ -94,7 +95,7 @@ namespace Avalonia.X11 new EglGlPlatformSurface((EglDisplay)feature.Display, feature.DeferredContext, new SurfaceInfo(_x11.DeferredDisplay, _handle, _renderHandle))); Surfaces = surfaces.ToArray(); - UpdateWmHits(); + UpdateMotifHits(); XFlush(_x11.Display); } @@ -128,7 +129,7 @@ namespace Avalonia.X11 public double Scaling { get; } = 1; } - void UpdateWmHits() + void UpdateMotifHits() { var functions = MotifFunctions.All; var decorations = MotifDecorations.All; @@ -154,13 +155,36 @@ namespace Avalonia.X11 _x11.Atoms._MOTIF_WM_HINTS, _x11.Atoms._MOTIF_WM_HINTS, 32, PropertyMode.Replace, ref hints, 5); } + + void UpdateSizeHits() + { + var hints = new XSizeHints(); + hints.min_width = (int)_minMaxSize.minSize.Width; + hints.min_height = (int)_minMaxSize.minSize.Height; + hints.height_inc = hints.width_inc = 1; + var flags = XSizeHintsFlags.PMinSize | XSizeHintsFlags.PResizeInc; + // People might be passing double.MaxValue + if (_minMaxSize.maxSize.Width < 100000 && _minMaxSize.maxSize.Height < 100000) + { + + hints.max_width = (int)Math.Max(100000, _minMaxSize.maxSize.Width); + hints.max_height = (int)Math.Max(100000, _minMaxSize.maxSize.Height); + flags |= XSizeHintsFlags.PMaxSize; + } + + hints.flags = (IntPtr)flags; + + XSetWMNormalHints(_x11.Display, _handle, ref hints); + } public Size ClientSize { get; private set; } + //TODO public double Scaling { get; } = 1; public IEnumerable Surfaces { get; } public Action Input { get; set; } public Action Paint { get; set; } public Action Resized { get; set; } + //TODO public Action ScalingChanged { get; set; } public Action Deactivated { get; set; } public Action Activated { get; set; } @@ -355,6 +379,7 @@ namespace Avalonia.X11 private bool _systemDecorations = true; private bool _canResize = true; + private (Size minSize, Size maxSize) _minMaxSize; void ScheduleInput(RawInputEventArgs args) { @@ -451,7 +476,7 @@ namespace Avalonia.X11 public void SetSystemDecorations(bool enabled) { _systemDecorations = enabled; - UpdateWmHits(); + UpdateMotifHits(); } @@ -477,7 +502,7 @@ namespace Avalonia.X11 public void CanResize(bool value) { _canResize = value; - UpdateWmHits(); + UpdateMotifHits(); } public void SetCursor(IPlatformHandle cursor) @@ -590,33 +615,42 @@ namespace Avalonia.X11 public void SetTitle(string title) { + var data = Encoding.UTF8.GetBytes(title); + fixed (void* pdata = data) + { + XChangeProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_NAME, _x11.Atoms.UTF8_STRING, 8, + PropertyMode.Replace, pdata, data.Length); + XStoreName(_x11.Display, _handle, title); + } } public void SetMinMaxSize(Size minSize, Size maxSize) { - + _minMaxSize = (minSize, maxSize); + UpdateSizeHits(); } public void SetTopmost(bool value) { - + SendNetWMMessage(_x11.Atoms._NET_WM_STATE, + (IntPtr)(value ? 1 : 0), _x11.Atoms._NET_WM_STATE_ABOVE, IntPtr.Zero); } public IDisposable ShowDialog() { + // TODO return Disposable.Empty; } public void SetIcon(IWindowIconImpl icon) { + //TODO } public void ShowTaskbarIcon(bool value) { - } - - - - + SendNetWMMessage(_x11.Atoms._NET_WM_STATE, + (IntPtr)(value ? 0 : 1), _x11.Atoms._NET_WM_STATE_SKIP_TASKBAR, IntPtr.Zero); + } } } diff --git a/src/Avalonia.X11/XLib.cs b/src/Avalonia.X11/XLib.cs index d6c4ec6593..0f27191f88 100644 --- a/src/Avalonia.X11/XLib.cs +++ b/src/Avalonia.X11/XLib.cs @@ -215,6 +215,9 @@ namespace Avalonia.X11 [DllImport(libX11)] public static extern int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, int format, PropertyMode mode, IntPtr[] data, int nelements); + [DllImport(libX11)] + public static extern int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, + int format, PropertyMode mode, void* data, int nelements); [DllImport(libX11)] public static extern int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, From 269fc2a5d2ab70cf33a8bb2b019921732c41b2f4 Mon Sep 17 00:00:00 2001 From: wojciech krysiak Date: Tue, 2 Oct 2018 16:13:32 +0200 Subject: [PATCH 017/374] MessCommit --- src/Avalonia.Controls/Grid.cs | 223 ++++++++++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) diff --git a/src/Avalonia.Controls/Grid.cs b/src/Avalonia.Controls/Grid.cs index 1a07ccaf7e..71ae414a4f 100644 --- a/src/Avalonia.Controls/Grid.cs +++ b/src/Avalonia.Controls/Grid.cs @@ -3,10 +3,12 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; using Avalonia.Collections; using Avalonia.Controls.Utils; +using Avalonia.VisualTree; using JetBrains.Annotations; namespace Avalonia.Controls @@ -44,6 +46,189 @@ namespace Avalonia.Controls public static readonly AttachedProperty RowSpanProperty = AvaloniaProperty.RegisterAttached("RowSpan", 1); + public static readonly AttachedProperty IsSharedSizeScopeProperty = + AvaloniaProperty.RegisterAttached("IsSharedSizeScope", false); + + private sealed class SharedSizeScopeHost : IDisposable + { + private class GridMeasureCache + { + public Grid Grid { get; } + public DefinitionBase Definition { get; } + public double CachedLength { get; set; } + } + + private readonly AvaloniaList _participatingGrids; + + private Dictionary _cachedSize = new Dictionary(); + + private Dictionary> _gridsInScopes = new Dictionary>(); + + private Dictionary> _scopeCache; + private int _leftToMeasure; + + public SharedSizeScopeHost(Control scope) + { + _participatingGrids = GetParticipatingGrids(scope); + + foreach (var grid in _participatingGrids) + { + grid.InvalidateMeasure(); + AddGridToScopes(grid); + } + } + + private bool _invalidating = false; + + internal void InvalidateMeasure(Grid grid) + { + if (_invalidating) + return; + _invalidating = true; + + List candidates = new List {grid}; + while (candidates.Any()) + { + var scopes = candidates.SelectMany(c => c.RowDefinitions.Select(rd => rd.SharedSizeGroup)) + .Concat(candidates.SelectMany(c => c.ColumnDefinitions.Select(rd => rd.SharedSizeGroup))).Distinct(); + + candidates = scopes.SelectMany(r => _scopeCache[r].Select(gmc => gmc.Grid)) + .Distinct().Where(c => c.IsMeasureValid).ToList(); + candidates.ForEach(c => c.InvalidateMeasure()); + } + + _invalidating = false; + } + + private void AddGridToScopes(Grid grid) + { + var scopeNames = grid.ColumnDefinitions.Select(g => g.SharedSizeGroup) + .Concat(grid.RowDefinitions.Select(g => g.SharedSizeGroup)).Distinct(); + foreach (var scopeName in scopeNames) + { + if (!_gridsInScopes.TryGetValue(scopeName, out var list)) + _gridsInScopes.Add(scopeName, list = new List() ); + list.Add(grid); + } + } + + private void RemoveGridFromScopes(Grid grid) + { + var scopeNames = grid.ColumnDefinitions.Select(g => g.SharedSizeGroup) + .Concat(grid.RowDefinitions.Select(g => g.SharedSizeGroup)).Distinct(); + foreach (var scopeName in scopeNames) + { + Debug.Assert(_gridsInScopes.TryGetValue(scopeName, out var list)); + list.Remove(grid); + if (!list.Any()) + _gridsInScopes.Remove(scopeName); + } + } + + internal void UpdateMeasureResult(GridLayout.MeasureResult result, ColumnDefinitions columnDefinitions) + { + for (var i = 0; i < columnDefinitions.Count; i++) + { + if (string.IsNullOrEmpty(columnDefinitions[i].SharedSizeGroup)) + continue; + // if any in this group is Absolute we don't care about measured values. + + } + } + + internal void UpdateMeasureResult(GridLayout.MeasureResult result, RowDefinitions rowDefinitions) + { + + } + + internal double GetExistingLimit(DefinitionBase definition) + { + List cache = _scopeCache[definition.SharedSizeGroup]; + + return cache.Where(gmc => gmc.Grid.IsMeasureValid) + .Aggregate(double.NaN, (a, gmc) => Math.Max(a, gmc.CachedLength)); + } + + internal void UpdateExistingLimit(DefinitionBase definition, double limit) + { + List cache = _scopeCache[definition.SharedSizeGroup]; + + cache.Single(gmc => ReferenceEquals(gmc.Definition, definition)).CachedLength = limit; + // if any other are lower - invalidate the grid. + } + + internal void BeginMeasurePass() + { + if (_leftToMeasure == 0) + { + _leftToMeasure = _participatingGrids.Count(g => !g.IsMeasureValid); + } + } + + private static AvaloniaList GetParticipatingGrids(Control scope) + { + var result = scope.GetVisualDescendants().OfType(); + + return new AvaloniaList(result.Where(g => g.HasSharedSizeGroups())); + } + + public void Dispose() + { + foreach (var grid in _participatingGrids) + { + grid.SharedScopeChanged(); + } + } + + internal void RegisterGrid(Grid toAdd) + { + Debug.Assert(!_participatingGrids.Contains(toAdd)); + _participatingGrids.Add(toAdd); + AddGridToScopes(toAdd); + } + + internal void UnegisterGrid(Grid toRemove) + { + Debug.Assert(_participatingGrids.Contains(toRemove)); + _participatingGrids.Remove(toRemove); + RemoveGridFromScopes(toRemove); + } + } + + protected override void OnMeasureInvalidated() + { + base.OnMeasureInvalidated(); + _sharedSizeHost?.InvalidateMeasure(this); + } + + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + var scope = this.GetVisualAncestors().OfType() + .FirstOrDefault(c => c.GetValue(IsSharedSizeScopeProperty)); + + Debug.Assert(_sharedSizeHost == null); + + if (scope != null) + { + _sharedSizeHost = scope.GetValue(s_sharedSizeScopeHostProperty); + _sharedSizeHost.RegisterGrid(this); + } + } + + protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnDetachedFromVisualTree(e); + + _sharedSizeHost?.UnegisterGrid(this); + _sharedSizeHost = null; + } + + private SharedSizeScopeHost _sharedSizeHost; + + private static readonly AttachedProperty s_sharedSizeScopeHostProperty = + AvaloniaProperty.RegisterAttached("&&SharedSizeScopeHost", null); + private ColumnDefinitions _columnDefinitions; private RowDefinitions _rowDefinitions; @@ -51,6 +236,23 @@ namespace Avalonia.Controls static Grid() { AffectsParentMeasure(ColumnProperty, ColumnSpanProperty, RowProperty, RowSpanProperty); + IsSharedSizeScopeProperty.Changed.AddClassHandler(IsSharedSizeScopeChanged); + } + + private static void IsSharedSizeScopeChanged(Control source, AvaloniaPropertyChangedEventArgs arg2) + { + if ((bool)arg2.NewValue) + { + Debug.Assert(source.GetValue(s_sharedSizeScopeHostProperty) == null); + source.SetValue(IsSharedSizeScopeProperty, new SharedSizeScopeHost(source)); + } + else + { + var host = source.GetValue(s_sharedSizeScopeHostProperty) as SharedSizeScopeHost; + Debug.Assert(host != null); + host.Dispose(); + source.SetValue(IsSharedSizeScopeProperty, null); + } } /// @@ -426,5 +628,26 @@ namespace Avalonia.Controls return value; } + + internal bool HasSharedSizeGroups() + { + return ColumnDefinitions.Any(cd => !string.IsNullOrEmpty(cd.SharedSizeGroup)) || + RowDefinitions.Any(rd => !string.IsNullOrEmpty(rd.SharedSizeGroup)); + } + + internal void SharedScopeChanged() + { + _sharedSizeHost = null; + var scope = this.GetVisualAncestors().OfType() + .FirstOrDefault(c => c.GetValue(IsSharedSizeScopeProperty)); + + if (scope != null) + { + _sharedSizeHost = scope.GetValue(s_sharedSizeScopeHostProperty); + _sharedSizeHost.RegisterGrid(this); + } + + InvalidateMeasure(); + } } } From 2754649edd05acf11cf7d15544a2bc3f79b20dc3 Mon Sep 17 00:00:00 2001 From: wojciech krysiak Date: Wed, 3 Oct 2018 07:49:07 +0200 Subject: [PATCH 018/374] Moar mess --- src/Avalonia.Controls/Grid.cs | 267 +++++++++++++++------- src/Avalonia.Controls/Utils/GridLayout.cs | 6 +- 2 files changed, 190 insertions(+), 83 deletions(-) diff --git a/src/Avalonia.Controls/Grid.cs b/src/Avalonia.Controls/Grid.cs index 71ae414a4f..5dc90414ee 100644 --- a/src/Avalonia.Controls/Grid.cs +++ b/src/Avalonia.Controls/Grid.cs @@ -51,147 +51,249 @@ namespace Avalonia.Controls private sealed class SharedSizeScopeHost : IDisposable { - private class GridMeasureCache + private enum MeasurementState { - public Grid Grid { get; } - public DefinitionBase Definition { get; } - public double CachedLength { get; set; } + Invalidated, + Measuring, + Cached } - private readonly AvaloniaList _participatingGrids; + private class MeasurementCache + { + public MeasurementCache(Grid grid) + { + Grid = grid; + Results = grid.RowDefinitions.Cast() + .Concat(grid.ColumnDefinitions) + .Select(d => new MeasurementResult(d)) + .ToList(); + } - private Dictionary _cachedSize = new Dictionary(); + public void UpdateMeasureResult(GridLayout.MeasureResult rowResult, GridLayout.MeasureResult columnResult) + { + RowResult = rowResult; + ColumnResult = columnResult; + MeasurementState = MeasurementState.Cached; + for (int i = 0; i < rowResult.LengthList.Count; i++) + { + Results[i].MeasuredResult = rowResult.LengthList[i]; + } + + for (int i = 0; i < columnResult.LengthList.Count; i++) + { + Results[i + rowResult.LengthList.Count].MeasuredResult = columnResult.LengthList[i]; + } + } - private Dictionary> _gridsInScopes = new Dictionary>(); + public void InvalidateMeasure() + { + MeasurementState = MeasurementState.Invalidated; + Results.ForEach(r => r.MeasuredResult = double.NaN); + } + + public Grid Grid { get; } + public GridLayout.MeasureResult RowResult { get; private set; } + public GridLayout.MeasureResult ColumnResult { get; private set; } + public MeasurementState MeasurementState { get; private set; } - private Dictionary> _scopeCache; - private int _leftToMeasure; + public List Results { get; } + } - public SharedSizeScopeHost(Control scope) + private readonly AvaloniaList _measurementCaches; + + private class MeasurementResult { - _participatingGrids = GetParticipatingGrids(scope); - - foreach (var grid in _participatingGrids) + public MeasurementResult(DefinitionBase @base) { - grid.InvalidateMeasure(); - AddGridToScopes(grid); + Definition = @base; + MeasuredResult = double.NaN; } + + public DefinitionBase Definition { get; } + public double MeasuredResult { get; set; } } - private bool _invalidating = false; + private enum ScopeType + { + Auto, + Fixed + } - internal void InvalidateMeasure(Grid grid) + private class Group { - if (_invalidating) - return; - _invalidating = true; + public bool IsFixed { get; set; } - List candidates = new List {grid}; - while (candidates.Any()) - { - var scopes = candidates.SelectMany(c => c.RowDefinitions.Select(rd => rd.SharedSizeGroup)) - .Concat(candidates.SelectMany(c => c.ColumnDefinitions.Select(rd => rd.SharedSizeGroup))).Distinct(); - - candidates = scopes.SelectMany(r => _scopeCache[r].Select(gmc => gmc.Grid)) - .Distinct().Where(c => c.IsMeasureValid).ToList(); - candidates.ForEach(c => c.InvalidateMeasure()); - } + public List Results { get; } - _invalidating = false; + public double CalculatedLength { get; } } - private void AddGridToScopes(Grid grid) + private Dictionary _groups = new Dictionary(); + + + public SharedSizeScopeHost(Control scope) { - var scopeNames = grid.ColumnDefinitions.Select(g => g.SharedSizeGroup) - .Concat(grid.RowDefinitions.Select(g => g.SharedSizeGroup)).Distinct(); - foreach (var scopeName in scopeNames) + _measurementCaches = GetParticipatingGrids(scope); + + foreach (var cache in _measurementCaches) { - if (!_gridsInScopes.TryGetValue(scopeName, out var list)) - _gridsInScopes.Add(scopeName, list = new List() ); - list.Add(grid); + cache.Grid.InvalidateMeasure(); + AddGridToScopes(cache); } } - private void RemoveGridFromScopes(Grid grid) + internal void InvalidateMeasure(Grid grid) { - var scopeNames = grid.ColumnDefinitions.Select(g => g.SharedSizeGroup) - .Concat(grid.RowDefinitions.Select(g => g.SharedSizeGroup)).Distinct(); - foreach (var scopeName in scopeNames) - { - Debug.Assert(_gridsInScopes.TryGetValue(scopeName, out var list)); - list.Remove(grid); - if (!list.Any()) - _gridsInScopes.Remove(scopeName); - } + var cache = _measurementCaches.FirstOrDefault(mc => ReferenceEquals(mc.Grid, grid)); + Debug.Assert(cache != null); + + cache.InvalidateMeasure(); } - internal void UpdateMeasureResult(GridLayout.MeasureResult result, ColumnDefinitions columnDefinitions) + internal void UpdateMeasureStatus(Grid grid, GridLayout.MeasureResult rowResult, GridLayout.MeasureResult columnResult) { - for (var i = 0; i < columnDefinitions.Count; i++) + var cache = _measurementCaches.FirstOrDefault(mc => ReferenceEquals(mc.Grid, grid)); + Debug.Assert(cache != null); + + cache.UpdateMeasureResult(rowResult, columnResult); + } + + internal (GridLayout.MeasureResult, GridLayout.MeasureResult) HandleArrange(Grid grid, GridLayout.MeasureResult rowResult, GridLayout.MeasureResult columnResult) + { + var rowConventions = rowResult.LeanLengthList.ToList(); + var rowLengths = rowResult.LengthList.ToList(); + var rowDesiredLength = 0.0; + for (int i = 0; i < grid.RowDefinitions.Count; i++) { - if (string.IsNullOrEmpty(columnDefinitions[i].SharedSizeGroup)) + var definition = grid.RowDefinitions[i]; + if (string.IsNullOrEmpty(definition.SharedSizeGroup)) + { + rowDesiredLength += rowResult.LengthList[i]; continue; - // if any in this group is Absolute we don't care about measured values. - + } + + var group = _groups[definition.SharedSizeGroup]; + + var length = group.Results.Max(g => g.MeasuredResult); + rowConventions[i] = new GridLayout.LengthConvention( + new GridLength(length), + rowResult.LeanLengthList[i].MinLength, + rowResult.LeanLengthList[i].MaxLength + ); + rowLengths[i] = length; + rowDesiredLength += length; + } - } - internal void UpdateMeasureResult(GridLayout.MeasureResult result, RowDefinitions rowDefinitions) - { + var columnConventions = columnResult.LeanLengthList.ToList(); + var columnLengths = columnResult.LengthList.ToList(); + var columnDesiredLength = 0.0; + for (int i = 0; i < grid.ColumnDefinitions.Count; i++) + { + var definition = grid.ColumnDefinitions[i]; + if (string.IsNullOrEmpty(definition.SharedSizeGroup)) + { + columnDesiredLength += rowResult.LengthList[i]; + continue; + } + + var group = _groups[definition.SharedSizeGroup]; + + var length = group.Results.Max(g => g.MeasuredResult); + columnConventions[i] = new GridLayout.LengthConvention( + new GridLength(length), + columnResult.LeanLengthList[i].MinLength, + columnResult.LeanLengthList[i].MaxLength + ); + columnLengths[i] = length; + columnDesiredLength += length; + } + return ( + new GridLayout.MeasureResult( + rowResult.ContainerLength, + rowDesiredLength, + rowResult.GreedyDesiredLength,//?? + rowConventions, + rowLengths), + new GridLayout.MeasureResult( + columnResult.ContainerLength, + columnDesiredLength, + columnResult.GreedyDesiredLength, //?? + columnConventions, + columnLengths) + ); } - internal double GetExistingLimit(DefinitionBase definition) + + private void AddGridToScopes(MeasurementCache cache) { - List cache = _scopeCache[definition.SharedSizeGroup]; + foreach (var result in cache.Results) + { + var scopeName = result.Definition.SharedSizeGroup; + if (!_groups.TryGetValue(scopeName, out var group)) + _groups.Add(scopeName, group = new Group()); + + group.IsFixed |= IsFixed(result.Definition); - return cache.Where(gmc => gmc.Grid.IsMeasureValid) - .Aggregate(double.NaN, (a, gmc) => Math.Max(a, gmc.CachedLength)); + group.Results.Add(result); + } } - internal void UpdateExistingLimit(DefinitionBase definition, double limit) + private bool IsFixed(DefinitionBase definition) { - List cache = _scopeCache[definition.SharedSizeGroup]; - - cache.Single(gmc => ReferenceEquals(gmc.Definition, definition)).CachedLength = limit; - // if any other are lower - invalidate the grid. + return ((definition as ColumnDefinition)?.Width ?? ((RowDefinition)definition).Height).IsAbsolute; } - internal void BeginMeasurePass() + private void RemoveGridFromScopes(MeasurementCache cache) { - if (_leftToMeasure == 0) + foreach (var result in cache.Results) { - _leftToMeasure = _participatingGrids.Count(g => !g.IsMeasureValid); + var scopeName = result.Definition.SharedSizeGroup; + Debug.Assert(_groups.TryGetValue(scopeName, out var group)); + + group.Results.Remove(result); + if (!group.Results.Any()) + _groups.Remove(scopeName); + else + { + group.IsFixed = group.Results.Select(r => r.Definition).Any(IsFixed); + } } } - private static AvaloniaList GetParticipatingGrids(Control scope) + private static AvaloniaList GetParticipatingGrids(Control scope) { var result = scope.GetVisualDescendants().OfType(); - return new AvaloniaList(result.Where(g => g.HasSharedSizeGroups())); + return new AvaloniaList( + result.Where(g => g.HasSharedSizeGroups()) + .Select(g => new MeasurementCache(g))); } public void Dispose() { - foreach (var grid in _participatingGrids) + foreach (var cache in _measurementCaches) { - grid.SharedScopeChanged(); + cache.Grid.SharedScopeChanged(); } } internal void RegisterGrid(Grid toAdd) { - Debug.Assert(!_participatingGrids.Contains(toAdd)); - _participatingGrids.Add(toAdd); - AddGridToScopes(toAdd); + Debug.Assert(!_measurementCaches.Any(mc => ReferenceEquals(mc.Grid,toAdd))); + var cache = new MeasurementCache(toAdd); + _measurementCaches.Add(cache); + AddGridToScopes(cache); } internal void UnegisterGrid(Grid toRemove) { - Debug.Assert(_participatingGrids.Contains(toRemove)); - _participatingGrids.Remove(toRemove); - RemoveGridFromScopes(toRemove); + var cache = _measurementCaches.FirstOrDefault(mc => ReferenceEquals(mc.Grid, toRemove)); + + Debug.Assert(cache != null); + _measurementCaches.Remove(cache); + RemoveGridFromScopes(cache); } } @@ -473,6 +575,8 @@ namespace Avalonia.Controls _rowLayoutCache = rowLayout; _columnLayoutCache = columnLayout; + _sharedSizeHost?.UpdateMeasureStatus(this, rowResult, columnResult); + return new Size(columnResult.DesiredLength, rowResult.DesiredLength); // Measure each child only once. @@ -521,9 +625,12 @@ namespace Avalonia.Controls var (safeColumns, safeRows) = GetSafeColumnRows(); var columnLayout = _columnLayoutCache; var rowLayout = _rowLayoutCache; + + var (rowCache, columnCache) = _sharedSizeHost?.HandleArrange(this, _rowMeasureCache, _columnMeasureCache) ?? (_rowMeasureCache, _columnMeasureCache); + // Calculate for arrange result. - var columnResult = columnLayout.Arrange(finalSize.Width, _columnMeasureCache); - var rowResult = rowLayout.Arrange(finalSize.Height, _rowMeasureCache); + var columnResult = columnLayout.Arrange(finalSize.Width, rowCache); + var rowResult = rowLayout.Arrange(finalSize.Height, columnCache); // Arrange the children. foreach (var child in Children.OfType()) { diff --git a/src/Avalonia.Controls/Utils/GridLayout.cs b/src/Avalonia.Controls/Utils/GridLayout.cs index 363428b289..b1dca09be2 100644 --- a/src/Avalonia.Controls/Utils/GridLayout.cs +++ b/src/Avalonia.Controls/Utils/GridLayout.cs @@ -147,10 +147,10 @@ namespace Avalonia.Controls.Utils /// The measured result that containing the desired size and all the column/row lengths. /// [NotNull, Pure] - internal MeasureResult Measure(double containerLength) + internal MeasureResult Measure(double containerLength, IReadOnlyList conventions = null) { // Prepare all the variables that this method needs to use. - var conventions = _conventions.Select(x => x.Clone()).ToList(); + conventions = conventions ?? _conventions.Select(x => x.Clone()).ToList(); var starCount = conventions.Where(x => x.Length.IsStar).Sum(x => x.Length.Value); var aggregatedLength = 0.0; double starUnitLength; @@ -306,7 +306,7 @@ namespace Avalonia.Controls.Utils if (finalLength - measure.ContainerLength > LayoutTolerance) { // If the final length is larger, we will rerun the whole measure. - measure = Measure(finalLength); + measure = Measure(finalLength, measure.LeanLengthList); } else if (finalLength - measure.ContainerLength < -LayoutTolerance) { From 5741d0ef140f3da153a8803dd7d1aefb94e1c82c Mon Sep 17 00:00:00 2001 From: wojciech krysiak Date: Wed, 3 Oct 2018 16:49:56 +0200 Subject: [PATCH 019/374] Base case works --- src/Avalonia.Controls/Grid.cs | 136 ++++++++++++++++++++-------------- 1 file changed, 82 insertions(+), 54 deletions(-) diff --git a/src/Avalonia.Controls/Grid.cs b/src/Avalonia.Controls/Grid.cs index 5dc90414ee..d89c2947ce 100644 --- a/src/Avalonia.Controls/Grid.cs +++ b/src/Avalonia.Controls/Grid.cs @@ -60,6 +60,7 @@ namespace Avalonia.Controls private class MeasurementCache { + public MeasurementCache(Grid grid) { Grid = grid; @@ -67,19 +68,20 @@ namespace Avalonia.Controls .Concat(grid.ColumnDefinitions) .Select(d => new MeasurementResult(d)) .ToList(); + + grid.RowDefinitions. + } public void UpdateMeasureResult(GridLayout.MeasureResult rowResult, GridLayout.MeasureResult columnResult) { - RowResult = rowResult; - ColumnResult = columnResult; MeasurementState = MeasurementState.Cached; - for (int i = 0; i < rowResult.LengthList.Count; i++) + for (int i = 0; i < Grid.RowDefinitions.Count; i++) { Results[i].MeasuredResult = rowResult.LengthList[i]; } - for (int i = 0; i < columnResult.LengthList.Count; i++) + for (int i = 0; i < Grid.ColumnDefinitions.Count; i++) { Results[i + rowResult.LengthList.Count].MeasuredResult = columnResult.LengthList[i]; } @@ -92,8 +94,6 @@ namespace Avalonia.Controls } public Grid Grid { get; } - public GridLayout.MeasureResult RowResult { get; private set; } - public GridLayout.MeasureResult ColumnResult { get; private set; } public MeasurementState MeasurementState { get; private set; } public List Results { get; } @@ -103,9 +103,9 @@ namespace Avalonia.Controls private class MeasurementResult { - public MeasurementResult(DefinitionBase @base) + public MeasurementResult(DefinitionBase definition) { - Definition = @base; + Definition = definition; MeasuredResult = double.NaN; } @@ -113,22 +113,16 @@ namespace Avalonia.Controls public double MeasuredResult { get; set; } } - private enum ScopeType - { - Auto, - Fixed - } - private class Group { public bool IsFixed { get; set; } - public List Results { get; } + public List Results { get; } = new List(); public double CalculatedLength { get; } } - private Dictionary _groups = new Dictionary(); + private readonly Dictionary _groups = new Dictionary(); public SharedSizeScopeHost(Control scope) @@ -158,57 +152,79 @@ namespace Avalonia.Controls cache.UpdateMeasureResult(rowResult, columnResult); } - internal (GridLayout.MeasureResult, GridLayout.MeasureResult) HandleArrange(Grid grid, GridLayout.MeasureResult rowResult, GridLayout.MeasureResult columnResult) + private double Gather(IEnumerable measurements) { - var rowConventions = rowResult.LeanLengthList.ToList(); - var rowLengths = rowResult.LengthList.ToList(); - var rowDesiredLength = 0.0; - for (int i = 0; i < grid.RowDefinitions.Count; i++) + var result = 0.0d; + + bool onlyFixed = false; + + foreach (var measurement in measurements) { - var definition = grid.RowDefinitions[i]; - if (string.IsNullOrEmpty(definition.SharedSizeGroup)) + if (measurement.Definition is ColumnDefinition column) { - rowDesiredLength += rowResult.LengthList[i]; - continue; + if (!onlyFixed && column.Width.IsAbsolute) + { + onlyFixed = true; + result = measurement.MeasuredResult; + } + else if (onlyFixed == column.Width.IsAbsolute) + result = Math.Max(result, measurement.MeasuredResult); + + result = Math.Max(result, column.MinWidth); } + if (measurement.Definition is RowDefinition row) + { + if (!onlyFixed && row.Height.IsAbsolute) + { + onlyFixed = true; + result = measurement.MeasuredResult; + } + else if (onlyFixed == row.Height.IsAbsolute) + result = Math.Max(result, measurement.MeasuredResult); + + result = Math.Max(result, row.MinHeight); + } + } - var group = _groups[definition.SharedSizeGroup]; - - var length = group.Results.Max(g => g.MeasuredResult); - rowConventions[i] = new GridLayout.LengthConvention( - new GridLength(length), - rowResult.LeanLengthList[i].MinLength, - rowResult.LeanLengthList[i].MaxLength - ); - rowLengths[i] = length; - rowDesiredLength += length; + return result; + } - } - var columnConventions = columnResult.LeanLengthList.ToList(); - var columnLengths = columnResult.LengthList.ToList(); - var columnDesiredLength = 0.0; - for (int i = 0; i < grid.ColumnDefinitions.Count; i++) + (List, List, double) Arrange(IReadOnlyList definitions, GridLayout.MeasureResult measureResult) + { + var conventions = measureResult.LeanLengthList.ToList(); + var lengths = measureResult.LengthList.ToList(); + var desiredLength = 0.0; + for (int i = 0; i < definitions.Count; i++) { - var definition = grid.ColumnDefinitions[i]; + var definition = definitions[i]; if (string.IsNullOrEmpty(definition.SharedSizeGroup)) { - columnDesiredLength += rowResult.LengthList[i]; + desiredLength += measureResult.LengthList[i]; continue; } var group = _groups[definition.SharedSizeGroup]; - var length = group.Results.Max(g => g.MeasuredResult); - columnConventions[i] = new GridLayout.LengthConvention( + var length = Gather(group.Results); + + conventions[i] = new GridLayout.LengthConvention( new GridLength(length), - columnResult.LeanLengthList[i].MinLength, - columnResult.LeanLengthList[i].MaxLength - ); - columnLengths[i] = length; - columnDesiredLength += length; + measureResult.LeanLengthList[i].MinLength, + measureResult.LeanLengthList[i].MaxLength + ); + lengths[i] = length; + desiredLength += length; } + return (conventions, lengths, desiredLength); + } + + internal (GridLayout.MeasureResult, GridLayout.MeasureResult) HandleArrange(Grid grid, GridLayout.MeasureResult rowResult, GridLayout.MeasureResult columnResult) + { + var (rowConventions, rowLengths, rowDesiredLength) = Arrange(grid.RowDefinitions, rowResult); + var (columnConventions, columnLengths, columnDesiredLength) = Arrange(grid.ColumnDefinitions, columnResult); + return ( new GridLayout.MeasureResult( rowResult.ContainerLength, @@ -231,6 +247,8 @@ namespace Avalonia.Controls foreach (var result in cache.Results) { var scopeName = result.Definition.SharedSizeGroup; + if (string.IsNullOrEmpty(scopeName)) + continue; if (!_groups.TryGetValue(scopeName, out var group)) _groups.Add(scopeName, group = new Group()); @@ -250,6 +268,8 @@ namespace Avalonia.Controls foreach (var result in cache.Results) { var scopeName = result.Definition.SharedSizeGroup; + if (string.IsNullOrEmpty(scopeName)) + continue; Debug.Assert(_groups.TryGetValue(scopeName, out var group)); group.Results.Remove(result); @@ -346,14 +366,14 @@ namespace Avalonia.Controls if ((bool)arg2.NewValue) { Debug.Assert(source.GetValue(s_sharedSizeScopeHostProperty) == null); - source.SetValue(IsSharedSizeScopeProperty, new SharedSizeScopeHost(source)); + source.SetValue(s_sharedSizeScopeHostProperty, new SharedSizeScopeHost(source)); } else { var host = source.GetValue(s_sharedSizeScopeHostProperty) as SharedSizeScopeHost; Debug.Assert(host != null); host.Dispose(); - source.SetValue(IsSharedSizeScopeProperty, null); + source.SetValue(s_sharedSizeScopeHostProperty, null); } } @@ -626,11 +646,19 @@ namespace Avalonia.Controls var columnLayout = _columnLayoutCache; var rowLayout = _rowLayoutCache; - var (rowCache, columnCache) = _sharedSizeHost?.HandleArrange(this, _rowMeasureCache, _columnMeasureCache) ?? (_rowMeasureCache, _columnMeasureCache); + var (rowCache, columnCache) = + _sharedSizeHost?.HandleArrange(this, _rowMeasureCache, _columnMeasureCache) ?? + (_rowMeasureCache, _columnMeasureCache); + + if (_sharedSizeHost != null) + { + rowCache = rowLayout.Measure(finalSize.Width, rowCache.LeanLengthList); + columnCache = columnLayout.Measure(finalSize.Width, columnCache.LeanLengthList); + } // Calculate for arrange result. - var columnResult = columnLayout.Arrange(finalSize.Width, rowCache); - var rowResult = rowLayout.Arrange(finalSize.Height, columnCache); + var columnResult = columnLayout.Arrange(finalSize.Width, columnCache); + var rowResult = rowLayout.Arrange(finalSize.Height, rowCache); // Arrange the children. foreach (var child in Children.OfType()) { From f876f14afd99812a738067a3a2e6fa5bcde39f8c Mon Sep 17 00:00:00 2001 From: wojciech krysiak Date: Thu, 4 Oct 2018 07:00:50 +0200 Subject: [PATCH 020/374] Suporting changes to the rows/columns --- src/Avalonia.Controls/Grid.cs | 164 +++++++++++++++++++++++++++++----- 1 file changed, 144 insertions(+), 20 deletions(-) diff --git a/src/Avalonia.Controls/Grid.cs b/src/Avalonia.Controls/Grid.cs index d89c2947ce..05255920a5 100644 --- a/src/Avalonia.Controls/Grid.cs +++ b/src/Avalonia.Controls/Grid.cs @@ -3,8 +3,13 @@ using System; using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel; using System.Diagnostics; using System.Linq; +using System.Reactive.Disposables; +using System.Reactive.Linq; +using System.Reactive.Subjects; using System.Runtime.CompilerServices; using Avalonia.Collections; using Avalonia.Controls.Utils; @@ -58,8 +63,13 @@ namespace Avalonia.Controls Cached } - private class MeasurementCache + private sealed class MeasurementCache : IDisposable { + CompositeDisposable _subscriptions; + + Subject<(string, string, MeasurementResult)> _groupChanged = new Subject<(string, string, MeasurementResult)>(); + + public ISubject<(string oldName, string newName, MeasurementResult result)> GroupChanged => _groupChanged; public MeasurementCache(Grid grid) { @@ -69,8 +79,96 @@ namespace Avalonia.Controls .Select(d => new MeasurementResult(d)) .ToList(); - grid.RowDefinitions. + grid.RowDefinitions.CollectionChanged += DefinitionsCollectionChanged; + grid.ColumnDefinitions.CollectionChanged += DefinitionsCollectionChanged; + + _subscriptions = new CompositeDisposable( + Disposable.Create(() => grid.RowDefinitions.CollectionChanged -= DefinitionsCollectionChanged), + Disposable.Create(() => grid.ColumnDefinitions.CollectionChanged -= DefinitionsCollectionChanged), + grid.RowDefinitions.TrackItemPropertyChanged(DefinitionPropertyChanged), + grid.ColumnDefinitions.TrackItemPropertyChanged(DefinitionPropertyChanged)); + + } + + private void DefinitionPropertyChanged(Tuple propertyChanged) + { + if (propertyChanged.Item2.PropertyName == nameof(DefinitionBase.SharedSizeGroup)) + { + var oldName = string.Empty; + var newName = (propertyChanged.Item1 as DefinitionBase).SharedSizeGroup; + var result = Results.Single(mr => ReferenceEquals(mr.Definition, propertyChanged.Item2)); + _groupChanged.OnNext((oldName, newName, result)); + } + } + + private void DefinitionsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + int offset = 0; + if (sender is ColumnDefinitions) + offset = Grid.RowDefinitions.Count; + + var newItems = e.NewItems?.OfType().Select(db => new MeasurementResult(db)).ToList() ?? new List(); + var oldItems = Results.GetRange(e.OldStartingIndex + offset, e.OldItems?.Count ?? 0); + + void NotifyNewItems() + { + foreach (var item in newItems) + { + if (string.IsNullOrEmpty(item.Definition.SharedSizeGroup)) + continue; + + _groupChanged.OnNext((null, item.Definition.SharedSizeGroup, item)); + } + } + + void NotifyOldItems() + { + foreach (var item in oldItems) + { + if (string.IsNullOrEmpty(item.Definition.SharedSizeGroup)) + continue; + + _groupChanged.OnNext((item.Definition.SharedSizeGroup, null, item)); + } + } + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + Results.InsertRange(e.NewStartingIndex + offset, newItems); + NotifyNewItems(); + break; + + case NotifyCollectionChangedAction.Remove: + Results.RemoveRange(e.OldStartingIndex + offset, oldItems.Count); + NotifyOldItems(); + break; + + case NotifyCollectionChangedAction.Move: + Results.RemoveRange(e.OldStartingIndex + offset, oldItems.Count); + Results.InsertRange(e.NewStartingIndex + offset, oldItems); + break; + + case NotifyCollectionChangedAction.Replace: + Results.RemoveRange(e.OldStartingIndex + offset, oldItems.Count); + Results.InsertRange(e.NewStartingIndex + offset, newItems); + + NotifyOldItems(); + NotifyNewItems(); + + break; + + case NotifyCollectionChangedAction.Reset: + oldItems = Results; + newItems = Results = Grid.RowDefinitions.Cast() + .Concat(Grid.ColumnDefinitions) + .Select(d => new MeasurementResult(d)) + .ToList(); + NotifyOldItems(); + NotifyNewItems(); + + break; + } } public void UpdateMeasureResult(GridLayout.MeasureResult rowResult, GridLayout.MeasureResult columnResult) @@ -93,10 +191,16 @@ namespace Avalonia.Controls Results.ForEach(r => r.MeasuredResult = double.NaN); } + public void Dispose() + { + _subscriptions.Dispose(); + _groupChanged.OnCompleted(); + } + public Grid Grid { get; } public MeasurementState MeasurementState { get; private set; } - public List Results { get; } + public List Results { get; private set; } } private readonly AvaloniaList _measurementCaches; @@ -133,9 +237,17 @@ namespace Avalonia.Controls { cache.Grid.InvalidateMeasure(); AddGridToScopes(cache); + + cache.GroupChanged.Subscribe(SharedGroupChanged); } } + void SharedGroupChanged((string oldName, string newName, MeasurementResult result) change) + { + RemoveFromGroup(change.oldName, change.result); + AddToGroup(change.newName, change.result); + } + internal void InvalidateMeasure(Grid grid) { var cache = _measurementCaches.FirstOrDefault(mc => ReferenceEquals(mc.Grid, grid)); @@ -247,15 +359,21 @@ namespace Avalonia.Controls foreach (var result in cache.Results) { var scopeName = result.Definition.SharedSizeGroup; - if (string.IsNullOrEmpty(scopeName)) - continue; - if (!_groups.TryGetValue(scopeName, out var group)) - _groups.Add(scopeName, group = new Group()); + AddToGroup(scopeName, result); + } + } - group.IsFixed |= IsFixed(result.Definition); + private void AddToGroup(string scopeName, MeasurementResult result) + { + if (string.IsNullOrEmpty(scopeName)) + return; - group.Results.Add(result); - } + if (!_groups.TryGetValue(scopeName, out var group)) + _groups.Add(scopeName, group = new Group()); + + group.IsFixed |= IsFixed(result.Definition); + + group.Results.Add(result); } private bool IsFixed(DefinitionBase definition) @@ -268,17 +386,23 @@ namespace Avalonia.Controls foreach (var result in cache.Results) { var scopeName = result.Definition.SharedSizeGroup; - if (string.IsNullOrEmpty(scopeName)) - continue; - Debug.Assert(_groups.TryGetValue(scopeName, out var group)); + RemoveFromGroup(scopeName, result); + } + } - group.Results.Remove(result); - if (!group.Results.Any()) - _groups.Remove(scopeName); - else - { - group.IsFixed = group.Results.Select(r => r.Definition).Any(IsFixed); - } + private void RemoveFromGroup(string scopeName, MeasurementResult result) + { + if (string.IsNullOrEmpty(scopeName)) + return; + + Debug.Assert(_groups.TryGetValue(scopeName, out var group)); + + group.Results.Remove(result); + if (!group.Results.Any()) + _groups.Remove(scopeName); + else + { + group.IsFixed = group.Results.Select(r => r.Definition).Any(IsFixed); } } From fe499cea89e0a81834a40fb931fe448f0df0a418 Mon Sep 17 00:00:00 2001 From: wojciech krysiak Date: Thu, 4 Oct 2018 13:04:42 +0200 Subject: [PATCH 021/374] Pre PR commit --- src/Avalonia.Controls/Grid.cs | 391 ----------------- .../Utils/SharedSizeScopeHost.cs | 400 ++++++++++++++++++ 2 files changed, 400 insertions(+), 391 deletions(-) create mode 100644 src/Avalonia.Controls/Utils/SharedSizeScopeHost.cs diff --git a/src/Avalonia.Controls/Grid.cs b/src/Avalonia.Controls/Grid.cs index 05255920a5..264695d3d7 100644 --- a/src/Avalonia.Controls/Grid.cs +++ b/src/Avalonia.Controls/Grid.cs @@ -3,13 +3,9 @@ using System; using System.Collections.Generic; -using System.Collections.Specialized; -using System.ComponentModel; using System.Diagnostics; using System.Linq; -using System.Reactive.Disposables; using System.Reactive.Linq; -using System.Reactive.Subjects; using System.Runtime.CompilerServices; using Avalonia.Collections; using Avalonia.Controls.Utils; @@ -54,393 +50,6 @@ namespace Avalonia.Controls public static readonly AttachedProperty IsSharedSizeScopeProperty = AvaloniaProperty.RegisterAttached("IsSharedSizeScope", false); - private sealed class SharedSizeScopeHost : IDisposable - { - private enum MeasurementState - { - Invalidated, - Measuring, - Cached - } - - private sealed class MeasurementCache : IDisposable - { - CompositeDisposable _subscriptions; - - Subject<(string, string, MeasurementResult)> _groupChanged = new Subject<(string, string, MeasurementResult)>(); - - public ISubject<(string oldName, string newName, MeasurementResult result)> GroupChanged => _groupChanged; - - public MeasurementCache(Grid grid) - { - Grid = grid; - Results = grid.RowDefinitions.Cast() - .Concat(grid.ColumnDefinitions) - .Select(d => new MeasurementResult(d)) - .ToList(); - - grid.RowDefinitions.CollectionChanged += DefinitionsCollectionChanged; - grid.ColumnDefinitions.CollectionChanged += DefinitionsCollectionChanged; - - _subscriptions = new CompositeDisposable( - Disposable.Create(() => grid.RowDefinitions.CollectionChanged -= DefinitionsCollectionChanged), - Disposable.Create(() => grid.ColumnDefinitions.CollectionChanged -= DefinitionsCollectionChanged), - grid.RowDefinitions.TrackItemPropertyChanged(DefinitionPropertyChanged), - grid.ColumnDefinitions.TrackItemPropertyChanged(DefinitionPropertyChanged)); - - } - - private void DefinitionPropertyChanged(Tuple propertyChanged) - { - if (propertyChanged.Item2.PropertyName == nameof(DefinitionBase.SharedSizeGroup)) - { - var oldName = string.Empty; - var newName = (propertyChanged.Item1 as DefinitionBase).SharedSizeGroup; - var result = Results.Single(mr => ReferenceEquals(mr.Definition, propertyChanged.Item2)); - _groupChanged.OnNext((oldName, newName, result)); - } - } - - private void DefinitionsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - int offset = 0; - if (sender is ColumnDefinitions) - offset = Grid.RowDefinitions.Count; - - var newItems = e.NewItems?.OfType().Select(db => new MeasurementResult(db)).ToList() ?? new List(); - var oldItems = Results.GetRange(e.OldStartingIndex + offset, e.OldItems?.Count ?? 0); - - void NotifyNewItems() - { - foreach (var item in newItems) - { - if (string.IsNullOrEmpty(item.Definition.SharedSizeGroup)) - continue; - - _groupChanged.OnNext((null, item.Definition.SharedSizeGroup, item)); - } - } - - void NotifyOldItems() - { - foreach (var item in oldItems) - { - if (string.IsNullOrEmpty(item.Definition.SharedSizeGroup)) - continue; - - _groupChanged.OnNext((item.Definition.SharedSizeGroup, null, item)); - } - } - - switch (e.Action) - { - case NotifyCollectionChangedAction.Add: - Results.InsertRange(e.NewStartingIndex + offset, newItems); - NotifyNewItems(); - break; - - case NotifyCollectionChangedAction.Remove: - Results.RemoveRange(e.OldStartingIndex + offset, oldItems.Count); - NotifyOldItems(); - break; - - case NotifyCollectionChangedAction.Move: - Results.RemoveRange(e.OldStartingIndex + offset, oldItems.Count); - Results.InsertRange(e.NewStartingIndex + offset, oldItems); - break; - - case NotifyCollectionChangedAction.Replace: - Results.RemoveRange(e.OldStartingIndex + offset, oldItems.Count); - Results.InsertRange(e.NewStartingIndex + offset, newItems); - - NotifyOldItems(); - NotifyNewItems(); - - break; - - case NotifyCollectionChangedAction.Reset: - oldItems = Results; - newItems = Results = Grid.RowDefinitions.Cast() - .Concat(Grid.ColumnDefinitions) - .Select(d => new MeasurementResult(d)) - .ToList(); - NotifyOldItems(); - NotifyNewItems(); - - break; - } - } - - public void UpdateMeasureResult(GridLayout.MeasureResult rowResult, GridLayout.MeasureResult columnResult) - { - MeasurementState = MeasurementState.Cached; - for (int i = 0; i < Grid.RowDefinitions.Count; i++) - { - Results[i].MeasuredResult = rowResult.LengthList[i]; - } - - for (int i = 0; i < Grid.ColumnDefinitions.Count; i++) - { - Results[i + rowResult.LengthList.Count].MeasuredResult = columnResult.LengthList[i]; - } - } - - public void InvalidateMeasure() - { - MeasurementState = MeasurementState.Invalidated; - Results.ForEach(r => r.MeasuredResult = double.NaN); - } - - public void Dispose() - { - _subscriptions.Dispose(); - _groupChanged.OnCompleted(); - } - - public Grid Grid { get; } - public MeasurementState MeasurementState { get; private set; } - - public List Results { get; private set; } - } - - private readonly AvaloniaList _measurementCaches; - - private class MeasurementResult - { - public MeasurementResult(DefinitionBase definition) - { - Definition = definition; - MeasuredResult = double.NaN; - } - - public DefinitionBase Definition { get; } - public double MeasuredResult { get; set; } - } - - private class Group - { - public bool IsFixed { get; set; } - - public List Results { get; } = new List(); - - public double CalculatedLength { get; } - } - - private readonly Dictionary _groups = new Dictionary(); - - - public SharedSizeScopeHost(Control scope) - { - _measurementCaches = GetParticipatingGrids(scope); - - foreach (var cache in _measurementCaches) - { - cache.Grid.InvalidateMeasure(); - AddGridToScopes(cache); - - cache.GroupChanged.Subscribe(SharedGroupChanged); - } - } - - void SharedGroupChanged((string oldName, string newName, MeasurementResult result) change) - { - RemoveFromGroup(change.oldName, change.result); - AddToGroup(change.newName, change.result); - } - - internal void InvalidateMeasure(Grid grid) - { - var cache = _measurementCaches.FirstOrDefault(mc => ReferenceEquals(mc.Grid, grid)); - Debug.Assert(cache != null); - - cache.InvalidateMeasure(); - } - - internal void UpdateMeasureStatus(Grid grid, GridLayout.MeasureResult rowResult, GridLayout.MeasureResult columnResult) - { - var cache = _measurementCaches.FirstOrDefault(mc => ReferenceEquals(mc.Grid, grid)); - Debug.Assert(cache != null); - - cache.UpdateMeasureResult(rowResult, columnResult); - } - - private double Gather(IEnumerable measurements) - { - var result = 0.0d; - - bool onlyFixed = false; - - foreach (var measurement in measurements) - { - if (measurement.Definition is ColumnDefinition column) - { - if (!onlyFixed && column.Width.IsAbsolute) - { - onlyFixed = true; - result = measurement.MeasuredResult; - } - else if (onlyFixed == column.Width.IsAbsolute) - result = Math.Max(result, measurement.MeasuredResult); - - result = Math.Max(result, column.MinWidth); - } - if (measurement.Definition is RowDefinition row) - { - if (!onlyFixed && row.Height.IsAbsolute) - { - onlyFixed = true; - result = measurement.MeasuredResult; - } - else if (onlyFixed == row.Height.IsAbsolute) - result = Math.Max(result, measurement.MeasuredResult); - - result = Math.Max(result, row.MinHeight); - } - } - - return result; - } - - - (List, List, double) Arrange(IReadOnlyList definitions, GridLayout.MeasureResult measureResult) - { - var conventions = measureResult.LeanLengthList.ToList(); - var lengths = measureResult.LengthList.ToList(); - var desiredLength = 0.0; - for (int i = 0; i < definitions.Count; i++) - { - var definition = definitions[i]; - if (string.IsNullOrEmpty(definition.SharedSizeGroup)) - { - desiredLength += measureResult.LengthList[i]; - continue; - } - - var group = _groups[definition.SharedSizeGroup]; - - var length = Gather(group.Results); - - conventions[i] = new GridLayout.LengthConvention( - new GridLength(length), - measureResult.LeanLengthList[i].MinLength, - measureResult.LeanLengthList[i].MaxLength - ); - lengths[i] = length; - desiredLength += length; - } - - return (conventions, lengths, desiredLength); - } - - internal (GridLayout.MeasureResult, GridLayout.MeasureResult) HandleArrange(Grid grid, GridLayout.MeasureResult rowResult, GridLayout.MeasureResult columnResult) - { - var (rowConventions, rowLengths, rowDesiredLength) = Arrange(grid.RowDefinitions, rowResult); - var (columnConventions, columnLengths, columnDesiredLength) = Arrange(grid.ColumnDefinitions, columnResult); - - return ( - new GridLayout.MeasureResult( - rowResult.ContainerLength, - rowDesiredLength, - rowResult.GreedyDesiredLength,//?? - rowConventions, - rowLengths), - new GridLayout.MeasureResult( - columnResult.ContainerLength, - columnDesiredLength, - columnResult.GreedyDesiredLength, //?? - columnConventions, - columnLengths) - ); - } - - - private void AddGridToScopes(MeasurementCache cache) - { - foreach (var result in cache.Results) - { - var scopeName = result.Definition.SharedSizeGroup; - AddToGroup(scopeName, result); - } - } - - private void AddToGroup(string scopeName, MeasurementResult result) - { - if (string.IsNullOrEmpty(scopeName)) - return; - - if (!_groups.TryGetValue(scopeName, out var group)) - _groups.Add(scopeName, group = new Group()); - - group.IsFixed |= IsFixed(result.Definition); - - group.Results.Add(result); - } - - private bool IsFixed(DefinitionBase definition) - { - return ((definition as ColumnDefinition)?.Width ?? ((RowDefinition)definition).Height).IsAbsolute; - } - - private void RemoveGridFromScopes(MeasurementCache cache) - { - foreach (var result in cache.Results) - { - var scopeName = result.Definition.SharedSizeGroup; - RemoveFromGroup(scopeName, result); - } - } - - private void RemoveFromGroup(string scopeName, MeasurementResult result) - { - if (string.IsNullOrEmpty(scopeName)) - return; - - Debug.Assert(_groups.TryGetValue(scopeName, out var group)); - - group.Results.Remove(result); - if (!group.Results.Any()) - _groups.Remove(scopeName); - else - { - group.IsFixed = group.Results.Select(r => r.Definition).Any(IsFixed); - } - } - - private static AvaloniaList GetParticipatingGrids(Control scope) - { - var result = scope.GetVisualDescendants().OfType(); - - return new AvaloniaList( - result.Where(g => g.HasSharedSizeGroups()) - .Select(g => new MeasurementCache(g))); - } - - public void Dispose() - { - foreach (var cache in _measurementCaches) - { - cache.Grid.SharedScopeChanged(); - } - } - - internal void RegisterGrid(Grid toAdd) - { - Debug.Assert(!_measurementCaches.Any(mc => ReferenceEquals(mc.Grid,toAdd))); - var cache = new MeasurementCache(toAdd); - _measurementCaches.Add(cache); - AddGridToScopes(cache); - } - - internal void UnegisterGrid(Grid toRemove) - { - var cache = _measurementCaches.FirstOrDefault(mc => ReferenceEquals(mc.Grid, toRemove)); - - Debug.Assert(cache != null); - _measurementCaches.Remove(cache); - RemoveGridFromScopes(cache); - } - } - protected override void OnMeasureInvalidated() { base.OnMeasureInvalidated(); diff --git a/src/Avalonia.Controls/Utils/SharedSizeScopeHost.cs b/src/Avalonia.Controls/Utils/SharedSizeScopeHost.cs new file mode 100644 index 0000000000..a0e2137934 --- /dev/null +++ b/src/Avalonia.Controls/Utils/SharedSizeScopeHost.cs @@ -0,0 +1,400 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Diagnostics; +using System.Linq; +using System.Reactive.Disposables; +using System.Reactive.Subjects; +using Avalonia.Collections; +using Avalonia.Controls.Utils; +using Avalonia.VisualTree; + +namespace Avalonia.Controls +{ + internal sealed class SharedSizeScopeHost : IDisposable + { + private enum MeasurementState + { + Invalidated, + Measuring, + Cached + } + + private sealed class MeasurementCache : IDisposable + { + readonly CompositeDisposable _subscriptions; + readonly Subject<(string, string, MeasurementResult)> _groupChanged = new Subject<(string, string, MeasurementResult)>(); + + public ISubject<(string oldName, string newName, MeasurementResult result)> GroupChanged => _groupChanged; + + public MeasurementCache(Grid grid) + { + Grid = grid; + Results = grid.RowDefinitions.Cast() + .Concat(grid.ColumnDefinitions) + .Select(d => new MeasurementResult(d)) + .ToList(); + + grid.RowDefinitions.CollectionChanged += DefinitionsCollectionChanged; + grid.ColumnDefinitions.CollectionChanged += DefinitionsCollectionChanged; + + _subscriptions = new CompositeDisposable( + Disposable.Create(() => grid.RowDefinitions.CollectionChanged -= DefinitionsCollectionChanged), + Disposable.Create(() => grid.ColumnDefinitions.CollectionChanged -= DefinitionsCollectionChanged), + grid.RowDefinitions.TrackItemPropertyChanged(DefinitionPropertyChanged), + grid.ColumnDefinitions.TrackItemPropertyChanged(DefinitionPropertyChanged)); + + } + + private void DefinitionPropertyChanged(Tuple propertyChanged) + { + if (propertyChanged.Item2.PropertyName == nameof(DefinitionBase.SharedSizeGroup)) + { + var oldName = string.Empty; // TODO: find how to determine the old name + var newName = (propertyChanged.Item1 as DefinitionBase).SharedSizeGroup; + var result = Results.Single(mr => ReferenceEquals(mr.Definition, propertyChanged.Item1)); + _groupChanged.OnNext((oldName, newName, result)); + } + } + + private void DefinitionsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + int offset = 0; + if (sender is ColumnDefinitions) + offset = Grid.RowDefinitions.Count; + + var newItems = e.NewItems?.OfType().Select(db => new MeasurementResult(db)).ToList() ?? new List(); + var oldItems = Results.GetRange(e.OldStartingIndex + offset, e.OldItems?.Count ?? 0); + + void NotifyNewItems() + { + foreach (var item in newItems) + { + if (string.IsNullOrEmpty(item.Definition.SharedSizeGroup)) + continue; + + _groupChanged.OnNext((null, item.Definition.SharedSizeGroup, item)); + } + } + + void NotifyOldItems() + { + foreach (var item in oldItems) + { + if (string.IsNullOrEmpty(item.Definition.SharedSizeGroup)) + continue; + + _groupChanged.OnNext((item.Definition.SharedSizeGroup, null, item)); + } + } + + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + Results.InsertRange(e.NewStartingIndex + offset, newItems); + NotifyNewItems(); + break; + + case NotifyCollectionChangedAction.Remove: + Results.RemoveRange(e.OldStartingIndex + offset, oldItems.Count); + NotifyOldItems(); + break; + + case NotifyCollectionChangedAction.Move: + Results.RemoveRange(e.OldStartingIndex + offset, oldItems.Count); + Results.InsertRange(e.NewStartingIndex + offset, oldItems); + break; + + case NotifyCollectionChangedAction.Replace: + Results.RemoveRange(e.OldStartingIndex + offset, oldItems.Count); + Results.InsertRange(e.NewStartingIndex + offset, newItems); + + NotifyOldItems(); + NotifyNewItems(); + + break; + + case NotifyCollectionChangedAction.Reset: + oldItems = Results; + newItems = Results = Grid.RowDefinitions.Cast() + .Concat(Grid.ColumnDefinitions) + .Select(d => new MeasurementResult(d)) + .ToList(); + NotifyOldItems(); + NotifyNewItems(); + + break; + } + } + + public void UpdateMeasureResult(GridLayout.MeasureResult rowResult, GridLayout.MeasureResult columnResult) + { + MeasurementState = MeasurementState.Cached; + for (int i = 0; i < Grid.RowDefinitions.Count; i++) + { + Results[i].MeasuredResult = rowResult.LengthList[i]; + } + + for (int i = 0; i < Grid.ColumnDefinitions.Count; i++) + { + Results[i + rowResult.LengthList.Count].MeasuredResult = columnResult.LengthList[i]; + } + } + + public void InvalidateMeasure() + { + MeasurementState = MeasurementState.Invalidated; + Results.ForEach(r => r.MeasuredResult = double.NaN); + } + + public void Dispose() + { + _subscriptions.Dispose(); + _groupChanged.OnCompleted(); + } + + public Grid Grid { get; } + public MeasurementState MeasurementState { get; private set; } + + public List Results { get; private set; } + } + + private readonly AvaloniaList _measurementCaches; + + private class MeasurementResult + { + public MeasurementResult(DefinitionBase definition) + { + Definition = definition; + MeasuredResult = double.NaN; + } + + public DefinitionBase Definition { get; } + public double MeasuredResult { get; set; } + } + + private class Group + { + public bool IsFixed { get; set; } + + public List Results { get; } = new List(); + + public double CalculatedLength { get; } + } + + private readonly Dictionary _groups = new Dictionary(); + + + public SharedSizeScopeHost(Control scope) + { + _measurementCaches = GetParticipatingGrids(scope); + + foreach (var cache in _measurementCaches) + { + cache.Grid.InvalidateMeasure(); + AddGridToScopes(cache); + + cache.GroupChanged.Subscribe(SharedGroupChanged); + } + } + + void SharedGroupChanged((string oldName, string newName, MeasurementResult result) change) + { + RemoveFromGroup(change.oldName, change.result); + AddToGroup(change.newName, change.result); + } + + internal void InvalidateMeasure(Grid grid) + { + var cache = _measurementCaches.FirstOrDefault(mc => ReferenceEquals(mc.Grid, grid)); + Debug.Assert(cache != null); + + cache.InvalidateMeasure(); + } + + internal void UpdateMeasureStatus(Grid grid, GridLayout.MeasureResult rowResult, GridLayout.MeasureResult columnResult) + { + var cache = _measurementCaches.FirstOrDefault(mc => ReferenceEquals(mc.Grid, grid)); + Debug.Assert(cache != null); + + cache.UpdateMeasureResult(rowResult, columnResult); + } + + private double Gather(IEnumerable measurements) + { + var result = 0.0d; + + bool onlyFixed = false; + + foreach (var measurement in measurements) + { + if (measurement.Definition is ColumnDefinition column) + { + if (!onlyFixed && column.Width.IsAbsolute) + { + onlyFixed = true; + result = measurement.MeasuredResult; + } + else if (onlyFixed == column.Width.IsAbsolute) + result = Math.Max(result, measurement.MeasuredResult); + + result = Math.Max(result, column.MinWidth); + } + if (measurement.Definition is RowDefinition row) + { + if (!onlyFixed && row.Height.IsAbsolute) + { + onlyFixed = true; + result = measurement.MeasuredResult; + } + else if (onlyFixed == row.Height.IsAbsolute) + result = Math.Max(result, measurement.MeasuredResult); + + result = Math.Max(result, row.MinHeight); + } + } + + return result; + } + + + (List, List, double) Arrange(IReadOnlyList definitions, GridLayout.MeasureResult measureResult) + { + var conventions = measureResult.LeanLengthList.ToList(); + var lengths = measureResult.LengthList.ToList(); + var desiredLength = 0.0; + for (int i = 0; i < definitions.Count; i++) + { + var definition = definitions[i]; + if (string.IsNullOrEmpty(definition.SharedSizeGroup)) + { + desiredLength += measureResult.LengthList[i]; + continue; + } + + var group = _groups[definition.SharedSizeGroup]; + + var length = Gather(group.Results); + + conventions[i] = new GridLayout.LengthConvention( + new GridLength(length), + measureResult.LeanLengthList[i].MinLength, + measureResult.LeanLengthList[i].MaxLength + ); + lengths[i] = length; + desiredLength += length; + } + + return (conventions, lengths, desiredLength); + } + + internal (GridLayout.MeasureResult, GridLayout.MeasureResult) HandleArrange(Grid grid, GridLayout.MeasureResult rowResult, GridLayout.MeasureResult columnResult) + { + var (rowConventions, rowLengths, rowDesiredLength) = Arrange(grid.RowDefinitions, rowResult); + var (columnConventions, columnLengths, columnDesiredLength) = Arrange(grid.ColumnDefinitions, columnResult); + + return ( + new GridLayout.MeasureResult( + rowResult.ContainerLength, + rowDesiredLength, + rowResult.GreedyDesiredLength,//?? + rowConventions, + rowLengths), + new GridLayout.MeasureResult( + columnResult.ContainerLength, + columnDesiredLength, + columnResult.GreedyDesiredLength, //?? + columnConventions, + columnLengths) + ); + } + + + private void AddGridToScopes(MeasurementCache cache) + { + foreach (var result in cache.Results) + { + var scopeName = result.Definition.SharedSizeGroup; + AddToGroup(scopeName, result); + } + } + + private void AddToGroup(string scopeName, MeasurementResult result) + { + if (string.IsNullOrEmpty(scopeName)) + return; + + if (!_groups.TryGetValue(scopeName, out var group)) + _groups.Add(scopeName, group = new Group()); + + group.IsFixed |= IsFixed(result.Definition); + + group.Results.Add(result); + } + + private bool IsFixed(DefinitionBase definition) + { + return ((definition as ColumnDefinition)?.Width ?? ((RowDefinition)definition).Height).IsAbsolute; + } + + private void RemoveGridFromScopes(MeasurementCache cache) + { + foreach (var result in cache.Results) + { + var scopeName = result.Definition.SharedSizeGroup; + RemoveFromGroup(scopeName, result); + } + } + + private void RemoveFromGroup(string scopeName, MeasurementResult result) + { + if (string.IsNullOrEmpty(scopeName)) + return; + + Debug.Assert(_groups.TryGetValue(scopeName, out var group)); + + group.Results.Remove(result); + if (!group.Results.Any()) + _groups.Remove(scopeName); + else + { + group.IsFixed = group.Results.Select(r => r.Definition).Any(IsFixed); + } + } + + private static AvaloniaList GetParticipatingGrids(Control scope) + { + var result = scope.GetVisualDescendants().OfType(); + + return new AvaloniaList( + result.Where(g => g.HasSharedSizeGroups()) + .Select(g => new MeasurementCache(g))); + } + + public void Dispose() + { + foreach (var cache in _measurementCaches) + { + cache.Grid.SharedScopeChanged(); + } + } + + internal void RegisterGrid(Grid toAdd) + { + Debug.Assert(!_measurementCaches.Any(mc => ReferenceEquals(mc.Grid, toAdd))); + var cache = new MeasurementCache(toAdd); + _measurementCaches.Add(cache); + AddGridToScopes(cache); + } + + internal void UnegisterGrid(Grid toRemove) + { + var cache = _measurementCaches.FirstOrDefault(mc => ReferenceEquals(mc.Grid, toRemove)); + + Debug.Assert(cache != null); + _measurementCaches.Remove(cache); + RemoveGridFromScopes(cache); + } + } +} From 4b02fb375f098052f833034bf416c998290e52ea Mon Sep 17 00:00:00 2001 From: wojciech krysiak Date: Sat, 6 Oct 2018 10:00:32 +0200 Subject: [PATCH 022/374] Corrected found issues --- .../Utils/SharedSizeScopeHost.cs | 194 +++++++++++++----- 1 file changed, 140 insertions(+), 54 deletions(-) diff --git a/src/Avalonia.Controls/Utils/SharedSizeScopeHost.cs b/src/Avalonia.Controls/Utils/SharedSizeScopeHost.cs index a0e2137934..25d6d7f6b8 100644 --- a/src/Avalonia.Controls/Utils/SharedSizeScopeHost.cs +++ b/src/Avalonia.Controls/Utils/SharedSizeScopeHost.cs @@ -8,6 +8,7 @@ using System.Reactive.Disposables; using System.Reactive.Subjects; using Avalonia.Collections; using Avalonia.Controls.Utils; +using Avalonia.Layout; using Avalonia.VisualTree; namespace Avalonia.Controls @@ -33,7 +34,7 @@ namespace Avalonia.Controls Grid = grid; Results = grid.RowDefinitions.Cast() .Concat(grid.ColumnDefinitions) - .Select(d => new MeasurementResult(d)) + .Select(d => new MeasurementResult(grid, d)) .ToList(); grid.RowDefinitions.CollectionChanged += DefinitionsCollectionChanged; @@ -51,9 +52,9 @@ namespace Avalonia.Controls { if (propertyChanged.Item2.PropertyName == nameof(DefinitionBase.SharedSizeGroup)) { - var oldName = string.Empty; // TODO: find how to determine the old name - var newName = (propertyChanged.Item1 as DefinitionBase).SharedSizeGroup; var result = Results.Single(mr => ReferenceEquals(mr.Definition, propertyChanged.Item1)); + var oldName = result.SizeGroup?.Name; + var newName = (propertyChanged.Item1 as DefinitionBase).SharedSizeGroup; _groupChanged.OnNext((oldName, newName, result)); } } @@ -64,7 +65,7 @@ namespace Avalonia.Controls if (sender is ColumnDefinitions) offset = Grid.RowDefinitions.Count; - var newItems = e.NewItems?.OfType().Select(db => new MeasurementResult(db)).ToList() ?? new List(); + var newItems = e.NewItems?.OfType().Select(db => new MeasurementResult(Grid, db)).ToList() ?? new List(); var oldItems = Results.GetRange(e.OldStartingIndex + offset, e.OldItems?.Count ?? 0); void NotifyNewItems() @@ -119,7 +120,7 @@ namespace Avalonia.Controls oldItems = Results; newItems = Results = Grid.RowDefinitions.Cast() .Concat(Grid.ColumnDefinitions) - .Select(d => new MeasurementResult(d)) + .Select(d => new MeasurementResult(Grid, d)) .ToList(); NotifyOldItems(); NotifyNewItems(); @@ -145,7 +146,11 @@ namespace Avalonia.Controls public void InvalidateMeasure() { MeasurementState = MeasurementState.Invalidated; - Results.ForEach(r => r.MeasuredResult = double.NaN); + Results.ForEach(r => + { + r.MeasuredResult = double.NaN; + r.SizeGroup?.Reset(); + }); } public void Dispose() @@ -160,31 +165,107 @@ namespace Avalonia.Controls public List Results { get; private set; } } - private readonly AvaloniaList _measurementCaches; - private class MeasurementResult { - public MeasurementResult(DefinitionBase definition) + public MeasurementResult(Grid owningGrid, DefinitionBase definition) { + OwningGrid = owningGrid; Definition = definition; MeasuredResult = double.NaN; } public DefinitionBase Definition { get; } public double MeasuredResult { get; set; } + public Group SizeGroup { get; set; } + public Grid OwningGrid { get; } } + private class Group { + private double? cachedResult; + private List _results = new List(); + + public string Name { get; } + + public Group(string name) + { + Name = name; + } + public bool IsFixed { get; set; } - public List Results { get; } = new List(); + public IReadOnlyList Results => _results; + + public double CalculatedLength => (cachedResult ?? (cachedResult = Gather())).Value; + + public void Reset() + { + cachedResult = null; + } + + public void Add(MeasurementResult result) + { + if (!_results.Contains(result)) + throw new AvaloniaInternalException( + $"Invalid call to Group.Add - The SharedSizeGroup {Name} already contains the passed result"); + + result.SizeGroup = this; + _results.Add(result); + } + + public void Remove(MeasurementResult result) + { + if (!_results.Contains(result)) + throw new AvaloniaInternalException( + $"Invalid call to Group.Remove - The SharedSizeGroup {Name} does not contain the passed result"); + result.SizeGroup = null; + _results.Remove(result); + } + + + private double Gather() + { + var result = 0.0d; + + bool onlyFixed = false; + + foreach (var measurement in Results) + { + if (measurement.Definition is ColumnDefinition column) + { + if (!onlyFixed && column.Width.IsAbsolute) + { + onlyFixed = true; + result = measurement.MeasuredResult; + } + else if (onlyFixed == column.Width.IsAbsolute) + result = Math.Max(result, measurement.MeasuredResult); + + result = Math.Max(result, column.MinWidth); + } + if (measurement.Definition is RowDefinition row) + { + if (!onlyFixed && row.Height.IsAbsolute) + { + onlyFixed = true; + result = measurement.MeasuredResult; + } + else if (onlyFixed == row.Height.IsAbsolute) + result = Math.Max(result, measurement.MeasuredResult); + + result = Math.Max(result, row.MinHeight); + } + } + + return result; + } - public double CalculatedLength { get; } } - private readonly Dictionary _groups = new Dictionary(); + private readonly AvaloniaList _measurementCaches; + private readonly Dictionary _groups = new Dictionary(); public SharedSizeScopeHost(Control scope) { @@ -205,59 +286,62 @@ namespace Avalonia.Controls AddToGroup(change.newName, change.result); } + private bool _invalidating; + internal void InvalidateMeasure(Grid grid) { - var cache = _measurementCaches.FirstOrDefault(mc => ReferenceEquals(mc.Grid, grid)); - Debug.Assert(cache != null); + // prevent stack overflow + if (_invalidating) + return; + _invalidating = true; - cache.InvalidateMeasure(); + InvalidateMeasureImpl(grid); + + _invalidating = false; } - internal void UpdateMeasureStatus(Grid grid, GridLayout.MeasureResult rowResult, GridLayout.MeasureResult columnResult) + private void InvalidateMeasureImpl(Grid grid) { var cache = _measurementCaches.FirstOrDefault(mc => ReferenceEquals(mc.Grid, grid)); - Debug.Assert(cache != null); - cache.UpdateMeasureResult(rowResult, columnResult); - } + if (cache == null) + throw new AvaloniaInternalException( + $"InvalidateMeasureImpl - called with a grid not present in the internal cache"); - private double Gather(IEnumerable measurements) - { - var result = 0.0d; + // already invalidated the cache, early out. + if (cache.MeasurementState == MeasurementState.Invalidated) + return; - bool onlyFixed = false; + cache.InvalidateMeasure(); - foreach (var measurement in measurements) + // maybe there is a condition to only call arrange on some of the calls? + grid.InvalidateMeasure(); + + // find all the scopes within the invalidated grid + var scopeNames = cache.Results + .Where(mr => mr.SizeGroup != null) + .Select(mr => mr.SizeGroup.Name) + .Distinct(); + // find all grids related to those scopes + var otherGrids = scopeNames.SelectMany(sn => _groups[sn].Results) + .Select(r => r.OwningGrid) + .Where(g => g.IsMeasureValid) + .Distinct(); + + // invalidate them as well + foreach (var otherGrid in otherGrids) { - if (measurement.Definition is ColumnDefinition column) - { - if (!onlyFixed && column.Width.IsAbsolute) - { - onlyFixed = true; - result = measurement.MeasuredResult; - } - else if (onlyFixed == column.Width.IsAbsolute) - result = Math.Max(result, measurement.MeasuredResult); - - result = Math.Max(result, column.MinWidth); - } - if (measurement.Definition is RowDefinition row) - { - if (!onlyFixed && row.Height.IsAbsolute) - { - onlyFixed = true; - result = measurement.MeasuredResult; - } - else if (onlyFixed == row.Height.IsAbsolute) - result = Math.Max(result, measurement.MeasuredResult); - - result = Math.Max(result, row.MinHeight); - } + InvalidateMeasureImpl(otherGrid); } - - return result; } + internal void UpdateMeasureStatus(Grid grid, GridLayout.MeasureResult rowResult, GridLayout.MeasureResult columnResult) + { + var cache = _measurementCaches.FirstOrDefault(mc => ReferenceEquals(mc.Grid, grid)); + Debug.Assert(cache != null); + + cache.UpdateMeasureResult(rowResult, columnResult); + } (List, List, double) Arrange(IReadOnlyList definitions, GridLayout.MeasureResult measureResult) { @@ -275,7 +359,7 @@ namespace Avalonia.Controls var group = _groups[definition.SharedSizeGroup]; - var length = Gather(group.Results); + var length = group.CalculatedLength; conventions[i] = new GridLayout.LengthConvention( new GridLength(length), @@ -326,11 +410,11 @@ namespace Avalonia.Controls return; if (!_groups.TryGetValue(scopeName, out var group)) - _groups.Add(scopeName, group = new Group()); + _groups.Add(scopeName, group = new Group(scopeName)); group.IsFixed |= IsFixed(result.Definition); - group.Results.Add(result); + group.Add(result); } private bool IsFixed(DefinitionBase definition) @@ -354,7 +438,7 @@ namespace Avalonia.Controls Debug.Assert(_groups.TryGetValue(scopeName, out var group)); - group.Results.Remove(result); + group.Remove(result); if (!group.Results.Any()) _groups.Remove(scopeName); else @@ -377,6 +461,7 @@ namespace Avalonia.Controls foreach (var cache in _measurementCaches) { cache.Grid.SharedScopeChanged(); + cache.Dispose(); } } @@ -395,6 +480,7 @@ namespace Avalonia.Controls Debug.Assert(cache != null); _measurementCaches.Remove(cache); RemoveGridFromScopes(cache); + cache.Dispose(); } } } From 49fda7256818deccbcd8dd726b823afef48a178f Mon Sep 17 00:00:00 2001 From: wojciech krysiak Date: Sun, 7 Oct 2018 11:14:18 +0200 Subject: [PATCH 023/374] Some more changes + GridSplitter Fix --- src/Avalonia.Controls/GridSplitter.cs | 67 ++++++++++++++----- .../Utils/SharedSizeScopeHost.cs | 10 ++- 2 files changed, 57 insertions(+), 20 deletions(-) diff --git a/src/Avalonia.Controls/GridSplitter.cs b/src/Avalonia.Controls/GridSplitter.cs index 1e4c6f2c2a..4d38d7389e 100644 --- a/src/Avalonia.Controls/GridSplitter.cs +++ b/src/Avalonia.Controls/GridSplitter.cs @@ -49,21 +49,41 @@ namespace Avalonia.Controls double min; GetDeltaConstraints(out min, out max); delta = Math.Min(Math.Max(delta, min), max); - foreach (var definition in _definitions) + + var prevIsStar = IsStar(_prevDefinition); + var nextIsStar = IsStar(_nextDefinition); + + if (prevIsStar && nextIsStar) { - if (definition == _prevDefinition) - { - SetLengthInStars(_prevDefinition, GetActualLength(_prevDefinition) + delta); - } - else if (definition == _nextDefinition) - { - SetLengthInStars(_nextDefinition, GetActualLength(_nextDefinition) - delta); - } - else if (IsStar(definition)) + foreach (var definition in _definitions) { - SetLengthInStars(definition, GetActualLength(definition)); // same size but in stars. + if (definition == _prevDefinition) + { + SetLengthInStars(_prevDefinition, GetActualLength(_prevDefinition) + delta); + } + else if (definition == _nextDefinition) + { + SetLengthInStars(_nextDefinition, GetActualLength(_nextDefinition) - delta); + } + else if (IsStar(definition)) + { + SetLengthInStars(definition, GetActualLength(definition)); // same size but in stars. + } } } + else if (prevIsStar) + { + SetLength(_nextDefinition, GetActualLength(_nextDefinition) - delta); + } + else if (nextIsStar) + { + SetLength(_prevDefinition, GetActualLength(_prevDefinition) + delta); + } + else + { + SetLength(_prevDefinition, GetActualLength(_prevDefinition) + delta); + SetLength(_nextDefinition, GetActualLength(_nextDefinition) - delta); + } } private double GetActualLength(DefinitionBase definition) @@ -71,7 +91,7 @@ namespace Avalonia.Controls if (definition == null) return 0; var columnDefinition = definition as ColumnDefinition; - return columnDefinition?.ActualWidth ?? ((RowDefinition) definition).ActualHeight; + return columnDefinition?.ActualWidth ?? ((RowDefinition)definition).ActualHeight; } private double GetMinLength(DefinitionBase definition) @@ -79,7 +99,7 @@ namespace Avalonia.Controls if (definition == null) return 0; var columnDefinition = definition as ColumnDefinition; - return columnDefinition?.MinWidth ?? ((RowDefinition) definition).MinHeight; + return columnDefinition?.MinWidth ?? ((RowDefinition)definition).MinHeight; } private double GetMaxLength(DefinitionBase definition) @@ -87,13 +107,13 @@ namespace Avalonia.Controls if (definition == null) return 0; var columnDefinition = definition as ColumnDefinition; - return columnDefinition?.MaxWidth ?? ((RowDefinition) definition).MaxHeight; + return columnDefinition?.MaxWidth ?? ((RowDefinition)definition).MaxHeight; } private bool IsStar(DefinitionBase definition) { var columnDefinition = definition as ColumnDefinition; - return columnDefinition?.Width.IsStar ?? ((RowDefinition) definition).Height.IsStar; + return columnDefinition?.Width.IsStar ?? ((RowDefinition)definition).Height.IsStar; } private void SetLengthInStars(DefinitionBase definition, double value) @@ -105,7 +125,20 @@ namespace Avalonia.Controls } else { - ((RowDefinition) definition).Height = new GridLength(value, GridUnitType.Star); + ((RowDefinition)definition).Height = new GridLength(value, GridUnitType.Star); + } + } + + private void SetLength(DefinitionBase definition, double value) + { + var columnDefinition = definition as ColumnDefinition; + if (columnDefinition != null) + { + columnDefinition.Width = new GridLength(value); + } + else + { + ((RowDefinition)definition).Height = new GridLength(value); } } @@ -160,7 +193,7 @@ namespace Avalonia.Controls } if (_grid.Children.OfType() // Decision based on other controls in the same column .Where(c => Grid.GetColumn(c) == col) - .Any(c => c.GetType() != typeof (GridSplitter))) + .Any(c => c.GetType() != typeof(GridSplitter))) { return Orientation.Horizontal; } diff --git a/src/Avalonia.Controls/Utils/SharedSizeScopeHost.cs b/src/Avalonia.Controls/Utils/SharedSizeScopeHost.cs index 25d6d7f6b8..0ff024c9a6 100644 --- a/src/Avalonia.Controls/Utils/SharedSizeScopeHost.cs +++ b/src/Avalonia.Controls/Utils/SharedSizeScopeHost.cs @@ -139,7 +139,7 @@ namespace Avalonia.Controls for (int i = 0; i < Grid.ColumnDefinitions.Count; i++) { - Results[i + rowResult.LengthList.Count].MeasuredResult = columnResult.LengthList[i]; + Results[i + Grid.RowDefinitions.Count].MeasuredResult = columnResult.LengthList[i]; } } @@ -206,7 +206,7 @@ namespace Avalonia.Controls public void Add(MeasurementResult result) { - if (!_results.Contains(result)) + if (_results.Contains(result)) throw new AvaloniaInternalException( $"Invalid call to Group.Add - The SharedSizeGroup {Name} already contains the passed result"); @@ -232,6 +232,9 @@ namespace Avalonia.Controls foreach (var measurement in Results) { + if (Double.IsInfinity(measurement.MeasuredResult)) + continue; + if (measurement.Definition is ColumnDefinition column) { if (!onlyFixed && column.Width.IsAbsolute) @@ -276,7 +279,6 @@ namespace Avalonia.Controls cache.Grid.InvalidateMeasure(); AddGridToScopes(cache); - cache.GroupChanged.Subscribe(SharedGroupChanged); } } @@ -397,6 +399,8 @@ namespace Avalonia.Controls private void AddGridToScopes(MeasurementCache cache) { + cache.GroupChanged.Subscribe(SharedGroupChanged); + foreach (var result in cache.Results) { var scopeName = result.Definition.SharedSizeGroup; From 184967a62b8c2fd45e4c9ac7111c15c386f8e58f Mon Sep 17 00:00:00 2001 From: Wojciech Krysiak Date: Sun, 21 Oct 2018 22:01:35 +0200 Subject: [PATCH 024/374] Corrected review comments + fixed some issues --- src/Avalonia.Controls/Grid.cs | 90 +++++++++------ src/Avalonia.Controls/Utils/GridLayout.cs | 17 ++- .../Utils/SharedSizeScopeHost.cs | 109 +++++++++++------- 3 files changed, 130 insertions(+), 86 deletions(-) diff --git a/src/Avalonia.Controls/Grid.cs b/src/Avalonia.Controls/Grid.cs index 264695d3d7..7bda4140a3 100644 --- a/src/Avalonia.Controls/Grid.cs +++ b/src/Avalonia.Controls/Grid.cs @@ -56,31 +56,12 @@ namespace Avalonia.Controls _sharedSizeHost?.InvalidateMeasure(this); } - protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) - { - base.OnAttachedToVisualTree(e); - var scope = this.GetVisualAncestors().OfType() - .FirstOrDefault(c => c.GetValue(IsSharedSizeScopeProperty)); - - Debug.Assert(_sharedSizeHost == null); - - if (scope != null) - { - _sharedSizeHost = scope.GetValue(s_sharedSizeScopeHostProperty); - _sharedSizeHost.RegisterGrid(this); - } - } - - protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) - { - base.OnDetachedFromVisualTree(e); - - _sharedSizeHost?.UnegisterGrid(this); - _sharedSizeHost = null; - } - private SharedSizeScopeHost _sharedSizeHost; + /// + /// Defines the SharedSizeScopeHost private property. + /// The ampersands are used to make accessing the property via xaml inconvenient. + /// private static readonly AttachedProperty s_sharedSizeScopeHostProperty = AvaloniaProperty.RegisterAttached("&&SharedSizeScopeHost", null); @@ -94,20 +75,53 @@ namespace Avalonia.Controls IsSharedSizeScopeProperty.Changed.AddClassHandler(IsSharedSizeScopeChanged); } - private static void IsSharedSizeScopeChanged(Control source, AvaloniaPropertyChangedEventArgs arg2) + public Grid() + { + this.AttachedToVisualTree += Grid_AttachedToVisualTree; + this.DetachedFromVisualTree += Grid_DetachedFromVisualTree; + } + + private void Grid_AttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e) { - if ((bool)arg2.NewValue) + var scope = + new Control[] { this }.Concat(this.GetVisualAncestors().OfType()) + .FirstOrDefault(c => c.GetValue(IsSharedSizeScopeProperty)); + + if (_sharedSizeHost != null) + throw new AvaloniaInternalException("Shared size scope already present when attaching to visual tree!"); + + if (scope != null) { - Debug.Assert(source.GetValue(s_sharedSizeScopeHostProperty) == null); - source.SetValue(s_sharedSizeScopeHostProperty, new SharedSizeScopeHost(source)); + _sharedSizeHost = scope.GetValue(s_sharedSizeScopeHostProperty); + _sharedSizeHost.RegisterGrid(this); } - else + } + + private void Grid_DetachedFromVisualTree(object sender, VisualTreeAttachmentEventArgs e) + { + _sharedSizeHost?.UnegisterGrid(this); + _sharedSizeHost = null; + } + + private static void IsSharedSizeScopeChanged(Control source, AvaloniaPropertyChangedEventArgs arg2) + { + var shouldDispose = (arg2.OldValue is bool d) && d; + if (shouldDispose) { var host = source.GetValue(s_sharedSizeScopeHostProperty) as SharedSizeScopeHost; - Debug.Assert(host != null); + if (host == null) + throw new AvaloniaInternalException("SharedScopeHost wasn't set when IsSharedSizeScope was true!"); host.Dispose(); source.SetValue(s_sharedSizeScopeHostProperty, null); } + + var shouldAssign = (arg2.NewValue is bool a) && a; + if (shouldAssign) + { + if (source.GetValue(s_sharedSizeScopeHostProperty) != null) + throw new AvaloniaInternalException("SharedScopeHost was already set when IsSharedSizeScope is only now being set to true!"); + source.SetValue(s_sharedSizeScopeHostProperty, new SharedSizeScopeHost(source)); + } } /// @@ -328,7 +342,10 @@ namespace Avalonia.Controls _rowLayoutCache = rowLayout; _columnLayoutCache = columnLayout; - _sharedSizeHost?.UpdateMeasureStatus(this, rowResult, columnResult); + if (_sharedSizeHost?.ParticipatesInScope(this) ?? false) + { + _sharedSizeHost.UpdateMeasureStatus(this, rowResult, columnResult); + } return new Size(columnResult.DesiredLength, rowResult.DesiredLength); @@ -379,13 +396,14 @@ namespace Avalonia.Controls var columnLayout = _columnLayoutCache; var rowLayout = _rowLayoutCache; - var (rowCache, columnCache) = - _sharedSizeHost?.HandleArrange(this, _rowMeasureCache, _columnMeasureCache) ?? - (_rowMeasureCache, _columnMeasureCache); + var rowCache = _rowMeasureCache; + var columnCache = _columnMeasureCache; - if (_sharedSizeHost != null) - { - rowCache = rowLayout.Measure(finalSize.Width, rowCache.LeanLengthList); + if (_sharedSizeHost?.ParticipatesInScope(this) ?? false) + { + (rowCache, columnCache) = _sharedSizeHost.HandleArrange(this, _rowMeasureCache, _columnMeasureCache); + + rowCache = rowLayout.Measure(finalSize.Height, rowCache.LeanLengthList); columnCache = columnLayout.Measure(finalSize.Width, columnCache.LeanLengthList); } diff --git a/src/Avalonia.Controls/Utils/GridLayout.cs b/src/Avalonia.Controls/Utils/GridLayout.cs index b1dca09be2..7704228a4e 100644 --- a/src/Avalonia.Controls/Utils/GridLayout.cs +++ b/src/Avalonia.Controls/Utils/GridLayout.cs @@ -143,6 +143,9 @@ namespace Avalonia.Controls.Utils /// /// The container length. Usually, it is the constraint of the method. /// + /// + /// Overriding conventions that allows the algorithm to handle external inputa + /// /// /// The measured result that containing the desired size and all the column/row lengths. /// @@ -248,7 +251,7 @@ namespace Avalonia.Controls.Utils // | min | max | | | min | | min max | max | // |#des#| fix |#des#| fix | fix | fix | fix | #des# |#des#| - var desiredStarMin = AggregateAdditionalConventionsForStars(conventions); + var (minLengths, desiredStarMin) = AggregateAdditionalConventionsForStars(conventions); aggregatedLength += desiredStarMin; // M6/7. Determine the desired length of the grid for current container length. Its value is stored in desiredLength. @@ -282,7 +285,7 @@ namespace Avalonia.Controls.Utils // Returns the measuring result. return new MeasureResult(containerLength, desiredLength, greedyDesiredLength, - conventions, dynamicConvention); + conventions, dynamicConvention, minLengths); } /// @@ -313,7 +316,7 @@ namespace Avalonia.Controls.Utils // If the final length is smaller, we measure the M6/6 procedure only. var dynamicConvention = ExpandStars(measure.LeanLengthList, finalLength); measure = new MeasureResult(finalLength, measure.DesiredLength, measure.GreedyDesiredLength, - measure.LeanLengthList, dynamicConvention); + measure.LeanLengthList, dynamicConvention, measure.MinLengths); } return new ArrangeResult(measure.LengthList); @@ -370,7 +373,7 @@ namespace Avalonia.Controls.Utils /// All the conventions that have almost been fixed except the rest *. /// The total desired length of all the * length. [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] - private double AggregateAdditionalConventionsForStars( + private (List, double) AggregateAdditionalConventionsForStars( IReadOnlyList conventions) { // 1. Determine all one-span column's desired widths or row's desired heights. @@ -403,7 +406,7 @@ namespace Avalonia.Controls.Utils lengthList[group.Key] = Math.Max(lengthList[group.Key], length > 0 ? length : 0); } - return lengthList.Sum() - fixedLength; + return (lengthList, lengthList.Sum() - fixedLength); } /// @@ -638,13 +641,14 @@ namespace Avalonia.Controls.Utils /// Initialize a new instance of . /// internal MeasureResult(double containerLength, double desiredLength, double greedyDesiredLength, - IReadOnlyList leanConventions, IReadOnlyList expandedConventions) + IReadOnlyList leanConventions, IReadOnlyList expandedConventions, IReadOnlyList minLengths) { ContainerLength = containerLength; DesiredLength = desiredLength; GreedyDesiredLength = greedyDesiredLength; LeanLengthList = leanConventions; LengthList = expandedConventions; + MinLengths = minLengths; } /// @@ -674,6 +678,7 @@ namespace Avalonia.Controls.Utils /// Gets the length list for each column/row. /// public IReadOnlyList LengthList { get; } + public IReadOnlyList MinLengths { get; } } /// diff --git a/src/Avalonia.Controls/Utils/SharedSizeScopeHost.cs b/src/Avalonia.Controls/Utils/SharedSizeScopeHost.cs index 0ff024c9a6..5948bd7f19 100644 --- a/src/Avalonia.Controls/Utils/SharedSizeScopeHost.cs +++ b/src/Avalonia.Controls/Utils/SharedSizeScopeHost.cs @@ -135,11 +135,13 @@ namespace Avalonia.Controls for (int i = 0; i < Grid.RowDefinitions.Count; i++) { Results[i].MeasuredResult = rowResult.LengthList[i]; + Results[i].MinLength = rowResult.MinLengths[i]; } for (int i = 0; i < Grid.ColumnDefinitions.Count; i++) { Results[i + Grid.RowDefinitions.Count].MeasuredResult = columnResult.LengthList[i]; + Results[i + Grid.RowDefinitions.Count].MinLength = columnResult.MinLengths[i]; } } @@ -176,8 +178,47 @@ namespace Avalonia.Controls public DefinitionBase Definition { get; } public double MeasuredResult { get; set; } + public double MinLength { get; set; } public Group SizeGroup { get; set; } public Grid OwningGrid { get; } + + public (double length, int priority) GetPriorityLength() + { + var length = (Definition as ColumnDefinition)?.Width ?? ((RowDefinition)Definition).Height; + + if (length.IsAbsolute) + return (MeasuredResult, 1); + if (length.IsAuto) + return (MeasuredResult, 2); + if (MinLength > 0) + return (MinLength, 3); + return (MeasuredResult, 4); + } + } + + + private class LentgthGatherer + { + public double Length { get; private set; } + private int gatheredPriority = 6; + + public void Visit(MeasurementResult result) + { + var (length, priority) = result.GetPriorityLength(); + + if (gatheredPriority < priority) + return; + + gatheredPriority = priority; + if (gatheredPriority == priority) + { + Length = Math.Max(length,Length); + } + else + { + Length = length; + } + } } @@ -208,7 +249,7 @@ namespace Avalonia.Controls { if (_results.Contains(result)) throw new AvaloniaInternalException( - $"Invalid call to Group.Add - The SharedSizeGroup {Name} already contains the passed result"); + $"SharedSizeScopeHost: Invalid call to Group.Add - The SharedSizeGroup {Name} already contains the passed result"); result.SizeGroup = this; _results.Add(result); @@ -218,7 +259,7 @@ namespace Avalonia.Controls { if (!_results.Contains(result)) throw new AvaloniaInternalException( - $"Invalid call to Group.Remove - The SharedSizeGroup {Name} does not contain the passed result"); + $"SharedSizeScopeHost: Invalid call to Group.Remove - The SharedSizeGroup {Name} does not contain the passed result"); result.SizeGroup = null; _results.Remove(result); } @@ -226,44 +267,12 @@ namespace Avalonia.Controls private double Gather() { - var result = 0.0d; + var visitor = new LentgthGatherer(); - bool onlyFixed = false; + _results.ForEach(visitor.Visit); - foreach (var measurement in Results) - { - if (Double.IsInfinity(measurement.MeasuredResult)) - continue; - - if (measurement.Definition is ColumnDefinition column) - { - if (!onlyFixed && column.Width.IsAbsolute) - { - onlyFixed = true; - result = measurement.MeasuredResult; - } - else if (onlyFixed == column.Width.IsAbsolute) - result = Math.Max(result, measurement.MeasuredResult); - - result = Math.Max(result, column.MinWidth); - } - if (measurement.Definition is RowDefinition row) - { - if (!onlyFixed && row.Height.IsAbsolute) - { - onlyFixed = true; - result = measurement.MeasuredResult; - } - else if (onlyFixed == row.Height.IsAbsolute) - result = Math.Max(result, measurement.MeasuredResult); - - result = Math.Max(result, row.MinHeight); - } - } - - return result; + return visitor.Length; } - } private readonly AvaloniaList _measurementCaches; @@ -308,7 +317,7 @@ namespace Avalonia.Controls if (cache == null) throw new AvaloniaInternalException( - $"InvalidateMeasureImpl - called with a grid not present in the internal cache"); + $"SharedSizeScopeHost: InvalidateMeasureImpl - called with a grid not present in the internal cache"); // already invalidated the cache, early out. if (cache.MeasurementState == MeasurementState.Invalidated) @@ -340,7 +349,8 @@ namespace Avalonia.Controls internal void UpdateMeasureStatus(Grid grid, GridLayout.MeasureResult rowResult, GridLayout.MeasureResult columnResult) { var cache = _measurementCaches.FirstOrDefault(mc => ReferenceEquals(mc.Grid, grid)); - Debug.Assert(cache != null); + if (cache == null) + throw new AvaloniaInternalException("SharedSizeScopeHost: Attempted to update measurement status for a grid that wasn't registered!"); cache.UpdateMeasureResult(rowResult, columnResult); } @@ -386,13 +396,15 @@ namespace Avalonia.Controls rowDesiredLength, rowResult.GreedyDesiredLength,//?? rowConventions, - rowLengths), + rowLengths, + rowResult.MinLengths), new GridLayout.MeasureResult( columnResult.ContainerLength, columnDesiredLength, columnResult.GreedyDesiredLength, //?? columnConventions, - columnLengths) + columnLengths, + columnResult.MinLengths) ); } @@ -440,7 +452,8 @@ namespace Avalonia.Controls if (string.IsNullOrEmpty(scopeName)) return; - Debug.Assert(_groups.TryGetValue(scopeName, out var group)); + if (!_groups.TryGetValue(scopeName, out var group)) + throw new AvaloniaInternalException($"SharedSizeScopeHost: The scope {scopeName} wasn't found in the shared size scope"); group.Remove(result); if (!group.Results.Any()) @@ -471,7 +484,9 @@ namespace Avalonia.Controls internal void RegisterGrid(Grid toAdd) { - Debug.Assert(!_measurementCaches.Any(mc => ReferenceEquals(mc.Grid, toAdd))); + if (_measurementCaches.Any(mc => ReferenceEquals(mc.Grid, toAdd))) + throw new AvaloniaInternalException("SharedSizeScopeHost: tried to register a grid twice!"); + var cache = new MeasurementCache(toAdd); _measurementCaches.Add(cache); AddGridToScopes(cache); @@ -480,11 +495,17 @@ namespace Avalonia.Controls internal void UnegisterGrid(Grid toRemove) { var cache = _measurementCaches.FirstOrDefault(mc => ReferenceEquals(mc.Grid, toRemove)); + if (cache == null) + throw new AvaloniaInternalException("SharedSizeScopeHost: tried to unregister a grid that wasn't registered before!"); - Debug.Assert(cache != null); _measurementCaches.Remove(cache); RemoveGridFromScopes(cache); cache.Dispose(); } + + internal bool ParticipatesInScope(Grid toCheck) + { + return _measurementCaches.FirstOrDefault(mc => ReferenceEquals(mc.Grid, toCheck))?.Results.Any() ?? false; + } } } From a33f5cb4dd5fb1410d26c31133090275b4edf480 Mon Sep 17 00:00:00 2001 From: Wojciech Krysiak Date: Sat, 27 Oct 2018 12:42:42 +0200 Subject: [PATCH 025/374] Some unit tests, bugfixes and refactorings. --- src/Avalonia.Controls/ColumnDefinition.cs | 2 +- src/Avalonia.Controls/Grid.cs | 159 ++++---- src/Avalonia.Controls/GridSplitter.cs | 6 + .../Utils/SharedSizeScopeHost.cs | 340 ++++++++++++------ .../SharedSizeScopeTests.cs | 191 ++++++++++ 5 files changed, 532 insertions(+), 166 deletions(-) create mode 100644 tests/Avalonia.Controls.UnitTests/SharedSizeScopeTests.cs diff --git a/src/Avalonia.Controls/ColumnDefinition.cs b/src/Avalonia.Controls/ColumnDefinition.cs index a6b34f8a16..d316881a05 100644 --- a/src/Avalonia.Controls/ColumnDefinition.cs +++ b/src/Avalonia.Controls/ColumnDefinition.cs @@ -88,4 +88,4 @@ namespace Avalonia.Controls set { SetValue(WidthProperty, value); } } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Controls/Grid.cs b/src/Avalonia.Controls/Grid.cs index 7bda4140a3..176c8cdb89 100644 --- a/src/Avalonia.Controls/Grid.cs +++ b/src/Avalonia.Controls/Grid.cs @@ -62,8 +62,8 @@ namespace Avalonia.Controls /// Defines the SharedSizeScopeHost private property. /// The ampersands are used to make accessing the property via xaml inconvenient. /// - private static readonly AttachedProperty s_sharedSizeScopeHostProperty = - AvaloniaProperty.RegisterAttached("&&SharedSizeScopeHost", null); + internal static readonly AttachedProperty s_sharedSizeScopeHostProperty = + AvaloniaProperty.RegisterAttached("&&SharedSizeScopeHost"); private ColumnDefinitions _columnDefinitions; @@ -81,49 +81,6 @@ namespace Avalonia.Controls this.DetachedFromVisualTree += Grid_DetachedFromVisualTree; } - private void Grid_AttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e) - { - var scope = - new Control[] { this }.Concat(this.GetVisualAncestors().OfType()) - .FirstOrDefault(c => c.GetValue(IsSharedSizeScopeProperty)); - - if (_sharedSizeHost != null) - throw new AvaloniaInternalException("Shared size scope already present when attaching to visual tree!"); - - if (scope != null) - { - _sharedSizeHost = scope.GetValue(s_sharedSizeScopeHostProperty); - _sharedSizeHost.RegisterGrid(this); - } - } - - private void Grid_DetachedFromVisualTree(object sender, VisualTreeAttachmentEventArgs e) - { - _sharedSizeHost?.UnegisterGrid(this); - _sharedSizeHost = null; - } - - private static void IsSharedSizeScopeChanged(Control source, AvaloniaPropertyChangedEventArgs arg2) - { - var shouldDispose = (arg2.OldValue is bool d) && d; - if (shouldDispose) - { - var host = source.GetValue(s_sharedSizeScopeHostProperty) as SharedSizeScopeHost; - if (host == null) - throw new AvaloniaInternalException("SharedScopeHost wasn't set when IsSharedSizeScope was true!"); - host.Dispose(); - source.SetValue(s_sharedSizeScopeHostProperty, null); - } - - var shouldAssign = (arg2.NewValue is bool a) && a; - if (shouldAssign) - { - if (source.GetValue(s_sharedSizeScopeHostProperty) != null) - throw new AvaloniaInternalException("SharedScopeHost was already set when IsSharedSizeScope is only now being set to true!"); - source.SetValue(s_sharedSizeScopeHostProperty, new SharedSizeScopeHost(source)); - } - } - /// /// Gets or sets the columns definitions for the grid. /// @@ -400,7 +357,7 @@ namespace Avalonia.Controls var columnCache = _columnMeasureCache; if (_sharedSizeHost?.ParticipatesInScope(this) ?? false) - { + { (rowCache, columnCache) = _sharedSizeHost.HandleArrange(this, _rowMeasureCache, _columnMeasureCache); rowCache = rowLayout.Measure(finalSize.Height, rowCache.LeanLengthList); @@ -438,6 +395,73 @@ namespace Avalonia.Controls return finalSize; } + /// + /// Tests whether this grid belongs to a shared size scope. + /// + /// True if the grid is registered in a shared size scope. + internal bool HasSharedSizeScope() + { + return _sharedSizeHost != null; + } + + /// + /// Called when the SharedSizeScope for a given grid has changed. + /// Unregisters the grid from it's current scope and finds a new one (if any) + /// + /// + /// This method, while not efficient, correctly handles nested scopes, with any order of scope changes. + /// + internal void SharedScopeChanged() + { + _sharedSizeHost?.UnegisterGrid(this); + + _sharedSizeHost = null; + var scope = this.GetVisualAncestors().OfType() + .FirstOrDefault(c => c.GetValue(IsSharedSizeScopeProperty)); + + if (scope != null) + { + _sharedSizeHost = scope.GetValue(s_sharedSizeScopeHostProperty); + _sharedSizeHost.RegisterGrid(this); + } + + InvalidateMeasure(); + } + + /// + /// Callback when a grid is attached to the visual tree. Finds the innermost SharedSizeScope and registers the grid + /// in it. + /// + /// The source of the event. + /// The event arguments. + private void Grid_AttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e) + { + var scope = + new Control[] { this }.Concat(this.GetVisualAncestors().OfType()) + .FirstOrDefault(c => c.GetValue(IsSharedSizeScopeProperty)); + + if (_sharedSizeHost != null) + throw new AvaloniaInternalException("Shared size scope already present when attaching to visual tree!"); + + if (scope != null) + { + _sharedSizeHost = scope.GetValue(s_sharedSizeScopeHostProperty); + _sharedSizeHost.RegisterGrid(this); + } + } + + /// + /// Callback when a grid is detached from the visual tree. Unregisters the grid from its SharedSizeScope if any. + /// + /// The source of the event. + /// The event arguments. + private void Grid_DetachedFromVisualTree(object sender, VisualTreeAttachmentEventArgs e) + { + _sharedSizeHost?.UnegisterGrid(this); + _sharedSizeHost = null; + } + + /// /// Get the safe column/columnspan and safe row/rowspan. /// This method ensures that none of the children has a column/row outside the bounds of the definitions. @@ -515,25 +539,40 @@ namespace Avalonia.Controls return value; } - internal bool HasSharedSizeGroups() - { - return ColumnDefinitions.Any(cd => !string.IsNullOrEmpty(cd.SharedSizeGroup)) || - RowDefinitions.Any(rd => !string.IsNullOrEmpty(rd.SharedSizeGroup)); - } - - internal void SharedScopeChanged() + /// + /// Called when the value of changes for a control. + /// + /// The control that triggered the change. + /// Change arguments. + private static void IsSharedSizeScopeChanged(Control source, AvaloniaPropertyChangedEventArgs arg2) { - _sharedSizeHost = null; - var scope = this.GetVisualAncestors().OfType() - .FirstOrDefault(c => c.GetValue(IsSharedSizeScopeProperty)); + var shouldDispose = (arg2.OldValue is bool d) && d; + if (shouldDispose) + { + var host = source.GetValue(s_sharedSizeScopeHostProperty) as SharedSizeScopeHost; + if (host == null) + throw new AvaloniaInternalException("SharedScopeHost wasn't set when IsSharedSizeScope was true!"); + host.Dispose(); + source.ClearValue(s_sharedSizeScopeHostProperty); + } - if (scope != null) + var shouldAssign = (arg2.NewValue is bool a) && a; + if (shouldAssign) { - _sharedSizeHost = scope.GetValue(s_sharedSizeScopeHostProperty); - _sharedSizeHost.RegisterGrid(this); + if (source.GetValue(s_sharedSizeScopeHostProperty) != null) + throw new AvaloniaInternalException("SharedScopeHost was already set when IsSharedSizeScope is only now being set to true!"); + source.SetValue(s_sharedSizeScopeHostProperty, new SharedSizeScopeHost()); } - InvalidateMeasure(); + // if the scope has changed, notify the descendant grids that they need to update. + if (source.GetVisualRoot() != null && shouldAssign || shouldDispose) + { + var participatingGrids = new[] { source }.Concat(source.GetVisualDescendants()).OfType(); + + foreach (var grid in participatingGrids) + grid.SharedScopeChanged(); + + } } } } diff --git a/src/Avalonia.Controls/GridSplitter.cs b/src/Avalonia.Controls/GridSplitter.cs index 4d38d7389e..304a760216 100644 --- a/src/Avalonia.Controls/GridSplitter.cs +++ b/src/Avalonia.Controls/GridSplitter.cs @@ -44,6 +44,12 @@ namespace Avalonia.Controls protected override void OnDragDelta(VectorEventArgs e) { + // WPF doesn't change anything when spliter is in the last row/column + // but resizes the splitter row/column when it's the first one. + // this is different, but more internally consistent. + if (_prevDefinition == null || _nextDefinition == null) + return; + var delta = _orientation == Orientation.Vertical ? e.Vector.X : e.Vector.Y; double max; double min; diff --git a/src/Avalonia.Controls/Utils/SharedSizeScopeHost.cs b/src/Avalonia.Controls/Utils/SharedSizeScopeHost.cs index 5948bd7f19..ec9c0b3eca 100644 --- a/src/Avalonia.Controls/Utils/SharedSizeScopeHost.cs +++ b/src/Avalonia.Controls/Utils/SharedSizeScopeHost.cs @@ -13,6 +13,12 @@ using Avalonia.VisualTree; namespace Avalonia.Controls { + /// + /// Shared size scope implementation. + /// Shares the size information between participating grids. + /// An instance of this class is attached to every that has its + /// IsSharedSizeScope property set to true. + /// internal sealed class SharedSizeScopeHost : IDisposable { private enum MeasurementState @@ -22,6 +28,12 @@ namespace Avalonia.Controls Cached } + /// + /// Class containing the measured rows/columns for a single grid. + /// Monitors changes to the row/column collections as well as the SharedSizeGroup changes + /// for the individual items in those collections. + /// Notifies the of SharedSizeGroup changes. + /// private sealed class MeasurementCache : IDisposable { readonly CompositeDisposable _subscriptions; @@ -129,6 +141,12 @@ namespace Avalonia.Controls } } + + /// + /// Updates the Results collection with Grid Measure results. + /// + /// Result of the GridLayout.Measure method for the RowDefinitions in the grid. + /// Result of the GridLayout.Measure method for the ColumnDefinitions in the grid. public void UpdateMeasureResult(GridLayout.MeasureResult rowResult, GridLayout.MeasureResult columnResult) { MeasurementState = MeasurementState.Cached; @@ -145,9 +163,16 @@ namespace Avalonia.Controls } } + /// + /// Clears the measurement cache, in preparation for the Measure pass. + /// public void InvalidateMeasure() { + var newItems = new List(); + var oldItems = new List(); + MeasurementState = MeasurementState.Invalidated; + Results.ForEach(r => { r.MeasuredResult = double.NaN; @@ -155,18 +180,38 @@ namespace Avalonia.Controls }); } + /// + /// Clears the subscriptions. + /// public void Dispose() { _subscriptions.Dispose(); _groupChanged.OnCompleted(); } + /// + /// Gets the for which this cache has been created. + /// public Grid Grid { get; } + + /// + /// Gets the of this cache. + /// public MeasurementState MeasurementState { get; private set; } + /// + /// Gets the list of instances. + /// + /// + /// The list is a 1-1 map of the concatenation of RowDefinitions and ColumnDefinitions + /// public List Results { get; private set; } } + + /// + /// Class containing the Measure result for a single Row/Column in a grid. + /// private class MeasurementResult { public MeasurementResult(Grid owningGrid, DefinitionBase definition) @@ -176,12 +221,35 @@ namespace Avalonia.Controls MeasuredResult = double.NaN; } + /// + /// Gets the / related to this + /// public DefinitionBase Definition { get; } + + /// + /// Gets or sets the actual result of the Measure operation for this column. + /// public double MeasuredResult { get; set; } + + /// + /// Gets or sets the Minimum constraint for a Row/Column - relevant for star Rows/Columns in unconstrained grids. + /// public double MinLength { get; set; } + + /// + /// Gets or sets the that this result belongs to. + /// public Group SizeGroup { get; set; } + + /// + /// Gets the Grid that is the parent of the Row/Column + /// public Grid OwningGrid { get; } + /// + /// Calculates the effective length that this Row/Column wishes to enforce in the SharedSizeGroup. + /// + /// A tuple of length and the priority in the shared size group. public (double length, int priority) GetPriorityLength() { var length = (Definition as ColumnDefinition)?.Width ?? ((RowDefinition)Definition).Height; @@ -196,12 +264,24 @@ namespace Avalonia.Controls } } - + /// + /// Visitor class used to gather the final length for a given SharedSizeGroup. + /// + /// + /// The values are applied according to priorities defined in . + /// private class LentgthGatherer { + /// + /// Gets the final Length to be applied to every Row/Column in a SharedSizeGroup + /// public double Length { get; private set; } private int gatheredPriority = 6; + /// + /// Visits the applying the result of to its internal cache. + /// + /// The instance to visit. public void Visit(MeasurementResult result) { var (length, priority) = result.GetPriorityLength(); @@ -221,12 +301,17 @@ namespace Avalonia.Controls } } - + /// + /// Representation of a SharedSizeGroup, containing Rows/Columns with the same SharedSizeGroup property value. + /// private class Group { private double? cachedResult; private List _results = new List(); + /// + /// Gets the name of the SharedSizeGroup. + /// public string Name { get; } public Group(string name) @@ -234,17 +319,29 @@ namespace Avalonia.Controls Name = name; } - public bool IsFixed { get; set; } - + /// + /// Gets the collection of the instances. + /// public IReadOnlyList Results => _results; + /// + /// Gets the final, calculated length for all Rows/Columns in the SharedSizeGroup. + /// public double CalculatedLength => (cachedResult ?? (cachedResult = Gather())).Value; + /// + /// Clears the previously cached result in preparation for measurement. + /// public void Reset() { cachedResult = null; } + /// + /// Ads a measurement result to this group and sets it's property + /// to this instance. + /// + /// The to include in this group. public void Add(MeasurementResult result) { if (_results.Contains(result)) @@ -255,6 +352,10 @@ namespace Avalonia.Controls _results.Add(result); } + /// + /// Removes the measurement result from this group and clears its value. + /// + /// The to clear. public void Remove(MeasurementResult result) { if (!_results.Contains(result)) @@ -275,30 +376,64 @@ namespace Avalonia.Controls } } - private readonly AvaloniaList _measurementCaches; - + private readonly AvaloniaList _measurementCaches = new AvaloniaList(); private readonly Dictionary _groups = new Dictionary(); + private bool _invalidating; - public SharedSizeScopeHost(Control scope) + /// + /// Removes the SharedSizeScope and notifies all affected grids of the change. + /// + public void Dispose() { - _measurementCaches = GetParticipatingGrids(scope); + while (_measurementCaches.Any()) + _measurementCaches[0].Grid.SharedScopeChanged(); + } - foreach (var cache in _measurementCaches) - { - cache.Grid.InvalidateMeasure(); - AddGridToScopes(cache); + /// + /// Registers the grid in this SharedSizeScope, to be called when the grid is added to the visual tree. + /// + /// The to add to this scope. + internal void RegisterGrid(Grid toAdd) + { + if (_measurementCaches.Any(mc => ReferenceEquals(mc.Grid, toAdd))) + throw new AvaloniaInternalException("SharedSizeScopeHost: tried to register a grid twice!"); - } + var cache = new MeasurementCache(toAdd); + _measurementCaches.Add(cache); + AddGridToScopes(cache); } - void SharedGroupChanged((string oldName, string newName, MeasurementResult result) change) + /// + /// Removes the registration for a grid in this SharedSizeScope. + /// + /// The to remove. + internal void UnegisterGrid(Grid toRemove) { - RemoveFromGroup(change.oldName, change.result); - AddToGroup(change.newName, change.result); + var cache = _measurementCaches.FirstOrDefault(mc => ReferenceEquals(mc.Grid, toRemove)); + if (cache == null) + throw new AvaloniaInternalException("SharedSizeScopeHost: tried to unregister a grid that wasn't registered before!"); + + _measurementCaches.Remove(cache); + RemoveGridFromScopes(cache); + cache.Dispose(); } - private bool _invalidating; + /// + /// Helper method to check if a grid needs to forward its Mesure results to, and requrest Arrange results from this scope. + /// + /// The that should be checked. + /// True if the grid should forward its calls. + internal bool ParticipatesInScope(Grid toCheck) + { + return _measurementCaches.FirstOrDefault(mc => ReferenceEquals(mc.Grid, toCheck)) + ?.Results.Any(r => r.SizeGroup != null) ?? false; + } + /// + /// Notifies the SharedSizeScope that a grid had requested its measurement to be invalidated. + /// Forwards the same call to all affected grids in this scope. + /// + /// The that had it's Measure invalidated. internal void InvalidateMeasure(Grid grid) { // prevent stack overflow @@ -311,6 +446,40 @@ namespace Avalonia.Controls _invalidating = false; } + /// + /// Updates the measurement cache with the results of the measurement pass. + /// + /// The that has been measured. + /// Measurement result for the grid's + /// Measurement result for the grid's + internal void UpdateMeasureStatus(Grid grid, GridLayout.MeasureResult rowResult, GridLayout.MeasureResult columnResult) + { + var cache = _measurementCaches.FirstOrDefault(mc => ReferenceEquals(mc.Grid, grid)); + if (cache == null) + throw new AvaloniaInternalException("SharedSizeScopeHost: Attempted to update measurement status for a grid that wasn't registered!"); + + cache.UpdateMeasureResult(rowResult, columnResult); + } + + /// + /// Calculates the measurement result including the impact of any SharedSizeGroups that might affect this grid. + /// + /// The that is being Arranged + /// The 's cached measurement result. + /// The 's cached measurement result. + /// Row and column measurement result updated with the SharedSizeScope constraints. + internal (GridLayout.MeasureResult, GridLayout.MeasureResult) HandleArrange(Grid grid, GridLayout.MeasureResult rowResult, GridLayout.MeasureResult columnResult) + { + return ( + Arrange(grid.RowDefinitions, rowResult), + Arrange(grid.ColumnDefinitions, columnResult) + ); + } + + /// + /// Invalidates the measure of all grids affected by the SharedSizeGroups contained within. + /// + /// The that is being invalidated. private void InvalidateMeasureImpl(Grid grid) { var cache = _measurementCaches.FirstOrDefault(mc => ReferenceEquals(mc.Grid, grid)); @@ -323,6 +492,10 @@ namespace Avalonia.Controls if (cache.MeasurementState == MeasurementState.Invalidated) return; + // we won't calculate, so we should not invalidate. + if (!ParticipatesInScope(grid)) + return; + cache.InvalidateMeasure(); // maybe there is a condition to only call arrange on some of the calls? @@ -346,16 +519,24 @@ namespace Avalonia.Controls } } - internal void UpdateMeasureStatus(Grid grid, GridLayout.MeasureResult rowResult, GridLayout.MeasureResult columnResult) + /// + /// callback notifying the scope that a has changed its + /// SharedSizeGroup + /// + /// Old and New name (either can be null) of the SharedSizeGroup, as well as the result. + private void SharedGroupChanged((string oldName, string newName, MeasurementResult result) change) { - var cache = _measurementCaches.FirstOrDefault(mc => ReferenceEquals(mc.Grid, grid)); - if (cache == null) - throw new AvaloniaInternalException("SharedSizeScopeHost: Attempted to update measurement status for a grid that wasn't registered!"); - - cache.UpdateMeasureResult(rowResult, columnResult); + RemoveFromGroup(change.oldName, change.result); + AddToGroup(change.newName, change.result); } - (List, List, double) Arrange(IReadOnlyList definitions, GridLayout.MeasureResult measureResult) + /// + /// Handles the impact of SharedSizeGroups on the Arrange of / + /// + /// Rows/Columns that were measured + /// The initial measurement result. + /// Modified measure result + private GridLayout.MeasureResult Arrange(IReadOnlyList definitions, GridLayout.MeasureResult measureResult) { var conventions = measureResult.LeanLengthList.ToList(); var lengths = measureResult.LengthList.ToList(); @@ -363,6 +544,8 @@ namespace Avalonia.Controls for (int i = 0; i < definitions.Count; i++) { var definition = definitions[i]; + + // for empty SharedSizeGroups pass on unmodified result. if (string.IsNullOrEmpty(definition.SharedSizeGroup)) { desiredLength += measureResult.LengthList[i]; @@ -370,7 +553,7 @@ namespace Avalonia.Controls } var group = _groups[definition.SharedSizeGroup]; - + // Length calculated over all Definitions participating in a SharedSizeGroup. var length = group.CalculatedLength; conventions[i] = new GridLayout.LengthConvention( @@ -382,33 +565,19 @@ namespace Avalonia.Controls desiredLength += length; } - return (conventions, lengths, desiredLength); - } - - internal (GridLayout.MeasureResult, GridLayout.MeasureResult) HandleArrange(Grid grid, GridLayout.MeasureResult rowResult, GridLayout.MeasureResult columnResult) - { - var (rowConventions, rowLengths, rowDesiredLength) = Arrange(grid.RowDefinitions, rowResult); - var (columnConventions, columnLengths, columnDesiredLength) = Arrange(grid.ColumnDefinitions, columnResult); - - return ( - new GridLayout.MeasureResult( - rowResult.ContainerLength, - rowDesiredLength, - rowResult.GreedyDesiredLength,//?? - rowConventions, - rowLengths, - rowResult.MinLengths), - new GridLayout.MeasureResult( - columnResult.ContainerLength, - columnDesiredLength, - columnResult.GreedyDesiredLength, //?? - columnConventions, - columnLengths, - columnResult.MinLengths) - ); + return new GridLayout.MeasureResult( + measureResult.ContainerLength, + desiredLength, + measureResult.GreedyDesiredLength,//?? + conventions, + lengths, + measureResult.MinLengths); } - + /// + /// Adds all measurement results for a grid to their repsective scopes. + /// + /// The for a grid to be added. private void AddGridToScopes(MeasurementCache cache) { cache.GroupChanged.Subscribe(SharedGroupChanged); @@ -420,6 +589,12 @@ namespace Avalonia.Controls } } + /// + /// Handles adding the to a SharedSizeGroup. + /// Does nothing for empty SharedSizeGroups. + /// + /// The name (can be null or empty) of the group to add the to. + /// The to add to a scope. private void AddToGroup(string scopeName, MeasurementResult result) { if (string.IsNullOrEmpty(scopeName)) @@ -428,16 +603,13 @@ namespace Avalonia.Controls if (!_groups.TryGetValue(scopeName, out var group)) _groups.Add(scopeName, group = new Group(scopeName)); - group.IsFixed |= IsFixed(result.Definition); - group.Add(result); } - private bool IsFixed(DefinitionBase definition) - { - return ((definition as ColumnDefinition)?.Width ?? ((RowDefinition)definition).Height).IsAbsolute; - } - + /// + /// Removes all measurement results for a grid from their respective scopes. + /// + /// The for a grid to be removed. private void RemoveGridFromScopes(MeasurementCache cache) { foreach (var result in cache.Results) @@ -447,6 +619,12 @@ namespace Avalonia.Controls } } + /// + /// Handles removing the from a SharedSizeGroup. + /// Does nothing for empty SharedSizeGroups. + /// + /// The name (can be null or empty) of the group to remove the from. + /// The to remove from a scope. private void RemoveFromGroup(string scopeName, MeasurementResult result) { if (string.IsNullOrEmpty(scopeName)) @@ -458,54 +636,6 @@ namespace Avalonia.Controls group.Remove(result); if (!group.Results.Any()) _groups.Remove(scopeName); - else - { - group.IsFixed = group.Results.Select(r => r.Definition).Any(IsFixed); - } - } - - private static AvaloniaList GetParticipatingGrids(Control scope) - { - var result = scope.GetVisualDescendants().OfType(); - - return new AvaloniaList( - result.Where(g => g.HasSharedSizeGroups()) - .Select(g => new MeasurementCache(g))); - } - - public void Dispose() - { - foreach (var cache in _measurementCaches) - { - cache.Grid.SharedScopeChanged(); - cache.Dispose(); - } - } - - internal void RegisterGrid(Grid toAdd) - { - if (_measurementCaches.Any(mc => ReferenceEquals(mc.Grid, toAdd))) - throw new AvaloniaInternalException("SharedSizeScopeHost: tried to register a grid twice!"); - - var cache = new MeasurementCache(toAdd); - _measurementCaches.Add(cache); - AddGridToScopes(cache); - } - - internal void UnegisterGrid(Grid toRemove) - { - var cache = _measurementCaches.FirstOrDefault(mc => ReferenceEquals(mc.Grid, toRemove)); - if (cache == null) - throw new AvaloniaInternalException("SharedSizeScopeHost: tried to unregister a grid that wasn't registered before!"); - - _measurementCaches.Remove(cache); - RemoveGridFromScopes(cache); - cache.Dispose(); - } - - internal bool ParticipatesInScope(Grid toCheck) - { - return _measurementCaches.FirstOrDefault(mc => ReferenceEquals(mc.Grid, toCheck))?.Results.Any() ?? false; } } } diff --git a/tests/Avalonia.Controls.UnitTests/SharedSizeScopeTests.cs b/tests/Avalonia.Controls.UnitTests/SharedSizeScopeTests.cs new file mode 100644 index 0000000000..0d0b9c2891 --- /dev/null +++ b/tests/Avalonia.Controls.UnitTests/SharedSizeScopeTests.cs @@ -0,0 +1,191 @@ +using System.Linq; +using Avalonia.Controls.Primitives; +using Avalonia.Input; +using Avalonia.Platform; +using Avalonia.UnitTests; + +using Moq; + +using Xunit; + +namespace Avalonia.Controls.UnitTests +{ + public class SharedSizeScopeTests + { + public SharedSizeScopeTests() + { + } + + [Fact] + public void All_Descendant_Grids_Are_Registered_When_Added_After_Setting_Scope() + { + var grids = new[] { new Grid(), new Grid(), new Grid() }; + var scope = new Panel(); + scope.Children.AddRange(grids); + + var root = new TestRoot(); + root.SetValue(Grid.IsSharedSizeScopeProperty, true); + root.Child = scope; + + Assert.All(grids, g => Assert.True(g.HasSharedSizeScope())); + } + + [Fact] + public void All_Descendant_Grids_Are_Registered_When_Setting_Scope() + { + var grids = new[] { new Grid(), new Grid(), new Grid() }; + var scope = new Panel(); + scope.Children.AddRange(grids); + + var root = new TestRoot(); + root.Child = scope; + root.SetValue(Grid.IsSharedSizeScopeProperty, true); + + Assert.All(grids, g => Assert.True(g.HasSharedSizeScope())); + } + + [Fact] + public void All_Descendant_Grids_Are_Unregistered_When_Resetting_Scope() + { + var grids = new[] { new Grid(), new Grid(), new Grid() }; + var scope = new Panel(); + scope.Children.AddRange(grids); + + var root = new TestRoot(); + root.SetValue(Grid.IsSharedSizeScopeProperty, true); + root.Child = scope; + + Assert.All(grids, g => Assert.True(g.HasSharedSizeScope())); + root.SetValue(Grid.IsSharedSizeScopeProperty, false); + Assert.All(grids, g => Assert.False(g.HasSharedSizeScope())); + Assert.Equal(null, root.GetValue(Grid.s_sharedSizeScopeHostProperty)); + } + + [Fact] + public void Size_Is_Propagated_Between_Grids() + { + var grids = new[] { CreateGrid("A", null),CreateGrid(("A",new GridLength(30)), (null, new GridLength()))}; + var scope = new Panel(); + scope.Children.AddRange(grids); + + var root = new TestRoot(); + root.SetValue(Grid.IsSharedSizeScopeProperty, true); + root.Child = scope; + + root.Measure(new Size(50, 50)); + root.Arrange(new Rect(new Point(), new Point(50, 50))); + Assert.Equal(30, grids[0].ColumnDefinitions[0].ActualWidth); + } + + [Fact] + public void Size_Propagation_Is_Constrained_To_Innermost_Scope() + { + var grids = new[] { CreateGrid("A", null), CreateGrid(("A", new GridLength(30)), (null, new GridLength())) }; + var innerScope = new Panel(); + innerScope.Children.AddRange(grids); + innerScope.SetValue(Grid.IsSharedSizeScopeProperty, true); + + var outerGrid = CreateGrid(("A", new GridLength(0))); + var outerScope = new Panel(); + outerScope.Children.AddRange(new[] { outerGrid, innerScope }); + + var root = new TestRoot(); + root.SetValue(Grid.IsSharedSizeScopeProperty, true); + root.Child = outerScope; + + root.Measure(new Size(50, 50)); + root.Arrange(new Rect(new Point(), new Point(50, 50))); + Assert.Equal(0, outerGrid.ColumnDefinitions[0].ActualWidth); + } + + [Fact] + public void Size_Is_Propagated_Between_Rows_And_Columns() + { + var grid = new Grid + { + ColumnDefinitions = new ColumnDefinitions("*,30"), + RowDefinitions = new RowDefinitions("*,10") + }; + + grid.ColumnDefinitions[1].SharedSizeGroup = "A"; + grid.RowDefinitions[1].SharedSizeGroup = "A"; + + var root = new TestRoot(); + root.SetValue(Grid.IsSharedSizeScopeProperty, true); + root.Child = grid; + + root.Measure(new Size(50, 50)); + root.Arrange(new Rect(new Point(), new Point(50, 50))); + Assert.Equal(30, grid.RowDefinitions[1].ActualHeight); + } + + [Fact] + public void Size_Group_Changes_Are_Tracked() + { + var grids = new[] { + CreateGrid((null, new GridLength(0, GridUnitType.Auto)), (null, new GridLength())), + CreateGrid(("A", new GridLength(30)), (null, new GridLength())) }; + var scope = new Panel(); + scope.Children.AddRange(grids); + + var root = new TestRoot(); + root.SetValue(Grid.IsSharedSizeScopeProperty, true); + root.Child = scope; + + root.Measure(new Size(50, 50)); + root.Arrange(new Rect(new Point(), new Point(50, 50))); + Assert.Equal(0, grids[0].ColumnDefinitions[0].ActualWidth); + + grids[0].ColumnDefinitions[0].SharedSizeGroup = "A"; + + root.Measure(new Size(51, 51)); + root.Arrange(new Rect(new Point(), new Point(51, 51))); + Assert.Equal(30, grids[0].ColumnDefinitions[0].ActualWidth); + + grids[0].ColumnDefinitions[0].SharedSizeGroup = null; + + root.Measure(new Size(52, 52)); + root.Arrange(new Rect(new Point(), new Point(52, 52))); + Assert.Equal(0, grids[0].ColumnDefinitions[0].ActualWidth); + } + + // grid creators + private Grid CreateGrid(params string[] columnGroups) + { + return CreateGrid(columnGroups.Select(s => (s, ColumnDefinition.WidthProperty.GetDefaultValue(typeof(ColumnDefinition)))).ToArray()); + } + + private Grid CreateGrid(params (string name, GridLength width)[] columns) + { + return CreateGrid(columns.Select(c => + (c.name, c.width, ColumnDefinition.MinWidthProperty.GetDefaultValue(typeof(ColumnDefinition)))).ToArray()); + } + + private Grid CreateGrid(params (string name, GridLength width, double minWidth)[] columns) + { + return CreateGrid(columns.Select(c => + (c.name, c.width, c.minWidth, ColumnDefinition.MaxWidthProperty.GetDefaultValue(typeof(ColumnDefinition)))).ToArray()); + } + + private Grid CreateGrid(params (string name, GridLength width, double minWidth, double maxWidth)[] columns) + { + var columnDefinitions = new ColumnDefinitions(); + + columnDefinitions.AddRange( + columns.Select(c => new ColumnDefinition + { + SharedSizeGroup = c.name, + Width = c.width, + MinWidth = c.minWidth, + MaxWidth = c.maxWidth + }) + ); + var grid = new Grid + { + ColumnDefinitions = columnDefinitions + }; + + return grid; + } + } +} From 0d7e1936931c2eb3b381aa7aa4f29e85244f45c8 Mon Sep 17 00:00:00 2001 From: Wojciech Krysiak Date: Mon, 29 Oct 2018 22:08:29 +0100 Subject: [PATCH 026/374] More tests, futureproofing + 2 bugfixes --- src/Avalonia.Controls/Grid.cs | 2 + .../Utils/SharedSizeScopeHost.cs | 12 ++- .../SharedSizeScopeTests.cs | 93 +++++++++++++++++++ 3 files changed, 106 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Grid.cs b/src/Avalonia.Controls/Grid.cs index 176c8cdb89..b51583d8b3 100644 --- a/src/Avalonia.Controls/Grid.cs +++ b/src/Avalonia.Controls/Grid.cs @@ -105,6 +105,7 @@ namespace Avalonia.Controls _columnDefinitions = value; _columnDefinitions.TrackItemPropertyChanged(_ => InvalidateMeasure()); + _columnDefinitions.CollectionChanged += (_, __) => InvalidateMeasure(); } } @@ -132,6 +133,7 @@ namespace Avalonia.Controls _rowDefinitions = value; _rowDefinitions.TrackItemPropertyChanged(_ => InvalidateMeasure()); + _rowDefinitions.CollectionChanged += (_, __) => InvalidateMeasure(); } } diff --git a/src/Avalonia.Controls/Utils/SharedSizeScopeHost.cs b/src/Avalonia.Controls/Utils/SharedSizeScopeHost.cs index ec9c0b3eca..8553165e4b 100644 --- a/src/Avalonia.Controls/Utils/SharedSizeScopeHost.cs +++ b/src/Avalonia.Controls/Utils/SharedSizeScopeHost.cs @@ -52,6 +52,7 @@ namespace Avalonia.Controls grid.RowDefinitions.CollectionChanged += DefinitionsCollectionChanged; grid.ColumnDefinitions.CollectionChanged += DefinitionsCollectionChanged; + _subscriptions = new CompositeDisposable( Disposable.Create(() => grid.RowDefinitions.CollectionChanged -= DefinitionsCollectionChanged), Disposable.Create(() => grid.ColumnDefinitions.CollectionChanged -= DefinitionsCollectionChanged), @@ -60,6 +61,13 @@ namespace Avalonia.Controls } + // method to be hooked up once RowDefinitions/ColumnDefinitions collections can be replaced on a grid + private void DefinitionsChanged(object sender, AvaloniaPropertyChangedEventArgs e) + { + // route to collection changed as a Reset. + DefinitionsCollectionChanged(null, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + } + private void DefinitionPropertyChanged(Tuple propertyChanged) { if (propertyChanged.Item2.PropertyName == nameof(DefinitionBase.SharedSizeGroup)) @@ -78,7 +86,9 @@ namespace Avalonia.Controls offset = Grid.RowDefinitions.Count; var newItems = e.NewItems?.OfType().Select(db => new MeasurementResult(Grid, db)).ToList() ?? new List(); - var oldItems = Results.GetRange(e.OldStartingIndex + offset, e.OldItems?.Count ?? 0); + var oldItems = e.OldStartingIndex >= 0 + ? Results.GetRange(e.OldStartingIndex + offset, e.OldItems.Count) + : new List(); void NotifyNewItems() { diff --git a/tests/Avalonia.Controls.UnitTests/SharedSizeScopeTests.cs b/tests/Avalonia.Controls.UnitTests/SharedSizeScopeTests.cs index 0d0b9c2891..715e9da880 100644 --- a/tests/Avalonia.Controls.UnitTests/SharedSizeScopeTests.cs +++ b/tests/Avalonia.Controls.UnitTests/SharedSizeScopeTests.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Linq; using Avalonia.Controls.Primitives; using Avalonia.Input; @@ -149,6 +150,90 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(0, grids[0].ColumnDefinitions[0].ActualWidth); } + [Fact] + public void Collection_Changes_Are_Tracked() + { + var grid = CreateGrid( + ("A", new GridLength(20)), + ("A", new GridLength(30)), + ("A", new GridLength(40)), + (null, new GridLength())); + + var scope = new Panel(); + scope.Children.Add(grid); + + var root = new TestRoot(); + root.SetValue(Grid.IsSharedSizeScopeProperty, true); + root.Child = scope; + + grid.Measure(new Size(200, 200)); + grid.Arrange(new Rect(new Point(), new Point(200, 200))); + Assert.All(grid.ColumnDefinitions.Where(cd => cd.SharedSizeGroup == "A"), cd => Assert.Equal(40, cd.ActualWidth)); + + grid.ColumnDefinitions.RemoveAt(2); + + grid.Measure(new Size(200, 200)); + grid.Arrange(new Rect(new Point(), new Point(200, 200))); + Assert.All(grid.ColumnDefinitions.Where(cd => cd.SharedSizeGroup == "A"), cd => Assert.Equal(30, cd.ActualWidth)); + + grid.ColumnDefinitions.Insert(1, new ColumnDefinition { Width = new GridLength(35), SharedSizeGroup = "A" }); + + grid.Measure(new Size(200, 200)); + grid.Arrange(new Rect(new Point(), new Point(200, 200))); + Assert.All(grid.ColumnDefinitions.Where(cd => cd.SharedSizeGroup == "A"), cd => Assert.Equal(35, cd.ActualWidth)); + + grid.ColumnDefinitions[1] = new ColumnDefinition { Width = new GridLength(10), SharedSizeGroup = "A" }; + + grid.Measure(new Size(200, 200)); + grid.Arrange(new Rect(new Point(), new Point(200, 200))); + Assert.All(grid.ColumnDefinitions.Where(cd => cd.SharedSizeGroup == "A"), cd => Assert.Equal(30, cd.ActualWidth)); + + grid.ColumnDefinitions[1] = new ColumnDefinition { Width = new GridLength(50), SharedSizeGroup = "A" }; + + grid.Measure(new Size(200, 200)); + grid.Arrange(new Rect(new Point(), new Point(200, 200))); + Assert.All(grid.ColumnDefinitions.Where(cd => cd.SharedSizeGroup == "A"), cd => Assert.Equal(50, cd.ActualWidth)); + } + + [Fact] + public void Size_Priorities_Are_Maintained() + { + var sizers = new List(); + var grid = CreateGrid( + ("A", new GridLength(20)), + ("A", new GridLength(20, GridUnitType.Auto)), + ("A", new GridLength(1, GridUnitType.Star)), + ("A", new GridLength(1, GridUnitType.Star)), + (null, new GridLength())); + for (int i = 0; i < 3; i++) + sizers.Add(AddSizer(grid, i, 6 + i * 6)); + var scope = new Panel(); + scope.Children.Add(grid); + + var root = new TestRoot(); + root.SetValue(Grid.IsSharedSizeScopeProperty, true); + root.Child = scope; + + grid.Measure(new Size(100, 100)); + grid.Arrange(new Rect(new Point(), new Point(100, 100))); + // all in group are equal to the first fixed column + Assert.All(grid.ColumnDefinitions.Where(cd => cd.SharedSizeGroup == "A"), cd => Assert.Equal(20, cd.ActualWidth)); + + grid.ColumnDefinitions[0].SharedSizeGroup = null; + + grid.Measure(new Size(100, 100)); + grid.Arrange(new Rect(new Point(), new Point(100, 100))); + // all in group are equal to width (MinWidth) of the sizer in the second column + Assert.All(grid.ColumnDefinitions.Where(cd => cd.SharedSizeGroup == "A"), cd => Assert.Equal(6 + 1 * 6, cd.ActualWidth)); + + grid.ColumnDefinitions[1].SharedSizeGroup = null; + + grid.Measure(new Size(double.PositiveInfinity, 100)); + grid.Arrange(new Rect(new Point(), new Point(100, 100))); + // with no constraint star columns default to the MinWidth of the sizer in the column + Assert.All(grid.ColumnDefinitions.Where(cd => cd.SharedSizeGroup == "A"), cd => Assert.Equal(6 + 2 * 6, cd.ActualWidth)); + } + // grid creators private Grid CreateGrid(params string[] columnGroups) { @@ -187,5 +272,13 @@ namespace Avalonia.Controls.UnitTests return grid; } + + private Control AddSizer(Grid grid, int column, double size = 30) + { + var ctrl = new Control { MinWidth = size, MinHeight = size }; + ctrl.SetValue(Grid.ColumnProperty,column); + grid.Children.Add(ctrl); + return ctrl; + } } } From e4838842a11024837936a1016604f42835bb6b30 Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Fri, 2 Nov 2018 16:24:53 +0200 Subject: [PATCH 027/374] simplify Layout transform no need to be ContentControl --- .../LayoutTransformControl.cs | 39 +++++++------------ src/Avalonia.Themes.Default/DefaultTheme.xaml | 1 - .../LayoutTransformControl.xaml | 13 ------- .../LayoutTransformControlTests.cs | 18 ++------- 4 files changed, 17 insertions(+), 54 deletions(-) delete mode 100644 src/Avalonia.Themes.Default/LayoutTransformControl.xaml diff --git a/src/Avalonia.Controls/LayoutTransformControl.cs b/src/Avalonia.Controls/LayoutTransformControl.cs index 87e3853643..73d5e61e03 100644 --- a/src/Avalonia.Controls/LayoutTransformControl.cs +++ b/src/Avalonia.Controls/LayoutTransformControl.cs @@ -5,20 +5,17 @@ // http://silverlight.codeplex.com/SourceControl/changeset/view/74775#Release/Silverlight4/Source/Controls.Layout.Toolkit/LayoutTransformer/LayoutTransformer.cs // -using Avalonia.Controls.Primitives; -using Avalonia.Media; -using Avalonia.VisualTree; using System; using System.Diagnostics.CodeAnalysis; -using System.Linq; using System.Reactive.Linq; +using Avalonia.Media; namespace Avalonia.Controls { /// /// Control that implements support for transformations as if applied by LayoutTransform. /// - public class LayoutTransformControl : ContentControl + public class LayoutTransformControl : Decorator { public static readonly AvaloniaProperty LayoutTransformProperty = AvaloniaProperty.Register(nameof(LayoutTransform)); @@ -27,6 +24,9 @@ namespace Avalonia.Controls { LayoutTransformProperty.Changed .AddClassHandler(x => x.OnLayoutTransformChanged); + + ChildProperty.Changed + .AddClassHandler(x => x.OnChildChanged); } /// @@ -38,8 +38,7 @@ namespace Avalonia.Controls set { SetValue(LayoutTransformProperty, value); } } - public Control TransformRoot => _transformRoot ?? - (_transformRoot = this.GetVisualChildren().OfType().FirstOrDefault()); + public IControl TransformRoot => Child; /// /// Provides the behavior for the "Arrange" pass of layout. @@ -132,16 +131,8 @@ namespace Avalonia.Controls return transformedDesiredSize; } - /// - /// Builds the visual tree for the LayoutTransformerControl when a new - /// template is applied. - /// - protected override void OnTemplateApplied(TemplateAppliedEventArgs e) + private void OnChildChanged(AvaloniaPropertyChangedEventArgs e) { - base.OnTemplateApplied(e); - - _matrixTransform = new MatrixTransform(); - if (null != TransformRoot) { TransformRoot.RenderTransform = _matrixTransform; @@ -169,14 +160,14 @@ namespace Avalonia.Controls /// /// RenderTransform/MatrixTransform applied to TransformRoot. /// - private MatrixTransform _matrixTransform; + private MatrixTransform _matrixTransform = new MatrixTransform(); /// /// Transformation matrix corresponding to _matrixTransform. /// private Matrix _transformation; private IDisposable _transformChangedEvent = null; - private Control _transformRoot; + /// /// Returns true if Size a is smaller than Size b in either dimension. /// @@ -215,7 +206,8 @@ namespace Avalonia.Controls /// private void ApplyLayoutTransform() { - if (LayoutTransform == null) return; + if (LayoutTransform == null) + return; // Get the transform matrix and apply it _transformation = RoundMatrix(LayoutTransform.Value, DecimalsAfterRound); @@ -376,11 +368,8 @@ namespace Avalonia.Controls { var newTransform = e.NewValue as Transform; - if (_transformChangedEvent != null) - { - _transformChangedEvent.Dispose(); - _transformChangedEvent = null; - } + _transformChangedEvent?.Dispose(); + _transformChangedEvent = null; if (newTransform != null) { @@ -392,4 +381,4 @@ namespace Avalonia.Controls ApplyLayoutTransform(); } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Themes.Default/DefaultTheme.xaml b/src/Avalonia.Themes.Default/DefaultTheme.xaml index 2b9132ee56..16705f91c2 100644 --- a/src/Avalonia.Themes.Default/DefaultTheme.xaml +++ b/src/Avalonia.Themes.Default/DefaultTheme.xaml @@ -12,7 +12,6 @@ - diff --git a/src/Avalonia.Themes.Default/LayoutTransformControl.xaml b/src/Avalonia.Themes.Default/LayoutTransformControl.xaml deleted file mode 100644 index b26f053622..0000000000 --- a/src/Avalonia.Themes.Default/LayoutTransformControl.xaml +++ /dev/null @@ -1,13 +0,0 @@ - \ No newline at end of file diff --git a/tests/Avalonia.Controls.UnitTests/LayoutTransformControlTests.cs b/tests/Avalonia.Controls.UnitTests/LayoutTransformControlTests.cs index d5f9818f89..13c946b549 100644 --- a/tests/Avalonia.Controls.UnitTests/LayoutTransformControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/LayoutTransformControlTests.cs @@ -1,6 +1,4 @@ -using Avalonia.Controls.Presenters; using Avalonia.Controls.Shapes; -using Avalonia.Controls.Templates; using Avalonia.Media; using Xunit; @@ -311,20 +309,10 @@ namespace Avalonia.Controls.UnitTests { var lt = new LayoutTransformControl() { - LayoutTransform = transform, - Template = new FuncControlTemplate( - p => new ContentPresenter() { Content = p.Content }) + LayoutTransform = transform }; - lt.Content = new Rectangle() { Width = width, Height = height }; - - lt.ApplyTemplate(); - - //we need to force create visual child - //so the measure after is correct - (lt.Presenter as ContentPresenter).UpdateChild(); - - Assert.NotNull(lt.Presenter?.Child); + lt.Child = new Rectangle() { Width = width, Height = height }; lt.Measure(Size.Infinity); lt.Arrange(new Rect(lt.DesiredSize)); @@ -332,4 +320,4 @@ namespace Avalonia.Controls.UnitTests return lt; } } -} \ No newline at end of file +} From 2b98500aaf9c2e0a86761b33695d65d27d2709cb Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Fri, 2 Nov 2018 18:00:45 +0200 Subject: [PATCH 028/374] add simple viewbox implementation --- src/Avalonia.Controls/ViewBox.cs | 123 +++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 src/Avalonia.Controls/ViewBox.cs diff --git a/src/Avalonia.Controls/ViewBox.cs b/src/Avalonia.Controls/ViewBox.cs new file mode 100644 index 0000000000..7492e32937 --- /dev/null +++ b/src/Avalonia.Controls/ViewBox.cs @@ -0,0 +1,123 @@ +using System; +using Avalonia.Media; + +namespace Avalonia.Controls +{ + /// + /// Viewbox is used to scale single child. + /// + /// + public class Viewbox : Decorator + { + /// + /// The stretch property + /// + public static AvaloniaProperty StretchProperty = + AvaloniaProperty.RegisterDirect(nameof(Stretch), + v => v.Stretch, (c, v) => c.Stretch = v, Stretch.Uniform); + + private Stretch _stretch = Stretch.Uniform; + + /// + /// Gets or sets the stretch mode, + /// which determines how child fits into the available space. + /// + /// + /// The stretch. + /// + public Stretch Stretch + { + get => _stretch; + set => SetAndRaise(StretchProperty, ref _stretch, value); + } + + static Viewbox() + { + AffectsMeasure(StretchProperty); + } + + protected override Size MeasureOverride(Size availableSize) + { + var child = Child; + + if (child != null) + { + child.Measure(Size.Infinity); + + var childSize = child.DesiredSize; + + var scale = GetScale(availableSize, childSize, Stretch); + + return childSize * scale; + } + + return new Size(); + } + + protected override Size ArrangeOverride(Size finalSize) + { + var child = Child; + + if (child != null) + { + var childSize = child.DesiredSize; + var scale = GetScale(finalSize, childSize, Stretch); + var scaleTransform = child.RenderTransform as ScaleTransform; + + if (scaleTransform == null) + { + child.RenderTransform = scaleTransform = new ScaleTransform(scale.X, scale.Y); + child.RenderTransformOrigin = RelativePoint.TopLeft; + } + + scaleTransform.ScaleX = scale.X; + scaleTransform.ScaleY = scale.Y; + + child.Arrange(new Rect(childSize)); + + return childSize * scale; + } + + return new Size(); + } + + private static Vector GetScale(Size availableSize, Size childSize, Stretch stretch) + { + double scaleX = 1.0; + double scaleY = 1.0; + + bool validWidth = !double.IsPositiveInfinity(availableSize.Width); + bool validHeight = !double.IsPositiveInfinity(availableSize.Height); + + if (stretch != Stretch.None && (validWidth || validHeight)) + { + scaleX = childSize.Width <= 0.0 ? 0.0 : availableSize.Width / childSize.Width; + scaleY = childSize.Height <= 0.0 ? 0.0 : availableSize.Height / childSize.Height; + + if (!validWidth) + { + scaleX = scaleY; + } + else if (!validHeight) + { + scaleY = scaleX; + } + else + { + switch (stretch) + { + case Stretch.Uniform: + scaleX = scaleY = Math.Min(scaleX, scaleY); + break; + + case Stretch.UniformToFill: + scaleX = scaleY = Math.Max(scaleX, scaleY); + break; + } + } + } + + return new Vector(scaleX, scaleY); + } + } +} From 37d7a49fee2edfbf4ae3c12aa54d2643e0a7c33d Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Fri, 2 Nov 2018 18:51:46 +0200 Subject: [PATCH 029/374] add some tests for Viewbox --- .../ViewboxTests.cs | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 tests/Avalonia.Controls.UnitTests/ViewboxTests.cs diff --git a/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs b/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs new file mode 100644 index 0000000000..ad0f318d2f --- /dev/null +++ b/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs @@ -0,0 +1,105 @@ +using Avalonia.Controls.Shapes; +using Avalonia.Media; +using Xunit; + +namespace Avalonia.Controls.UnitTests +{ + public class ViewboxTests + { + [Fact] + public void Viewbox_Stretch_Uniform_Child() + { + var target = new Viewbox() { Child = new Rectangle() { Width = 100, Height = 50 } }; + + target.Measure(new Size(200, 200)); + target.Arrange(new Rect(new Point(0, 0), target.DesiredSize)); + + Assert.Equal(new Size(200, 100), target.DesiredSize); + var scaleTransform = target.Child.RenderTransform as ScaleTransform; + + Assert.NotNull(scaleTransform); + Assert.Equal(2.0, scaleTransform.ScaleX); + Assert.Equal(2.0, scaleTransform.ScaleY); + } + + [Fact] + public void Viewbox_Stretch_None_Child() + { + var target = new Viewbox() { Stretch = Stretch.None, Child = new Rectangle() { Width = 100, Height = 50 } }; + + target.Measure(new Size(200, 200)); + target.Arrange(new Rect(new Point(0, 0), target.DesiredSize)); + + Assert.Equal(new Size(100, 50), target.DesiredSize); + var scaleTransform = target.Child.RenderTransform as ScaleTransform; + + Assert.NotNull(scaleTransform); + Assert.Equal(1.0, scaleTransform.ScaleX); + Assert.Equal(1.0, scaleTransform.ScaleY); + } + + [Fact] + public void Viewbox_Stretch_Fill_Child() + { + var target = new Viewbox() { Stretch = Stretch.Fill, Child = new Rectangle() { Width = 100, Height = 50 } }; + + target.Measure(new Size(200, 200)); + target.Arrange(new Rect(new Point(0, 0), target.DesiredSize)); + + Assert.Equal(new Size(200, 200), target.DesiredSize); + var scaleTransform = target.Child.RenderTransform as ScaleTransform; + + Assert.NotNull(scaleTransform); + Assert.Equal(2.0, scaleTransform.ScaleX); + Assert.Equal(4.0, scaleTransform.ScaleY); + } + + [Fact] + public void Viewbox_Stretch_UniformToFill_Child() + { + var target = new Viewbox() { Stretch = Stretch.UniformToFill, Child = new Rectangle() { Width = 100, Height = 50 } }; + + target.Measure(new Size(200, 200)); + target.Arrange(new Rect(new Point(0, 0), target.DesiredSize)); + + Assert.Equal(new Size(200, 200), target.DesiredSize); + var scaleTransform = target.Child.RenderTransform as ScaleTransform; + + Assert.NotNull(scaleTransform); + Assert.Equal(4.0, scaleTransform.ScaleX); + Assert.Equal(4.0, scaleTransform.ScaleY); + } + + [Fact] + public void Viewbox_Stretch_Uniform_Child_With_Unrestricted_Width() + { + var target = new Viewbox() { Child = new Rectangle() { Width = 100, Height = 50 } }; + + target.Measure(new Size(double.PositiveInfinity, 200)); + target.Arrange(new Rect(new Point(0, 0), target.DesiredSize)); + + Assert.Equal(new Size(400, 200), target.DesiredSize); + var scaleTransform = target.Child.RenderTransform as ScaleTransform; + + Assert.NotNull(scaleTransform); + Assert.Equal(4.0, scaleTransform.ScaleX); + Assert.Equal(4.0, scaleTransform.ScaleY); + } + + [Fact] + public void Viewbox_Stretch_Uniform_Child_With_Unrestricted_Height() + { + var target = new Viewbox() { Child = new Rectangle() { Width = 100, Height = 50 } }; + + target.Measure(new Size(200, double.PositiveInfinity)); + target.Arrange(new Rect(new Point(0, 0), target.DesiredSize)); + + Assert.Equal(new Size(200, 100), target.DesiredSize); + var scaleTransform = target.Child.RenderTransform as ScaleTransform; + + Assert.NotNull(scaleTransform); + Assert.Equal(2.0, scaleTransform.ScaleX); + Assert.Equal(2.0, scaleTransform.ScaleY); + } + } +} From b515f7a30e6901c54b4e4e1aa1fcbef9f917ca9e Mon Sep 17 00:00:00 2001 From: ahopper Date: Fri, 2 Nov 2018 17:09:08 +0000 Subject: [PATCH 030/374] allow for pointerover control being removed from visual tree --- src/Avalonia.Input/MouseDevice.cs | 54 +++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/src/Avalonia.Input/MouseDevice.cs b/src/Avalonia.Input/MouseDevice.cs index e581772978..893ffe491f 100644 --- a/src/Avalonia.Input/MouseDevice.cs +++ b/src/Avalonia.Input/MouseDevice.cs @@ -298,21 +298,48 @@ namespace Avalonia.Input Contract.Requires(device != null); Contract.Requires(root != null); - var element = root.PointerOverElement; + var element = root.PointerOverElement; var e = new PointerEventArgs { RoutedEvent = InputElement.PointerLeaveEvent, Device = device, }; - while (element != null) + if (element!=null && !element.IsAttachedToVisualTree) + { + // element has been removed from visual tree so do top down cleanup + if (root.IsPointerOver) + ClearChildrenPointerOver(e, root,true); + } + else + { + while (element != null) + { + e.Source = element; + e.Handled = false; + element.RaiseEvent(e); + element = (IInputElement)element.VisualParent; + } + } + root.PointerOverElement = null; + } + + private void ClearChildrenPointerOver(PointerEventArgs e, IInputElement element,bool clearRoot) + { + foreach (IInputElement el in element.VisualChildren) + { + if (el.IsPointerOver) + { + ClearChildrenPointerOver(e, el, true); + break; + } + } + if(clearRoot) { e.Source = element; + e.Handled = false; element.RaiseEvent(e); - element = (IInputElement)element.VisualParent; } - - root.PointerOverElement = null; } private IInputElement SetPointerOver(IPointerDevice device, IInputRoot root, Point p) @@ -361,12 +388,19 @@ namespace Avalonia.Input el = root.PointerOverElement; e.RoutedEvent = InputElement.PointerLeaveEvent; - while (el != null && el != branch) + if (el!=null && branch!=null && !el.IsAttachedToVisualTree) { - e.Source = el; - e.Handled = false; - el.RaiseEvent(e); - el = (IInputElement)el.VisualParent; + ClearChildrenPointerOver(e,branch,false); + } + else + { + while (el != null && el != branch) + { + e.Source = el; + e.Handled = false; + el.RaiseEvent(e); + el = (IInputElement)el.VisualParent; + } } el = root.PointerOverElement = element; From 22879fe31266b3b7074df466c6753b40a7239d0d Mon Sep 17 00:00:00 2001 From: Tom Daffin Date: Sat, 3 Nov 2018 08:00:01 -0600 Subject: [PATCH 031/374] Call GtkImContextFilterKeypress after issuing the KeyDown event to get the same sequence of events as windows --- src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs b/src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs index 0273f6a7d8..304d17b33e 100644 --- a/src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs +++ b/src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs @@ -222,14 +222,14 @@ namespace Avalonia.Gtk3 { var evnt = (GdkEventKey*) pev; _lastKbdEvent = evnt->time; - if (Native.GtkImContextFilterKeypress(_imContext, pev)) - return true; var e = new RawKeyEventArgs( Gtk3Platform.Keyboard, evnt->time, evnt->type == GdkEventType.KeyPress ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp, Avalonia.Gtk.Common.KeyTransform.ConvertKey((GdkKey)evnt->keyval), GetModifierKeys((GdkModifierType)evnt->state)); OnInput(e); + if (Native.GtkImContextFilterKeypress(_imContext, pev)) + return true; return true; } From b15996a091f06094b49a0fb6c2d140957f8ac96e Mon Sep 17 00:00:00 2001 From: ahopper Date: Wed, 7 Nov 2018 08:00:09 +0000 Subject: [PATCH 032/374] fix hittest exception during scene changes --- src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs index ffa0b0bcc5..ad4c475d89 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs @@ -160,7 +160,7 @@ namespace Avalonia.Rendering.SceneGraph private IEnumerable HitTest(IVisualNode node, Point p, Rect? clip, Func filter) { - if (filter?.Invoke(node.Visual) != false) + if (filter?.Invoke(node.Visual) != false && node.Visual.IsAttachedToVisualTree) { var clipped = false; @@ -186,7 +186,7 @@ namespace Avalonia.Rendering.SceneGraph } } - if (node.HitTest(p) && node.Visual.IsAttachedToVisualTree) + if (node.HitTest(p)) { yield return node.Visual; } From 779cdec09356f7c825b5f70f87b5b4732a6b0036 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 7 Nov 2018 15:24:49 +0000 Subject: [PATCH 033/374] initial work to fixing clipboard text encoding issues. --- native/Avalonia.Native/inc/avalonia-native.h | 8 ++- .../project.pbxproj | 6 ++ native/Avalonia.Native/src/OSX/AvnString.h | 14 +++++ native/Avalonia.Native/src/OSX/AvnString.mm | 55 +++++++++++++++++++ native/Avalonia.Native/src/OSX/clipboard.mm | 15 +++-- src/Avalonia.Native/ClipboardImpl.cs | 9 +-- 6 files changed, 97 insertions(+), 10 deletions(-) create mode 100644 native/Avalonia.Native/src/OSX/AvnString.h create mode 100644 native/Avalonia.Native/src/OSX/AvnString.mm diff --git a/native/Avalonia.Native/inc/avalonia-native.h b/native/Avalonia.Native/inc/avalonia-native.h index 0c965b7498..1d5a112929 100644 --- a/native/Avalonia.Native/inc/avalonia-native.h +++ b/native/Avalonia.Native/inc/avalonia-native.h @@ -173,6 +173,12 @@ public: virtual HRESULT ObtainGlFeature(IAvnGlFeature** ppv) = 0; }; +AVNCOM(IAvnString, 17) : IUnknown +{ + virtual HRESULT GetPointer(void**retOut) = 0; + virtual HRESULT GetLength(int*ret) = 0; +}; + AVNCOM(IAvnWindowBase, 02) : IUnknown { virtual HRESULT Show() = 0; @@ -315,7 +321,7 @@ AVNCOM(IAvnScreens, 0e) : IUnknown AVNCOM(IAvnClipboard, 0f) : IUnknown { - virtual HRESULT GetText (void** retOut) = 0; + virtual HRESULT GetText (IAvnString** ppv ) = 0; virtual HRESULT SetText (char* text) = 0; virtual HRESULT Clear() = 0; }; diff --git a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj index bd8ac481a8..cc74d5669f 100644 --- a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj +++ b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 37A517B32159597E00FBA241 /* Screens.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37A517B22159597E00FBA241 /* Screens.mm */; }; 37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37C09D8721580FE4006A6758 /* SystemDialogs.mm */; }; + 37DDA9B0219330F8002E132B /* AvnString.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37DDA9AF219330F8002E132B /* AvnString.mm */; }; 37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37E2330E21583241000CB7E2 /* KeyTransform.mm */; }; 5B21A982216530F500CEE36E /* cursor.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B21A981216530F500CEE36E /* cursor.mm */; }; 5B8BD94F215BFEA6005ED2A7 /* clipboard.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */; }; @@ -26,6 +27,8 @@ 37A517B22159597E00FBA241 /* Screens.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = Screens.mm; sourceTree = ""; }; 37C09D8721580FE4006A6758 /* SystemDialogs.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SystemDialogs.mm; sourceTree = ""; }; 37C09D8A21581EF2006A6758 /* window.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = window.h; sourceTree = ""; }; + 37DDA9AF219330F8002E132B /* AvnString.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnString.mm; sourceTree = ""; }; + 37DDA9B121933371002E132B /* AvnString.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AvnString.h; sourceTree = ""; }; 37E2330E21583241000CB7E2 /* KeyTransform.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = KeyTransform.mm; sourceTree = ""; }; 5B21A981216530F500CEE36E /* cursor.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = cursor.mm; sourceTree = ""; }; 5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = clipboard.mm; sourceTree = ""; }; @@ -65,6 +68,8 @@ AB7A61E62147C814003C5833 = { isa = PBXGroup; children = ( + 37DDA9B121933371002E132B /* AvnString.h */, + 37DDA9AF219330F8002E132B /* AvnString.mm */, 37A4E71A2178846A00EACBCD /* headers */, AB573DC3217605E400D389A2 /* gl.mm */, 5BF943652167AD1D009CAE35 /* cursor.h */, @@ -161,6 +166,7 @@ files = ( 5B8BD94F215BFEA6005ED2A7 /* clipboard.mm in Sources */, 5B21A982216530F500CEE36E /* cursor.mm in Sources */, + 37DDA9B0219330F8002E132B /* AvnString.mm in Sources */, AB8F7D6B21482D7F0057DBA5 /* platformthreading.mm in Sources */, 37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */, 37A517B32159597E00FBA241 /* Screens.mm in Sources */, diff --git a/native/Avalonia.Native/src/OSX/AvnString.h b/native/Avalonia.Native/src/OSX/AvnString.h new file mode 100644 index 0000000000..9a8f5a1318 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/AvnString.h @@ -0,0 +1,14 @@ +// +// AvnString.h +// Avalonia.Native.OSX +// +// Created by Dan Walmsley on 07/11/2018. +// Copyright © 2018 Avalonia. All rights reserved. +// + +#ifndef AvnString_h +#define AvnString_h + +extern IAvnString* CreateAvnString(NSString* string); + +#endif /* AvnString_h */ diff --git a/native/Avalonia.Native/src/OSX/AvnString.mm b/native/Avalonia.Native/src/OSX/AvnString.mm new file mode 100644 index 0000000000..a16c286634 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/AvnString.mm @@ -0,0 +1,55 @@ +// +// AvnString.m +// Avalonia.Native.OSX +// +// Created by Dan Walmsley on 07/11/2018. +// Copyright © 2018 Avalonia. All rights reserved. +// + +#include "common.h" + +class AvnStringImpl : public virtual ComSingleObject +{ +private: + NSString* _string; + +public: + FORWARD_IUNKNOWN() + + AvnStringImpl(NSString* string) + { + _string = string; + } + + virtual HRESULT GetPointer(void**retOut) override + { + @autoreleasepool + { + if(retOut == nullptr) + { + return E_POINTER; + } + + *retOut = (void*)_string.UTF8String; + + return S_OK; + } + } + + virtual HRESULT GetLength(int*retOut) override + { + if(retOut == nullptr) + { + return E_POINTER; + } + + *retOut = (int)_string.length; + + return S_OK; + } +}; + +IAvnString* CreateAvnString(NSString* string) +{ + return new AvnStringImpl(string); +} diff --git a/native/Avalonia.Native/src/OSX/clipboard.mm b/native/Avalonia.Native/src/OSX/clipboard.mm index 19e5c25801..f941e8ca6c 100644 --- a/native/Avalonia.Native/src/OSX/clipboard.mm +++ b/native/Avalonia.Native/src/OSX/clipboard.mm @@ -2,20 +2,25 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. #include "common.h" +#include "AvnString.h" class Clipboard : public ComSingleObject { public: FORWARD_IUNKNOWN() - virtual HRESULT GetText (void** retOut) override + virtual HRESULT GetText (IAvnString** retOut) override { @autoreleasepool { - NSString *str = [[NSPasteboard generalPasteboard] stringForType:NSPasteboardTypeString]; - *retOut = (void *)str.UTF8String; - } + if(retOut == nullptr) + { + return E_POINTER; + } + + *retOut = CreateAvnString([[NSPasteboard generalPasteboard] stringForType:NSPasteboardTypeString]); - return S_OK; + return S_OK; + } } virtual HRESULT SetText (char* text) override diff --git a/src/Avalonia.Native/ClipboardImpl.cs b/src/Avalonia.Native/ClipboardImpl.cs index d54bc95fbb..a2a1416645 100644 --- a/src/Avalonia.Native/ClipboardImpl.cs +++ b/src/Avalonia.Native/ClipboardImpl.cs @@ -24,12 +24,13 @@ namespace Avalonia.Native return Task.CompletedTask; } - public Task GetTextAsync() + public unsafe Task GetTextAsync() { - var outPtr = _native.GetText(); - var text = Marshal.PtrToStringAnsi(outPtr); + var text = _native.GetText(); - return Task.FromResult(text); + var result = System.Text.Encoding.UTF8.GetString((byte*)text.GetPointer(), text.GetLength()); + + return Task.FromResult(result); } public Task SetTextAsync(string text) From 9d5b607707406e0124eac1b6a14694d171024440 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Wed, 7 Nov 2018 18:03:43 +0100 Subject: [PATCH 034/374] csproj fix --- src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj b/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj index b408f2a0be..d854a136e9 100644 --- a/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj +++ b/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj @@ -25,4 +25,3 @@ - From e87967a4ad8e943a2214d74370786edd05ee8504 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Wed, 7 Nov 2018 18:14:21 +0100 Subject: [PATCH 035/374] Fix csproj --- samples/ControlCatalog/ControlCatalog.csproj | 14 +++----------- .../Avalonia.Themes.Default.csproj | 13 +++++-------- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj index 13b79d75b5..4af6171a89 100644 --- a/samples/ControlCatalog/ControlCatalog.csproj +++ b/samples/ControlCatalog/ControlCatalog.csproj @@ -11,9 +11,7 @@ - - - + @@ -27,15 +25,9 @@ - - TabControlPage.xaml - - - - - + MSBuild:Compile - + diff --git a/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj b/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj index d854a136e9..af1899bab1 100644 --- a/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj +++ b/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj @@ -3,9 +3,6 @@ netstandard2.0 false - - - @@ -16,12 +13,12 @@ + + + + MSBuild:Compile + - - - MSBuild:Compile - - From e1fae9692df4a9ec143b0891e7b9ad1f811c72d8 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Wed, 7 Nov 2018 18:19:21 +0100 Subject: [PATCH 036/374] Remove extra ItemGroup --- samples/ControlCatalog/ControlCatalog.csproj | 6 ------ 1 file changed, 6 deletions(-) diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj index 4af6171a89..7b30951d61 100644 --- a/samples/ControlCatalog/ControlCatalog.csproj +++ b/samples/ControlCatalog/ControlCatalog.csproj @@ -23,12 +23,6 @@ - - - - MSBuild:Compile - - From 26fa4f0463dc0b8adec78a6961d2fb962af40a1a Mon Sep 17 00:00:00 2001 From: ahopper Date: Thu, 8 Nov 2018 09:22:47 +0000 Subject: [PATCH 037/374] :pointerover effect added to ButtonSpinner --- src/Avalonia.Themes.Default/ButtonSpinner.xaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Themes.Default/ButtonSpinner.xaml b/src/Avalonia.Themes.Default/ButtonSpinner.xaml index 8284b4eddf..ddeef44011 100644 --- a/src/Avalonia.Themes.Default/ButtonSpinner.xaml +++ b/src/Avalonia.Themes.Default/ButtonSpinner.xaml @@ -87,6 +87,9 @@ - + + + - \ No newline at end of file + From f6d080feecd1a68ce5ba8028dbdab37b5e8e8264 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 8 Nov 2018 17:20:51 +0000 Subject: [PATCH 038/374] [OSX] fix pasting from clipboard --- native/Avalonia.Native/inc/avalonia-native.h | 6 +++--- native/Avalonia.Native/src/OSX/AvnString.mm | 4 ++-- native/Avalonia.Native/src/OSX/clipboard.mm | 11 ++--------- src/Avalonia.Native/ClipboardImpl.cs | 9 +++++---- 4 files changed, 12 insertions(+), 18 deletions(-) diff --git a/native/Avalonia.Native/inc/avalonia-native.h b/native/Avalonia.Native/inc/avalonia-native.h index 1d5a112929..b9e16e6643 100644 --- a/native/Avalonia.Native/inc/avalonia-native.h +++ b/native/Avalonia.Native/inc/avalonia-native.h @@ -175,8 +175,8 @@ public: AVNCOM(IAvnString, 17) : IUnknown { - virtual HRESULT GetPointer(void**retOut) = 0; - virtual HRESULT GetLength(int*ret) = 0; + virtual HRESULT Pointer(void**retOut) = 0; + virtual HRESULT Length(int*ret) = 0; }; AVNCOM(IAvnWindowBase, 02) : IUnknown @@ -321,7 +321,7 @@ AVNCOM(IAvnScreens, 0e) : IUnknown AVNCOM(IAvnClipboard, 0f) : IUnknown { - virtual HRESULT GetText (IAvnString** ppv ) = 0; + virtual IAvnString* GetText () = 0; virtual HRESULT SetText (char* text) = 0; virtual HRESULT Clear() = 0; }; diff --git a/native/Avalonia.Native/src/OSX/AvnString.mm b/native/Avalonia.Native/src/OSX/AvnString.mm index a16c286634..b491cf2a92 100644 --- a/native/Avalonia.Native/src/OSX/AvnString.mm +++ b/native/Avalonia.Native/src/OSX/AvnString.mm @@ -21,7 +21,7 @@ public: _string = string; } - virtual HRESULT GetPointer(void**retOut) override + virtual HRESULT Pointer(void**retOut) override { @autoreleasepool { @@ -36,7 +36,7 @@ public: } } - virtual HRESULT GetLength(int*retOut) override + virtual HRESULT Length(int*retOut) override { if(retOut == nullptr) { diff --git a/native/Avalonia.Native/src/OSX/clipboard.mm b/native/Avalonia.Native/src/OSX/clipboard.mm index f941e8ca6c..8f95433f64 100644 --- a/native/Avalonia.Native/src/OSX/clipboard.mm +++ b/native/Avalonia.Native/src/OSX/clipboard.mm @@ -8,18 +8,11 @@ class Clipboard : public ComSingleObject { public: FORWARD_IUNKNOWN() - virtual HRESULT GetText (IAvnString** retOut) override + virtual IAvnString* GetText () override { @autoreleasepool { - if(retOut == nullptr) - { - return E_POINTER; - } - - *retOut = CreateAvnString([[NSPasteboard generalPasteboard] stringForType:NSPasteboardTypeString]); - - return S_OK; + return CreateAvnString([[NSPasteboard generalPasteboard] stringForType:NSPasteboardTypeString]); } } diff --git a/src/Avalonia.Native/ClipboardImpl.cs b/src/Avalonia.Native/ClipboardImpl.cs index a2a1416645..9a49976683 100644 --- a/src/Avalonia.Native/ClipboardImpl.cs +++ b/src/Avalonia.Native/ClipboardImpl.cs @@ -26,11 +26,12 @@ namespace Avalonia.Native public unsafe Task GetTextAsync() { - var text = _native.GetText(); - - var result = System.Text.Encoding.UTF8.GetString((byte*)text.GetPointer(), text.GetLength()); + using (var text = _native.GetText()) + { + var result = System.Text.Encoding.UTF8.GetString((byte*)text.Pointer(), text.Length()); - return Task.FromResult(result); + return Task.FromResult(result); + } } public Task SetTextAsync(string text) From d06ed4a7dba0729b303b84b02014052b0b0e0718 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 8 Nov 2018 17:38:28 +0000 Subject: [PATCH 039/374] [OSX] correctly use UTF8 encoded text for passing strings. SetTitle on Window, SetText on Clipboard. --- native/Avalonia.Native/inc/avalonia-native.h | 4 ++-- .../xcshareddata/IDEWorkspaceChecks.plist | 8 ++++++++ native/Avalonia.Native/src/OSX/clipboard.mm | 4 ++-- native/Avalonia.Native/src/OSX/window.mm | 4 ++-- src/Avalonia.Native/ClipboardImpl.cs | 6 +++++- src/Avalonia.Native/WindowImpl.cs | 6 +++++- 6 files changed, 24 insertions(+), 8 deletions(-) create mode 100644 native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/native/Avalonia.Native/inc/avalonia-native.h b/native/Avalonia.Native/inc/avalonia-native.h index b9e16e6643..f353509346 100644 --- a/native/Avalonia.Native/inc/avalonia-native.h +++ b/native/Avalonia.Native/inc/avalonia-native.h @@ -216,7 +216,7 @@ AVNCOM(IAvnWindow, 04) : virtual IAvnWindowBase virtual HRESULT ShowDialog (IUnknown**ppv) = 0; virtual HRESULT SetCanResize(bool value) = 0; virtual HRESULT SetHasDecorations(bool value) = 0; - virtual HRESULT SetTitle (const char* title) = 0; + virtual HRESULT SetTitle (void* utf8Title) = 0; virtual HRESULT SetTitleBarColor (AvnColor color) = 0; virtual HRESULT SetWindowState(AvnWindowState state) = 0; virtual HRESULT GetWindowState(AvnWindowState*ret) = 0; @@ -322,7 +322,7 @@ AVNCOM(IAvnScreens, 0e) : IUnknown AVNCOM(IAvnClipboard, 0f) : IUnknown { virtual IAvnString* GetText () = 0; - virtual HRESULT SetText (char* text) = 0; + virtual HRESULT SetText (void* utf8Text) = 0; virtual HRESULT Clear() = 0; }; diff --git a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000000..18d981003d --- /dev/null +++ b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/native/Avalonia.Native/src/OSX/clipboard.mm b/native/Avalonia.Native/src/OSX/clipboard.mm index 8f95433f64..be77ff52d8 100644 --- a/native/Avalonia.Native/src/OSX/clipboard.mm +++ b/native/Avalonia.Native/src/OSX/clipboard.mm @@ -16,13 +16,13 @@ public: } } - virtual HRESULT SetText (char* text) override + virtual HRESULT SetText (void* utf8String) override { @autoreleasepool { NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard]; [pasteBoard clearContents]; - [pasteBoard setString:@(text) forType:NSPasteboardTypeString]; + [pasteBoard setString:[NSString stringWithUTF8String:(const char*)utf8String] forType:NSPasteboardTypeString]; } return S_OK; diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 362b765b3d..16b21efcd5 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -530,11 +530,11 @@ private: } } - virtual HRESULT SetTitle (const char* title) override + virtual HRESULT SetTitle (void* utf8title) override { @autoreleasepool { - _lastTitle = [NSString stringWithUTF8String:title]; + _lastTitle = [NSString stringWithUTF8String:(const char*)utf8title]; [Window setTitle:_lastTitle]; [Window setTitleVisibility:NSWindowTitleVisible]; diff --git a/src/Avalonia.Native/ClipboardImpl.cs b/src/Avalonia.Native/ClipboardImpl.cs index 9a49976683..c756a6d9c2 100644 --- a/src/Avalonia.Native/ClipboardImpl.cs +++ b/src/Avalonia.Native/ClipboardImpl.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using System.Runtime.InteropServices; using Avalonia.Input.Platform; using Avalonia.Native.Interop; +using Avalonia.Platform.Interop; namespace Avalonia.Native { @@ -40,7 +41,10 @@ namespace Avalonia.Native if (text != null) { - _native.SetText(text); + using (var buffer = new Utf8Buffer(text)) + { + _native.SetText(buffer.DangerousGetHandle()); + } } return Task.CompletedTask; diff --git a/src/Avalonia.Native/WindowImpl.cs b/src/Avalonia.Native/WindowImpl.cs index 5d30408e52..3b1b4ff3f9 100644 --- a/src/Avalonia.Native/WindowImpl.cs +++ b/src/Avalonia.Native/WindowImpl.cs @@ -5,6 +5,7 @@ using System; using Avalonia.Controls; using Avalonia.Native.Interop; using Avalonia.Platform; +using Avalonia.Platform.Interop; namespace Avalonia.Native { @@ -68,7 +69,10 @@ namespace Avalonia.Native public void SetTitle(string title) { - _native.SetTitle(title); + using (var buffer = new Utf8Buffer(title)) + { + _native.SetTitle(buffer.DangerousGetHandle()); + } } public WindowState WindowState From 5af1c1784a8dd58491b9e834bf97c489a235cc95 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 8 Nov 2018 17:43:36 +0000 Subject: [PATCH 040/374] [Avalonia.Native OSX] fix api for clipboard --- native/Avalonia.Native/inc/avalonia-native.h | 2 +- native/Avalonia.Native/src/OSX/clipboard.mm | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/native/Avalonia.Native/inc/avalonia-native.h b/native/Avalonia.Native/inc/avalonia-native.h index f353509346..0e3edaa2dc 100644 --- a/native/Avalonia.Native/inc/avalonia-native.h +++ b/native/Avalonia.Native/inc/avalonia-native.h @@ -321,7 +321,7 @@ AVNCOM(IAvnScreens, 0e) : IUnknown AVNCOM(IAvnClipboard, 0f) : IUnknown { - virtual IAvnString* GetText () = 0; + virtual HRESULT GetText (IAvnString**ppv) = 0; virtual HRESULT SetText (void* utf8Text) = 0; virtual HRESULT Clear() = 0; }; diff --git a/native/Avalonia.Native/src/OSX/clipboard.mm b/native/Avalonia.Native/src/OSX/clipboard.mm index be77ff52d8..53c1fe3c2c 100644 --- a/native/Avalonia.Native/src/OSX/clipboard.mm +++ b/native/Avalonia.Native/src/OSX/clipboard.mm @@ -8,11 +8,18 @@ class Clipboard : public ComSingleObject { public: FORWARD_IUNKNOWN() - virtual IAvnString* GetText () override + virtual HRESULT GetText (IAvnString**ppv) override { @autoreleasepool { - return CreateAvnString([[NSPasteboard generalPasteboard] stringForType:NSPasteboardTypeString]); + if(ppv == nullptr) + { + return E_POINTER; + } + + *ppv = CreateAvnString([[NSPasteboard generalPasteboard] stringForType:NSPasteboardTypeString]); + + return S_OK; } } From 7762fa6079b06850087b1d1888b7b3a463b2541a Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 8 Nov 2018 17:46:52 +0000 Subject: [PATCH 041/374] remove plist file --- .../xcshareddata/IDEWorkspaceChecks.plist | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003d..0000000000 --- a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - From df6e3eff03aa9d51481c5318300a44019b004f74 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Thu, 8 Nov 2018 22:06:58 +0300 Subject: [PATCH 042/374] [X11] Basic (non-IM) keyboard input and some refactorings/fixes --- src/Avalonia.X11/Keysyms.cs | 2108 +++++++++++++++++++++++++++ src/Avalonia.X11/X11Info.cs | 4 + src/Avalonia.X11/X11KeyTransform.cs | 232 +++ src/Avalonia.X11/X11Structs.cs | 2 +- src/Avalonia.X11/X11Window.cs | 143 +- src/Avalonia.X11/XLib.cs | 36 + 6 files changed, 2478 insertions(+), 47 deletions(-) create mode 100644 src/Avalonia.X11/Keysyms.cs create mode 100644 src/Avalonia.X11/X11KeyTransform.cs diff --git a/src/Avalonia.X11/Keysyms.cs b/src/Avalonia.X11/Keysyms.cs new file mode 100644 index 0000000000..651a06f574 --- /dev/null +++ b/src/Avalonia.X11/Keysyms.cs @@ -0,0 +1,2108 @@ +// ReSharper disable UnusedMember.Global +// ReSharper disable IdentifierTypo +// ReSharper disable CommentTypo +namespace Avalonia.X11 +{ + enum X11Key + { + VoidSymbol = 0xffffff /* Void symbol */, + BackSpace = 0xff08 /* Back space, back char */, + Tab = 0xff09, + Linefeed = 0xff0a /* Linefeed, LF */, + Clear = 0xff0b, + Return = 0xff0d /* Return, enter */, + Pause = 0xff13 /* Pause, hold */, + Scroll_Lock = 0xff14, + Sys_Req = 0xff15, + Escape = 0xff1b, + Delete = 0xffff /* Delete, rubout */, + Multi_key = 0xff20 /* Multi-key character compose */, + Codeinput = 0xff37, + SingleCandidate = 0xff3c, + MultipleCandidate = 0xff3d, + PreviousCandidate = 0xff3e, + Kanji = 0xff21 /* Kanji, Kanji convert */, + Muhenkan = 0xff22 /* Cancel Conversion */, + Henkan_Mode = 0xff23 /* Start/Stop Conversion */, + Henkan = 0xff23 /* Alias for Henkan_Mode */, + Romaji = 0xff24 /* to Romaji */, + Hiragana = 0xff25 /* to Hiragana */, + Katakana = 0xff26 /* to Katakana */, + Hiragana_Katakana = 0xff27 /* Hiragana/Katakana toggle */, + Zenkaku = 0xff28 /* to Zenkaku */, + Hankaku = 0xff29 /* to Hankaku */, + Zenkaku_Hankaku = 0xff2a /* Zenkaku/Hankaku toggle */, + Touroku = 0xff2b /* Add to Dictionary */, + Massyo = 0xff2c /* Delete from Dictionary */, + Kana_Lock = 0xff2d /* Kana Lock */, + Kana_Shift = 0xff2e /* Kana Shift */, + Eisu_Shift = 0xff2f /* Alphanumeric Shift */, + Eisu_toggle = 0xff30 /* Alphanumeric toggle */, + Kanji_Bangou = 0xff37 /* Codeinput */, + Zen_Koho = 0xff3d /* Multiple/All Candidate(s) */, + Mae_Koho = 0xff3e /* Previous Candidate */, + Home = 0xff50, + Left = 0xff51 /* Move left, left arrow */, + Up = 0xff52 /* Move up, up arrow */, + Right = 0xff53 /* Move right, right arrow */, + Down = 0xff54 /* Move down, down arrow */, + Prior = 0xff55 /* Prior, previous */, + Page_Up = 0xff55, + Next = 0xff56 /* Next */, + Page_Down = 0xff56, + End = 0xff57 /* EOL */, + Begin = 0xff58 /* BOL */, + Select = 0xff60 /* Select, mark */, + Print = 0xff61, + Execute = 0xff62 /* Execute, run, do */, + Insert = 0xff63 /* Insert, insert here */, + Undo = 0xff65, + Redo = 0xff66 /* Redo, again */, + Menu = 0xff67, + Find = 0xff68 /* Find, search */, + Cancel = 0xff69 /* Cancel, stop, abort, exit */, + Help = 0xff6a /* Help */, + Break = 0xff6b, + Mode_switch = 0xff7e /* Character set switch */, + script_switch = 0xff7e /* Alias for mode_switch */, + Num_Lock = 0xff7f, + KP_Space = 0xff80 /* Space */, + KP_Tab = 0xff89, + KP_Enter = 0xff8d /* Enter */, + KP_F1 = 0xff91 /* PF1, KP_A, ... */, + KP_F2 = 0xff92, + KP_F3 = 0xff93, + KP_F4 = 0xff94, + KP_Home = 0xff95, + KP_Left = 0xff96, + KP_Up = 0xff97, + KP_Right = 0xff98, + KP_Down = 0xff99, + KP_Prior = 0xff9a, + KP_Page_Up = 0xff9a, + KP_Next = 0xff9b, + KP_Page_Down = 0xff9b, + KP_End = 0xff9c, + KP_Begin = 0xff9d, + KP_Insert = 0xff9e, + KP_Delete = 0xff9f, + KP_Equal = 0xffbd /* Equals */, + KP_Multiply = 0xffaa, + KP_Add = 0xffab, + KP_Separator = 0xffac /* Separator, often comma */, + KP_Subtract = 0xffad, + KP_Decimal = 0xffae, + KP_Divide = 0xffaf, + KP_0 = 0xffb0, + KP_1 = 0xffb1, + KP_2 = 0xffb2, + KP_3 = 0xffb3, + KP_4 = 0xffb4, + KP_5 = 0xffb5, + KP_6 = 0xffb6, + KP_7 = 0xffb7, + KP_8 = 0xffb8, + KP_9 = 0xffb9, + F1 = 0xffbe, + F2 = 0xffbf, + F3 = 0xffc0, + F4 = 0xffc1, + F5 = 0xffc2, + F6 = 0xffc3, + F7 = 0xffc4, + F8 = 0xffc5, + F9 = 0xffc6, + F10 = 0xffc7, + F11 = 0xffc8, + L1 = 0xffc8, + F12 = 0xffc9, + L2 = 0xffc9, + F13 = 0xffca, + L3 = 0xffca, + F14 = 0xffcb, + L4 = 0xffcb, + F15 = 0xffcc, + L5 = 0xffcc, + F16 = 0xffcd, + L6 = 0xffcd, + F17 = 0xffce, + L7 = 0xffce, + F18 = 0xffcf, + L8 = 0xffcf, + F19 = 0xffd0, + L9 = 0xffd0, + F20 = 0xffd1, + L10 = 0xffd1, + F21 = 0xffd2, + R1 = 0xffd2, + F22 = 0xffd3, + R2 = 0xffd3, + F23 = 0xffd4, + R3 = 0xffd4, + F24 = 0xffd5, + R4 = 0xffd5, + F25 = 0xffd6, + R5 = 0xffd6, + F26 = 0xffd7, + R6 = 0xffd7, + F27 = 0xffd8, + R7 = 0xffd8, + F28 = 0xffd9, + R8 = 0xffd9, + F29 = 0xffda, + R9 = 0xffda, + F30 = 0xffdb, + R10 = 0xffdb, + F31 = 0xffdc, + R11 = 0xffdc, + F32 = 0xffdd, + R12 = 0xffdd, + F33 = 0xffde, + R13 = 0xffde, + F34 = 0xffdf, + R14 = 0xffdf, + F35 = 0xffe0, + R15 = 0xffe0, + Shift_L = 0xffe1 /* Left shift */, + Shift_R = 0xffe2 /* Right shift */, + Control_L = 0xffe3 /* Left control */, + Control_R = 0xffe4 /* Right control */, + Caps_Lock = 0xffe5 /* Caps lock */, + Shift_Lock = 0xffe6 /* Shift lock */, + Meta_L = 0xffe7 /* Left meta */, + Meta_R = 0xffe8 /* Right meta */, + Alt_L = 0xffe9 /* Left alt */, + Alt_R = 0xffea /* Right alt */, + Super_L = 0xffeb /* Left super */, + Super_R = 0xffec /* Right super */, + Hyper_L = 0xffed /* Left hyper */, + Hyper_R = 0xffee /* Right hyper */, + ISO_Lock = 0xfe01, + ISO_Level2_Latch = 0xfe02, + ISO_Level3_Shift = 0xfe03, + ISO_Level3_Latch = 0xfe04, + ISO_Level3_Lock = 0xfe05, + ISO_Level5_Shift = 0xfe11, + ISO_Level5_Latch = 0xfe12, + ISO_Level5_Lock = 0xfe13, + ISO_Group_Shift = 0xff7e /* Alias for mode_switch */, + ISO_Group_Latch = 0xfe06, + ISO_Group_Lock = 0xfe07, + ISO_Next_Group = 0xfe08, + ISO_Next_Group_Lock = 0xfe09, + ISO_Prev_Group = 0xfe0a, + ISO_Prev_Group_Lock = 0xfe0b, + ISO_First_Group = 0xfe0c, + ISO_First_Group_Lock = 0xfe0d, + ISO_Last_Group = 0xfe0e, + ISO_Last_Group_Lock = 0xfe0f, + ISO_Left_Tab = 0xfe20, + ISO_Move_Line_Up = 0xfe21, + ISO_Move_Line_Down = 0xfe22, + ISO_Partial_Line_Up = 0xfe23, + ISO_Partial_Line_Down = 0xfe24, + ISO_Partial_Space_Left = 0xfe25, + ISO_Partial_Space_Right = 0xfe26, + ISO_Set_Margin_Left = 0xfe27, + ISO_Set_Margin_Right = 0xfe28, + ISO_Release_Margin_Left = 0xfe29, + ISO_Release_Margin_Right = 0xfe2a, + ISO_Release_Both_Margins = 0xfe2b, + ISO_Fast_Cursor_Left = 0xfe2c, + ISO_Fast_Cursor_Right = 0xfe2d, + ISO_Fast_Cursor_Up = 0xfe2e, + ISO_Fast_Cursor_Down = 0xfe2f, + ISO_Continuous_Underline = 0xfe30, + ISO_Discontinuous_Underline = 0xfe31, + ISO_Emphasize = 0xfe32, + ISO_Center_Object = 0xfe33, + ISO_Enter = 0xfe34, + dead_grave = 0xfe50, + dead_acute = 0xfe51, + dead_circumflex = 0xfe52, + dead_tilde = 0xfe53, + dead_perispomeni = 0xfe53 /* alias for dead_tilde */, + dead_macron = 0xfe54, + dead_breve = 0xfe55, + dead_abovedot = 0xfe56, + dead_diaeresis = 0xfe57, + dead_abovering = 0xfe58, + dead_doubleacute = 0xfe59, + dead_caron = 0xfe5a, + dead_cedilla = 0xfe5b, + dead_ogonek = 0xfe5c, + dead_iota = 0xfe5d, + dead_voiced_sound = 0xfe5e, + dead_semivoiced_sound = 0xfe5f, + dead_belowdot = 0xfe60, + dead_hook = 0xfe61, + dead_horn = 0xfe62, + dead_stroke = 0xfe63, + dead_abovecomma = 0xfe64, + dead_psili = 0xfe64 /* alias for dead_abovecomma */, + dead_abovereversedcomma = 0xfe65, + dead_dasia = 0xfe65 /* alias for dead_abovereversedcomma */, + dead_doublegrave = 0xfe66, + dead_belowring = 0xfe67, + dead_belowmacron = 0xfe68, + dead_belowcircumflex = 0xfe69, + dead_belowtilde = 0xfe6a, + dead_belowbreve = 0xfe6b, + dead_belowdiaeresis = 0xfe6c, + dead_invertedbreve = 0xfe6d, + dead_belowcomma = 0xfe6e, + dead_currency = 0xfe6f, + dead_lowline = 0xfe90, + dead_aboveverticalline = 0xfe91, + dead_belowverticalline = 0xfe92, + dead_longsolidusoverlay = 0xfe93, + dead_a = 0xfe80, + dead_A = 0xfe81, + dead_e = 0xfe82, + dead_E = 0xfe83, + dead_i = 0xfe84, + dead_I = 0xfe85, + dead_o = 0xfe86, + dead_O = 0xfe87, + dead_u = 0xfe88, + dead_U = 0xfe89, + dead_small_schwa = 0xfe8a, + dead_capital_schwa = 0xfe8b, + dead_greek = 0xfe8c, + First_Virtual_Screen = 0xfed0, + Prev_Virtual_Screen = 0xfed1, + Next_Virtual_Screen = 0xfed2, + Last_Virtual_Screen = 0xfed4, + Terminate_Server = 0xfed5, + AccessX_Enable = 0xfe70, + AccessX_Feedback_Enable = 0xfe71, + RepeatKeys_Enable = 0xfe72, + SlowKeys_Enable = 0xfe73, + BounceKeys_Enable = 0xfe74, + StickyKeys_Enable = 0xfe75, + MouseKeys_Enable = 0xfe76, + MouseKeys_Accel_Enable = 0xfe77, + Overlay1_Enable = 0xfe78, + Overlay2_Enable = 0xfe79, + AudibleBell_Enable = 0xfe7a, + Pointer_Left = 0xfee0, + Pointer_Right = 0xfee1, + Pointer_Up = 0xfee2, + Pointer_Down = 0xfee3, + Pointer_UpLeft = 0xfee4, + Pointer_UpRight = 0xfee5, + Pointer_DownLeft = 0xfee6, + Pointer_DownRight = 0xfee7, + Pointer_Button_Dflt = 0xfee8, + Pointer_Button1 = 0xfee9, + Pointer_Button2 = 0xfeea, + Pointer_Button3 = 0xfeeb, + Pointer_Button4 = 0xfeec, + Pointer_Button5 = 0xfeed, + Pointer_DblClick_Dflt = 0xfeee, + Pointer_DblClick1 = 0xfeef, + Pointer_DblClick2 = 0xfef0, + Pointer_DblClick3 = 0xfef1, + Pointer_DblClick4 = 0xfef2, + Pointer_DblClick5 = 0xfef3, + Pointer_Drag_Dflt = 0xfef4, + Pointer_Drag1 = 0xfef5, + Pointer_Drag2 = 0xfef6, + Pointer_Drag3 = 0xfef7, + Pointer_Drag4 = 0xfef8, + Pointer_Drag5 = 0xfefd, + Pointer_EnableKeys = 0xfef9, + Pointer_Accelerate = 0xfefa, + Pointer_DfltBtnNext = 0xfefb, + Pointer_DfltBtnPrev = 0xfefc, + ch = 0xfea0, + Ch = 0xfea1, + CH = 0xfea2, + c_h = 0xfea3, + C_h = 0xfea4, + C_H = 0xfea5, + XK_3270_Duplicate = 0xfd01, + XK_3270_FieldMark = 0xfd02, + XK_3270_Right2 = 0xfd03, + XK_3270_Left2 = 0xfd04, + XK_3270_BackTab = 0xfd05, + XK_3270_EraseEOF = 0xfd06, + XK_3270_EraseInput = 0xfd07, + XK_3270_Reset = 0xfd08, + XK_3270_Quit = 0xfd09, + XK_3270_PA1 = 0xfd0a, + XK_3270_PA2 = 0xfd0b, + XK_3270_PA3 = 0xfd0c, + XK_3270_Test = 0xfd0d, + XK_3270_Attn = 0xfd0e, + XK_3270_CursorBlink = 0xfd0f, + XK_3270_AltCursor = 0xfd10, + XK_3270_KeyClick = 0xfd11, + XK_3270_Jump = 0xfd12, + XK_3270_Ident = 0xfd13, + XK_3270_Rule = 0xfd14, + XK_3270_Copy = 0xfd15, + XK_3270_Play = 0xfd16, + XK_3270_Setup = 0xfd17, + XK_3270_Record = 0xfd18, + XK_3270_ChangeScreen = 0xfd19, + XK_3270_DeleteWord = 0xfd1a, + XK_3270_ExSelect = 0xfd1b, + XK_3270_CursorSelect = 0xfd1c, + XK_3270_PrintScreen = 0xfd1d, + XK_3270_Enter = 0xfd1e, + space = 0x0020 /* U+0020 SPACE */, + exclam = 0x0021 /* U+0021 EXCLAMATION MARK */, + quotedbl = 0x0022 /* U+0022 QUOTATION MARK */, + numbersign = 0x0023 /* U+0023 NUMBER SIGN */, + dollar = 0x0024 /* U+0024 DOLLAR SIGN */, + percent = 0x0025 /* U+0025 PERCENT SIGN */, + ampersand = 0x0026 /* U+0026 AMPERSAND */, + apostrophe = 0x0027 /* U+0027 APOSTROPHE */, + quoteright = 0x0027 /* deprecated */, + parenleft = 0x0028 /* U+0028 LEFT PARENTHESIS */, + parenright = 0x0029 /* U+0029 RIGHT PARENTHESIS */, + asterisk = 0x002a /* U+002A ASTERISK */, + plus = 0x002b /* U+002B PLUS SIGN */, + comma = 0x002c /* U+002C COMMA */, + minus = 0x002d /* U+002D HYPHEN-MINUS */, + period = 0x002e /* U+002E FULL STOP */, + slash = 0x002f /* U+002F SOLIDUS */, + XK_0 = 0x0030 /* U+0030 DIGIT ZERO */, + XK_1 = 0x0031 /* U+0031 DIGIT ONE */, + XK_2 = 0x0032 /* U+0032 DIGIT TWO */, + XK_3 = 0x0033 /* U+0033 DIGIT THREE */, + XK_4 = 0x0034 /* U+0034 DIGIT FOUR */, + XK_5 = 0x0035 /* U+0035 DIGIT FIVE */, + XK_6 = 0x0036 /* U+0036 DIGIT SIX */, + XK_7 = 0x0037 /* U+0037 DIGIT SEVEN */, + XK_8 = 0x0038 /* U+0038 DIGIT EIGHT */, + XK_9 = 0x0039 /* U+0039 DIGIT NINE */, + colon = 0x003a /* U+003A COLON */, + semicolon = 0x003b /* U+003B SEMICOLON */, + less = 0x003c /* U+003C LESS-THAN SIGN */, + equal = 0x003d /* U+003D EQUALS SIGN */, + greater = 0x003e /* U+003E GREATER-THAN SIGN */, + question = 0x003f /* U+003F QUESTION MARK */, + at = 0x0040 /* U+0040 COMMERCIAL AT */, + A = 0x0041 /* U+0041 LATIN CAPITAL LETTER A */, + B = 0x0042 /* U+0042 LATIN CAPITAL LETTER B */, + C = 0x0043 /* U+0043 LATIN CAPITAL LETTER C */, + D = 0x0044 /* U+0044 LATIN CAPITAL LETTER D */, + E = 0x0045 /* U+0045 LATIN CAPITAL LETTER E */, + F = 0x0046 /* U+0046 LATIN CAPITAL LETTER F */, + G = 0x0047 /* U+0047 LATIN CAPITAL LETTER G */, + H = 0x0048 /* U+0048 LATIN CAPITAL LETTER H */, + I = 0x0049 /* U+0049 LATIN CAPITAL LETTER I */, + J = 0x004a /* U+004A LATIN CAPITAL LETTER J */, + K = 0x004b /* U+004B LATIN CAPITAL LETTER K */, + L = 0x004c /* U+004C LATIN CAPITAL LETTER L */, + M = 0x004d /* U+004D LATIN CAPITAL LETTER M */, + N = 0x004e /* U+004E LATIN CAPITAL LETTER N */, + O = 0x004f /* U+004F LATIN CAPITAL LETTER O */, + P = 0x0050 /* U+0050 LATIN CAPITAL LETTER P */, + Q = 0x0051 /* U+0051 LATIN CAPITAL LETTER Q */, + R = 0x0052 /* U+0052 LATIN CAPITAL LETTER R */, + S = 0x0053 /* U+0053 LATIN CAPITAL LETTER S */, + T = 0x0054 /* U+0054 LATIN CAPITAL LETTER T */, + U = 0x0055 /* U+0055 LATIN CAPITAL LETTER U */, + V = 0x0056 /* U+0056 LATIN CAPITAL LETTER V */, + W = 0x0057 /* U+0057 LATIN CAPITAL LETTER W */, + X = 0x0058 /* U+0058 LATIN CAPITAL LETTER X */, + Y = 0x0059 /* U+0059 LATIN CAPITAL LETTER Y */, + Z = 0x005a /* U+005A LATIN CAPITAL LETTER Z */, + bracketleft = 0x005b /* U+005B LEFT SQUARE BRACKET */, + backslash = 0x005c /* U+005C REVERSE SOLIDUS */, + bracketright = 0x005d /* U+005D RIGHT SQUARE BRACKET */, + asciicircum = 0x005e /* U+005E CIRCUMFLEX ACCENT */, + underscore = 0x005f /* U+005F LOW LINE */, + grave = 0x0060 /* U+0060 GRAVE ACCENT */, + quoteleft = 0x0060 /* deprecated */, + a = 0x0061 /* U+0061 LATIN SMALL LETTER A */, + b = 0x0062 /* U+0062 LATIN SMALL LETTER B */, + c = 0x0063 /* U+0063 LATIN SMALL LETTER C */, + d = 0x0064 /* U+0064 LATIN SMALL LETTER D */, + e = 0x0065 /* U+0065 LATIN SMALL LETTER E */, + f = 0x0066 /* U+0066 LATIN SMALL LETTER F */, + g = 0x0067 /* U+0067 LATIN SMALL LETTER G */, + h = 0x0068 /* U+0068 LATIN SMALL LETTER H */, + i = 0x0069 /* U+0069 LATIN SMALL LETTER I */, + j = 0x006a /* U+006A LATIN SMALL LETTER J */, + k = 0x006b /* U+006B LATIN SMALL LETTER K */, + l = 0x006c /* U+006C LATIN SMALL LETTER L */, + m = 0x006d /* U+006D LATIN SMALL LETTER M */, + n = 0x006e /* U+006E LATIN SMALL LETTER N */, + o = 0x006f /* U+006F LATIN SMALL LETTER O */, + p = 0x0070 /* U+0070 LATIN SMALL LETTER P */, + q = 0x0071 /* U+0071 LATIN SMALL LETTER Q */, + r = 0x0072 /* U+0072 LATIN SMALL LETTER R */, + s = 0x0073 /* U+0073 LATIN SMALL LETTER S */, + t = 0x0074 /* U+0074 LATIN SMALL LETTER T */, + u = 0x0075 /* U+0075 LATIN SMALL LETTER U */, + v = 0x0076 /* U+0076 LATIN SMALL LETTER V */, + w = 0x0077 /* U+0077 LATIN SMALL LETTER W */, + x = 0x0078 /* U+0078 LATIN SMALL LETTER X */, + y = 0x0079 /* U+0079 LATIN SMALL LETTER Y */, + z = 0x007a /* U+007A LATIN SMALL LETTER Z */, + braceleft = 0x007b /* U+007B LEFT CURLY BRACKET */, + bar = 0x007c /* U+007C VERTICAL LINE */, + braceright = 0x007d /* U+007D RIGHT CURLY BRACKET */, + asciitilde = 0x007e /* U+007E TILDE */, + nobreakspace = 0x00a0 /* U+00A0 NO-BREAK SPACE */, + exclamdown = 0x00a1 /* U+00A1 INVERTED EXCLAMATION MARK */, + cent = 0x00a2 /* U+00A2 CENT SIGN */, + sterling = 0x00a3 /* U+00A3 POUND SIGN */, + currency = 0x00a4 /* U+00A4 CURRENCY SIGN */, + yen = 0x00a5 /* U+00A5 YEN SIGN */, + brokenbar = 0x00a6 /* U+00A6 BROKEN BAR */, + section = 0x00a7 /* U+00A7 SECTION SIGN */, + diaeresis = 0x00a8 /* U+00A8 DIAERESIS */, + copyright = 0x00a9 /* U+00A9 COPYRIGHT SIGN */, + ordfeminine = 0x00aa /* U+00AA FEMININE ORDINAL INDICATOR */, + guillemotleft = 0x00ab /* U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK */, + notsign = 0x00ac /* U+00AC NOT SIGN */, + hyphen = 0x00ad /* U+00AD SOFT HYPHEN */, + registered = 0x00ae /* U+00AE REGISTERED SIGN */, + macron = 0x00af /* U+00AF MACRON */, + degree = 0x00b0 /* U+00B0 DEGREE SIGN */, + plusminus = 0x00b1 /* U+00B1 PLUS-MINUS SIGN */, + twosuperior = 0x00b2 /* U+00B2 SUPERSCRIPT TWO */, + threesuperior = 0x00b3 /* U+00B3 SUPERSCRIPT THREE */, + acute = 0x00b4 /* U+00B4 ACUTE ACCENT */, + mu = 0x00b5 /* U+00B5 MICRO SIGN */, + paragraph = 0x00b6 /* U+00B6 PILCROW SIGN */, + periodcentered = 0x00b7 /* U+00B7 MIDDLE DOT */, + cedilla = 0x00b8 /* U+00B8 CEDILLA */, + onesuperior = 0x00b9 /* U+00B9 SUPERSCRIPT ONE */, + masculine = 0x00ba /* U+00BA MASCULINE ORDINAL INDICATOR */, + guillemotright = 0x00bb /* U+00BB RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK */, + onequarter = 0x00bc /* U+00BC VULGAR FRACTION ONE QUARTER */, + onehalf = 0x00bd /* U+00BD VULGAR FRACTION ONE HALF */, + threequarters = 0x00be /* U+00BE VULGAR FRACTION THREE QUARTERS */, + questiondown = 0x00bf /* U+00BF INVERTED QUESTION MARK */, + Agrave = 0x00c0 /* U+00C0 LATIN CAPITAL LETTER A WITH GRAVE */, + Aacute = 0x00c1 /* U+00C1 LATIN CAPITAL LETTER A WITH ACUTE */, + Acircumflex = 0x00c2 /* U+00C2 LATIN CAPITAL LETTER A WITH CIRCUMFLEX */, + Atilde = 0x00c3 /* U+00C3 LATIN CAPITAL LETTER A WITH TILDE */, + Adiaeresis = 0x00c4 /* U+00C4 LATIN CAPITAL LETTER A WITH DIAERESIS */, + Aring = 0x00c5 /* U+00C5 LATIN CAPITAL LETTER A WITH RING ABOVE */, + AE = 0x00c6 /* U+00C6 LATIN CAPITAL LETTER AE */, + Ccedilla = 0x00c7 /* U+00C7 LATIN CAPITAL LETTER C WITH CEDILLA */, + Egrave = 0x00c8 /* U+00C8 LATIN CAPITAL LETTER E WITH GRAVE */, + Eacute = 0x00c9 /* U+00C9 LATIN CAPITAL LETTER E WITH ACUTE */, + Ecircumflex = 0x00ca /* U+00CA LATIN CAPITAL LETTER E WITH CIRCUMFLEX */, + Ediaeresis = 0x00cb /* U+00CB LATIN CAPITAL LETTER E WITH DIAERESIS */, + Igrave = 0x00cc /* U+00CC LATIN CAPITAL LETTER I WITH GRAVE */, + Iacute = 0x00cd /* U+00CD LATIN CAPITAL LETTER I WITH ACUTE */, + Icircumflex = 0x00ce /* U+00CE LATIN CAPITAL LETTER I WITH CIRCUMFLEX */, + Idiaeresis = 0x00cf /* U+00CF LATIN CAPITAL LETTER I WITH DIAERESIS */, + ETH = 0x00d0 /* U+00D0 LATIN CAPITAL LETTER ETH */, + Eth = 0x00d0 /* deprecated */, + Ntilde = 0x00d1 /* U+00D1 LATIN CAPITAL LETTER N WITH TILDE */, + Ograve = 0x00d2 /* U+00D2 LATIN CAPITAL LETTER O WITH GRAVE */, + Oacute = 0x00d3 /* U+00D3 LATIN CAPITAL LETTER O WITH ACUTE */, + Ocircumflex = 0x00d4 /* U+00D4 LATIN CAPITAL LETTER O WITH CIRCUMFLEX */, + Otilde = 0x00d5 /* U+00D5 LATIN CAPITAL LETTER O WITH TILDE */, + Odiaeresis = 0x00d6 /* U+00D6 LATIN CAPITAL LETTER O WITH DIAERESIS */, + multiply = 0x00d7 /* U+00D7 MULTIPLICATION SIGN */, + Oslash = 0x00d8 /* U+00D8 LATIN CAPITAL LETTER O WITH STROKE */, + Ooblique = 0x00d8 /* U+00D8 LATIN CAPITAL LETTER O WITH STROKE */, + Ugrave = 0x00d9 /* U+00D9 LATIN CAPITAL LETTER U WITH GRAVE */, + Uacute = 0x00da /* U+00DA LATIN CAPITAL LETTER U WITH ACUTE */, + Ucircumflex = 0x00db /* U+00DB LATIN CAPITAL LETTER U WITH CIRCUMFLEX */, + Udiaeresis = 0x00dc /* U+00DC LATIN CAPITAL LETTER U WITH DIAERESIS */, + Yacute = 0x00dd /* U+00DD LATIN CAPITAL LETTER Y WITH ACUTE */, + THORN = 0x00de /* U+00DE LATIN CAPITAL LETTER THORN */, + Thorn = 0x00de /* deprecated */, + ssharp = 0x00df /* U+00DF LATIN SMALL LETTER SHARP S */, + agrave = 0x00e0 /* U+00E0 LATIN SMALL LETTER A WITH GRAVE */, + aacute = 0x00e1 /* U+00E1 LATIN SMALL LETTER A WITH ACUTE */, + acircumflex = 0x00e2 /* U+00E2 LATIN SMALL LETTER A WITH CIRCUMFLEX */, + atilde = 0x00e3 /* U+00E3 LATIN SMALL LETTER A WITH TILDE */, + adiaeresis = 0x00e4 /* U+00E4 LATIN SMALL LETTER A WITH DIAERESIS */, + aring = 0x00e5 /* U+00E5 LATIN SMALL LETTER A WITH RING ABOVE */, + ae = 0x00e6 /* U+00E6 LATIN SMALL LETTER AE */, + ccedilla = 0x00e7 /* U+00E7 LATIN SMALL LETTER C WITH CEDILLA */, + egrave = 0x00e8 /* U+00E8 LATIN SMALL LETTER E WITH GRAVE */, + eacute = 0x00e9 /* U+00E9 LATIN SMALL LETTER E WITH ACUTE */, + ecircumflex = 0x00ea /* U+00EA LATIN SMALL LETTER E WITH CIRCUMFLEX */, + ediaeresis = 0x00eb /* U+00EB LATIN SMALL LETTER E WITH DIAERESIS */, + igrave = 0x00ec /* U+00EC LATIN SMALL LETTER I WITH GRAVE */, + iacute = 0x00ed /* U+00ED LATIN SMALL LETTER I WITH ACUTE */, + icircumflex = 0x00ee /* U+00EE LATIN SMALL LETTER I WITH CIRCUMFLEX */, + idiaeresis = 0x00ef /* U+00EF LATIN SMALL LETTER I WITH DIAERESIS */, + eth = 0x00f0 /* U+00F0 LATIN SMALL LETTER ETH */, + ntilde = 0x00f1 /* U+00F1 LATIN SMALL LETTER N WITH TILDE */, + ograve = 0x00f2 /* U+00F2 LATIN SMALL LETTER O WITH GRAVE */, + oacute = 0x00f3 /* U+00F3 LATIN SMALL LETTER O WITH ACUTE */, + ocircumflex = 0x00f4 /* U+00F4 LATIN SMALL LETTER O WITH CIRCUMFLEX */, + otilde = 0x00f5 /* U+00F5 LATIN SMALL LETTER O WITH TILDE */, + odiaeresis = 0x00f6 /* U+00F6 LATIN SMALL LETTER O WITH DIAERESIS */, + division = 0x00f7 /* U+00F7 DIVISION SIGN */, + oslash = 0x00f8 /* U+00F8 LATIN SMALL LETTER O WITH STROKE */, + ooblique = 0x00f8 /* U+00F8 LATIN SMALL LETTER O WITH STROKE */, + ugrave = 0x00f9 /* U+00F9 LATIN SMALL LETTER U WITH GRAVE */, + uacute = 0x00fa /* U+00FA LATIN SMALL LETTER U WITH ACUTE */, + ucircumflex = 0x00fb /* U+00FB LATIN SMALL LETTER U WITH CIRCUMFLEX */, + udiaeresis = 0x00fc /* U+00FC LATIN SMALL LETTER U WITH DIAERESIS */, + yacute = 0x00fd /* U+00FD LATIN SMALL LETTER Y WITH ACUTE */, + thorn = 0x00fe /* U+00FE LATIN SMALL LETTER THORN */, + ydiaeresis = 0x00ff /* U+00FF LATIN SMALL LETTER Y WITH DIAERESIS */, + Aogonek = 0x01a1 /* U+0104 LATIN CAPITAL LETTER A WITH OGONEK */, + breve = 0x01a2 /* U+02D8 BREVE */, + Lstroke = 0x01a3 /* U+0141 LATIN CAPITAL LETTER L WITH STROKE */, + Lcaron = 0x01a5 /* U+013D LATIN CAPITAL LETTER L WITH CARON */, + Sacute = 0x01a6 /* U+015A LATIN CAPITAL LETTER S WITH ACUTE */, + Scaron = 0x01a9 /* U+0160 LATIN CAPITAL LETTER S WITH CARON */, + Scedilla = 0x01aa /* U+015E LATIN CAPITAL LETTER S WITH CEDILLA */, + Tcaron = 0x01ab /* U+0164 LATIN CAPITAL LETTER T WITH CARON */, + Zacute = 0x01ac /* U+0179 LATIN CAPITAL LETTER Z WITH ACUTE */, + Zcaron = 0x01ae /* U+017D LATIN CAPITAL LETTER Z WITH CARON */, + Zabovedot = 0x01af /* U+017B LATIN CAPITAL LETTER Z WITH DOT ABOVE */, + aogonek = 0x01b1 /* U+0105 LATIN SMALL LETTER A WITH OGONEK */, + ogonek = 0x01b2 /* U+02DB OGONEK */, + lstroke = 0x01b3 /* U+0142 LATIN SMALL LETTER L WITH STROKE */, + lcaron = 0x01b5 /* U+013E LATIN SMALL LETTER L WITH CARON */, + sacute = 0x01b6 /* U+015B LATIN SMALL LETTER S WITH ACUTE */, + caron = 0x01b7 /* U+02C7 CARON */, + scaron = 0x01b9 /* U+0161 LATIN SMALL LETTER S WITH CARON */, + scedilla = 0x01ba /* U+015F LATIN SMALL LETTER S WITH CEDILLA */, + tcaron = 0x01bb /* U+0165 LATIN SMALL LETTER T WITH CARON */, + zacute = 0x01bc /* U+017A LATIN SMALL LETTER Z WITH ACUTE */, + doubleacute = 0x01bd /* U+02DD DOUBLE ACUTE ACCENT */, + zcaron = 0x01be /* U+017E LATIN SMALL LETTER Z WITH CARON */, + zabovedot = 0x01bf /* U+017C LATIN SMALL LETTER Z WITH DOT ABOVE */, + Racute = 0x01c0 /* U+0154 LATIN CAPITAL LETTER R WITH ACUTE */, + Abreve = 0x01c3 /* U+0102 LATIN CAPITAL LETTER A WITH BREVE */, + Lacute = 0x01c5 /* U+0139 LATIN CAPITAL LETTER L WITH ACUTE */, + Cacute = 0x01c6 /* U+0106 LATIN CAPITAL LETTER C WITH ACUTE */, + Ccaron = 0x01c8 /* U+010C LATIN CAPITAL LETTER C WITH CARON */, + Eogonek = 0x01ca /* U+0118 LATIN CAPITAL LETTER E WITH OGONEK */, + Ecaron = 0x01cc /* U+011A LATIN CAPITAL LETTER E WITH CARON */, + Dcaron = 0x01cf /* U+010E LATIN CAPITAL LETTER D WITH CARON */, + Dstroke = 0x01d0 /* U+0110 LATIN CAPITAL LETTER D WITH STROKE */, + Nacute = 0x01d1 /* U+0143 LATIN CAPITAL LETTER N WITH ACUTE */, + Ncaron = 0x01d2 /* U+0147 LATIN CAPITAL LETTER N WITH CARON */, + Odoubleacute = 0x01d5 /* U+0150 LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */, + Rcaron = 0x01d8 /* U+0158 LATIN CAPITAL LETTER R WITH CARON */, + Uring = 0x01d9 /* U+016E LATIN CAPITAL LETTER U WITH RING ABOVE */, + Udoubleacute = 0x01db /* U+0170 LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */, + Tcedilla = 0x01de /* U+0162 LATIN CAPITAL LETTER T WITH CEDILLA */, + racute = 0x01e0 /* U+0155 LATIN SMALL LETTER R WITH ACUTE */, + abreve = 0x01e3 /* U+0103 LATIN SMALL LETTER A WITH BREVE */, + lacute = 0x01e5 /* U+013A LATIN SMALL LETTER L WITH ACUTE */, + cacute = 0x01e6 /* U+0107 LATIN SMALL LETTER C WITH ACUTE */, + ccaron = 0x01e8 /* U+010D LATIN SMALL LETTER C WITH CARON */, + eogonek = 0x01ea /* U+0119 LATIN SMALL LETTER E WITH OGONEK */, + ecaron = 0x01ec /* U+011B LATIN SMALL LETTER E WITH CARON */, + dcaron = 0x01ef /* U+010F LATIN SMALL LETTER D WITH CARON */, + dstroke = 0x01f0 /* U+0111 LATIN SMALL LETTER D WITH STROKE */, + nacute = 0x01f1 /* U+0144 LATIN SMALL LETTER N WITH ACUTE */, + ncaron = 0x01f2 /* U+0148 LATIN SMALL LETTER N WITH CARON */, + odoubleacute = 0x01f5 /* U+0151 LATIN SMALL LETTER O WITH DOUBLE ACUTE */, + rcaron = 0x01f8 /* U+0159 LATIN SMALL LETTER R WITH CARON */, + uring = 0x01f9 /* U+016F LATIN SMALL LETTER U WITH RING ABOVE */, + udoubleacute = 0x01fb /* U+0171 LATIN SMALL LETTER U WITH DOUBLE ACUTE */, + tcedilla = 0x01fe /* U+0163 LATIN SMALL LETTER T WITH CEDILLA */, + abovedot = 0x01ff /* U+02D9 DOT ABOVE */, + Hstroke = 0x02a1 /* U+0126 LATIN CAPITAL LETTER H WITH STROKE */, + Hcircumflex = 0x02a6 /* U+0124 LATIN CAPITAL LETTER H WITH CIRCUMFLEX */, + Iabovedot = 0x02a9 /* U+0130 LATIN CAPITAL LETTER I WITH DOT ABOVE */, + Gbreve = 0x02ab /* U+011E LATIN CAPITAL LETTER G WITH BREVE */, + Jcircumflex = 0x02ac /* U+0134 LATIN CAPITAL LETTER J WITH CIRCUMFLEX */, + hstroke = 0x02b1 /* U+0127 LATIN SMALL LETTER H WITH STROKE */, + hcircumflex = 0x02b6 /* U+0125 LATIN SMALL LETTER H WITH CIRCUMFLEX */, + idotless = 0x02b9 /* U+0131 LATIN SMALL LETTER DOTLESS I */, + gbreve = 0x02bb /* U+011F LATIN SMALL LETTER G WITH BREVE */, + jcircumflex = 0x02bc /* U+0135 LATIN SMALL LETTER J WITH CIRCUMFLEX */, + Cabovedot = 0x02c5 /* U+010A LATIN CAPITAL LETTER C WITH DOT ABOVE */, + Ccircumflex = 0x02c6 /* U+0108 LATIN CAPITAL LETTER C WITH CIRCUMFLEX */, + Gabovedot = 0x02d5 /* U+0120 LATIN CAPITAL LETTER G WITH DOT ABOVE */, + Gcircumflex = 0x02d8 /* U+011C LATIN CAPITAL LETTER G WITH CIRCUMFLEX */, + Ubreve = 0x02dd /* U+016C LATIN CAPITAL LETTER U WITH BREVE */, + Scircumflex = 0x02de /* U+015C LATIN CAPITAL LETTER S WITH CIRCUMFLEX */, + cabovedot = 0x02e5 /* U+010B LATIN SMALL LETTER C WITH DOT ABOVE */, + ccircumflex = 0x02e6 /* U+0109 LATIN SMALL LETTER C WITH CIRCUMFLEX */, + gabovedot = 0x02f5 /* U+0121 LATIN SMALL LETTER G WITH DOT ABOVE */, + gcircumflex = 0x02f8 /* U+011D LATIN SMALL LETTER G WITH CIRCUMFLEX */, + ubreve = 0x02fd /* U+016D LATIN SMALL LETTER U WITH BREVE */, + scircumflex = 0x02fe /* U+015D LATIN SMALL LETTER S WITH CIRCUMFLEX */, + kra = 0x03a2 /* U+0138 LATIN SMALL LETTER KRA */, + kappa = 0x03a2 /* deprecated */, + Rcedilla = 0x03a3 /* U+0156 LATIN CAPITAL LETTER R WITH CEDILLA */, + Itilde = 0x03a5 /* U+0128 LATIN CAPITAL LETTER I WITH TILDE */, + Lcedilla = 0x03a6 /* U+013B LATIN CAPITAL LETTER L WITH CEDILLA */, + Emacron = 0x03aa /* U+0112 LATIN CAPITAL LETTER E WITH MACRON */, + Gcedilla = 0x03ab /* U+0122 LATIN CAPITAL LETTER G WITH CEDILLA */, + Tslash = 0x03ac /* U+0166 LATIN CAPITAL LETTER T WITH STROKE */, + rcedilla = 0x03b3 /* U+0157 LATIN SMALL LETTER R WITH CEDILLA */, + itilde = 0x03b5 /* U+0129 LATIN SMALL LETTER I WITH TILDE */, + lcedilla = 0x03b6 /* U+013C LATIN SMALL LETTER L WITH CEDILLA */, + emacron = 0x03ba /* U+0113 LATIN SMALL LETTER E WITH MACRON */, + gcedilla = 0x03bb /* U+0123 LATIN SMALL LETTER G WITH CEDILLA */, + tslash = 0x03bc /* U+0167 LATIN SMALL LETTER T WITH STROKE */, + ENG = 0x03bd /* U+014A LATIN CAPITAL LETTER ENG */, + eng = 0x03bf /* U+014B LATIN SMALL LETTER ENG */, + Amacron = 0x03c0 /* U+0100 LATIN CAPITAL LETTER A WITH MACRON */, + Iogonek = 0x03c7 /* U+012E LATIN CAPITAL LETTER I WITH OGONEK */, + Eabovedot = 0x03cc /* U+0116 LATIN CAPITAL LETTER E WITH DOT ABOVE */, + Imacron = 0x03cf /* U+012A LATIN CAPITAL LETTER I WITH MACRON */, + Ncedilla = 0x03d1 /* U+0145 LATIN CAPITAL LETTER N WITH CEDILLA */, + Omacron = 0x03d2 /* U+014C LATIN CAPITAL LETTER O WITH MACRON */, + Kcedilla = 0x03d3 /* U+0136 LATIN CAPITAL LETTER K WITH CEDILLA */, + Uogonek = 0x03d9 /* U+0172 LATIN CAPITAL LETTER U WITH OGONEK */, + Utilde = 0x03dd /* U+0168 LATIN CAPITAL LETTER U WITH TILDE */, + Umacron = 0x03de /* U+016A LATIN CAPITAL LETTER U WITH MACRON */, + amacron = 0x03e0 /* U+0101 LATIN SMALL LETTER A WITH MACRON */, + iogonek = 0x03e7 /* U+012F LATIN SMALL LETTER I WITH OGONEK */, + eabovedot = 0x03ec /* U+0117 LATIN SMALL LETTER E WITH DOT ABOVE */, + imacron = 0x03ef /* U+012B LATIN SMALL LETTER I WITH MACRON */, + ncedilla = 0x03f1 /* U+0146 LATIN SMALL LETTER N WITH CEDILLA */, + omacron = 0x03f2 /* U+014D LATIN SMALL LETTER O WITH MACRON */, + kcedilla = 0x03f3 /* U+0137 LATIN SMALL LETTER K WITH CEDILLA */, + uogonek = 0x03f9 /* U+0173 LATIN SMALL LETTER U WITH OGONEK */, + utilde = 0x03fd /* U+0169 LATIN SMALL LETTER U WITH TILDE */, + umacron = 0x03fe /* U+016B LATIN SMALL LETTER U WITH MACRON */, + Wcircumflex = 0x1000174 /* U+0174 LATIN CAPITAL LETTER W WITH CIRCUMFLEX */, + wcircumflex = 0x1000175 /* U+0175 LATIN SMALL LETTER W WITH CIRCUMFLEX */, + Ycircumflex = 0x1000176 /* U+0176 LATIN CAPITAL LETTER Y WITH CIRCUMFLEX */, + ycircumflex = 0x1000177 /* U+0177 LATIN SMALL LETTER Y WITH CIRCUMFLEX */, + Babovedot = 0x1001e02 /* U+1E02 LATIN CAPITAL LETTER B WITH DOT ABOVE */, + babovedot = 0x1001e03 /* U+1E03 LATIN SMALL LETTER B WITH DOT ABOVE */, + Dabovedot = 0x1001e0a /* U+1E0A LATIN CAPITAL LETTER D WITH DOT ABOVE */, + dabovedot = 0x1001e0b /* U+1E0B LATIN SMALL LETTER D WITH DOT ABOVE */, + Fabovedot = 0x1001e1e /* U+1E1E LATIN CAPITAL LETTER F WITH DOT ABOVE */, + fabovedot = 0x1001e1f /* U+1E1F LATIN SMALL LETTER F WITH DOT ABOVE */, + Mabovedot = 0x1001e40 /* U+1E40 LATIN CAPITAL LETTER M WITH DOT ABOVE */, + mabovedot = 0x1001e41 /* U+1E41 LATIN SMALL LETTER M WITH DOT ABOVE */, + Pabovedot = 0x1001e56 /* U+1E56 LATIN CAPITAL LETTER P WITH DOT ABOVE */, + pabovedot = 0x1001e57 /* U+1E57 LATIN SMALL LETTER P WITH DOT ABOVE */, + Sabovedot = 0x1001e60 /* U+1E60 LATIN CAPITAL LETTER S WITH DOT ABOVE */, + sabovedot = 0x1001e61 /* U+1E61 LATIN SMALL LETTER S WITH DOT ABOVE */, + Tabovedot = 0x1001e6a /* U+1E6A LATIN CAPITAL LETTER T WITH DOT ABOVE */, + tabovedot = 0x1001e6b /* U+1E6B LATIN SMALL LETTER T WITH DOT ABOVE */, + Wgrave = 0x1001e80 /* U+1E80 LATIN CAPITAL LETTER W WITH GRAVE */, + wgrave = 0x1001e81 /* U+1E81 LATIN SMALL LETTER W WITH GRAVE */, + Wacute = 0x1001e82 /* U+1E82 LATIN CAPITAL LETTER W WITH ACUTE */, + wacute = 0x1001e83 /* U+1E83 LATIN SMALL LETTER W WITH ACUTE */, + Wdiaeresis = 0x1001e84 /* U+1E84 LATIN CAPITAL LETTER W WITH DIAERESIS */, + wdiaeresis = 0x1001e85 /* U+1E85 LATIN SMALL LETTER W WITH DIAERESIS */, + Ygrave = 0x1001ef2 /* U+1EF2 LATIN CAPITAL LETTER Y WITH GRAVE */, + ygrave = 0x1001ef3 /* U+1EF3 LATIN SMALL LETTER Y WITH GRAVE */, + OE = 0x13bc /* U+0152 LATIN CAPITAL LIGATURE OE */, + oe = 0x13bd /* U+0153 LATIN SMALL LIGATURE OE */, + Ydiaeresis = 0x13be /* U+0178 LATIN CAPITAL LETTER Y WITH DIAERESIS */, + overline = 0x047e /* U+203E OVERLINE */, + kana_fullstop = 0x04a1 /* U+3002 IDEOGRAPHIC FULL STOP */, + kana_openingbracket = 0x04a2 /* U+300C LEFT CORNER BRACKET */, + kana_closingbracket = 0x04a3 /* U+300D RIGHT CORNER BRACKET */, + kana_comma = 0x04a4 /* U+3001 IDEOGRAPHIC COMMA */, + kana_conjunctive = 0x04a5 /* U+30FB KATAKANA MIDDLE DOT */, + kana_middledot = 0x04a5 /* deprecated */, + kana_WO = 0x04a6 /* U+30F2 KATAKANA LETTER WO */, + kana_a = 0x04a7 /* U+30A1 KATAKANA LETTER SMALL A */, + kana_i = 0x04a8 /* U+30A3 KATAKANA LETTER SMALL I */, + kana_u = 0x04a9 /* U+30A5 KATAKANA LETTER SMALL U */, + kana_e = 0x04aa /* U+30A7 KATAKANA LETTER SMALL E */, + kana_o = 0x04ab /* U+30A9 KATAKANA LETTER SMALL O */, + kana_ya = 0x04ac /* U+30E3 KATAKANA LETTER SMALL YA */, + kana_yu = 0x04ad /* U+30E5 KATAKANA LETTER SMALL YU */, + kana_yo = 0x04ae /* U+30E7 KATAKANA LETTER SMALL YO */, + kana_tsu = 0x04af /* U+30C3 KATAKANA LETTER SMALL TU */, + kana_tu = 0x04af /* deprecated */, + prolongedsound = 0x04b0 /* U+30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK */, + kana_A = 0x04b1 /* U+30A2 KATAKANA LETTER A */, + kana_I = 0x04b2 /* U+30A4 KATAKANA LETTER I */, + kana_U = 0x04b3 /* U+30A6 KATAKANA LETTER U */, + kana_E = 0x04b4 /* U+30A8 KATAKANA LETTER E */, + kana_O = 0x04b5 /* U+30AA KATAKANA LETTER O */, + kana_KA = 0x04b6 /* U+30AB KATAKANA LETTER KA */, + kana_KI = 0x04b7 /* U+30AD KATAKANA LETTER KI */, + kana_KU = 0x04b8 /* U+30AF KATAKANA LETTER KU */, + kana_KE = 0x04b9 /* U+30B1 KATAKANA LETTER KE */, + kana_KO = 0x04ba /* U+30B3 KATAKANA LETTER KO */, + kana_SA = 0x04bb /* U+30B5 KATAKANA LETTER SA */, + kana_SHI = 0x04bc /* U+30B7 KATAKANA LETTER SI */, + kana_SU = 0x04bd /* U+30B9 KATAKANA LETTER SU */, + kana_SE = 0x04be /* U+30BB KATAKANA LETTER SE */, + kana_SO = 0x04bf /* U+30BD KATAKANA LETTER SO */, + kana_TA = 0x04c0 /* U+30BF KATAKANA LETTER TA */, + kana_CHI = 0x04c1 /* U+30C1 KATAKANA LETTER TI */, + kana_TI = 0x04c1 /* deprecated */, + kana_TSU = 0x04c2 /* U+30C4 KATAKANA LETTER TU */, + kana_TU = 0x04c2 /* deprecated */, + kana_TE = 0x04c3 /* U+30C6 KATAKANA LETTER TE */, + kana_TO = 0x04c4 /* U+30C8 KATAKANA LETTER TO */, + kana_NA = 0x04c5 /* U+30CA KATAKANA LETTER NA */, + kana_NI = 0x04c6 /* U+30CB KATAKANA LETTER NI */, + kana_NU = 0x04c7 /* U+30CC KATAKANA LETTER NU */, + kana_NE = 0x04c8 /* U+30CD KATAKANA LETTER NE */, + kana_NO = 0x04c9 /* U+30CE KATAKANA LETTER NO */, + kana_HA = 0x04ca /* U+30CF KATAKANA LETTER HA */, + kana_HI = 0x04cb /* U+30D2 KATAKANA LETTER HI */, + kana_FU = 0x04cc /* U+30D5 KATAKANA LETTER HU */, + kana_HU = 0x04cc /* deprecated */, + kana_HE = 0x04cd /* U+30D8 KATAKANA LETTER HE */, + kana_HO = 0x04ce /* U+30DB KATAKANA LETTER HO */, + kana_MA = 0x04cf /* U+30DE KATAKANA LETTER MA */, + kana_MI = 0x04d0 /* U+30DF KATAKANA LETTER MI */, + kana_MU = 0x04d1 /* U+30E0 KATAKANA LETTER MU */, + kana_ME = 0x04d2 /* U+30E1 KATAKANA LETTER ME */, + kana_MO = 0x04d3 /* U+30E2 KATAKANA LETTER MO */, + kana_YA = 0x04d4 /* U+30E4 KATAKANA LETTER YA */, + kana_YU = 0x04d5 /* U+30E6 KATAKANA LETTER YU */, + kana_YO = 0x04d6 /* U+30E8 KATAKANA LETTER YO */, + kana_RA = 0x04d7 /* U+30E9 KATAKANA LETTER RA */, + kana_RI = 0x04d8 /* U+30EA KATAKANA LETTER RI */, + kana_RU = 0x04d9 /* U+30EB KATAKANA LETTER RU */, + kana_RE = 0x04da /* U+30EC KATAKANA LETTER RE */, + kana_RO = 0x04db /* U+30ED KATAKANA LETTER RO */, + kana_WA = 0x04dc /* U+30EF KATAKANA LETTER WA */, + kana_N = 0x04dd /* U+30F3 KATAKANA LETTER N */, + voicedsound = 0x04de /* U+309B KATAKANA-HIRAGANA VOICED SOUND MARK */, + semivoicedsound = 0x04df /* U+309C KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */, + kana_switch = 0xff7e /* Alias for mode_switch */, + Farsi_0 = 0x10006f0 /* U+06F0 EXTENDED ARABIC-INDIC DIGIT ZERO */, + Farsi_1 = 0x10006f1 /* U+06F1 EXTENDED ARABIC-INDIC DIGIT ONE */, + Farsi_2 = 0x10006f2 /* U+06F2 EXTENDED ARABIC-INDIC DIGIT TWO */, + Farsi_3 = 0x10006f3 /* U+06F3 EXTENDED ARABIC-INDIC DIGIT THREE */, + Farsi_4 = 0x10006f4 /* U+06F4 EXTENDED ARABIC-INDIC DIGIT FOUR */, + Farsi_5 = 0x10006f5 /* U+06F5 EXTENDED ARABIC-INDIC DIGIT FIVE */, + Farsi_6 = 0x10006f6 /* U+06F6 EXTENDED ARABIC-INDIC DIGIT SIX */, + Farsi_7 = 0x10006f7 /* U+06F7 EXTENDED ARABIC-INDIC DIGIT SEVEN */, + Farsi_8 = 0x10006f8 /* U+06F8 EXTENDED ARABIC-INDIC DIGIT EIGHT */, + Farsi_9 = 0x10006f9 /* U+06F9 EXTENDED ARABIC-INDIC DIGIT NINE */, + Arabic_percent = 0x100066a /* U+066A ARABIC PERCENT SIGN */, + Arabic_superscript_alef = 0x1000670 /* U+0670 ARABIC LETTER SUPERSCRIPT ALEF */, + Arabic_tteh = 0x1000679 /* U+0679 ARABIC LETTER TTEH */, + Arabic_peh = 0x100067e /* U+067E ARABIC LETTER PEH */, + Arabic_tcheh = 0x1000686 /* U+0686 ARABIC LETTER TCHEH */, + Arabic_ddal = 0x1000688 /* U+0688 ARABIC LETTER DDAL */, + Arabic_rreh = 0x1000691 /* U+0691 ARABIC LETTER RREH */, + Arabic_comma = 0x05ac /* U+060C ARABIC COMMA */, + Arabic_fullstop = 0x10006d4 /* U+06D4 ARABIC FULL STOP */, + Arabic_0 = 0x1000660 /* U+0660 ARABIC-INDIC DIGIT ZERO */, + Arabic_1 = 0x1000661 /* U+0661 ARABIC-INDIC DIGIT ONE */, + Arabic_2 = 0x1000662 /* U+0662 ARABIC-INDIC DIGIT TWO */, + Arabic_3 = 0x1000663 /* U+0663 ARABIC-INDIC DIGIT THREE */, + Arabic_4 = 0x1000664 /* U+0664 ARABIC-INDIC DIGIT FOUR */, + Arabic_5 = 0x1000665 /* U+0665 ARABIC-INDIC DIGIT FIVE */, + Arabic_6 = 0x1000666 /* U+0666 ARABIC-INDIC DIGIT SIX */, + Arabic_7 = 0x1000667 /* U+0667 ARABIC-INDIC DIGIT SEVEN */, + Arabic_8 = 0x1000668 /* U+0668 ARABIC-INDIC DIGIT EIGHT */, + Arabic_9 = 0x1000669 /* U+0669 ARABIC-INDIC DIGIT NINE */, + Arabic_semicolon = 0x05bb /* U+061B ARABIC SEMICOLON */, + Arabic_question_mark = 0x05bf /* U+061F ARABIC QUESTION MARK */, + Arabic_hamza = 0x05c1 /* U+0621 ARABIC LETTER HAMZA */, + Arabic_maddaonalef = 0x05c2 /* U+0622 ARABIC LETTER ALEF WITH MADDA ABOVE */, + Arabic_hamzaonalef = 0x05c3 /* U+0623 ARABIC LETTER ALEF WITH HAMZA ABOVE */, + Arabic_hamzaonwaw = 0x05c4 /* U+0624 ARABIC LETTER WAW WITH HAMZA ABOVE */, + Arabic_hamzaunderalef = 0x05c5 /* U+0625 ARABIC LETTER ALEF WITH HAMZA BELOW */, + Arabic_hamzaonyeh = 0x05c6 /* U+0626 ARABIC LETTER YEH WITH HAMZA ABOVE */, + Arabic_alef = 0x05c7 /* U+0627 ARABIC LETTER ALEF */, + Arabic_beh = 0x05c8 /* U+0628 ARABIC LETTER BEH */, + Arabic_tehmarbuta = 0x05c9 /* U+0629 ARABIC LETTER TEH MARBUTA */, + Arabic_teh = 0x05ca /* U+062A ARABIC LETTER TEH */, + Arabic_theh = 0x05cb /* U+062B ARABIC LETTER THEH */, + Arabic_jeem = 0x05cc /* U+062C ARABIC LETTER JEEM */, + Arabic_hah = 0x05cd /* U+062D ARABIC LETTER HAH */, + Arabic_khah = 0x05ce /* U+062E ARABIC LETTER KHAH */, + Arabic_dal = 0x05cf /* U+062F ARABIC LETTER DAL */, + Arabic_thal = 0x05d0 /* U+0630 ARABIC LETTER THAL */, + Arabic_ra = 0x05d1 /* U+0631 ARABIC LETTER REH */, + Arabic_zain = 0x05d2 /* U+0632 ARABIC LETTER ZAIN */, + Arabic_seen = 0x05d3 /* U+0633 ARABIC LETTER SEEN */, + Arabic_sheen = 0x05d4 /* U+0634 ARABIC LETTER SHEEN */, + Arabic_sad = 0x05d5 /* U+0635 ARABIC LETTER SAD */, + Arabic_dad = 0x05d6 /* U+0636 ARABIC LETTER DAD */, + Arabic_tah = 0x05d7 /* U+0637 ARABIC LETTER TAH */, + Arabic_zah = 0x05d8 /* U+0638 ARABIC LETTER ZAH */, + Arabic_ain = 0x05d9 /* U+0639 ARABIC LETTER AIN */, + Arabic_ghain = 0x05da /* U+063A ARABIC LETTER GHAIN */, + Arabic_tatweel = 0x05e0 /* U+0640 ARABIC TATWEEL */, + Arabic_feh = 0x05e1 /* U+0641 ARABIC LETTER FEH */, + Arabic_qaf = 0x05e2 /* U+0642 ARABIC LETTER QAF */, + Arabic_kaf = 0x05e3 /* U+0643 ARABIC LETTER KAF */, + Arabic_lam = 0x05e4 /* U+0644 ARABIC LETTER LAM */, + Arabic_meem = 0x05e5 /* U+0645 ARABIC LETTER MEEM */, + Arabic_noon = 0x05e6 /* U+0646 ARABIC LETTER NOON */, + Arabic_ha = 0x05e7 /* U+0647 ARABIC LETTER HEH */, + Arabic_heh = 0x05e7 /* deprecated */, + Arabic_waw = 0x05e8 /* U+0648 ARABIC LETTER WAW */, + Arabic_alefmaksura = 0x05e9 /* U+0649 ARABIC LETTER ALEF MAKSURA */, + Arabic_yeh = 0x05ea /* U+064A ARABIC LETTER YEH */, + Arabic_fathatan = 0x05eb /* U+064B ARABIC FATHATAN */, + Arabic_dammatan = 0x05ec /* U+064C ARABIC DAMMATAN */, + Arabic_kasratan = 0x05ed /* U+064D ARABIC KASRATAN */, + Arabic_fatha = 0x05ee /* U+064E ARABIC FATHA */, + Arabic_damma = 0x05ef /* U+064F ARABIC DAMMA */, + Arabic_kasra = 0x05f0 /* U+0650 ARABIC KASRA */, + Arabic_shadda = 0x05f1 /* U+0651 ARABIC SHADDA */, + Arabic_sukun = 0x05f2 /* U+0652 ARABIC SUKUN */, + Arabic_madda_above = 0x1000653 /* U+0653 ARABIC MADDAH ABOVE */, + Arabic_hamza_above = 0x1000654 /* U+0654 ARABIC HAMZA ABOVE */, + Arabic_hamza_below = 0x1000655 /* U+0655 ARABIC HAMZA BELOW */, + Arabic_jeh = 0x1000698 /* U+0698 ARABIC LETTER JEH */, + Arabic_veh = 0x10006a4 /* U+06A4 ARABIC LETTER VEH */, + Arabic_keheh = 0x10006a9 /* U+06A9 ARABIC LETTER KEHEH */, + Arabic_gaf = 0x10006af /* U+06AF ARABIC LETTER GAF */, + Arabic_noon_ghunna = 0x10006ba /* U+06BA ARABIC LETTER NOON GHUNNA */, + Arabic_heh_doachashmee = 0x10006be /* U+06BE ARABIC LETTER HEH DOACHASHMEE */, + Farsi_yeh = 0x10006cc /* U+06CC ARABIC LETTER FARSI YEH */, + Arabic_farsi_yeh = 0x10006cc /* U+06CC ARABIC LETTER FARSI YEH */, + Arabic_yeh_baree = 0x10006d2 /* U+06D2 ARABIC LETTER YEH BARREE */, + Arabic_heh_goal = 0x10006c1 /* U+06C1 ARABIC LETTER HEH GOAL */, + Arabic_switch = 0xff7e /* Alias for mode_switch */, + Cyrillic_GHE_bar = 0x1000492 /* U+0492 CYRILLIC CAPITAL LETTER GHE WITH STROKE */, + Cyrillic_ghe_bar = 0x1000493 /* U+0493 CYRILLIC SMALL LETTER GHE WITH STROKE */, + Cyrillic_ZHE_descender = 0x1000496 /* U+0496 CYRILLIC CAPITAL LETTER ZHE WITH DESCENDER */, + Cyrillic_zhe_descender = 0x1000497 /* U+0497 CYRILLIC SMALL LETTER ZHE WITH DESCENDER */, + Cyrillic_KA_descender = 0x100049a /* U+049A CYRILLIC CAPITAL LETTER KA WITH DESCENDER */, + Cyrillic_ka_descender = 0x100049b /* U+049B CYRILLIC SMALL LETTER KA WITH DESCENDER */, + Cyrillic_KA_vertstroke = 0x100049c /* U+049C CYRILLIC CAPITAL LETTER KA WITH VERTICAL STROKE */, + Cyrillic_ka_vertstroke = 0x100049d /* U+049D CYRILLIC SMALL LETTER KA WITH VERTICAL STROKE */, + Cyrillic_EN_descender = 0x10004a2 /* U+04A2 CYRILLIC CAPITAL LETTER EN WITH DESCENDER */, + Cyrillic_en_descender = 0x10004a3 /* U+04A3 CYRILLIC SMALL LETTER EN WITH DESCENDER */, + Cyrillic_U_straight = 0x10004ae /* U+04AE CYRILLIC CAPITAL LETTER STRAIGHT U */, + Cyrillic_u_straight = 0x10004af /* U+04AF CYRILLIC SMALL LETTER STRAIGHT U */, + Cyrillic_U_straight_bar = 0x10004b0 /* U+04B0 CYRILLIC CAPITAL LETTER STRAIGHT U WITH STROKE */, + Cyrillic_u_straight_bar = 0x10004b1 /* U+04B1 CYRILLIC SMALL LETTER STRAIGHT U WITH STROKE */, + Cyrillic_HA_descender = 0x10004b2 /* U+04B2 CYRILLIC CAPITAL LETTER HA WITH DESCENDER */, + Cyrillic_ha_descender = 0x10004b3 /* U+04B3 CYRILLIC SMALL LETTER HA WITH DESCENDER */, + Cyrillic_CHE_descender = 0x10004b6 /* U+04B6 CYRILLIC CAPITAL LETTER CHE WITH DESCENDER */, + Cyrillic_che_descender = 0x10004b7 /* U+04B7 CYRILLIC SMALL LETTER CHE WITH DESCENDER */, + Cyrillic_CHE_vertstroke = 0x10004b8 /* U+04B8 CYRILLIC CAPITAL LETTER CHE WITH VERTICAL STROKE */, + Cyrillic_che_vertstroke = 0x10004b9 /* U+04B9 CYRILLIC SMALL LETTER CHE WITH VERTICAL STROKE */, + Cyrillic_SHHA = 0x10004ba /* U+04BA CYRILLIC CAPITAL LETTER SHHA */, + Cyrillic_shha = 0x10004bb /* U+04BB CYRILLIC SMALL LETTER SHHA */, + Cyrillic_SCHWA = 0x10004d8 /* U+04D8 CYRILLIC CAPITAL LETTER SCHWA */, + Cyrillic_schwa = 0x10004d9 /* U+04D9 CYRILLIC SMALL LETTER SCHWA */, + Cyrillic_I_macron = 0x10004e2 /* U+04E2 CYRILLIC CAPITAL LETTER I WITH MACRON */, + Cyrillic_i_macron = 0x10004e3 /* U+04E3 CYRILLIC SMALL LETTER I WITH MACRON */, + Cyrillic_O_bar = 0x10004e8 /* U+04E8 CYRILLIC CAPITAL LETTER BARRED O */, + Cyrillic_o_bar = 0x10004e9 /* U+04E9 CYRILLIC SMALL LETTER BARRED O */, + Cyrillic_U_macron = 0x10004ee /* U+04EE CYRILLIC CAPITAL LETTER U WITH MACRON */, + Cyrillic_u_macron = 0x10004ef /* U+04EF CYRILLIC SMALL LETTER U WITH MACRON */, + Serbian_dje = 0x06a1 /* U+0452 CYRILLIC SMALL LETTER DJE */, + Macedonia_gje = 0x06a2 /* U+0453 CYRILLIC SMALL LETTER GJE */, + Cyrillic_io = 0x06a3 /* U+0451 CYRILLIC SMALL LETTER IO */, + Ukrainian_ie = 0x06a4 /* U+0454 CYRILLIC SMALL LETTER UKRAINIAN IE */, + Ukranian_je = 0x06a4 /* deprecated */, + Macedonia_dse = 0x06a5 /* U+0455 CYRILLIC SMALL LETTER DZE */, + Ukrainian_i = 0x06a6 /* U+0456 CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I */, + Ukranian_i = 0x06a6 /* deprecated */, + Ukrainian_yi = 0x06a7 /* U+0457 CYRILLIC SMALL LETTER YI */, + Ukranian_yi = 0x06a7 /* deprecated */, + Cyrillic_je = 0x06a8 /* U+0458 CYRILLIC SMALL LETTER JE */, + Serbian_je = 0x06a8 /* deprecated */, + Cyrillic_lje = 0x06a9 /* U+0459 CYRILLIC SMALL LETTER LJE */, + Serbian_lje = 0x06a9 /* deprecated */, + Cyrillic_nje = 0x06aa /* U+045A CYRILLIC SMALL LETTER NJE */, + Serbian_nje = 0x06aa /* deprecated */, + Serbian_tshe = 0x06ab /* U+045B CYRILLIC SMALL LETTER TSHE */, + Macedonia_kje = 0x06ac /* U+045C CYRILLIC SMALL LETTER KJE */, + Ukrainian_ghe_with_upturn = 0x06ad /* U+0491 CYRILLIC SMALL LETTER GHE WITH UPTURN */, + Byelorussian_shortu = 0x06ae /* U+045E CYRILLIC SMALL LETTER SHORT U */, + Cyrillic_dzhe = 0x06af /* U+045F CYRILLIC SMALL LETTER DZHE */, + Serbian_dze = 0x06af /* deprecated */, + numerosign = 0x06b0 /* U+2116 NUMERO SIGN */, + Serbian_DJE = 0x06b1 /* U+0402 CYRILLIC CAPITAL LETTER DJE */, + Macedonia_GJE = 0x06b2 /* U+0403 CYRILLIC CAPITAL LETTER GJE */, + Cyrillic_IO = 0x06b3 /* U+0401 CYRILLIC CAPITAL LETTER IO */, + Ukrainian_IE = 0x06b4 /* U+0404 CYRILLIC CAPITAL LETTER UKRAINIAN IE */, + Ukranian_JE = 0x06b4 /* deprecated */, + Macedonia_DSE = 0x06b5 /* U+0405 CYRILLIC CAPITAL LETTER DZE */, + Ukrainian_I = 0x06b6 /* U+0406 CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I */, + Ukranian_I = 0x06b6 /* deprecated */, + Ukrainian_YI = 0x06b7 /* U+0407 CYRILLIC CAPITAL LETTER YI */, + Ukranian_YI = 0x06b7 /* deprecated */, + Cyrillic_JE = 0x06b8 /* U+0408 CYRILLIC CAPITAL LETTER JE */, + Serbian_JE = 0x06b8 /* deprecated */, + Cyrillic_LJE = 0x06b9 /* U+0409 CYRILLIC CAPITAL LETTER LJE */, + Serbian_LJE = 0x06b9 /* deprecated */, + Cyrillic_NJE = 0x06ba /* U+040A CYRILLIC CAPITAL LETTER NJE */, + Serbian_NJE = 0x06ba /* deprecated */, + Serbian_TSHE = 0x06bb /* U+040B CYRILLIC CAPITAL LETTER TSHE */, + Macedonia_KJE = 0x06bc /* U+040C CYRILLIC CAPITAL LETTER KJE */, + Ukrainian_GHE_WITH_UPTURN = 0x06bd /* U+0490 CYRILLIC CAPITAL LETTER GHE WITH UPTURN */, + Byelorussian_SHORTU = 0x06be /* U+040E CYRILLIC CAPITAL LETTER SHORT U */, + Cyrillic_DZHE = 0x06bf /* U+040F CYRILLIC CAPITAL LETTER DZHE */, + Serbian_DZE = 0x06bf /* deprecated */, + Cyrillic_yu = 0x06c0 /* U+044E CYRILLIC SMALL LETTER YU */, + Cyrillic_a = 0x06c1 /* U+0430 CYRILLIC SMALL LETTER A */, + Cyrillic_be = 0x06c2 /* U+0431 CYRILLIC SMALL LETTER BE */, + Cyrillic_tse = 0x06c3 /* U+0446 CYRILLIC SMALL LETTER TSE */, + Cyrillic_de = 0x06c4 /* U+0434 CYRILLIC SMALL LETTER DE */, + Cyrillic_ie = 0x06c5 /* U+0435 CYRILLIC SMALL LETTER IE */, + Cyrillic_ef = 0x06c6 /* U+0444 CYRILLIC SMALL LETTER EF */, + Cyrillic_ghe = 0x06c7 /* U+0433 CYRILLIC SMALL LETTER GHE */, + Cyrillic_ha = 0x06c8 /* U+0445 CYRILLIC SMALL LETTER HA */, + Cyrillic_i = 0x06c9 /* U+0438 CYRILLIC SMALL LETTER I */, + Cyrillic_shorti = 0x06ca /* U+0439 CYRILLIC SMALL LETTER SHORT I */, + Cyrillic_ka = 0x06cb /* U+043A CYRILLIC SMALL LETTER KA */, + Cyrillic_el = 0x06cc /* U+043B CYRILLIC SMALL LETTER EL */, + Cyrillic_em = 0x06cd /* U+043C CYRILLIC SMALL LETTER EM */, + Cyrillic_en = 0x06ce /* U+043D CYRILLIC SMALL LETTER EN */, + Cyrillic_o = 0x06cf /* U+043E CYRILLIC SMALL LETTER O */, + Cyrillic_pe = 0x06d0 /* U+043F CYRILLIC SMALL LETTER PE */, + Cyrillic_ya = 0x06d1 /* U+044F CYRILLIC SMALL LETTER YA */, + Cyrillic_er = 0x06d2 /* U+0440 CYRILLIC SMALL LETTER ER */, + Cyrillic_es = 0x06d3 /* U+0441 CYRILLIC SMALL LETTER ES */, + Cyrillic_te = 0x06d4 /* U+0442 CYRILLIC SMALL LETTER TE */, + Cyrillic_u = 0x06d5 /* U+0443 CYRILLIC SMALL LETTER U */, + Cyrillic_zhe = 0x06d6 /* U+0436 CYRILLIC SMALL LETTER ZHE */, + Cyrillic_ve = 0x06d7 /* U+0432 CYRILLIC SMALL LETTER VE */, + Cyrillic_softsign = 0x06d8 /* U+044C CYRILLIC SMALL LETTER SOFT SIGN */, + Cyrillic_yeru = 0x06d9 /* U+044B CYRILLIC SMALL LETTER YERU */, + Cyrillic_ze = 0x06da /* U+0437 CYRILLIC SMALL LETTER ZE */, + Cyrillic_sha = 0x06db /* U+0448 CYRILLIC SMALL LETTER SHA */, + Cyrillic_e = 0x06dc /* U+044D CYRILLIC SMALL LETTER E */, + Cyrillic_shcha = 0x06dd /* U+0449 CYRILLIC SMALL LETTER SHCHA */, + Cyrillic_che = 0x06de /* U+0447 CYRILLIC SMALL LETTER CHE */, + Cyrillic_hardsign = 0x06df /* U+044A CYRILLIC SMALL LETTER HARD SIGN */, + Cyrillic_YU = 0x06e0 /* U+042E CYRILLIC CAPITAL LETTER YU */, + Cyrillic_A = 0x06e1 /* U+0410 CYRILLIC CAPITAL LETTER A */, + Cyrillic_BE = 0x06e2 /* U+0411 CYRILLIC CAPITAL LETTER BE */, + Cyrillic_TSE = 0x06e3 /* U+0426 CYRILLIC CAPITAL LETTER TSE */, + Cyrillic_DE = 0x06e4 /* U+0414 CYRILLIC CAPITAL LETTER DE */, + Cyrillic_IE = 0x06e5 /* U+0415 CYRILLIC CAPITAL LETTER IE */, + Cyrillic_EF = 0x06e6 /* U+0424 CYRILLIC CAPITAL LETTER EF */, + Cyrillic_GHE = 0x06e7 /* U+0413 CYRILLIC CAPITAL LETTER GHE */, + Cyrillic_HA = 0x06e8 /* U+0425 CYRILLIC CAPITAL LETTER HA */, + Cyrillic_I = 0x06e9 /* U+0418 CYRILLIC CAPITAL LETTER I */, + Cyrillic_SHORTI = 0x06ea /* U+0419 CYRILLIC CAPITAL LETTER SHORT I */, + Cyrillic_KA = 0x06eb /* U+041A CYRILLIC CAPITAL LETTER KA */, + Cyrillic_EL = 0x06ec /* U+041B CYRILLIC CAPITAL LETTER EL */, + Cyrillic_EM = 0x06ed /* U+041C CYRILLIC CAPITAL LETTER EM */, + Cyrillic_EN = 0x06ee /* U+041D CYRILLIC CAPITAL LETTER EN */, + Cyrillic_O = 0x06ef /* U+041E CYRILLIC CAPITAL LETTER O */, + Cyrillic_PE = 0x06f0 /* U+041F CYRILLIC CAPITAL LETTER PE */, + Cyrillic_YA = 0x06f1 /* U+042F CYRILLIC CAPITAL LETTER YA */, + Cyrillic_ER = 0x06f2 /* U+0420 CYRILLIC CAPITAL LETTER ER */, + Cyrillic_ES = 0x06f3 /* U+0421 CYRILLIC CAPITAL LETTER ES */, + Cyrillic_TE = 0x06f4 /* U+0422 CYRILLIC CAPITAL LETTER TE */, + Cyrillic_U = 0x06f5 /* U+0423 CYRILLIC CAPITAL LETTER U */, + Cyrillic_ZHE = 0x06f6 /* U+0416 CYRILLIC CAPITAL LETTER ZHE */, + Cyrillic_VE = 0x06f7 /* U+0412 CYRILLIC CAPITAL LETTER VE */, + Cyrillic_SOFTSIGN = 0x06f8 /* U+042C CYRILLIC CAPITAL LETTER SOFT SIGN */, + Cyrillic_YERU = 0x06f9 /* U+042B CYRILLIC CAPITAL LETTER YERU */, + Cyrillic_ZE = 0x06fa /* U+0417 CYRILLIC CAPITAL LETTER ZE */, + Cyrillic_SHA = 0x06fb /* U+0428 CYRILLIC CAPITAL LETTER SHA */, + Cyrillic_E = 0x06fc /* U+042D CYRILLIC CAPITAL LETTER E */, + Cyrillic_SHCHA = 0x06fd /* U+0429 CYRILLIC CAPITAL LETTER SHCHA */, + Cyrillic_CHE = 0x06fe /* U+0427 CYRILLIC CAPITAL LETTER CHE */, + Cyrillic_HARDSIGN = 0x06ff /* U+042A CYRILLIC CAPITAL LETTER HARD SIGN */, + Greek_ALPHAaccent = 0x07a1 /* U+0386 GREEK CAPITAL LETTER ALPHA WITH TONOS */, + Greek_EPSILONaccent = 0x07a2 /* U+0388 GREEK CAPITAL LETTER EPSILON WITH TONOS */, + Greek_ETAaccent = 0x07a3 /* U+0389 GREEK CAPITAL LETTER ETA WITH TONOS */, + Greek_IOTAaccent = 0x07a4 /* U+038A GREEK CAPITAL LETTER IOTA WITH TONOS */, + Greek_IOTAdieresis = 0x07a5 /* U+03AA GREEK CAPITAL LETTER IOTA WITH DIALYTIKA */, + Greek_IOTAdiaeresis = 0x07a5 /* old typo */, + Greek_OMICRONaccent = 0x07a7 /* U+038C GREEK CAPITAL LETTER OMICRON WITH TONOS */, + Greek_UPSILONaccent = 0x07a8 /* U+038E GREEK CAPITAL LETTER UPSILON WITH TONOS */, + Greek_UPSILONdieresis = 0x07a9 /* U+03AB GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA */, + Greek_OMEGAaccent = 0x07ab /* U+038F GREEK CAPITAL LETTER OMEGA WITH TONOS */, + Greek_accentdieresis = 0x07ae /* U+0385 GREEK DIALYTIKA TONOS */, + Greek_horizbar = 0x07af /* U+2015 HORIZONTAL BAR */, + Greek_alphaaccent = 0x07b1 /* U+03AC GREEK SMALL LETTER ALPHA WITH TONOS */, + Greek_epsilonaccent = 0x07b2 /* U+03AD GREEK SMALL LETTER EPSILON WITH TONOS */, + Greek_etaaccent = 0x07b3 /* U+03AE GREEK SMALL LETTER ETA WITH TONOS */, + Greek_iotaaccent = 0x07b4 /* U+03AF GREEK SMALL LETTER IOTA WITH TONOS */, + Greek_iotadieresis = 0x07b5 /* U+03CA GREEK SMALL LETTER IOTA WITH DIALYTIKA */, + Greek_iotaaccentdieresis = 0x07b6 /* U+0390 GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS */, + Greek_omicronaccent = 0x07b7 /* U+03CC GREEK SMALL LETTER OMICRON WITH TONOS */, + Greek_upsilonaccent = 0x07b8 /* U+03CD GREEK SMALL LETTER UPSILON WITH TONOS */, + Greek_upsilondieresis = 0x07b9 /* U+03CB GREEK SMALL LETTER UPSILON WITH DIALYTIKA */, + Greek_upsilonaccentdieresis = 0x07ba /* U+03B0 GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS */, + Greek_omegaaccent = 0x07bb /* U+03CE GREEK SMALL LETTER OMEGA WITH TONOS */, + Greek_ALPHA = 0x07c1 /* U+0391 GREEK CAPITAL LETTER ALPHA */, + Greek_BETA = 0x07c2 /* U+0392 GREEK CAPITAL LETTER BETA */, + Greek_GAMMA = 0x07c3 /* U+0393 GREEK CAPITAL LETTER GAMMA */, + Greek_DELTA = 0x07c4 /* U+0394 GREEK CAPITAL LETTER DELTA */, + Greek_EPSILON = 0x07c5 /* U+0395 GREEK CAPITAL LETTER EPSILON */, + Greek_ZETA = 0x07c6 /* U+0396 GREEK CAPITAL LETTER ZETA */, + Greek_ETA = 0x07c7 /* U+0397 GREEK CAPITAL LETTER ETA */, + Greek_THETA = 0x07c8 /* U+0398 GREEK CAPITAL LETTER THETA */, + Greek_IOTA = 0x07c9 /* U+0399 GREEK CAPITAL LETTER IOTA */, + Greek_KAPPA = 0x07ca /* U+039A GREEK CAPITAL LETTER KAPPA */, + Greek_LAMDA = 0x07cb /* U+039B GREEK CAPITAL LETTER LAMDA */, + Greek_LAMBDA = 0x07cb /* U+039B GREEK CAPITAL LETTER LAMDA */, + Greek_MU = 0x07cc /* U+039C GREEK CAPITAL LETTER MU */, + Greek_NU = 0x07cd /* U+039D GREEK CAPITAL LETTER NU */, + Greek_XI = 0x07ce /* U+039E GREEK CAPITAL LETTER XI */, + Greek_OMICRON = 0x07cf /* U+039F GREEK CAPITAL LETTER OMICRON */, + Greek_PI = 0x07d0 /* U+03A0 GREEK CAPITAL LETTER PI */, + Greek_RHO = 0x07d1 /* U+03A1 GREEK CAPITAL LETTER RHO */, + Greek_SIGMA = 0x07d2 /* U+03A3 GREEK CAPITAL LETTER SIGMA */, + Greek_TAU = 0x07d4 /* U+03A4 GREEK CAPITAL LETTER TAU */, + Greek_UPSILON = 0x07d5 /* U+03A5 GREEK CAPITAL LETTER UPSILON */, + Greek_PHI = 0x07d6 /* U+03A6 GREEK CAPITAL LETTER PHI */, + Greek_CHI = 0x07d7 /* U+03A7 GREEK CAPITAL LETTER CHI */, + Greek_PSI = 0x07d8 /* U+03A8 GREEK CAPITAL LETTER PSI */, + Greek_OMEGA = 0x07d9 /* U+03A9 GREEK CAPITAL LETTER OMEGA */, + Greek_alpha = 0x07e1 /* U+03B1 GREEK SMALL LETTER ALPHA */, + Greek_beta = 0x07e2 /* U+03B2 GREEK SMALL LETTER BETA */, + Greek_gamma = 0x07e3 /* U+03B3 GREEK SMALL LETTER GAMMA */, + Greek_delta = 0x07e4 /* U+03B4 GREEK SMALL LETTER DELTA */, + Greek_epsilon = 0x07e5 /* U+03B5 GREEK SMALL LETTER EPSILON */, + Greek_zeta = 0x07e6 /* U+03B6 GREEK SMALL LETTER ZETA */, + Greek_eta = 0x07e7 /* U+03B7 GREEK SMALL LETTER ETA */, + Greek_theta = 0x07e8 /* U+03B8 GREEK SMALL LETTER THETA */, + Greek_iota = 0x07e9 /* U+03B9 GREEK SMALL LETTER IOTA */, + Greek_kappa = 0x07ea /* U+03BA GREEK SMALL LETTER KAPPA */, + Greek_lamda = 0x07eb /* U+03BB GREEK SMALL LETTER LAMDA */, + Greek_lambda = 0x07eb /* U+03BB GREEK SMALL LETTER LAMDA */, + Greek_mu = 0x07ec /* U+03BC GREEK SMALL LETTER MU */, + Greek_nu = 0x07ed /* U+03BD GREEK SMALL LETTER NU */, + Greek_xi = 0x07ee /* U+03BE GREEK SMALL LETTER XI */, + Greek_omicron = 0x07ef /* U+03BF GREEK SMALL LETTER OMICRON */, + Greek_pi = 0x07f0 /* U+03C0 GREEK SMALL LETTER PI */, + Greek_rho = 0x07f1 /* U+03C1 GREEK SMALL LETTER RHO */, + Greek_sigma = 0x07f2 /* U+03C3 GREEK SMALL LETTER SIGMA */, + Greek_finalsmallsigma = 0x07f3 /* U+03C2 GREEK SMALL LETTER FINAL SIGMA */, + Greek_tau = 0x07f4 /* U+03C4 GREEK SMALL LETTER TAU */, + Greek_upsilon = 0x07f5 /* U+03C5 GREEK SMALL LETTER UPSILON */, + Greek_phi = 0x07f6 /* U+03C6 GREEK SMALL LETTER PHI */, + Greek_chi = 0x07f7 /* U+03C7 GREEK SMALL LETTER CHI */, + Greek_psi = 0x07f8 /* U+03C8 GREEK SMALL LETTER PSI */, + Greek_omega = 0x07f9 /* U+03C9 GREEK SMALL LETTER OMEGA */, + Greek_switch = 0xff7e /* Alias for mode_switch */, + leftradical = 0x08a1 /* U+23B7 RADICAL SYMBOL BOTTOM */, + topleftradical = 0x08a2 /*(U+250C BOX DRAWINGS LIGHT DOWN AND RIGHT)*/, + horizconnector = 0x08a3 /*(U+2500 BOX DRAWINGS LIGHT HORIZONTAL)*/, + topintegral = 0x08a4 /* U+2320 TOP HALF INTEGRAL */, + botintegral = 0x08a5 /* U+2321 BOTTOM HALF INTEGRAL */, + vertconnector = 0x08a6 /*(U+2502 BOX DRAWINGS LIGHT VERTICAL)*/, + topleftsqbracket = 0x08a7 /* U+23A1 LEFT SQUARE BRACKET UPPER CORNER */, + botleftsqbracket = 0x08a8 /* U+23A3 LEFT SQUARE BRACKET LOWER CORNER */, + toprightsqbracket = 0x08a9 /* U+23A4 RIGHT SQUARE BRACKET UPPER CORNER */, + botrightsqbracket = 0x08aa /* U+23A6 RIGHT SQUARE BRACKET LOWER CORNER */, + topleftparens = 0x08ab /* U+239B LEFT PARENTHESIS UPPER HOOK */, + botleftparens = 0x08ac /* U+239D LEFT PARENTHESIS LOWER HOOK */, + toprightparens = 0x08ad /* U+239E RIGHT PARENTHESIS UPPER HOOK */, + botrightparens = 0x08ae /* U+23A0 RIGHT PARENTHESIS LOWER HOOK */, + leftmiddlecurlybrace = 0x08af /* U+23A8 LEFT CURLY BRACKET MIDDLE PIECE */, + rightmiddlecurlybrace = 0x08b0 /* U+23AC RIGHT CURLY BRACKET MIDDLE PIECE */, + topleftsummation = 0x08b1, + botleftsummation = 0x08b2, + topvertsummationconnector = 0x08b3, + botvertsummationconnector = 0x08b4, + toprightsummation = 0x08b5, + botrightsummation = 0x08b6, + rightmiddlesummation = 0x08b7, + lessthanequal = 0x08bc /* U+2264 LESS-THAN OR EQUAL TO */, + notequal = 0x08bd /* U+2260 NOT EQUAL TO */, + greaterthanequal = 0x08be /* U+2265 GREATER-THAN OR EQUAL TO */, + integral = 0x08bf /* U+222B INTEGRAL */, + therefore = 0x08c0 /* U+2234 THEREFORE */, + variation = 0x08c1 /* U+221D PROPORTIONAL TO */, + infinity = 0x08c2 /* U+221E INFINITY */, + nabla = 0x08c5 /* U+2207 NABLA */, + approximate = 0x08c8 /* U+223C TILDE OPERATOR */, + similarequal = 0x08c9 /* U+2243 ASYMPTOTICALLY EQUAL TO */, + ifonlyif = 0x08cd /* U+21D4 LEFT RIGHT DOUBLE ARROW */, + implies = 0x08ce /* U+21D2 RIGHTWARDS DOUBLE ARROW */, + identical = 0x08cf /* U+2261 IDENTICAL TO */, + radical = 0x08d6 /* U+221A SQUARE ROOT */, + includedin = 0x08da /* U+2282 SUBSET OF */, + includes = 0x08db /* U+2283 SUPERSET OF */, + intersection = 0x08dc /* U+2229 INTERSECTION */, + union = 0x08dd /* U+222A UNION */, + logicaland = 0x08de /* U+2227 LOGICAL AND */, + logicalor = 0x08df /* U+2228 LOGICAL OR */, + partialderivative = 0x08ef /* U+2202 PARTIAL DIFFERENTIAL */, + function = 0x08f6 /* U+0192 LATIN SMALL LETTER F WITH HOOK */, + leftarrow = 0x08fb /* U+2190 LEFTWARDS ARROW */, + uparrow = 0x08fc /* U+2191 UPWARDS ARROW */, + rightarrow = 0x08fd /* U+2192 RIGHTWARDS ARROW */, + downarrow = 0x08fe /* U+2193 DOWNWARDS ARROW */, + blank = 0x09df, + soliddiamond = 0x09e0 /* U+25C6 BLACK DIAMOND */, + checkerboard = 0x09e1 /* U+2592 MEDIUM SHADE */, + ht = 0x09e2 /* U+2409 SYMBOL FOR HORIZONTAL TABULATION */, + ff = 0x09e3 /* U+240C SYMBOL FOR FORM FEED */, + cr = 0x09e4 /* U+240D SYMBOL FOR CARRIAGE RETURN */, + lf = 0x09e5 /* U+240A SYMBOL FOR LINE FEED */, + nl = 0x09e8 /* U+2424 SYMBOL FOR NEWLINE */, + vt = 0x09e9 /* U+240B SYMBOL FOR VERTICAL TABULATION */, + lowrightcorner = 0x09ea /* U+2518 BOX DRAWINGS LIGHT UP AND LEFT */, + uprightcorner = 0x09eb /* U+2510 BOX DRAWINGS LIGHT DOWN AND LEFT */, + upleftcorner = 0x09ec /* U+250C BOX DRAWINGS LIGHT DOWN AND RIGHT */, + lowleftcorner = 0x09ed /* U+2514 BOX DRAWINGS LIGHT UP AND RIGHT */, + crossinglines = 0x09ee /* U+253C BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */, + horizlinescan1 = 0x09ef /* U+23BA HORIZONTAL SCAN LINE-1 */, + horizlinescan3 = 0x09f0 /* U+23BB HORIZONTAL SCAN LINE-3 */, + horizlinescan5 = 0x09f1 /* U+2500 BOX DRAWINGS LIGHT HORIZONTAL */, + horizlinescan7 = 0x09f2 /* U+23BC HORIZONTAL SCAN LINE-7 */, + horizlinescan9 = 0x09f3 /* U+23BD HORIZONTAL SCAN LINE-9 */, + leftt = 0x09f4 /* U+251C BOX DRAWINGS LIGHT VERTICAL AND RIGHT */, + rightt = 0x09f5 /* U+2524 BOX DRAWINGS LIGHT VERTICAL AND LEFT */, + bott = 0x09f6 /* U+2534 BOX DRAWINGS LIGHT UP AND HORIZONTAL */, + topt = 0x09f7 /* U+252C BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */, + vertbar = 0x09f8 /* U+2502 BOX DRAWINGS LIGHT VERTICAL */, + emspace = 0x0aa1 /* U+2003 EM SPACE */, + enspace = 0x0aa2 /* U+2002 EN SPACE */, + em3space = 0x0aa3 /* U+2004 THREE-PER-EM SPACE */, + em4space = 0x0aa4 /* U+2005 FOUR-PER-EM SPACE */, + digitspace = 0x0aa5 /* U+2007 FIGURE SPACE */, + punctspace = 0x0aa6 /* U+2008 PUNCTUATION SPACE */, + thinspace = 0x0aa7 /* U+2009 THIN SPACE */, + hairspace = 0x0aa8 /* U+200A HAIR SPACE */, + emdash = 0x0aa9 /* U+2014 EM DASH */, + endash = 0x0aaa /* U+2013 EN DASH */, + signifblank = 0x0aac /*(U+2423 OPEN BOX)*/, + ellipsis = 0x0aae /* U+2026 HORIZONTAL ELLIPSIS */, + doubbaselinedot = 0x0aaf /* U+2025 TWO DOT LEADER */, + onethird = 0x0ab0 /* U+2153 VULGAR FRACTION ONE THIRD */, + twothirds = 0x0ab1 /* U+2154 VULGAR FRACTION TWO THIRDS */, + onefifth = 0x0ab2 /* U+2155 VULGAR FRACTION ONE FIFTH */, + twofifths = 0x0ab3 /* U+2156 VULGAR FRACTION TWO FIFTHS */, + threefifths = 0x0ab4 /* U+2157 VULGAR FRACTION THREE FIFTHS */, + fourfifths = 0x0ab5 /* U+2158 VULGAR FRACTION FOUR FIFTHS */, + onesixth = 0x0ab6 /* U+2159 VULGAR FRACTION ONE SIXTH */, + fivesixths = 0x0ab7 /* U+215A VULGAR FRACTION FIVE SIXTHS */, + careof = 0x0ab8 /* U+2105 CARE OF */, + figdash = 0x0abb /* U+2012 FIGURE DASH */, + leftanglebracket = 0x0abc /*(U+27E8 MATHEMATICAL LEFT ANGLE BRACKET)*/, + decimalpoint = 0x0abd /*(U+002E FULL STOP)*/, + rightanglebracket = 0x0abe /*(U+27E9 MATHEMATICAL RIGHT ANGLE BRACKET)*/, + marker = 0x0abf, + oneeighth = 0x0ac3 /* U+215B VULGAR FRACTION ONE EIGHTH */, + threeeighths = 0x0ac4 /* U+215C VULGAR FRACTION THREE EIGHTHS */, + fiveeighths = 0x0ac5 /* U+215D VULGAR FRACTION FIVE EIGHTHS */, + seveneighths = 0x0ac6 /* U+215E VULGAR FRACTION SEVEN EIGHTHS */, + trademark = 0x0ac9 /* U+2122 TRADE MARK SIGN */, + signaturemark = 0x0aca /*(U+2613 SALTIRE)*/, + trademarkincircle = 0x0acb, + leftopentriangle = 0x0acc /*(U+25C1 WHITE LEFT-POINTING TRIANGLE)*/, + rightopentriangle = 0x0acd /*(U+25B7 WHITE RIGHT-POINTING TRIANGLE)*/, + emopencircle = 0x0ace /*(U+25CB WHITE CIRCLE)*/, + emopenrectangle = 0x0acf /*(U+25AF WHITE VERTICAL RECTANGLE)*/, + leftsinglequotemark = 0x0ad0 /* U+2018 LEFT SINGLE QUOTATION MARK */, + rightsinglequotemark = 0x0ad1 /* U+2019 RIGHT SINGLE QUOTATION MARK */, + leftdoublequotemark = 0x0ad2 /* U+201C LEFT DOUBLE QUOTATION MARK */, + rightdoublequotemark = 0x0ad3 /* U+201D RIGHT DOUBLE QUOTATION MARK */, + prescription = 0x0ad4 /* U+211E PRESCRIPTION TAKE */, + permille = 0x0ad5 /* U+2030 PER MILLE SIGN */, + minutes = 0x0ad6 /* U+2032 PRIME */, + seconds = 0x0ad7 /* U+2033 DOUBLE PRIME */, + latincross = 0x0ad9 /* U+271D LATIN CROSS */, + hexagram = 0x0ada, + filledrectbullet = 0x0adb /*(U+25AC BLACK RECTANGLE)*/, + filledlefttribullet = 0x0adc /*(U+25C0 BLACK LEFT-POINTING TRIANGLE)*/, + filledrighttribullet = 0x0add /*(U+25B6 BLACK RIGHT-POINTING TRIANGLE)*/, + emfilledcircle = 0x0ade /*(U+25CF BLACK CIRCLE)*/, + emfilledrect = 0x0adf /*(U+25AE BLACK VERTICAL RECTANGLE)*/, + enopencircbullet = 0x0ae0 /*(U+25E6 WHITE BULLET)*/, + enopensquarebullet = 0x0ae1 /*(U+25AB WHITE SMALL SQUARE)*/, + openrectbullet = 0x0ae2 /*(U+25AD WHITE RECTANGLE)*/, + opentribulletup = 0x0ae3 /*(U+25B3 WHITE UP-POINTING TRIANGLE)*/, + opentribulletdown = 0x0ae4 /*(U+25BD WHITE DOWN-POINTING TRIANGLE)*/, + openstar = 0x0ae5 /*(U+2606 WHITE STAR)*/, + enfilledcircbullet = 0x0ae6 /*(U+2022 BULLET)*/, + enfilledsqbullet = 0x0ae7 /*(U+25AA BLACK SMALL SQUARE)*/, + filledtribulletup = 0x0ae8 /*(U+25B2 BLACK UP-POINTING TRIANGLE)*/, + filledtribulletdown = 0x0ae9 /*(U+25BC BLACK DOWN-POINTING TRIANGLE)*/, + leftpointer = 0x0aea /*(U+261C WHITE LEFT POINTING INDEX)*/, + rightpointer = 0x0aeb /*(U+261E WHITE RIGHT POINTING INDEX)*/, + club = 0x0aec /* U+2663 BLACK CLUB SUIT */, + diamond = 0x0aed /* U+2666 BLACK DIAMOND SUIT */, + heart = 0x0aee /* U+2665 BLACK HEART SUIT */, + maltesecross = 0x0af0 /* U+2720 MALTESE CROSS */, + dagger = 0x0af1 /* U+2020 DAGGER */, + doubledagger = 0x0af2 /* U+2021 DOUBLE DAGGER */, + checkmark = 0x0af3 /* U+2713 CHECK MARK */, + ballotcross = 0x0af4 /* U+2717 BALLOT X */, + musicalsharp = 0x0af5 /* U+266F MUSIC SHARP SIGN */, + musicalflat = 0x0af6 /* U+266D MUSIC FLAT SIGN */, + malesymbol = 0x0af7 /* U+2642 MALE SIGN */, + femalesymbol = 0x0af8 /* U+2640 FEMALE SIGN */, + telephone = 0x0af9 /* U+260E BLACK TELEPHONE */, + telephonerecorder = 0x0afa /* U+2315 TELEPHONE RECORDER */, + phonographcopyright = 0x0afb /* U+2117 SOUND RECORDING COPYRIGHT */, + caret = 0x0afc /* U+2038 CARET */, + singlelowquotemark = 0x0afd /* U+201A SINGLE LOW-9 QUOTATION MARK */, + doublelowquotemark = 0x0afe /* U+201E DOUBLE LOW-9 QUOTATION MARK */, + cursor = 0x0aff, + leftcaret = 0x0ba3 /*(U+003C LESS-THAN SIGN)*/, + rightcaret = 0x0ba6 /*(U+003E GREATER-THAN SIGN)*/, + downcaret = 0x0ba8 /*(U+2228 LOGICAL OR)*/, + upcaret = 0x0ba9 /*(U+2227 LOGICAL AND)*/, + overbar = 0x0bc0 /*(U+00AF MACRON)*/, + downtack = 0x0bc2 /* U+22A4 DOWN TACK */, + upshoe = 0x0bc3 /*(U+2229 INTERSECTION)*/, + downstile = 0x0bc4 /* U+230A LEFT FLOOR */, + underbar = 0x0bc6 /*(U+005F LOW LINE)*/, + jot = 0x0bca /* U+2218 RING OPERATOR */, + quad = 0x0bcc /* U+2395 APL FUNCTIONAL SYMBOL QUAD */, + uptack = 0x0bce /* U+22A5 UP TACK */, + circle = 0x0bcf /* U+25CB WHITE CIRCLE */, + upstile = 0x0bd3 /* U+2308 LEFT CEILING */, + downshoe = 0x0bd6 /*(U+222A UNION)*/, + rightshoe = 0x0bd8 /*(U+2283 SUPERSET OF)*/, + leftshoe = 0x0bda /*(U+2282 SUBSET OF)*/, + lefttack = 0x0bdc /* U+22A3 LEFT TACK */, + righttack = 0x0bfc /* U+22A2 RIGHT TACK */, + hebrew_doublelowline = 0x0cdf /* U+2017 DOUBLE LOW LINE */, + hebrew_aleph = 0x0ce0 /* U+05D0 HEBREW LETTER ALEF */, + hebrew_bet = 0x0ce1 /* U+05D1 HEBREW LETTER BET */, + hebrew_beth = 0x0ce1 /* deprecated */, + hebrew_gimel = 0x0ce2 /* U+05D2 HEBREW LETTER GIMEL */, + hebrew_gimmel = 0x0ce2 /* deprecated */, + hebrew_dalet = 0x0ce3 /* U+05D3 HEBREW LETTER DALET */, + hebrew_daleth = 0x0ce3 /* deprecated */, + hebrew_he = 0x0ce4 /* U+05D4 HEBREW LETTER HE */, + hebrew_waw = 0x0ce5 /* U+05D5 HEBREW LETTER VAV */, + hebrew_zain = 0x0ce6 /* U+05D6 HEBREW LETTER ZAYIN */, + hebrew_zayin = 0x0ce6 /* deprecated */, + hebrew_chet = 0x0ce7 /* U+05D7 HEBREW LETTER HET */, + hebrew_het = 0x0ce7 /* deprecated */, + hebrew_tet = 0x0ce8 /* U+05D8 HEBREW LETTER TET */, + hebrew_teth = 0x0ce8 /* deprecated */, + hebrew_yod = 0x0ce9 /* U+05D9 HEBREW LETTER YOD */, + hebrew_finalkaph = 0x0cea /* U+05DA HEBREW LETTER FINAL KAF */, + hebrew_kaph = 0x0ceb /* U+05DB HEBREW LETTER KAF */, + hebrew_lamed = 0x0cec /* U+05DC HEBREW LETTER LAMED */, + hebrew_finalmem = 0x0ced /* U+05DD HEBREW LETTER FINAL MEM */, + hebrew_mem = 0x0cee /* U+05DE HEBREW LETTER MEM */, + hebrew_finalnun = 0x0cef /* U+05DF HEBREW LETTER FINAL NUN */, + hebrew_nun = 0x0cf0 /* U+05E0 HEBREW LETTER NUN */, + hebrew_samech = 0x0cf1 /* U+05E1 HEBREW LETTER SAMEKH */, + hebrew_samekh = 0x0cf1 /* deprecated */, + hebrew_ayin = 0x0cf2 /* U+05E2 HEBREW LETTER AYIN */, + hebrew_finalpe = 0x0cf3 /* U+05E3 HEBREW LETTER FINAL PE */, + hebrew_pe = 0x0cf4 /* U+05E4 HEBREW LETTER PE */, + hebrew_finalzade = 0x0cf5 /* U+05E5 HEBREW LETTER FINAL TSADI */, + hebrew_finalzadi = 0x0cf5 /* deprecated */, + hebrew_zade = 0x0cf6 /* U+05E6 HEBREW LETTER TSADI */, + hebrew_zadi = 0x0cf6 /* deprecated */, + hebrew_qoph = 0x0cf7 /* U+05E7 HEBREW LETTER QOF */, + hebrew_kuf = 0x0cf7 /* deprecated */, + hebrew_resh = 0x0cf8 /* U+05E8 HEBREW LETTER RESH */, + hebrew_shin = 0x0cf9 /* U+05E9 HEBREW LETTER SHIN */, + hebrew_taw = 0x0cfa /* U+05EA HEBREW LETTER TAV */, + hebrew_taf = 0x0cfa /* deprecated */, + Hebrew_switch = 0xff7e /* Alias for mode_switch */, + Thai_kokai = 0x0da1 /* U+0E01 THAI CHARACTER KO KAI */, + Thai_khokhai = 0x0da2 /* U+0E02 THAI CHARACTER KHO KHAI */, + Thai_khokhuat = 0x0da3 /* U+0E03 THAI CHARACTER KHO KHUAT */, + Thai_khokhwai = 0x0da4 /* U+0E04 THAI CHARACTER KHO KHWAI */, + Thai_khokhon = 0x0da5 /* U+0E05 THAI CHARACTER KHO KHON */, + Thai_khorakhang = 0x0da6 /* U+0E06 THAI CHARACTER KHO RAKHANG */, + Thai_ngongu = 0x0da7 /* U+0E07 THAI CHARACTER NGO NGU */, + Thai_chochan = 0x0da8 /* U+0E08 THAI CHARACTER CHO CHAN */, + Thai_choching = 0x0da9 /* U+0E09 THAI CHARACTER CHO CHING */, + Thai_chochang = 0x0daa /* U+0E0A THAI CHARACTER CHO CHANG */, + Thai_soso = 0x0dab /* U+0E0B THAI CHARACTER SO SO */, + Thai_chochoe = 0x0dac /* U+0E0C THAI CHARACTER CHO CHOE */, + Thai_yoying = 0x0dad /* U+0E0D THAI CHARACTER YO YING */, + Thai_dochada = 0x0dae /* U+0E0E THAI CHARACTER DO CHADA */, + Thai_topatak = 0x0daf /* U+0E0F THAI CHARACTER TO PATAK */, + Thai_thothan = 0x0db0 /* U+0E10 THAI CHARACTER THO THAN */, + Thai_thonangmontho = 0x0db1 /* U+0E11 THAI CHARACTER THO NANGMONTHO */, + Thai_thophuthao = 0x0db2 /* U+0E12 THAI CHARACTER THO PHUTHAO */, + Thai_nonen = 0x0db3 /* U+0E13 THAI CHARACTER NO NEN */, + Thai_dodek = 0x0db4 /* U+0E14 THAI CHARACTER DO DEK */, + Thai_totao = 0x0db5 /* U+0E15 THAI CHARACTER TO TAO */, + Thai_thothung = 0x0db6 /* U+0E16 THAI CHARACTER THO THUNG */, + Thai_thothahan = 0x0db7 /* U+0E17 THAI CHARACTER THO THAHAN */, + Thai_thothong = 0x0db8 /* U+0E18 THAI CHARACTER THO THONG */, + Thai_nonu = 0x0db9 /* U+0E19 THAI CHARACTER NO NU */, + Thai_bobaimai = 0x0dba /* U+0E1A THAI CHARACTER BO BAIMAI */, + Thai_popla = 0x0dbb /* U+0E1B THAI CHARACTER PO PLA */, + Thai_phophung = 0x0dbc /* U+0E1C THAI CHARACTER PHO PHUNG */, + Thai_fofa = 0x0dbd /* U+0E1D THAI CHARACTER FO FA */, + Thai_phophan = 0x0dbe /* U+0E1E THAI CHARACTER PHO PHAN */, + Thai_fofan = 0x0dbf /* U+0E1F THAI CHARACTER FO FAN */, + Thai_phosamphao = 0x0dc0 /* U+0E20 THAI CHARACTER PHO SAMPHAO */, + Thai_moma = 0x0dc1 /* U+0E21 THAI CHARACTER MO MA */, + Thai_yoyak = 0x0dc2 /* U+0E22 THAI CHARACTER YO YAK */, + Thai_rorua = 0x0dc3 /* U+0E23 THAI CHARACTER RO RUA */, + Thai_ru = 0x0dc4 /* U+0E24 THAI CHARACTER RU */, + Thai_loling = 0x0dc5 /* U+0E25 THAI CHARACTER LO LING */, + Thai_lu = 0x0dc6 /* U+0E26 THAI CHARACTER LU */, + Thai_wowaen = 0x0dc7 /* U+0E27 THAI CHARACTER WO WAEN */, + Thai_sosala = 0x0dc8 /* U+0E28 THAI CHARACTER SO SALA */, + Thai_sorusi = 0x0dc9 /* U+0E29 THAI CHARACTER SO RUSI */, + Thai_sosua = 0x0dca /* U+0E2A THAI CHARACTER SO SUA */, + Thai_hohip = 0x0dcb /* U+0E2B THAI CHARACTER HO HIP */, + Thai_lochula = 0x0dcc /* U+0E2C THAI CHARACTER LO CHULA */, + Thai_oang = 0x0dcd /* U+0E2D THAI CHARACTER O ANG */, + Thai_honokhuk = 0x0dce /* U+0E2E THAI CHARACTER HO NOKHUK */, + Thai_paiyannoi = 0x0dcf /* U+0E2F THAI CHARACTER PAIYANNOI */, + Thai_saraa = 0x0dd0 /* U+0E30 THAI CHARACTER SARA A */, + Thai_maihanakat = 0x0dd1 /* U+0E31 THAI CHARACTER MAI HAN-AKAT */, + Thai_saraaa = 0x0dd2 /* U+0E32 THAI CHARACTER SARA AA */, + Thai_saraam = 0x0dd3 /* U+0E33 THAI CHARACTER SARA AM */, + Thai_sarai = 0x0dd4 /* U+0E34 THAI CHARACTER SARA I */, + Thai_saraii = 0x0dd5 /* U+0E35 THAI CHARACTER SARA II */, + Thai_saraue = 0x0dd6 /* U+0E36 THAI CHARACTER SARA UE */, + Thai_sarauee = 0x0dd7 /* U+0E37 THAI CHARACTER SARA UEE */, + Thai_sarau = 0x0dd8 /* U+0E38 THAI CHARACTER SARA U */, + Thai_sarauu = 0x0dd9 /* U+0E39 THAI CHARACTER SARA UU */, + Thai_phinthu = 0x0dda /* U+0E3A THAI CHARACTER PHINTHU */, + Thai_maihanakat_maitho = 0x0dde, + Thai_baht = 0x0ddf /* U+0E3F THAI CURRENCY SYMBOL BAHT */, + Thai_sarae = 0x0de0 /* U+0E40 THAI CHARACTER SARA E */, + Thai_saraae = 0x0de1 /* U+0E41 THAI CHARACTER SARA AE */, + Thai_sarao = 0x0de2 /* U+0E42 THAI CHARACTER SARA O */, + Thai_saraaimaimuan = 0x0de3 /* U+0E43 THAI CHARACTER SARA AI MAIMUAN */, + Thai_saraaimaimalai = 0x0de4 /* U+0E44 THAI CHARACTER SARA AI MAIMALAI */, + Thai_lakkhangyao = 0x0de5 /* U+0E45 THAI CHARACTER LAKKHANGYAO */, + Thai_maiyamok = 0x0de6 /* U+0E46 THAI CHARACTER MAIYAMOK */, + Thai_maitaikhu = 0x0de7 /* U+0E47 THAI CHARACTER MAITAIKHU */, + Thai_maiek = 0x0de8 /* U+0E48 THAI CHARACTER MAI EK */, + Thai_maitho = 0x0de9 /* U+0E49 THAI CHARACTER MAI THO */, + Thai_maitri = 0x0dea /* U+0E4A THAI CHARACTER MAI TRI */, + Thai_maichattawa = 0x0deb /* U+0E4B THAI CHARACTER MAI CHATTAWA */, + Thai_thanthakhat = 0x0dec /* U+0E4C THAI CHARACTER THANTHAKHAT */, + Thai_nikhahit = 0x0ded /* U+0E4D THAI CHARACTER NIKHAHIT */, + Thai_leksun = 0x0df0 /* U+0E50 THAI DIGIT ZERO */, + Thai_leknung = 0x0df1 /* U+0E51 THAI DIGIT ONE */, + Thai_leksong = 0x0df2 /* U+0E52 THAI DIGIT TWO */, + Thai_leksam = 0x0df3 /* U+0E53 THAI DIGIT THREE */, + Thai_leksi = 0x0df4 /* U+0E54 THAI DIGIT FOUR */, + Thai_lekha = 0x0df5 /* U+0E55 THAI DIGIT FIVE */, + Thai_lekhok = 0x0df6 /* U+0E56 THAI DIGIT SIX */, + Thai_lekchet = 0x0df7 /* U+0E57 THAI DIGIT SEVEN */, + Thai_lekpaet = 0x0df8 /* U+0E58 THAI DIGIT EIGHT */, + Thai_lekkao = 0x0df9 /* U+0E59 THAI DIGIT NINE */, + Hangul = 0xff31 /* Hangul start/stop(toggle) */, + Hangul_Start = 0xff32 /* Hangul start */, + Hangul_End = 0xff33 /* Hangul end, English start */, + Hangul_Hanja = 0xff34 /* Start Hangul->Hanja Conversion */, + Hangul_Jamo = 0xff35 /* Hangul Jamo mode */, + Hangul_Romaja = 0xff36 /* Hangul Romaja mode */, + Hangul_Codeinput = 0xff37 /* Hangul code input mode */, + Hangul_Jeonja = 0xff38 /* Jeonja mode */, + Hangul_Banja = 0xff39 /* Banja mode */, + Hangul_PreHanja = 0xff3a /* Pre Hanja conversion */, + Hangul_PostHanja = 0xff3b /* Post Hanja conversion */, + Hangul_SingleCandidate = 0xff3c /* Single candidate */, + Hangul_MultipleCandidate = 0xff3d /* Multiple candidate */, + Hangul_PreviousCandidate = 0xff3e /* Previous candidate */, + Hangul_Special = 0xff3f /* Special symbols */, + Hangul_switch = 0xff7e /* Alias for mode_switch */, + Hangul_Kiyeog = 0x0ea1, + Hangul_SsangKiyeog = 0x0ea2, + Hangul_KiyeogSios = 0x0ea3, + Hangul_Nieun = 0x0ea4, + Hangul_NieunJieuj = 0x0ea5, + Hangul_NieunHieuh = 0x0ea6, + Hangul_Dikeud = 0x0ea7, + Hangul_SsangDikeud = 0x0ea8, + Hangul_Rieul = 0x0ea9, + Hangul_RieulKiyeog = 0x0eaa, + Hangul_RieulMieum = 0x0eab, + Hangul_RieulPieub = 0x0eac, + Hangul_RieulSios = 0x0ead, + Hangul_RieulTieut = 0x0eae, + Hangul_RieulPhieuf = 0x0eaf, + Hangul_RieulHieuh = 0x0eb0, + Hangul_Mieum = 0x0eb1, + Hangul_Pieub = 0x0eb2, + Hangul_SsangPieub = 0x0eb3, + Hangul_PieubSios = 0x0eb4, + Hangul_Sios = 0x0eb5, + Hangul_SsangSios = 0x0eb6, + Hangul_Ieung = 0x0eb7, + Hangul_Jieuj = 0x0eb8, + Hangul_SsangJieuj = 0x0eb9, + Hangul_Cieuc = 0x0eba, + Hangul_Khieuq = 0x0ebb, + Hangul_Tieut = 0x0ebc, + Hangul_Phieuf = 0x0ebd, + Hangul_Hieuh = 0x0ebe, + Hangul_A = 0x0ebf, + Hangul_AE = 0x0ec0, + Hangul_YA = 0x0ec1, + Hangul_YAE = 0x0ec2, + Hangul_EO = 0x0ec3, + Hangul_E = 0x0ec4, + Hangul_YEO = 0x0ec5, + Hangul_YE = 0x0ec6, + Hangul_O = 0x0ec7, + Hangul_WA = 0x0ec8, + Hangul_WAE = 0x0ec9, + Hangul_OE = 0x0eca, + Hangul_YO = 0x0ecb, + Hangul_U = 0x0ecc, + Hangul_WEO = 0x0ecd, + Hangul_WE = 0x0ece, + Hangul_WI = 0x0ecf, + Hangul_YU = 0x0ed0, + Hangul_EU = 0x0ed1, + Hangul_YI = 0x0ed2, + Hangul_I = 0x0ed3, + Hangul_J_Kiyeog = 0x0ed4, + Hangul_J_SsangKiyeog = 0x0ed5, + Hangul_J_KiyeogSios = 0x0ed6, + Hangul_J_Nieun = 0x0ed7, + Hangul_J_NieunJieuj = 0x0ed8, + Hangul_J_NieunHieuh = 0x0ed9, + Hangul_J_Dikeud = 0x0eda, + Hangul_J_Rieul = 0x0edb, + Hangul_J_RieulKiyeog = 0x0edc, + Hangul_J_RieulMieum = 0x0edd, + Hangul_J_RieulPieub = 0x0ede, + Hangul_J_RieulSios = 0x0edf, + Hangul_J_RieulTieut = 0x0ee0, + Hangul_J_RieulPhieuf = 0x0ee1, + Hangul_J_RieulHieuh = 0x0ee2, + Hangul_J_Mieum = 0x0ee3, + Hangul_J_Pieub = 0x0ee4, + Hangul_J_PieubSios = 0x0ee5, + Hangul_J_Sios = 0x0ee6, + Hangul_J_SsangSios = 0x0ee7, + Hangul_J_Ieung = 0x0ee8, + Hangul_J_Jieuj = 0x0ee9, + Hangul_J_Cieuc = 0x0eea, + Hangul_J_Khieuq = 0x0eeb, + Hangul_J_Tieut = 0x0eec, + Hangul_J_Phieuf = 0x0eed, + Hangul_J_Hieuh = 0x0eee, + Hangul_RieulYeorinHieuh = 0x0eef, + Hangul_SunkyeongeumMieum = 0x0ef0, + Hangul_SunkyeongeumPieub = 0x0ef1, + Hangul_PanSios = 0x0ef2, + Hangul_KkogjiDalrinIeung = 0x0ef3, + Hangul_SunkyeongeumPhieuf = 0x0ef4, + Hangul_YeorinHieuh = 0x0ef5, + Hangul_AraeA = 0x0ef6, + Hangul_AraeAE = 0x0ef7, + Hangul_J_PanSios = 0x0ef8, + Hangul_J_KkogjiDalrinIeung = 0x0ef9, + Hangul_J_YeorinHieuh = 0x0efa, + Korean_Won = 0x0eff /*(U+20A9 WON SIGN)*/, + Armenian_ligature_ew = 0x1000587 /* U+0587 ARMENIAN SMALL LIGATURE ECH YIWN */, + Armenian_full_stop = 0x1000589 /* U+0589 ARMENIAN FULL STOP */, + Armenian_verjaket = 0x1000589 /* U+0589 ARMENIAN FULL STOP */, + Armenian_separation_mark = 0x100055d /* U+055D ARMENIAN COMMA */, + Armenian_but = 0x100055d /* U+055D ARMENIAN COMMA */, + Armenian_hyphen = 0x100058a /* U+058A ARMENIAN HYPHEN */, + Armenian_yentamna = 0x100058a /* U+058A ARMENIAN HYPHEN */, + Armenian_exclam = 0x100055c /* U+055C ARMENIAN EXCLAMATION MARK */, + Armenian_amanak = 0x100055c /* U+055C ARMENIAN EXCLAMATION MARK */, + Armenian_accent = 0x100055b /* U+055B ARMENIAN EMPHASIS MARK */, + Armenian_shesht = 0x100055b /* U+055B ARMENIAN EMPHASIS MARK */, + Armenian_question = 0x100055e /* U+055E ARMENIAN QUESTION MARK */, + Armenian_paruyk = 0x100055e /* U+055E ARMENIAN QUESTION MARK */, + Armenian_AYB = 0x1000531 /* U+0531 ARMENIAN CAPITAL LETTER AYB */, + Armenian_ayb = 0x1000561 /* U+0561 ARMENIAN SMALL LETTER AYB */, + Armenian_BEN = 0x1000532 /* U+0532 ARMENIAN CAPITAL LETTER BEN */, + Armenian_ben = 0x1000562 /* U+0562 ARMENIAN SMALL LETTER BEN */, + Armenian_GIM = 0x1000533 /* U+0533 ARMENIAN CAPITAL LETTER GIM */, + Armenian_gim = 0x1000563 /* U+0563 ARMENIAN SMALL LETTER GIM */, + Armenian_DA = 0x1000534 /* U+0534 ARMENIAN CAPITAL LETTER DA */, + Armenian_da = 0x1000564 /* U+0564 ARMENIAN SMALL LETTER DA */, + Armenian_YECH = 0x1000535 /* U+0535 ARMENIAN CAPITAL LETTER ECH */, + Armenian_yech = 0x1000565 /* U+0565 ARMENIAN SMALL LETTER ECH */, + Armenian_ZA = 0x1000536 /* U+0536 ARMENIAN CAPITAL LETTER ZA */, + Armenian_za = 0x1000566 /* U+0566 ARMENIAN SMALL LETTER ZA */, + Armenian_E = 0x1000537 /* U+0537 ARMENIAN CAPITAL LETTER EH */, + Armenian_e = 0x1000567 /* U+0567 ARMENIAN SMALL LETTER EH */, + Armenian_AT = 0x1000538 /* U+0538 ARMENIAN CAPITAL LETTER ET */, + Armenian_at = 0x1000568 /* U+0568 ARMENIAN SMALL LETTER ET */, + Armenian_TO = 0x1000539 /* U+0539 ARMENIAN CAPITAL LETTER TO */, + Armenian_to = 0x1000569 /* U+0569 ARMENIAN SMALL LETTER TO */, + Armenian_ZHE = 0x100053a /* U+053A ARMENIAN CAPITAL LETTER ZHE */, + Armenian_zhe = 0x100056a /* U+056A ARMENIAN SMALL LETTER ZHE */, + Armenian_INI = 0x100053b /* U+053B ARMENIAN CAPITAL LETTER INI */, + Armenian_ini = 0x100056b /* U+056B ARMENIAN SMALL LETTER INI */, + Armenian_LYUN = 0x100053c /* U+053C ARMENIAN CAPITAL LETTER LIWN */, + Armenian_lyun = 0x100056c /* U+056C ARMENIAN SMALL LETTER LIWN */, + Armenian_KHE = 0x100053d /* U+053D ARMENIAN CAPITAL LETTER XEH */, + Armenian_khe = 0x100056d /* U+056D ARMENIAN SMALL LETTER XEH */, + Armenian_TSA = 0x100053e /* U+053E ARMENIAN CAPITAL LETTER CA */, + Armenian_tsa = 0x100056e /* U+056E ARMENIAN SMALL LETTER CA */, + Armenian_KEN = 0x100053f /* U+053F ARMENIAN CAPITAL LETTER KEN */, + Armenian_ken = 0x100056f /* U+056F ARMENIAN SMALL LETTER KEN */, + Armenian_HO = 0x1000540 /* U+0540 ARMENIAN CAPITAL LETTER HO */, + Armenian_ho = 0x1000570 /* U+0570 ARMENIAN SMALL LETTER HO */, + Armenian_DZA = 0x1000541 /* U+0541 ARMENIAN CAPITAL LETTER JA */, + Armenian_dza = 0x1000571 /* U+0571 ARMENIAN SMALL LETTER JA */, + Armenian_GHAT = 0x1000542 /* U+0542 ARMENIAN CAPITAL LETTER GHAD */, + Armenian_ghat = 0x1000572 /* U+0572 ARMENIAN SMALL LETTER GHAD */, + Armenian_TCHE = 0x1000543 /* U+0543 ARMENIAN CAPITAL LETTER CHEH */, + Armenian_tche = 0x1000573 /* U+0573 ARMENIAN SMALL LETTER CHEH */, + Armenian_MEN = 0x1000544 /* U+0544 ARMENIAN CAPITAL LETTER MEN */, + Armenian_men = 0x1000574 /* U+0574 ARMENIAN SMALL LETTER MEN */, + Armenian_HI = 0x1000545 /* U+0545 ARMENIAN CAPITAL LETTER YI */, + Armenian_hi = 0x1000575 /* U+0575 ARMENIAN SMALL LETTER YI */, + Armenian_NU = 0x1000546 /* U+0546 ARMENIAN CAPITAL LETTER NOW */, + Armenian_nu = 0x1000576 /* U+0576 ARMENIAN SMALL LETTER NOW */, + Armenian_SHA = 0x1000547 /* U+0547 ARMENIAN CAPITAL LETTER SHA */, + Armenian_sha = 0x1000577 /* U+0577 ARMENIAN SMALL LETTER SHA */, + Armenian_VO = 0x1000548 /* U+0548 ARMENIAN CAPITAL LETTER VO */, + Armenian_vo = 0x1000578 /* U+0578 ARMENIAN SMALL LETTER VO */, + Armenian_CHA = 0x1000549 /* U+0549 ARMENIAN CAPITAL LETTER CHA */, + Armenian_cha = 0x1000579 /* U+0579 ARMENIAN SMALL LETTER CHA */, + Armenian_PE = 0x100054a /* U+054A ARMENIAN CAPITAL LETTER PEH */, + Armenian_pe = 0x100057a /* U+057A ARMENIAN SMALL LETTER PEH */, + Armenian_JE = 0x100054b /* U+054B ARMENIAN CAPITAL LETTER JHEH */, + Armenian_je = 0x100057b /* U+057B ARMENIAN SMALL LETTER JHEH */, + Armenian_RA = 0x100054c /* U+054C ARMENIAN CAPITAL LETTER RA */, + Armenian_ra = 0x100057c /* U+057C ARMENIAN SMALL LETTER RA */, + Armenian_SE = 0x100054d /* U+054D ARMENIAN CAPITAL LETTER SEH */, + Armenian_se = 0x100057d /* U+057D ARMENIAN SMALL LETTER SEH */, + Armenian_VEV = 0x100054e /* U+054E ARMENIAN CAPITAL LETTER VEW */, + Armenian_vev = 0x100057e /* U+057E ARMENIAN SMALL LETTER VEW */, + Armenian_TYUN = 0x100054f /* U+054F ARMENIAN CAPITAL LETTER TIWN */, + Armenian_tyun = 0x100057f /* U+057F ARMENIAN SMALL LETTER TIWN */, + Armenian_RE = 0x1000550 /* U+0550 ARMENIAN CAPITAL LETTER REH */, + Armenian_re = 0x1000580 /* U+0580 ARMENIAN SMALL LETTER REH */, + Armenian_TSO = 0x1000551 /* U+0551 ARMENIAN CAPITAL LETTER CO */, + Armenian_tso = 0x1000581 /* U+0581 ARMENIAN SMALL LETTER CO */, + Armenian_VYUN = 0x1000552 /* U+0552 ARMENIAN CAPITAL LETTER YIWN */, + Armenian_vyun = 0x1000582 /* U+0582 ARMENIAN SMALL LETTER YIWN */, + Armenian_PYUR = 0x1000553 /* U+0553 ARMENIAN CAPITAL LETTER PIWR */, + Armenian_pyur = 0x1000583 /* U+0583 ARMENIAN SMALL LETTER PIWR */, + Armenian_KE = 0x1000554 /* U+0554 ARMENIAN CAPITAL LETTER KEH */, + Armenian_ke = 0x1000584 /* U+0584 ARMENIAN SMALL LETTER KEH */, + Armenian_O = 0x1000555 /* U+0555 ARMENIAN CAPITAL LETTER OH */, + Armenian_o = 0x1000585 /* U+0585 ARMENIAN SMALL LETTER OH */, + Armenian_FE = 0x1000556 /* U+0556 ARMENIAN CAPITAL LETTER FEH */, + Armenian_fe = 0x1000586 /* U+0586 ARMENIAN SMALL LETTER FEH */, + Armenian_apostrophe = 0x100055a /* U+055A ARMENIAN APOSTROPHE */, + Georgian_an = 0x10010d0 /* U+10D0 GEORGIAN LETTER AN */, + Georgian_ban = 0x10010d1 /* U+10D1 GEORGIAN LETTER BAN */, + Georgian_gan = 0x10010d2 /* U+10D2 GEORGIAN LETTER GAN */, + Georgian_don = 0x10010d3 /* U+10D3 GEORGIAN LETTER DON */, + Georgian_en = 0x10010d4 /* U+10D4 GEORGIAN LETTER EN */, + Georgian_vin = 0x10010d5 /* U+10D5 GEORGIAN LETTER VIN */, + Georgian_zen = 0x10010d6 /* U+10D6 GEORGIAN LETTER ZEN */, + Georgian_tan = 0x10010d7 /* U+10D7 GEORGIAN LETTER TAN */, + Georgian_in = 0x10010d8 /* U+10D8 GEORGIAN LETTER IN */, + Georgian_kan = 0x10010d9 /* U+10D9 GEORGIAN LETTER KAN */, + Georgian_las = 0x10010da /* U+10DA GEORGIAN LETTER LAS */, + Georgian_man = 0x10010db /* U+10DB GEORGIAN LETTER MAN */, + Georgian_nar = 0x10010dc /* U+10DC GEORGIAN LETTER NAR */, + Georgian_on = 0x10010dd /* U+10DD GEORGIAN LETTER ON */, + Georgian_par = 0x10010de /* U+10DE GEORGIAN LETTER PAR */, + Georgian_zhar = 0x10010df /* U+10DF GEORGIAN LETTER ZHAR */, + Georgian_rae = 0x10010e0 /* U+10E0 GEORGIAN LETTER RAE */, + Georgian_san = 0x10010e1 /* U+10E1 GEORGIAN LETTER SAN */, + Georgian_tar = 0x10010e2 /* U+10E2 GEORGIAN LETTER TAR */, + Georgian_un = 0x10010e3 /* U+10E3 GEORGIAN LETTER UN */, + Georgian_phar = 0x10010e4 /* U+10E4 GEORGIAN LETTER PHAR */, + Georgian_khar = 0x10010e5 /* U+10E5 GEORGIAN LETTER KHAR */, + Georgian_ghan = 0x10010e6 /* U+10E6 GEORGIAN LETTER GHAN */, + Georgian_qar = 0x10010e7 /* U+10E7 GEORGIAN LETTER QAR */, + Georgian_shin = 0x10010e8 /* U+10E8 GEORGIAN LETTER SHIN */, + Georgian_chin = 0x10010e9 /* U+10E9 GEORGIAN LETTER CHIN */, + Georgian_can = 0x10010ea /* U+10EA GEORGIAN LETTER CAN */, + Georgian_jil = 0x10010eb /* U+10EB GEORGIAN LETTER JIL */, + Georgian_cil = 0x10010ec /* U+10EC GEORGIAN LETTER CIL */, + Georgian_char = 0x10010ed /* U+10ED GEORGIAN LETTER CHAR */, + Georgian_xan = 0x10010ee /* U+10EE GEORGIAN LETTER XAN */, + Georgian_jhan = 0x10010ef /* U+10EF GEORGIAN LETTER JHAN */, + Georgian_hae = 0x10010f0 /* U+10F0 GEORGIAN LETTER HAE */, + Georgian_he = 0x10010f1 /* U+10F1 GEORGIAN LETTER HE */, + Georgian_hie = 0x10010f2 /* U+10F2 GEORGIAN LETTER HIE */, + Georgian_we = 0x10010f3 /* U+10F3 GEORGIAN LETTER WE */, + Georgian_har = 0x10010f4 /* U+10F4 GEORGIAN LETTER HAR */, + Georgian_hoe = 0x10010f5 /* U+10F5 GEORGIAN LETTER HOE */, + Georgian_fi = 0x10010f6 /* U+10F6 GEORGIAN LETTER FI */, + Xabovedot = 0x1001e8a /* U+1E8A LATIN CAPITAL LETTER X WITH DOT ABOVE */, + Ibreve = 0x100012c /* U+012C LATIN CAPITAL LETTER I WITH BREVE */, + Zstroke = 0x10001b5 /* U+01B5 LATIN CAPITAL LETTER Z WITH STROKE */, + Gcaron = 0x10001e6 /* U+01E6 LATIN CAPITAL LETTER G WITH CARON */, + Ocaron = 0x10001d1 /* U+01D2 LATIN CAPITAL LETTER O WITH CARON */, + Obarred = 0x100019f /* U+019F LATIN CAPITAL LETTER O WITH MIDDLE TILDE */, + xabovedot = 0x1001e8b /* U+1E8B LATIN SMALL LETTER X WITH DOT ABOVE */, + ibreve = 0x100012d /* U+012D LATIN SMALL LETTER I WITH BREVE */, + zstroke = 0x10001b6 /* U+01B6 LATIN SMALL LETTER Z WITH STROKE */, + gcaron = 0x10001e7 /* U+01E7 LATIN SMALL LETTER G WITH CARON */, + ocaron = 0x10001d2 /* U+01D2 LATIN SMALL LETTER O WITH CARON */, + obarred = 0x1000275 /* U+0275 LATIN SMALL LETTER BARRED O */, + SCHWA = 0x100018f /* U+018F LATIN CAPITAL LETTER SCHWA */, + schwa = 0x1000259 /* U+0259 LATIN SMALL LETTER SCHWA */, + EZH = 0x10001b7 /* U+01B7 LATIN CAPITAL LETTER EZH */, + ezh = 0x1000292 /* U+0292 LATIN SMALL LETTER EZH */, + Lbelowdot = 0x1001e36 /* U+1E36 LATIN CAPITAL LETTER L WITH DOT BELOW */, + lbelowdot = 0x1001e37 /* U+1E37 LATIN SMALL LETTER L WITH DOT BELOW */, + Abelowdot = 0x1001ea0 /* U+1EA0 LATIN CAPITAL LETTER A WITH DOT BELOW */, + abelowdot = 0x1001ea1 /* U+1EA1 LATIN SMALL LETTER A WITH DOT BELOW */, + Ahook = 0x1001ea2 /* U+1EA2 LATIN CAPITAL LETTER A WITH HOOK ABOVE */, + ahook = 0x1001ea3 /* U+1EA3 LATIN SMALL LETTER A WITH HOOK ABOVE */, + Acircumflexacute = 0x1001ea4 /* U+1EA4 LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND ACUTE */, + acircumflexacute = 0x1001ea5 /* U+1EA5 LATIN SMALL LETTER A WITH CIRCUMFLEX AND ACUTE */, + Acircumflexgrave = 0x1001ea6 /* U+1EA6 LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND GRAVE */, + acircumflexgrave = 0x1001ea7 /* U+1EA7 LATIN SMALL LETTER A WITH CIRCUMFLEX AND GRAVE */, + Acircumflexhook = 0x1001ea8 /* U+1EA8 LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE */, + acircumflexhook = 0x1001ea9 /* U+1EA9 LATIN SMALL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE */, + Acircumflextilde = 0x1001eaa /* U+1EAA LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND TILDE */, + acircumflextilde = 0x1001eab /* U+1EAB LATIN SMALL LETTER A WITH CIRCUMFLEX AND TILDE */, + Acircumflexbelowdot = 0x1001eac /* U+1EAC LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND DOT BELOW */, + acircumflexbelowdot = 0x1001ead /* U+1EAD LATIN SMALL LETTER A WITH CIRCUMFLEX AND DOT BELOW */, + Abreveacute = 0x1001eae /* U+1EAE LATIN CAPITAL LETTER A WITH BREVE AND ACUTE */, + abreveacute = 0x1001eaf /* U+1EAF LATIN SMALL LETTER A WITH BREVE AND ACUTE */, + Abrevegrave = 0x1001eb0 /* U+1EB0 LATIN CAPITAL LETTER A WITH BREVE AND GRAVE */, + abrevegrave = 0x1001eb1 /* U+1EB1 LATIN SMALL LETTER A WITH BREVE AND GRAVE */, + Abrevehook = 0x1001eb2 /* U+1EB2 LATIN CAPITAL LETTER A WITH BREVE AND HOOK ABOVE */, + abrevehook = 0x1001eb3 /* U+1EB3 LATIN SMALL LETTER A WITH BREVE AND HOOK ABOVE */, + Abrevetilde = 0x1001eb4 /* U+1EB4 LATIN CAPITAL LETTER A WITH BREVE AND TILDE */, + abrevetilde = 0x1001eb5 /* U+1EB5 LATIN SMALL LETTER A WITH BREVE AND TILDE */, + Abrevebelowdot = 0x1001eb6 /* U+1EB6 LATIN CAPITAL LETTER A WITH BREVE AND DOT BELOW */, + abrevebelowdot = 0x1001eb7 /* U+1EB7 LATIN SMALL LETTER A WITH BREVE AND DOT BELOW */, + Ebelowdot = 0x1001eb8 /* U+1EB8 LATIN CAPITAL LETTER E WITH DOT BELOW */, + ebelowdot = 0x1001eb9 /* U+1EB9 LATIN SMALL LETTER E WITH DOT BELOW */, + Ehook = 0x1001eba /* U+1EBA LATIN CAPITAL LETTER E WITH HOOK ABOVE */, + ehook = 0x1001ebb /* U+1EBB LATIN SMALL LETTER E WITH HOOK ABOVE */, + Etilde = 0x1001ebc /* U+1EBC LATIN CAPITAL LETTER E WITH TILDE */, + etilde = 0x1001ebd /* U+1EBD LATIN SMALL LETTER E WITH TILDE */, + Ecircumflexacute = 0x1001ebe /* U+1EBE LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND ACUTE */, + ecircumflexacute = 0x1001ebf /* U+1EBF LATIN SMALL LETTER E WITH CIRCUMFLEX AND ACUTE */, + Ecircumflexgrave = 0x1001ec0 /* U+1EC0 LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND GRAVE */, + ecircumflexgrave = 0x1001ec1 /* U+1EC1 LATIN SMALL LETTER E WITH CIRCUMFLEX AND GRAVE */, + Ecircumflexhook = 0x1001ec2 /* U+1EC2 LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE */, + ecircumflexhook = 0x1001ec3 /* U+1EC3 LATIN SMALL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE */, + Ecircumflextilde = 0x1001ec4 /* U+1EC4 LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND TILDE */, + ecircumflextilde = 0x1001ec5 /* U+1EC5 LATIN SMALL LETTER E WITH CIRCUMFLEX AND TILDE */, + Ecircumflexbelowdot = 0x1001ec6 /* U+1EC6 LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND DOT BELOW */, + ecircumflexbelowdot = 0x1001ec7 /* U+1EC7 LATIN SMALL LETTER E WITH CIRCUMFLEX AND DOT BELOW */, + Ihook = 0x1001ec8 /* U+1EC8 LATIN CAPITAL LETTER I WITH HOOK ABOVE */, + ihook = 0x1001ec9 /* U+1EC9 LATIN SMALL LETTER I WITH HOOK ABOVE */, + Ibelowdot = 0x1001eca /* U+1ECA LATIN CAPITAL LETTER I WITH DOT BELOW */, + ibelowdot = 0x1001ecb /* U+1ECB LATIN SMALL LETTER I WITH DOT BELOW */, + Obelowdot = 0x1001ecc /* U+1ECC LATIN CAPITAL LETTER O WITH DOT BELOW */, + obelowdot = 0x1001ecd /* U+1ECD LATIN SMALL LETTER O WITH DOT BELOW */, + Ohook = 0x1001ece /* U+1ECE LATIN CAPITAL LETTER O WITH HOOK ABOVE */, + ohook = 0x1001ecf /* U+1ECF LATIN SMALL LETTER O WITH HOOK ABOVE */, + Ocircumflexacute = 0x1001ed0 /* U+1ED0 LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND ACUTE */, + ocircumflexacute = 0x1001ed1 /* U+1ED1 LATIN SMALL LETTER O WITH CIRCUMFLEX AND ACUTE */, + Ocircumflexgrave = 0x1001ed2 /* U+1ED2 LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND GRAVE */, + ocircumflexgrave = 0x1001ed3 /* U+1ED3 LATIN SMALL LETTER O WITH CIRCUMFLEX AND GRAVE */, + Ocircumflexhook = 0x1001ed4 /* U+1ED4 LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE */, + ocircumflexhook = 0x1001ed5 /* U+1ED5 LATIN SMALL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE */, + Ocircumflextilde = 0x1001ed6 /* U+1ED6 LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND TILDE */, + ocircumflextilde = 0x1001ed7 /* U+1ED7 LATIN SMALL LETTER O WITH CIRCUMFLEX AND TILDE */, + Ocircumflexbelowdot = 0x1001ed8 /* U+1ED8 LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND DOT BELOW */, + ocircumflexbelowdot = 0x1001ed9 /* U+1ED9 LATIN SMALL LETTER O WITH CIRCUMFLEX AND DOT BELOW */, + Ohornacute = 0x1001eda /* U+1EDA LATIN CAPITAL LETTER O WITH HORN AND ACUTE */, + ohornacute = 0x1001edb /* U+1EDB LATIN SMALL LETTER O WITH HORN AND ACUTE */, + Ohorngrave = 0x1001edc /* U+1EDC LATIN CAPITAL LETTER O WITH HORN AND GRAVE */, + ohorngrave = 0x1001edd /* U+1EDD LATIN SMALL LETTER O WITH HORN AND GRAVE */, + Ohornhook = 0x1001ede /* U+1EDE LATIN CAPITAL LETTER O WITH HORN AND HOOK ABOVE */, + ohornhook = 0x1001edf /* U+1EDF LATIN SMALL LETTER O WITH HORN AND HOOK ABOVE */, + Ohorntilde = 0x1001ee0 /* U+1EE0 LATIN CAPITAL LETTER O WITH HORN AND TILDE */, + ohorntilde = 0x1001ee1 /* U+1EE1 LATIN SMALL LETTER O WITH HORN AND TILDE */, + Ohornbelowdot = 0x1001ee2 /* U+1EE2 LATIN CAPITAL LETTER O WITH HORN AND DOT BELOW */, + ohornbelowdot = 0x1001ee3 /* U+1EE3 LATIN SMALL LETTER O WITH HORN AND DOT BELOW */, + Ubelowdot = 0x1001ee4 /* U+1EE4 LATIN CAPITAL LETTER U WITH DOT BELOW */, + ubelowdot = 0x1001ee5 /* U+1EE5 LATIN SMALL LETTER U WITH DOT BELOW */, + Uhook = 0x1001ee6 /* U+1EE6 LATIN CAPITAL LETTER U WITH HOOK ABOVE */, + uhook = 0x1001ee7 /* U+1EE7 LATIN SMALL LETTER U WITH HOOK ABOVE */, + Uhornacute = 0x1001ee8 /* U+1EE8 LATIN CAPITAL LETTER U WITH HORN AND ACUTE */, + uhornacute = 0x1001ee9 /* U+1EE9 LATIN SMALL LETTER U WITH HORN AND ACUTE */, + Uhorngrave = 0x1001eea /* U+1EEA LATIN CAPITAL LETTER U WITH HORN AND GRAVE */, + uhorngrave = 0x1001eeb /* U+1EEB LATIN SMALL LETTER U WITH HORN AND GRAVE */, + Uhornhook = 0x1001eec /* U+1EEC LATIN CAPITAL LETTER U WITH HORN AND HOOK ABOVE */, + uhornhook = 0x1001eed /* U+1EED LATIN SMALL LETTER U WITH HORN AND HOOK ABOVE */, + Uhorntilde = 0x1001eee /* U+1EEE LATIN CAPITAL LETTER U WITH HORN AND TILDE */, + uhorntilde = 0x1001eef /* U+1EEF LATIN SMALL LETTER U WITH HORN AND TILDE */, + Uhornbelowdot = 0x1001ef0 /* U+1EF0 LATIN CAPITAL LETTER U WITH HORN AND DOT BELOW */, + uhornbelowdot = 0x1001ef1 /* U+1EF1 LATIN SMALL LETTER U WITH HORN AND DOT BELOW */, + Ybelowdot = 0x1001ef4 /* U+1EF4 LATIN CAPITAL LETTER Y WITH DOT BELOW */, + ybelowdot = 0x1001ef5 /* U+1EF5 LATIN SMALL LETTER Y WITH DOT BELOW */, + Yhook = 0x1001ef6 /* U+1EF6 LATIN CAPITAL LETTER Y WITH HOOK ABOVE */, + yhook = 0x1001ef7 /* U+1EF7 LATIN SMALL LETTER Y WITH HOOK ABOVE */, + Ytilde = 0x1001ef8 /* U+1EF8 LATIN CAPITAL LETTER Y WITH TILDE */, + ytilde = 0x1001ef9 /* U+1EF9 LATIN SMALL LETTER Y WITH TILDE */, + Ohorn = 0x10001a0 /* U+01A0 LATIN CAPITAL LETTER O WITH HORN */, + ohorn = 0x10001a1 /* U+01A1 LATIN SMALL LETTER O WITH HORN */, + Uhorn = 0x10001af /* U+01AF LATIN CAPITAL LETTER U WITH HORN */, + uhorn = 0x10001b0 /* U+01B0 LATIN SMALL LETTER U WITH HORN */, + EcuSign = 0x10020a0 /* U+20A0 EURO-CURRENCY SIGN */, + ColonSign = 0x10020a1 /* U+20A1 COLON SIGN */, + CruzeiroSign = 0x10020a2 /* U+20A2 CRUZEIRO SIGN */, + FFrancSign = 0x10020a3 /* U+20A3 FRENCH FRANC SIGN */, + LiraSign = 0x10020a4 /* U+20A4 LIRA SIGN */, + MillSign = 0x10020a5 /* U+20A5 MILL SIGN */, + NairaSign = 0x10020a6 /* U+20A6 NAIRA SIGN */, + PesetaSign = 0x10020a7 /* U+20A7 PESETA SIGN */, + RupeeSign = 0x10020a8 /* U+20A8 RUPEE SIGN */, + WonSign = 0x10020a9 /* U+20A9 WON SIGN */, + NewSheqelSign = 0x10020aa /* U+20AA NEW SHEQEL SIGN */, + DongSign = 0x10020ab /* U+20AB DONG SIGN */, + EuroSign = 0x20ac /* U+20AC EURO SIGN */, + zerosuperior = 0x1002070 /* U+2070 SUPERSCRIPT ZERO */, + foursuperior = 0x1002074 /* U+2074 SUPERSCRIPT FOUR */, + fivesuperior = 0x1002075 /* U+2075 SUPERSCRIPT FIVE */, + sixsuperior = 0x1002076 /* U+2076 SUPERSCRIPT SIX */, + sevensuperior = 0x1002077 /* U+2077 SUPERSCRIPT SEVEN */, + eightsuperior = 0x1002078 /* U+2078 SUPERSCRIPT EIGHT */, + ninesuperior = 0x1002079 /* U+2079 SUPERSCRIPT NINE */, + zerosubscript = 0x1002080 /* U+2080 SUBSCRIPT ZERO */, + onesubscript = 0x1002081 /* U+2081 SUBSCRIPT ONE */, + twosubscript = 0x1002082 /* U+2082 SUBSCRIPT TWO */, + threesubscript = 0x1002083 /* U+2083 SUBSCRIPT THREE */, + foursubscript = 0x1002084 /* U+2084 SUBSCRIPT FOUR */, + fivesubscript = 0x1002085 /* U+2085 SUBSCRIPT FIVE */, + sixsubscript = 0x1002086 /* U+2086 SUBSCRIPT SIX */, + sevensubscript = 0x1002087 /* U+2087 SUBSCRIPT SEVEN */, + eightsubscript = 0x1002088 /* U+2088 SUBSCRIPT EIGHT */, + ninesubscript = 0x1002089 /* U+2089 SUBSCRIPT NINE */, + partdifferential = 0x1002202 /* U+2202 PARTIAL DIFFERENTIAL */, + emptyset = 0x1002205 /* U+2205 NULL SET */, + elementof = 0x1002208 /* U+2208 ELEMENT OF */, + notelementof = 0x1002209 /* U+2209 NOT AN ELEMENT OF */, + containsas = 0x100220B /* U+220B CONTAINS AS MEMBER */, + squareroot = 0x100221A /* U+221A SQUARE ROOT */, + cuberoot = 0x100221B /* U+221B CUBE ROOT */, + fourthroot = 0x100221C /* U+221C FOURTH ROOT */, + dintegral = 0x100222C /* U+222C DOUBLE INTEGRAL */, + tintegral = 0x100222D /* U+222D TRIPLE INTEGRAL */, + because = 0x1002235 /* U+2235 BECAUSE */, + approxeq = 0x1002248 /* U+2245 ALMOST EQUAL TO */, + notapproxeq = 0x1002247 /* U+2247 NOT ALMOST EQUAL TO */, + notidentical = 0x1002262 /* U+2262 NOT IDENTICAL TO */, + stricteq = 0x1002263 /* U+2263 STRICTLY EQUIVALENT TO */, + braille_dot_1 = 0xfff1, + braille_dot_2 = 0xfff2, + braille_dot_3 = 0xfff3, + braille_dot_4 = 0xfff4, + braille_dot_5 = 0xfff5, + braille_dot_6 = 0xfff6, + braille_dot_7 = 0xfff7, + braille_dot_8 = 0xfff8, + braille_dot_9 = 0xfff9, + braille_dot_10 = 0xfffa, + braille_blank = 0x1002800 /* U+2800 BRAILLE PATTERN BLANK */, + braille_dots_1 = 0x1002801 /* U+2801 BRAILLE PATTERN DOTS-1 */, + braille_dots_2 = 0x1002802 /* U+2802 BRAILLE PATTERN DOTS-2 */, + braille_dots_12 = 0x1002803 /* U+2803 BRAILLE PATTERN DOTS-12 */, + braille_dots_3 = 0x1002804 /* U+2804 BRAILLE PATTERN DOTS-3 */, + braille_dots_13 = 0x1002805 /* U+2805 BRAILLE PATTERN DOTS-13 */, + braille_dots_23 = 0x1002806 /* U+2806 BRAILLE PATTERN DOTS-23 */, + braille_dots_123 = 0x1002807 /* U+2807 BRAILLE PATTERN DOTS-123 */, + braille_dots_4 = 0x1002808 /* U+2808 BRAILLE PATTERN DOTS-4 */, + braille_dots_14 = 0x1002809 /* U+2809 BRAILLE PATTERN DOTS-14 */, + braille_dots_24 = 0x100280a /* U+280a BRAILLE PATTERN DOTS-24 */, + braille_dots_124 = 0x100280b /* U+280b BRAILLE PATTERN DOTS-124 */, + braille_dots_34 = 0x100280c /* U+280c BRAILLE PATTERN DOTS-34 */, + braille_dots_134 = 0x100280d /* U+280d BRAILLE PATTERN DOTS-134 */, + braille_dots_234 = 0x100280e /* U+280e BRAILLE PATTERN DOTS-234 */, + braille_dots_1234 = 0x100280f /* U+280f BRAILLE PATTERN DOTS-1234 */, + braille_dots_5 = 0x1002810 /* U+2810 BRAILLE PATTERN DOTS-5 */, + braille_dots_15 = 0x1002811 /* U+2811 BRAILLE PATTERN DOTS-15 */, + braille_dots_25 = 0x1002812 /* U+2812 BRAILLE PATTERN DOTS-25 */, + braille_dots_125 = 0x1002813 /* U+2813 BRAILLE PATTERN DOTS-125 */, + braille_dots_35 = 0x1002814 /* U+2814 BRAILLE PATTERN DOTS-35 */, + braille_dots_135 = 0x1002815 /* U+2815 BRAILLE PATTERN DOTS-135 */, + braille_dots_235 = 0x1002816 /* U+2816 BRAILLE PATTERN DOTS-235 */, + braille_dots_1235 = 0x1002817 /* U+2817 BRAILLE PATTERN DOTS-1235 */, + braille_dots_45 = 0x1002818 /* U+2818 BRAILLE PATTERN DOTS-45 */, + braille_dots_145 = 0x1002819 /* U+2819 BRAILLE PATTERN DOTS-145 */, + braille_dots_245 = 0x100281a /* U+281a BRAILLE PATTERN DOTS-245 */, + braille_dots_1245 = 0x100281b /* U+281b BRAILLE PATTERN DOTS-1245 */, + braille_dots_345 = 0x100281c /* U+281c BRAILLE PATTERN DOTS-345 */, + braille_dots_1345 = 0x100281d /* U+281d BRAILLE PATTERN DOTS-1345 */, + braille_dots_2345 = 0x100281e /* U+281e BRAILLE PATTERN DOTS-2345 */, + braille_dots_12345 = 0x100281f /* U+281f BRAILLE PATTERN DOTS-12345 */, + braille_dots_6 = 0x1002820 /* U+2820 BRAILLE PATTERN DOTS-6 */, + braille_dots_16 = 0x1002821 /* U+2821 BRAILLE PATTERN DOTS-16 */, + braille_dots_26 = 0x1002822 /* U+2822 BRAILLE PATTERN DOTS-26 */, + braille_dots_126 = 0x1002823 /* U+2823 BRAILLE PATTERN DOTS-126 */, + braille_dots_36 = 0x1002824 /* U+2824 BRAILLE PATTERN DOTS-36 */, + braille_dots_136 = 0x1002825 /* U+2825 BRAILLE PATTERN DOTS-136 */, + braille_dots_236 = 0x1002826 /* U+2826 BRAILLE PATTERN DOTS-236 */, + braille_dots_1236 = 0x1002827 /* U+2827 BRAILLE PATTERN DOTS-1236 */, + braille_dots_46 = 0x1002828 /* U+2828 BRAILLE PATTERN DOTS-46 */, + braille_dots_146 = 0x1002829 /* U+2829 BRAILLE PATTERN DOTS-146 */, + braille_dots_246 = 0x100282a /* U+282a BRAILLE PATTERN DOTS-246 */, + braille_dots_1246 = 0x100282b /* U+282b BRAILLE PATTERN DOTS-1246 */, + braille_dots_346 = 0x100282c /* U+282c BRAILLE PATTERN DOTS-346 */, + braille_dots_1346 = 0x100282d /* U+282d BRAILLE PATTERN DOTS-1346 */, + braille_dots_2346 = 0x100282e /* U+282e BRAILLE PATTERN DOTS-2346 */, + braille_dots_12346 = 0x100282f /* U+282f BRAILLE PATTERN DOTS-12346 */, + braille_dots_56 = 0x1002830 /* U+2830 BRAILLE PATTERN DOTS-56 */, + braille_dots_156 = 0x1002831 /* U+2831 BRAILLE PATTERN DOTS-156 */, + braille_dots_256 = 0x1002832 /* U+2832 BRAILLE PATTERN DOTS-256 */, + braille_dots_1256 = 0x1002833 /* U+2833 BRAILLE PATTERN DOTS-1256 */, + braille_dots_356 = 0x1002834 /* U+2834 BRAILLE PATTERN DOTS-356 */, + braille_dots_1356 = 0x1002835 /* U+2835 BRAILLE PATTERN DOTS-1356 */, + braille_dots_2356 = 0x1002836 /* U+2836 BRAILLE PATTERN DOTS-2356 */, + braille_dots_12356 = 0x1002837 /* U+2837 BRAILLE PATTERN DOTS-12356 */, + braille_dots_456 = 0x1002838 /* U+2838 BRAILLE PATTERN DOTS-456 */, + braille_dots_1456 = 0x1002839 /* U+2839 BRAILLE PATTERN DOTS-1456 */, + braille_dots_2456 = 0x100283a /* U+283a BRAILLE PATTERN DOTS-2456 */, + braille_dots_12456 = 0x100283b /* U+283b BRAILLE PATTERN DOTS-12456 */, + braille_dots_3456 = 0x100283c /* U+283c BRAILLE PATTERN DOTS-3456 */, + braille_dots_13456 = 0x100283d /* U+283d BRAILLE PATTERN DOTS-13456 */, + braille_dots_23456 = 0x100283e /* U+283e BRAILLE PATTERN DOTS-23456 */, + braille_dots_123456 = 0x100283f /* U+283f BRAILLE PATTERN DOTS-123456 */, + braille_dots_7 = 0x1002840 /* U+2840 BRAILLE PATTERN DOTS-7 */, + braille_dots_17 = 0x1002841 /* U+2841 BRAILLE PATTERN DOTS-17 */, + braille_dots_27 = 0x1002842 /* U+2842 BRAILLE PATTERN DOTS-27 */, + braille_dots_127 = 0x1002843 /* U+2843 BRAILLE PATTERN DOTS-127 */, + braille_dots_37 = 0x1002844 /* U+2844 BRAILLE PATTERN DOTS-37 */, + braille_dots_137 = 0x1002845 /* U+2845 BRAILLE PATTERN DOTS-137 */, + braille_dots_237 = 0x1002846 /* U+2846 BRAILLE PATTERN DOTS-237 */, + braille_dots_1237 = 0x1002847 /* U+2847 BRAILLE PATTERN DOTS-1237 */, + braille_dots_47 = 0x1002848 /* U+2848 BRAILLE PATTERN DOTS-47 */, + braille_dots_147 = 0x1002849 /* U+2849 BRAILLE PATTERN DOTS-147 */, + braille_dots_247 = 0x100284a /* U+284a BRAILLE PATTERN DOTS-247 */, + braille_dots_1247 = 0x100284b /* U+284b BRAILLE PATTERN DOTS-1247 */, + braille_dots_347 = 0x100284c /* U+284c BRAILLE PATTERN DOTS-347 */, + braille_dots_1347 = 0x100284d /* U+284d BRAILLE PATTERN DOTS-1347 */, + braille_dots_2347 = 0x100284e /* U+284e BRAILLE PATTERN DOTS-2347 */, + braille_dots_12347 = 0x100284f /* U+284f BRAILLE PATTERN DOTS-12347 */, + braille_dots_57 = 0x1002850 /* U+2850 BRAILLE PATTERN DOTS-57 */, + braille_dots_157 = 0x1002851 /* U+2851 BRAILLE PATTERN DOTS-157 */, + braille_dots_257 = 0x1002852 /* U+2852 BRAILLE PATTERN DOTS-257 */, + braille_dots_1257 = 0x1002853 /* U+2853 BRAILLE PATTERN DOTS-1257 */, + braille_dots_357 = 0x1002854 /* U+2854 BRAILLE PATTERN DOTS-357 */, + braille_dots_1357 = 0x1002855 /* U+2855 BRAILLE PATTERN DOTS-1357 */, + braille_dots_2357 = 0x1002856 /* U+2856 BRAILLE PATTERN DOTS-2357 */, + braille_dots_12357 = 0x1002857 /* U+2857 BRAILLE PATTERN DOTS-12357 */, + braille_dots_457 = 0x1002858 /* U+2858 BRAILLE PATTERN DOTS-457 */, + braille_dots_1457 = 0x1002859 /* U+2859 BRAILLE PATTERN DOTS-1457 */, + braille_dots_2457 = 0x100285a /* U+285a BRAILLE PATTERN DOTS-2457 */, + braille_dots_12457 = 0x100285b /* U+285b BRAILLE PATTERN DOTS-12457 */, + braille_dots_3457 = 0x100285c /* U+285c BRAILLE PATTERN DOTS-3457 */, + braille_dots_13457 = 0x100285d /* U+285d BRAILLE PATTERN DOTS-13457 */, + braille_dots_23457 = 0x100285e /* U+285e BRAILLE PATTERN DOTS-23457 */, + braille_dots_123457 = 0x100285f /* U+285f BRAILLE PATTERN DOTS-123457 */, + braille_dots_67 = 0x1002860 /* U+2860 BRAILLE PATTERN DOTS-67 */, + braille_dots_167 = 0x1002861 /* U+2861 BRAILLE PATTERN DOTS-167 */, + braille_dots_267 = 0x1002862 /* U+2862 BRAILLE PATTERN DOTS-267 */, + braille_dots_1267 = 0x1002863 /* U+2863 BRAILLE PATTERN DOTS-1267 */, + braille_dots_367 = 0x1002864 /* U+2864 BRAILLE PATTERN DOTS-367 */, + braille_dots_1367 = 0x1002865 /* U+2865 BRAILLE PATTERN DOTS-1367 */, + braille_dots_2367 = 0x1002866 /* U+2866 BRAILLE PATTERN DOTS-2367 */, + braille_dots_12367 = 0x1002867 /* U+2867 BRAILLE PATTERN DOTS-12367 */, + braille_dots_467 = 0x1002868 /* U+2868 BRAILLE PATTERN DOTS-467 */, + braille_dots_1467 = 0x1002869 /* U+2869 BRAILLE PATTERN DOTS-1467 */, + braille_dots_2467 = 0x100286a /* U+286a BRAILLE PATTERN DOTS-2467 */, + braille_dots_12467 = 0x100286b /* U+286b BRAILLE PATTERN DOTS-12467 */, + braille_dots_3467 = 0x100286c /* U+286c BRAILLE PATTERN DOTS-3467 */, + braille_dots_13467 = 0x100286d /* U+286d BRAILLE PATTERN DOTS-13467 */, + braille_dots_23467 = 0x100286e /* U+286e BRAILLE PATTERN DOTS-23467 */, + braille_dots_123467 = 0x100286f /* U+286f BRAILLE PATTERN DOTS-123467 */, + braille_dots_567 = 0x1002870 /* U+2870 BRAILLE PATTERN DOTS-567 */, + braille_dots_1567 = 0x1002871 /* U+2871 BRAILLE PATTERN DOTS-1567 */, + braille_dots_2567 = 0x1002872 /* U+2872 BRAILLE PATTERN DOTS-2567 */, + braille_dots_12567 = 0x1002873 /* U+2873 BRAILLE PATTERN DOTS-12567 */, + braille_dots_3567 = 0x1002874 /* U+2874 BRAILLE PATTERN DOTS-3567 */, + braille_dots_13567 = 0x1002875 /* U+2875 BRAILLE PATTERN DOTS-13567 */, + braille_dots_23567 = 0x1002876 /* U+2876 BRAILLE PATTERN DOTS-23567 */, + braille_dots_123567 = 0x1002877 /* U+2877 BRAILLE PATTERN DOTS-123567 */, + braille_dots_4567 = 0x1002878 /* U+2878 BRAILLE PATTERN DOTS-4567 */, + braille_dots_14567 = 0x1002879 /* U+2879 BRAILLE PATTERN DOTS-14567 */, + braille_dots_24567 = 0x100287a /* U+287a BRAILLE PATTERN DOTS-24567 */, + braille_dots_124567 = 0x100287b /* U+287b BRAILLE PATTERN DOTS-124567 */, + braille_dots_34567 = 0x100287c /* U+287c BRAILLE PATTERN DOTS-34567 */, + braille_dots_134567 = 0x100287d /* U+287d BRAILLE PATTERN DOTS-134567 */, + braille_dots_234567 = 0x100287e /* U+287e BRAILLE PATTERN DOTS-234567 */, + braille_dots_1234567 = 0x100287f /* U+287f BRAILLE PATTERN DOTS-1234567 */, + braille_dots_8 = 0x1002880 /* U+2880 BRAILLE PATTERN DOTS-8 */, + braille_dots_18 = 0x1002881 /* U+2881 BRAILLE PATTERN DOTS-18 */, + braille_dots_28 = 0x1002882 /* U+2882 BRAILLE PATTERN DOTS-28 */, + braille_dots_128 = 0x1002883 /* U+2883 BRAILLE PATTERN DOTS-128 */, + braille_dots_38 = 0x1002884 /* U+2884 BRAILLE PATTERN DOTS-38 */, + braille_dots_138 = 0x1002885 /* U+2885 BRAILLE PATTERN DOTS-138 */, + braille_dots_238 = 0x1002886 /* U+2886 BRAILLE PATTERN DOTS-238 */, + braille_dots_1238 = 0x1002887 /* U+2887 BRAILLE PATTERN DOTS-1238 */, + braille_dots_48 = 0x1002888 /* U+2888 BRAILLE PATTERN DOTS-48 */, + braille_dots_148 = 0x1002889 /* U+2889 BRAILLE PATTERN DOTS-148 */, + braille_dots_248 = 0x100288a /* U+288a BRAILLE PATTERN DOTS-248 */, + braille_dots_1248 = 0x100288b /* U+288b BRAILLE PATTERN DOTS-1248 */, + braille_dots_348 = 0x100288c /* U+288c BRAILLE PATTERN DOTS-348 */, + braille_dots_1348 = 0x100288d /* U+288d BRAILLE PATTERN DOTS-1348 */, + braille_dots_2348 = 0x100288e /* U+288e BRAILLE PATTERN DOTS-2348 */, + braille_dots_12348 = 0x100288f /* U+288f BRAILLE PATTERN DOTS-12348 */, + braille_dots_58 = 0x1002890 /* U+2890 BRAILLE PATTERN DOTS-58 */, + braille_dots_158 = 0x1002891 /* U+2891 BRAILLE PATTERN DOTS-158 */, + braille_dots_258 = 0x1002892 /* U+2892 BRAILLE PATTERN DOTS-258 */, + braille_dots_1258 = 0x1002893 /* U+2893 BRAILLE PATTERN DOTS-1258 */, + braille_dots_358 = 0x1002894 /* U+2894 BRAILLE PATTERN DOTS-358 */, + braille_dots_1358 = 0x1002895 /* U+2895 BRAILLE PATTERN DOTS-1358 */, + braille_dots_2358 = 0x1002896 /* U+2896 BRAILLE PATTERN DOTS-2358 */, + braille_dots_12358 = 0x1002897 /* U+2897 BRAILLE PATTERN DOTS-12358 */, + braille_dots_458 = 0x1002898 /* U+2898 BRAILLE PATTERN DOTS-458 */, + braille_dots_1458 = 0x1002899 /* U+2899 BRAILLE PATTERN DOTS-1458 */, + braille_dots_2458 = 0x100289a /* U+289a BRAILLE PATTERN DOTS-2458 */, + braille_dots_12458 = 0x100289b /* U+289b BRAILLE PATTERN DOTS-12458 */, + braille_dots_3458 = 0x100289c /* U+289c BRAILLE PATTERN DOTS-3458 */, + braille_dots_13458 = 0x100289d /* U+289d BRAILLE PATTERN DOTS-13458 */, + braille_dots_23458 = 0x100289e /* U+289e BRAILLE PATTERN DOTS-23458 */, + braille_dots_123458 = 0x100289f /* U+289f BRAILLE PATTERN DOTS-123458 */, + braille_dots_68 = 0x10028a0 /* U+28a0 BRAILLE PATTERN DOTS-68 */, + braille_dots_168 = 0x10028a1 /* U+28a1 BRAILLE PATTERN DOTS-168 */, + braille_dots_268 = 0x10028a2 /* U+28a2 BRAILLE PATTERN DOTS-268 */, + braille_dots_1268 = 0x10028a3 /* U+28a3 BRAILLE PATTERN DOTS-1268 */, + braille_dots_368 = 0x10028a4 /* U+28a4 BRAILLE PATTERN DOTS-368 */, + braille_dots_1368 = 0x10028a5 /* U+28a5 BRAILLE PATTERN DOTS-1368 */, + braille_dots_2368 = 0x10028a6 /* U+28a6 BRAILLE PATTERN DOTS-2368 */, + braille_dots_12368 = 0x10028a7 /* U+28a7 BRAILLE PATTERN DOTS-12368 */, + braille_dots_468 = 0x10028a8 /* U+28a8 BRAILLE PATTERN DOTS-468 */, + braille_dots_1468 = 0x10028a9 /* U+28a9 BRAILLE PATTERN DOTS-1468 */, + braille_dots_2468 = 0x10028aa /* U+28aa BRAILLE PATTERN DOTS-2468 */, + braille_dots_12468 = 0x10028ab /* U+28ab BRAILLE PATTERN DOTS-12468 */, + braille_dots_3468 = 0x10028ac /* U+28ac BRAILLE PATTERN DOTS-3468 */, + braille_dots_13468 = 0x10028ad /* U+28ad BRAILLE PATTERN DOTS-13468 */, + braille_dots_23468 = 0x10028ae /* U+28ae BRAILLE PATTERN DOTS-23468 */, + braille_dots_123468 = 0x10028af /* U+28af BRAILLE PATTERN DOTS-123468 */, + braille_dots_568 = 0x10028b0 /* U+28b0 BRAILLE PATTERN DOTS-568 */, + braille_dots_1568 = 0x10028b1 /* U+28b1 BRAILLE PATTERN DOTS-1568 */, + braille_dots_2568 = 0x10028b2 /* U+28b2 BRAILLE PATTERN DOTS-2568 */, + braille_dots_12568 = 0x10028b3 /* U+28b3 BRAILLE PATTERN DOTS-12568 */, + braille_dots_3568 = 0x10028b4 /* U+28b4 BRAILLE PATTERN DOTS-3568 */, + braille_dots_13568 = 0x10028b5 /* U+28b5 BRAILLE PATTERN DOTS-13568 */, + braille_dots_23568 = 0x10028b6 /* U+28b6 BRAILLE PATTERN DOTS-23568 */, + braille_dots_123568 = 0x10028b7 /* U+28b7 BRAILLE PATTERN DOTS-123568 */, + braille_dots_4568 = 0x10028b8 /* U+28b8 BRAILLE PATTERN DOTS-4568 */, + braille_dots_14568 = 0x10028b9 /* U+28b9 BRAILLE PATTERN DOTS-14568 */, + braille_dots_24568 = 0x10028ba /* U+28ba BRAILLE PATTERN DOTS-24568 */, + braille_dots_124568 = 0x10028bb /* U+28bb BRAILLE PATTERN DOTS-124568 */, + braille_dots_34568 = 0x10028bc /* U+28bc BRAILLE PATTERN DOTS-34568 */, + braille_dots_134568 = 0x10028bd /* U+28bd BRAILLE PATTERN DOTS-134568 */, + braille_dots_234568 = 0x10028be /* U+28be BRAILLE PATTERN DOTS-234568 */, + braille_dots_1234568 = 0x10028bf /* U+28bf BRAILLE PATTERN DOTS-1234568 */, + braille_dots_78 = 0x10028c0 /* U+28c0 BRAILLE PATTERN DOTS-78 */, + braille_dots_178 = 0x10028c1 /* U+28c1 BRAILLE PATTERN DOTS-178 */, + braille_dots_278 = 0x10028c2 /* U+28c2 BRAILLE PATTERN DOTS-278 */, + braille_dots_1278 = 0x10028c3 /* U+28c3 BRAILLE PATTERN DOTS-1278 */, + braille_dots_378 = 0x10028c4 /* U+28c4 BRAILLE PATTERN DOTS-378 */, + braille_dots_1378 = 0x10028c5 /* U+28c5 BRAILLE PATTERN DOTS-1378 */, + braille_dots_2378 = 0x10028c6 /* U+28c6 BRAILLE PATTERN DOTS-2378 */, + braille_dots_12378 = 0x10028c7 /* U+28c7 BRAILLE PATTERN DOTS-12378 */, + braille_dots_478 = 0x10028c8 /* U+28c8 BRAILLE PATTERN DOTS-478 */, + braille_dots_1478 = 0x10028c9 /* U+28c9 BRAILLE PATTERN DOTS-1478 */, + braille_dots_2478 = 0x10028ca /* U+28ca BRAILLE PATTERN DOTS-2478 */, + braille_dots_12478 = 0x10028cb /* U+28cb BRAILLE PATTERN DOTS-12478 */, + braille_dots_3478 = 0x10028cc /* U+28cc BRAILLE PATTERN DOTS-3478 */, + braille_dots_13478 = 0x10028cd /* U+28cd BRAILLE PATTERN DOTS-13478 */, + braille_dots_23478 = 0x10028ce /* U+28ce BRAILLE PATTERN DOTS-23478 */, + braille_dots_123478 = 0x10028cf /* U+28cf BRAILLE PATTERN DOTS-123478 */, + braille_dots_578 = 0x10028d0 /* U+28d0 BRAILLE PATTERN DOTS-578 */, + braille_dots_1578 = 0x10028d1 /* U+28d1 BRAILLE PATTERN DOTS-1578 */, + braille_dots_2578 = 0x10028d2 /* U+28d2 BRAILLE PATTERN DOTS-2578 */, + braille_dots_12578 = 0x10028d3 /* U+28d3 BRAILLE PATTERN DOTS-12578 */, + braille_dots_3578 = 0x10028d4 /* U+28d4 BRAILLE PATTERN DOTS-3578 */, + braille_dots_13578 = 0x10028d5 /* U+28d5 BRAILLE PATTERN DOTS-13578 */, + braille_dots_23578 = 0x10028d6 /* U+28d6 BRAILLE PATTERN DOTS-23578 */, + braille_dots_123578 = 0x10028d7 /* U+28d7 BRAILLE PATTERN DOTS-123578 */, + braille_dots_4578 = 0x10028d8 /* U+28d8 BRAILLE PATTERN DOTS-4578 */, + braille_dots_14578 = 0x10028d9 /* U+28d9 BRAILLE PATTERN DOTS-14578 */, + braille_dots_24578 = 0x10028da /* U+28da BRAILLE PATTERN DOTS-24578 */, + braille_dots_124578 = 0x10028db /* U+28db BRAILLE PATTERN DOTS-124578 */, + braille_dots_34578 = 0x10028dc /* U+28dc BRAILLE PATTERN DOTS-34578 */, + braille_dots_134578 = 0x10028dd /* U+28dd BRAILLE PATTERN DOTS-134578 */, + braille_dots_234578 = 0x10028de /* U+28de BRAILLE PATTERN DOTS-234578 */, + braille_dots_1234578 = 0x10028df /* U+28df BRAILLE PATTERN DOTS-1234578 */, + braille_dots_678 = 0x10028e0 /* U+28e0 BRAILLE PATTERN DOTS-678 */, + braille_dots_1678 = 0x10028e1 /* U+28e1 BRAILLE PATTERN DOTS-1678 */, + braille_dots_2678 = 0x10028e2 /* U+28e2 BRAILLE PATTERN DOTS-2678 */, + braille_dots_12678 = 0x10028e3 /* U+28e3 BRAILLE PATTERN DOTS-12678 */, + braille_dots_3678 = 0x10028e4 /* U+28e4 BRAILLE PATTERN DOTS-3678 */, + braille_dots_13678 = 0x10028e5 /* U+28e5 BRAILLE PATTERN DOTS-13678 */, + braille_dots_23678 = 0x10028e6 /* U+28e6 BRAILLE PATTERN DOTS-23678 */, + braille_dots_123678 = 0x10028e7 /* U+28e7 BRAILLE PATTERN DOTS-123678 */, + braille_dots_4678 = 0x10028e8 /* U+28e8 BRAILLE PATTERN DOTS-4678 */, + braille_dots_14678 = 0x10028e9 /* U+28e9 BRAILLE PATTERN DOTS-14678 */, + braille_dots_24678 = 0x10028ea /* U+28ea BRAILLE PATTERN DOTS-24678 */, + braille_dots_124678 = 0x10028eb /* U+28eb BRAILLE PATTERN DOTS-124678 */, + braille_dots_34678 = 0x10028ec /* U+28ec BRAILLE PATTERN DOTS-34678 */, + braille_dots_134678 = 0x10028ed /* U+28ed BRAILLE PATTERN DOTS-134678 */, + braille_dots_234678 = 0x10028ee /* U+28ee BRAILLE PATTERN DOTS-234678 */, + braille_dots_1234678 = 0x10028ef /* U+28ef BRAILLE PATTERN DOTS-1234678 */, + braille_dots_5678 = 0x10028f0 /* U+28f0 BRAILLE PATTERN DOTS-5678 */, + braille_dots_15678 = 0x10028f1 /* U+28f1 BRAILLE PATTERN DOTS-15678 */, + braille_dots_25678 = 0x10028f2 /* U+28f2 BRAILLE PATTERN DOTS-25678 */, + braille_dots_125678 = 0x10028f3 /* U+28f3 BRAILLE PATTERN DOTS-125678 */, + braille_dots_35678 = 0x10028f4 /* U+28f4 BRAILLE PATTERN DOTS-35678 */, + braille_dots_135678 = 0x10028f5 /* U+28f5 BRAILLE PATTERN DOTS-135678 */, + braille_dots_235678 = 0x10028f6 /* U+28f6 BRAILLE PATTERN DOTS-235678 */, + braille_dots_1235678 = 0x10028f7 /* U+28f7 BRAILLE PATTERN DOTS-1235678 */, + braille_dots_45678 = 0x10028f8 /* U+28f8 BRAILLE PATTERN DOTS-45678 */, + braille_dots_145678 = 0x10028f9 /* U+28f9 BRAILLE PATTERN DOTS-145678 */, + braille_dots_245678 = 0x10028fa /* U+28fa BRAILLE PATTERN DOTS-245678 */, + braille_dots_1245678 = 0x10028fb /* U+28fb BRAILLE PATTERN DOTS-1245678 */, + braille_dots_345678 = 0x10028fc /* U+28fc BRAILLE PATTERN DOTS-345678 */, + braille_dots_1345678 = 0x10028fd /* U+28fd BRAILLE PATTERN DOTS-1345678 */, + braille_dots_2345678 = 0x10028fe /* U+28fe BRAILLE PATTERN DOTS-2345678 */, + braille_dots_12345678 = 0x10028ff /* U+28ff BRAILLE PATTERN DOTS-12345678 */, + Sinh_ng = 0x1000d82 /* U+0D82 SINHALA ANUSVARAYA */, + Sinh_h2 = 0x1000d83 /* U+0D83 SINHALA VISARGAYA */, + Sinh_a = 0x1000d85 /* U+0D85 SINHALA AYANNA */, + Sinh_aa = 0x1000d86 /* U+0D86 SINHALA AAYANNA */, + Sinh_ae = 0x1000d87 /* U+0D87 SINHALA AEYANNA */, + Sinh_aee = 0x1000d88 /* U+0D88 SINHALA AEEYANNA */, + Sinh_i = 0x1000d89 /* U+0D89 SINHALA IYANNA */, + Sinh_ii = 0x1000d8a /* U+0D8A SINHALA IIYANNA */, + Sinh_u = 0x1000d8b /* U+0D8B SINHALA UYANNA */, + Sinh_uu = 0x1000d8c /* U+0D8C SINHALA UUYANNA */, + Sinh_ri = 0x1000d8d /* U+0D8D SINHALA IRUYANNA */, + Sinh_rii = 0x1000d8e /* U+0D8E SINHALA IRUUYANNA */, + Sinh_lu = 0x1000d8f /* U+0D8F SINHALA ILUYANNA */, + Sinh_luu = 0x1000d90 /* U+0D90 SINHALA ILUUYANNA */, + Sinh_e = 0x1000d91 /* U+0D91 SINHALA EYANNA */, + Sinh_ee = 0x1000d92 /* U+0D92 SINHALA EEYANNA */, + Sinh_ai = 0x1000d93 /* U+0D93 SINHALA AIYANNA */, + Sinh_o = 0x1000d94 /* U+0D94 SINHALA OYANNA */, + Sinh_oo = 0x1000d95 /* U+0D95 SINHALA OOYANNA */, + Sinh_au = 0x1000d96 /* U+0D96 SINHALA AUYANNA */, + Sinh_ka = 0x1000d9a /* U+0D9A SINHALA KAYANNA */, + Sinh_kha = 0x1000d9b /* U+0D9B SINHALA MAHA. KAYANNA */, + Sinh_ga = 0x1000d9c /* U+0D9C SINHALA GAYANNA */, + Sinh_gha = 0x1000d9d /* U+0D9D SINHALA MAHA. GAYANNA */, + Sinh_ng2 = 0x1000d9e /* U+0D9E SINHALA KANTAJA NAASIKYAYA */, + Sinh_nga = 0x1000d9f /* U+0D9F SINHALA SANYAKA GAYANNA */, + Sinh_ca = 0x1000da0 /* U+0DA0 SINHALA CAYANNA */, + Sinh_cha = 0x1000da1 /* U+0DA1 SINHALA MAHA. CAYANNA */, + Sinh_ja = 0x1000da2 /* U+0DA2 SINHALA JAYANNA */, + Sinh_jha = 0x1000da3 /* U+0DA3 SINHALA MAHA. JAYANNA */, + Sinh_nya = 0x1000da4 /* U+0DA4 SINHALA TAALUJA NAASIKYAYA */, + Sinh_jnya = 0x1000da5 /* U+0DA5 SINHALA TAALUJA SANYOOGA NAASIKYAYA */, + Sinh_nja = 0x1000da6 /* U+0DA6 SINHALA SANYAKA JAYANNA */, + Sinh_tta = 0x1000da7 /* U+0DA7 SINHALA TTAYANNA */, + Sinh_ttha = 0x1000da8 /* U+0DA8 SINHALA MAHA. TTAYANNA */, + Sinh_dda = 0x1000da9 /* U+0DA9 SINHALA DDAYANNA */, + Sinh_ddha = 0x1000daa /* U+0DAA SINHALA MAHA. DDAYANNA */, + Sinh_nna = 0x1000dab /* U+0DAB SINHALA MUURDHAJA NAYANNA */, + Sinh_ndda = 0x1000dac /* U+0DAC SINHALA SANYAKA DDAYANNA */, + Sinh_tha = 0x1000dad /* U+0DAD SINHALA TAYANNA */, + Sinh_thha = 0x1000dae /* U+0DAE SINHALA MAHA. TAYANNA */, + Sinh_dha = 0x1000daf /* U+0DAF SINHALA DAYANNA */, + Sinh_dhha = 0x1000db0 /* U+0DB0 SINHALA MAHA. DAYANNA */, + Sinh_na = 0x1000db1 /* U+0DB1 SINHALA DANTAJA NAYANNA */, + Sinh_ndha = 0x1000db3 /* U+0DB3 SINHALA SANYAKA DAYANNA */, + Sinh_pa = 0x1000db4 /* U+0DB4 SINHALA PAYANNA */, + Sinh_pha = 0x1000db5 /* U+0DB5 SINHALA MAHA. PAYANNA */, + Sinh_ba = 0x1000db6 /* U+0DB6 SINHALA BAYANNA */, + Sinh_bha = 0x1000db7 /* U+0DB7 SINHALA MAHA. BAYANNA */, + Sinh_ma = 0x1000db8 /* U+0DB8 SINHALA MAYANNA */, + Sinh_mba = 0x1000db9 /* U+0DB9 SINHALA AMBA BAYANNA */, + Sinh_ya = 0x1000dba /* U+0DBA SINHALA YAYANNA */, + Sinh_ra = 0x1000dbb /* U+0DBB SINHALA RAYANNA */, + Sinh_la = 0x1000dbd /* U+0DBD SINHALA DANTAJA LAYANNA */, + Sinh_va = 0x1000dc0 /* U+0DC0 SINHALA VAYANNA */, + Sinh_sha = 0x1000dc1 /* U+0DC1 SINHALA TAALUJA SAYANNA */, + Sinh_ssha = 0x1000dc2 /* U+0DC2 SINHALA MUURDHAJA SAYANNA */, + Sinh_sa = 0x1000dc3 /* U+0DC3 SINHALA DANTAJA SAYANNA */, + Sinh_ha = 0x1000dc4 /* U+0DC4 SINHALA HAYANNA */, + Sinh_lla = 0x1000dc5 /* U+0DC5 SINHALA MUURDHAJA LAYANNA */, + Sinh_fa = 0x1000dc6 /* U+0DC6 SINHALA FAYANNA */, + Sinh_al = 0x1000dca /* U+0DCA SINHALA AL-LAKUNA */, + Sinh_aa2 = 0x1000dcf /* U+0DCF SINHALA AELA-PILLA */, + Sinh_ae2 = 0x1000dd0 /* U+0DD0 SINHALA AEDA-PILLA */, + Sinh_aee2 = 0x1000dd1 /* U+0DD1 SINHALA DIGA AEDA-PILLA */, + Sinh_i2 = 0x1000dd2 /* U+0DD2 SINHALA IS-PILLA */, + Sinh_ii2 = 0x1000dd3 /* U+0DD3 SINHALA DIGA IS-PILLA */, + Sinh_u2 = 0x1000dd4 /* U+0DD4 SINHALA PAA-PILLA */, + Sinh_uu2 = 0x1000dd6 /* U+0DD6 SINHALA DIGA PAA-PILLA */, + Sinh_ru2 = 0x1000dd8 /* U+0DD8 SINHALA GAETTA-PILLA */, + Sinh_e2 = 0x1000dd9 /* U+0DD9 SINHALA KOMBUVA */, + Sinh_ee2 = 0x1000dda /* U+0DDA SINHALA DIGA KOMBUVA */, + Sinh_ai2 = 0x1000ddb /* U+0DDB SINHALA KOMBU DEKA */, + Sinh_o2 = 0x1000ddc /* U+0DDC SINHALA KOMBUVA HAA AELA-PILLA*/, + Sinh_oo2 = 0x1000ddd /* U+0DDD SINHALA KOMBUVA HAA DIGA AELA-PILLA*/, + Sinh_au2 = 0x1000dde /* U+0DDE SINHALA KOMBUVA HAA GAYANUKITTA */, + Sinh_lu2 = 0x1000ddf /* U+0DDF SINHALA GAYANUKITTA */, + Sinh_ruu2 = 0x1000df2 /* U+0DF2 SINHALA DIGA GAETTA-PILLA */, + Sinh_luu2 = 0x1000df3 /* U+0DF3 SINHALA DIGA GAYANUKITTA */, + Sinh_kunddaliya = 0x1000df4 /* U+0DF4 SINHALA KUNDDALIYA */ + } +} diff --git a/src/Avalonia.X11/X11Info.cs b/src/Avalonia.X11/X11Info.cs index 527cdc31ef..fece7c2ba2 100644 --- a/src/Avalonia.X11/X11Info.cs +++ b/src/Avalonia.X11/X11Info.cs @@ -16,6 +16,7 @@ namespace Avalonia.X11 public IntPtr DefaultRootWindow { get; } public IntPtr DefaultCursor { get; } public X11Atoms Atoms { get; } + public IntPtr Xim { get; } public IntPtr LastActivityTimestamp { get; set; } @@ -29,6 +30,9 @@ namespace Avalonia.X11 DefaultCursor = XCreateFontCursor(display, CursorFontShape.XC_arrow); DefaultRootWindow = XDefaultRootWindow(display); Atoms = new X11Atoms(display); + //TODO: Open an actual XIM once we get support for preedit in our textbox + XSetLocaleModifiers("@im=none"); + Xim = XOpenIM(display, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); } } } diff --git a/src/Avalonia.X11/X11KeyTransform.cs b/src/Avalonia.X11/X11KeyTransform.cs new file mode 100644 index 0000000000..26495111d1 --- /dev/null +++ b/src/Avalonia.X11/X11KeyTransform.cs @@ -0,0 +1,232 @@ +using System; +using System.Collections.Generic; +using Avalonia.Input; + +namespace Avalonia.X11 +{ + static class X11KeyTransform + { + private static readonly Dictionary KeyDic = new Dictionary + { + {X11Key.Cancel, Key.Cancel}, + {X11Key.BackSpace, Key.Back}, + {X11Key.Tab, Key.Tab}, + {X11Key.Linefeed, Key.LineFeed}, + {X11Key.Clear, Key.Clear}, + {X11Key.Return, Key.Return}, + {X11Key.KP_Enter, Key.Return}, + {X11Key.Pause, Key.Pause}, + {X11Key.Caps_Lock, Key.CapsLock}, + //{ X11Key.?, Key.HangulMode } + //{ X11Key.?, Key.JunjaMode } + //{ X11Key.?, Key.FinalMode } + //{ X11Key.?, Key.KanjiMode } + {X11Key.Escape, Key.Escape}, + //{ X11Key.?, Key.ImeConvert } + //{ X11Key.?, Key.ImeNonConvert } + //{ X11Key.?, Key.ImeAccept } + //{ X11Key.?, Key.ImeModeChange } + {X11Key.space, Key.Space}, + {X11Key.Prior, Key.Prior}, + {X11Key.KP_Prior, Key.Prior}, + {X11Key.Page_Down, Key.PageDown}, + {X11Key.KP_Page_Down, Key.PageDown}, + {X11Key.End, Key.End}, + {X11Key.KP_End, Key.End}, + {X11Key.Home, Key.Home}, + {X11Key.KP_Home, Key.Home}, + {X11Key.Left, Key.Left}, + {X11Key.KP_Left, Key.Left}, + {X11Key.Up, Key.Up}, + {X11Key.KP_Up, Key.Up}, + {X11Key.Right, Key.Right}, + {X11Key.KP_Right, Key.Right}, + {X11Key.Down, Key.Down}, + {X11Key.KP_Down, Key.Down}, + {X11Key.Select, Key.Select}, + {X11Key.Print, Key.Print}, + {X11Key.Execute, Key.Execute}, + //{ X11Key.?, Key.Snapshot } + {X11Key.Insert, Key.Insert}, + {X11Key.KP_Insert, Key.Insert}, + {X11Key.Delete, Key.Delete}, + {X11Key.KP_Delete, Key.Delete}, + {X11Key.Help, Key.Help}, + {X11Key.XK_0, Key.D0}, + {X11Key.XK_1, Key.D1}, + {X11Key.XK_2, Key.D2}, + {X11Key.XK_3, Key.D3}, + {X11Key.XK_4, Key.D4}, + {X11Key.XK_5, Key.D5}, + {X11Key.XK_6, Key.D6}, + {X11Key.XK_7, Key.D7}, + {X11Key.XK_8, Key.D8}, + {X11Key.XK_9, Key.D9}, + {X11Key.A, Key.A}, + {X11Key.B, Key.B}, + {X11Key.C, Key.C}, + {X11Key.D, Key.D}, + {X11Key.E, Key.E}, + {X11Key.F, Key.F}, + {X11Key.G, Key.G}, + {X11Key.H, Key.H}, + {X11Key.I, Key.I}, + {X11Key.J, Key.J}, + {X11Key.K, Key.K}, + {X11Key.L, Key.L}, + {X11Key.M, Key.M}, + {X11Key.N, Key.N}, + {X11Key.O, Key.O}, + {X11Key.P, Key.P}, + {X11Key.Q, Key.Q}, + {X11Key.R, Key.R}, + {X11Key.S, Key.S}, + {X11Key.T, Key.T}, + {X11Key.U, Key.U}, + {X11Key.V, Key.V}, + {X11Key.W, Key.W}, + {X11Key.X, Key.X}, + {X11Key.Y, Key.Y}, + {X11Key.Z, Key.Z}, + {X11Key.a, Key.A}, + {X11Key.b, Key.B}, + {X11Key.c, Key.C}, + {X11Key.d, Key.D}, + {X11Key.e, Key.E}, + {X11Key.f, Key.F}, + {X11Key.g, Key.G}, + {X11Key.h, Key.H}, + {X11Key.i, Key.I}, + {X11Key.j, Key.J}, + {X11Key.k, Key.K}, + {X11Key.l, Key.L}, + {X11Key.m, Key.M}, + {X11Key.n, Key.N}, + {X11Key.o, Key.O}, + {X11Key.p, Key.P}, + {X11Key.q, Key.Q}, + {X11Key.r, Key.R}, + {X11Key.s, Key.S}, + {X11Key.t, Key.T}, + {X11Key.u, Key.U}, + {X11Key.v, Key.V}, + {X11Key.w, Key.W}, + {X11Key.x, Key.X}, + {X11Key.y, Key.Y}, + {X11Key.z, Key.Z}, + //{ X11Key.?, Key.LWin } + //{ X11Key.?, Key.RWin } + {X11Key.Menu, Key.Apps}, + //{ X11Key.?, Key.Sleep } + {X11Key.KP_0, Key.NumPad0}, + {X11Key.KP_1, Key.NumPad1}, + {X11Key.KP_2, Key.NumPad2}, + {X11Key.KP_3, Key.NumPad3}, + {X11Key.KP_4, Key.NumPad4}, + {X11Key.KP_5, Key.NumPad5}, + {X11Key.KP_6, Key.NumPad6}, + {X11Key.KP_7, Key.NumPad7}, + {X11Key.KP_8, Key.NumPad8}, + {X11Key.KP_9, Key.NumPad9}, + {X11Key.multiply, Key.Multiply}, + {X11Key.KP_Multiply, Key.Multiply}, + {X11Key.KP_Add, Key.Add}, + //{ X11Key.?, Key.Separator } + {X11Key.KP_Subtract, Key.Subtract}, + {X11Key.KP_Decimal, Key.Decimal}, + {X11Key.KP_Divide, Key.Divide}, + {X11Key.F1, Key.F1}, + {X11Key.F2, Key.F2}, + {X11Key.F3, Key.F3}, + {X11Key.F4, Key.F4}, + {X11Key.F5, Key.F5}, + {X11Key.F6, Key.F6}, + {X11Key.F7, Key.F7}, + {X11Key.F8, Key.F8}, + {X11Key.F9, Key.F9}, + {X11Key.F10, Key.F10}, + {X11Key.F11, Key.F11}, + {X11Key.F12, Key.F12}, + {X11Key.L3, Key.F13}, + {X11Key.F14, Key.F14}, + {X11Key.L5, Key.F15}, + {X11Key.F16, Key.F16}, + {X11Key.F17, Key.F17}, + {X11Key.L8, Key.F18}, + {X11Key.L9, Key.F19}, + {X11Key.L10, Key.F20}, + {X11Key.R1, Key.F21}, + {X11Key.R2, Key.F22}, + {X11Key.F23, Key.F23}, + {X11Key.R4, Key.F24}, + {X11Key.Num_Lock, Key.NumLock}, + {X11Key.Scroll_Lock, Key.Scroll}, + {X11Key.Shift_L, Key.LeftShift}, + {X11Key.Shift_R, Key.RightShift}, + {X11Key.Control_L, Key.LeftCtrl}, + {X11Key.Control_R, Key.RightCtrl}, + {X11Key.Alt_L, Key.LeftAlt}, + {X11Key.Alt_R, Key.RightAlt}, + //{ X11Key.?, Key.BrowserBack } + //{ X11Key.?, Key.BrowserForward } + //{ X11Key.?, Key.BrowserRefresh } + //{ X11Key.?, Key.BrowserStop } + //{ X11Key.?, Key.BrowserSearch } + //{ X11Key.?, Key.BrowserFavorites } + //{ X11Key.?, Key.BrowserHome } + //{ X11Key.?, Key.VolumeMute } + //{ X11Key.?, Key.VolumeDown } + //{ X11Key.?, Key.VolumeUp } + //{ X11Key.?, Key.MediaNextTrack } + //{ X11Key.?, Key.MediaPreviousTrack } + //{ X11Key.?, Key.MediaStop } + //{ X11Key.?, Key.MediaPlayPause } + //{ X11Key.?, Key.LaunchMail } + //{ X11Key.?, Key.SelectMedia } + //{ X11Key.?, Key.LaunchApplication1 } + //{ X11Key.?, Key.LaunchApplication2 } + {X11Key.semicolon, Key.OemSemicolon}, + {X11Key.plus, Key.OemPlus}, + {X11Key.equal, Key.OemPlus}, + {X11Key.comma, Key.OemComma}, + {X11Key.minus, Key.OemMinus}, + {X11Key.period, Key.OemPeriod}, + {X11Key.slash, Key.Oem2}, + {X11Key.grave, Key.OemTilde}, + //{ X11Key.?, Key.AbntC1 } + //{ X11Key.?, Key.AbntC2 } + {X11Key.bracketleft, Key.OemOpenBrackets}, + {X11Key.backslash, Key.OemPipe}, + {X11Key.bracketright, Key.OemCloseBrackets}, + {X11Key.apostrophe, Key.OemQuotes}, + //{ X11Key.?, Key.Oem8 } + //{ X11Key.?, Key.Oem102 } + //{ X11Key.?, Key.ImeProcessed } + //{ X11Key.?, Key.System } + //{ X11Key.?, Key.OemAttn } + //{ X11Key.?, Key.OemFinish } + //{ X11Key.?, Key.DbeHiragana } + //{ X11Key.?, Key.OemAuto } + //{ X11Key.?, Key.DbeDbcsChar } + //{ X11Key.?, Key.OemBackTab } + //{ X11Key.?, Key.Attn } + //{ X11Key.?, Key.DbeEnterWordRegisterMode } + //{ X11Key.?, Key.DbeEnterImeConfigureMode } + //{ X11Key.?, Key.EraseEof } + //{ X11Key.?, Key.Play } + //{ X11Key.?, Key.Zoom } + //{ X11Key.?, Key.NoName } + //{ X11Key.?, Key.DbeEnterDialogConversionMode } + //{ X11Key.?, Key.OemClear } + //{ X11Key.?, Key.DeadCharProcessed } + }; + + public static Key ConvertKey(IntPtr key) + { + var ikey = key.ToInt32(); + Key result; + return KeyDic.TryGetValue((X11Key)ikey, out result) ? result : Key.None; + } +} + +} diff --git a/src/Avalonia.X11/X11Structs.cs b/src/Avalonia.X11/X11Structs.cs index a18bd4be74..6d73975b39 100644 --- a/src/Avalonia.X11/X11Structs.cs +++ b/src/Avalonia.X11/X11Structs.cs @@ -72,7 +72,7 @@ namespace Avalonia.X11 { internal int y; internal int x_root; internal int y_root; - internal int state; + internal XModifierMask state; internal int keycode; internal bool same_screen; } diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index 523d4dae29..16907cfe1a 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -13,6 +13,8 @@ using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.Threading; using static Avalonia.X11.XLib; +// ReSharper disable IdentifierTypo +// ReSharper disable StringLiteralTypo namespace Avalonia.X11 { unsafe class X11Window : IWindowImpl, IPopupImpl @@ -20,13 +22,14 @@ namespace Avalonia.X11 private readonly AvaloniaX11Platform _platform; private readonly bool _popup; private readonly X11Info _x11; - private readonly Action _eventHandler; private bool _invalidated; private XConfigureEvent? _configure; private IInputRoot _inputRoot; - private IMouseDevice _mouse; + private readonly IMouseDevice _mouse; + private readonly IKeyboardDevice _keyboard; private Point _position; private IntPtr _handle; + private IntPtr _xic; private IntPtr _renderHandle; private bool _mapped; @@ -34,7 +37,7 @@ namespace Avalonia.X11 { public RawInputEventArgs Event; } - private Queue _inputQueue = new Queue(); + private readonly Queue _inputQueue = new Queue(); private InputEventContainer _lastEvent; public X11Window(AvaloniaX11Platform platform, bool popup) @@ -43,7 +46,9 @@ namespace Avalonia.X11 _popup = popup; _x11 = platform.Info; _mouse = platform.MouseDevice; - + _keyboard = platform.KeyboardDevice; + _xic = XCreateIC(_x11.Xim, XNames.XNInputStyle, XIMProperties.XIMPreeditNothing | XIMProperties.XIMStatusNothing, + XNames.XNClientWindow, _handle, IntPtr.Zero); XSetWindowAttributes attr = new XSetWindowAttributes(); var valueMask = default(SetWindowValuemask); @@ -73,8 +78,7 @@ namespace Avalonia.X11 Handle = new PlatformHandle(_handle, "XID"); ClientSize = new Size(400, 400); - _eventHandler = OnEvent; - platform.Windows[_handle] = _eventHandler; + platform.Windows[_handle] = OnEvent; XSelectInput(_x11.Display, _handle, new IntPtr(0xffffff ^ (int)XEventMask.SubstructureRedirectMask @@ -145,12 +149,14 @@ namespace Avalonia.X11 decorations ^= MotifDecorations.Maximize | MotifDecorations.ResizeH; } - var hints = new MotifWmHints(); - hints.flags = new IntPtr((int)(MotifFlags.Decorations | MotifFlags.Functions)); + var hints = new MotifWmHints + { + flags = new IntPtr((int)(MotifFlags.Decorations | MotifFlags.Functions)), + decorations = new IntPtr((int)decorations), + functions = new IntPtr((int)functions) + }; + - hints.decorations = new IntPtr((int)decorations); - hints.functions = new IntPtr((int)functions); - XChangeProperty(_x11.Display, _handle, _x11.Atoms._MOTIF_WM_HINTS, _x11.Atoms._MOTIF_WM_HINTS, 32, PropertyMode.Replace, ref hints, 5); @@ -158,9 +164,11 @@ namespace Avalonia.X11 void UpdateSizeHits() { - var hints = new XSizeHints(); - hints.min_width = (int)_minMaxSize.minSize.Width; - hints.min_height = (int)_minMaxSize.minSize.Height; + var hints = new XSizeHints + { + min_width = (int)_minMaxSize.minSize.Width, + min_height = (int)_minMaxSize.minSize.Height + }; hints.height_inc = hints.width_inc = 1; var flags = XSizeHintsFlags.PMinSize | XSizeHintsFlags.PResizeInc; // People might be passing double.MaxValue @@ -196,8 +204,10 @@ namespace Avalonia.X11 public IRenderer CreateRenderer(IRenderRoot root) => new DeferredRenderer(root, AvaloniaLocator.Current.GetService()); - unsafe void OnEvent(XEvent ev) + void OnEvent(XEvent ev) { + if(XFilterEvent(ref ev, _handle)) + return; if (ev.type == XEventName.MapNotify) { _mapped = true; @@ -212,7 +222,7 @@ namespace Avalonia.X11 else if (ev.type == XEventName.FocusOut) Deactivated?.Invoke(); else if (ev.type == XEventName.MotionNotify) - MouseEvent(RawMouseEventType.Move, ev, ev.MotionEvent.state); + MouseEvent(RawMouseEventType.Move, ref ev, ev.MotionEvent.state); else if (ev.type == XEventName.PropertyNotify) { OnPropertyChange(ev.PropertyEvent.atom, ev.PropertyEvent.state == 0); @@ -222,7 +232,7 @@ namespace Avalonia.X11 if (ev.ButtonEvent.button < 4) MouseEvent(ev.ButtonEvent.button == 1 ? RawMouseEventType.LeftButtonDown : ev.ButtonEvent.button == 2 ? RawMouseEventType.MiddleButtonDown - : RawMouseEventType.RightButtonDown, ev, ev.ButtonEvent.state); + : RawMouseEventType.RightButtonDown, ref ev, ev.ButtonEvent.state); else { var delta = ev.ButtonEvent.button == 4 @@ -234,7 +244,7 @@ namespace Avalonia.X11 : new Vector(-1, 0); ScheduleInput(new RawMouseWheelEventArgs(_mouse, (ulong)ev.ButtonEvent.time.ToInt64(), _inputRoot, new Point(ev.ButtonEvent.x, ev.ButtonEvent.y), delta, - TranslateModifiers(ev.ButtonEvent.state))); + TranslateModifiers(ev.ButtonEvent.state)), ref ev); } } @@ -243,12 +253,13 @@ namespace Avalonia.X11 if (ev.ButtonEvent.button < 4) MouseEvent(ev.ButtonEvent.button == 1 ? RawMouseEventType.LeftButtonUp : ev.ButtonEvent.button == 2 ? RawMouseEventType.MiddleButtonUp - : RawMouseEventType.RightButtonUp, ev, ev.ButtonEvent.state); + : RawMouseEventType.RightButtonUp, ref ev, ev.ButtonEvent.state); } else if (ev.type == XEventName.ConfigureNotify) { var needEnqueue = (_configure == null); _configure = ev.ConfigureEvent; + Console.WriteLine($"{ev.ConfigureEvent.x} {ev.ConfigureEvent.y} {ev.ConfigureEvent.width} {ev.ConfigureEvent.height}"); if (needEnqueue) Dispatcher.UIThread.Post(() => { @@ -257,7 +268,9 @@ namespace Avalonia.X11 var cev = _configure.Value; _configure = null; var nsize = new Size(cev.width, cev.height); - var npos = new Point(cev.x, cev.y); + XTranslateCoordinates(_x11.Display, _handle, _x11.DefaultRootWindow, 0, 0, out var xret, + out var yret, out var _); + var npos = new Point(xret, yret); var changedSize = ClientSize != nsize; var changedPos = npos != _position; ClientSize = nsize; @@ -285,12 +298,37 @@ namespace Avalonia.X11 } } + else if (ev.type == XEventName.KeyPress || ev.type == XEventName.KeyRelease) + { + var buffer = stackalloc byte[40]; + + var latinKeysym = XKeycodeToKeysym(_x11.Display, ev.KeyEvent.keycode, 0); + ScheduleInput(new RawKeyEventArgs(_keyboard, (ulong)ev.KeyEvent.time.ToInt64(), + ev.type == XEventName.KeyPress ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp, + X11KeyTransform.ConvertKey(latinKeysym), TranslateModifiers(ev.KeyEvent.state)), ref ev); + + if (ev.type == XEventName.KeyPress) + { + var len = Xutf8LookupString(_xic, ref ev, buffer, 40, out _, out _); + if (len != 0) + { + var text = Encoding.UTF8.GetString(buffer, len); + if (text.Length == 1) + { + if (text[0] < ' ' || text[0] == 0x7f) //Control codes or DEL + return; + } + ScheduleInput(new RawTextInputEventArgs(_keyboard, (ulong)ev.KeyEvent.time.ToInt64(), text), + ref ev); + } + } + } } private WindowState _lastWindowState; public WindowState WindowState { - get { return _lastWindowState; } + get => _lastWindowState; set { _lastWindowState = value; @@ -322,7 +360,7 @@ namespace Avalonia.X11 { XGetWindowProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_STATE, IntPtr.Zero, new IntPtr(256), - false, (IntPtr)Atom.XA_ATOM, out var actualAtom, out var actualFormat, out var nitems, out var bytesAfter, + false, (IntPtr)Atom.XA_ATOM, out _, out _, out var nitems, out _, out var prop); int maximized = 0; var pitems = (IntPtr*)prop.ToPointer(); @@ -381,8 +419,9 @@ namespace Avalonia.X11 private bool _canResize = true; private (Size minSize, Size maxSize) _minMaxSize; - void ScheduleInput(RawInputEventArgs args) + void ScheduleInput(RawInputEventArgs args, ref XEvent xev) { + _x11.LastActivityTimestamp = xev.ButtonEvent.time; _lastEvent = new InputEventContainer() {Event = args}; _inputQueue.Enqueue(_lastEvent); if (_inputQueue.Count == 1) @@ -399,9 +438,8 @@ namespace Avalonia.X11 } } - void MouseEvent(RawMouseEventType type, XEvent ev, XModifierMask mods) + void MouseEvent(RawMouseEventType type, ref XEvent ev, XModifierMask mods) { - _x11.LastActivityTimestamp = ev.ButtonEvent.time; var mev = new RawMouseEventArgs( _mouse, (ulong)ev.ButtonEvent.time.ToInt64(), _inputRoot, type, new Point(ev.ButtonEvent.x, ev.ButtonEvent.y), TranslateModifiers(mods)); @@ -411,7 +449,7 @@ namespace Avalonia.X11 _lastEvent.Event = mev; return; } - ScheduleInput(mev); + ScheduleInput(mev, ref ev); } void DoPaint() @@ -448,6 +486,12 @@ namespace Avalonia.X11 void Cleanup() { + if (_xic != IntPtr.Zero) + { + XDestroyIC(_xic); + _xic = IntPtr.Zero; + } + if (_handle != IntPtr.Zero) { XDestroyWindow(_x11.Display, _handle); @@ -455,7 +499,7 @@ namespace Avalonia.X11 _handle = IntPtr.Zero; Closed?.Invoke(); } - + if (_renderHandle != IntPtr.Zero) { XDestroyWindow(_x11.Display, _renderHandle); @@ -484,9 +528,11 @@ namespace Avalonia.X11 { if (clientSize == ClientSize) return; - var changes = new XWindowChanges(); - changes.width = (int)clientSize.Width; - changes.height = (int)clientSize.Height; + var changes = new XWindowChanges + { + width = (int)clientSize.Width, + height = (int)clientSize.Height + }; var needResize = clientSize != ClientSize; XConfigureWindow(_x11.Display, _handle, ChangeWindowFlags.CWHeight | ChangeWindowFlags.CWWidth, ref changes); @@ -524,9 +570,11 @@ namespace Avalonia.X11 get => _position; set { - var changes = new XWindowChanges(); - changes.x = (int)value.X; - changes.y = (int)value.Y; + var changes = new XWindowChanges + { + x = (int)value.X, + y = (int)value.Y + }; XConfigureWindow(_x11.Display, _handle, ChangeWindowFlags.CWX | ChangeWindowFlags.CWY, ref changes); XFlush(_x11.Display); @@ -536,7 +584,7 @@ namespace Avalonia.X11 public IMouseDevice MouseDevice => _mouse; - public unsafe void Activate() + public void Activate() { if (_x11.Atoms._NET_ACTIVE_WINDOW != IntPtr.Zero) { @@ -559,18 +607,21 @@ namespace Avalonia.X11 void SendNetWMMessage(IntPtr message_type, IntPtr l0, IntPtr? l1 = null, IntPtr? l2 = null, IntPtr? l3 = null, IntPtr? l4 = null) { - XEvent xev; - - xev = new XEvent(); - xev.ClientMessageEvent.type = XEventName.ClientMessage; - xev.ClientMessageEvent.send_event = true; - xev.ClientMessageEvent.window = _handle; - xev.ClientMessageEvent.message_type = message_type; - xev.ClientMessageEvent.format = 32; - xev.ClientMessageEvent.ptr1 = l0; - xev.ClientMessageEvent.ptr2 = l1 ?? IntPtr.Zero; - xev.ClientMessageEvent.ptr3 = l2 ?? IntPtr.Zero; - xev.ClientMessageEvent.ptr4 = l3 ?? IntPtr.Zero; + var xev = new XEvent + { + ClientMessageEvent = + { + type = XEventName.ClientMessage, + send_event = true, + window = _handle, + message_type = message_type, + format = 32, + ptr1 = l0, + ptr2 = l1 ?? IntPtr.Zero, + ptr3 = l2 ?? IntPtr.Zero, + ptr4 = l3 ?? IntPtr.Zero + } + }; xev.ClientMessageEvent.ptr4 = l4 ?? IntPtr.Zero; XSendEvent(_x11.Display, _x11.RootWindow, false, new IntPtr((int)(EventMask.SubstructureRedirectMask | EventMask.SubstructureNotifyMask)), ref xev); diff --git a/src/Avalonia.X11/XLib.cs b/src/Avalonia.X11/XLib.cs index 0f27191f88..32f563b14b 100644 --- a/src/Avalonia.X11/XLib.cs +++ b/src/Avalonia.X11/XLib.cs @@ -402,6 +402,42 @@ namespace Avalonia.X11 [DllImport(libX11)] public static extern IntPtr XCreateColormap(IntPtr display, IntPtr window, IntPtr visual, int create); + public enum XLookupStatus + { + XBufferOverflow = -1, + XLookupNone = 1, + XLookupChars = 2, + XLookupKeySym = 3, + XLookupBoth = 4 + } + + [DllImport (libX11)] + public static extern unsafe int XLookupString(ref XEvent xevent, void* buffer, int num_bytes, out IntPtr keysym, out IntPtr status); + + [DllImport ("libX11")] + public static extern unsafe int Xutf8LookupString(IntPtr xic, ref XEvent xevent, void* buffer, int num_bytes, out IntPtr keysym, out IntPtr status); + + [DllImport (libX11)] + public static extern unsafe IntPtr XKeycodeToKeysym(IntPtr display, int keycode, int index); + + [DllImport (libX11)] + public static extern unsafe IntPtr XSetLocaleModifiers(string modifiers); + + [DllImport (libX11)] + public static extern IntPtr XOpenIM (IntPtr display, IntPtr rdb, IntPtr res_name, IntPtr res_class); + + [DllImport (libX11)] + public static extern IntPtr XCreateIC (IntPtr xim, string name, XIMProperties im_style, string name2, IntPtr value2, IntPtr terminator); + + [DllImport (libX11)] + public static extern IntPtr XCreateIC (IntPtr xim, string name, XIMProperties im_style, string name2, IntPtr value2, string name3, IntPtr value3, IntPtr terminator); + + [DllImport (libX11)] + public static extern void XCloseIM (IntPtr xim); + + [DllImport (libX11)] + public static extern void XDestroyIC (IntPtr xic); + public struct XGeometry { public IntPtr root; From 666f779cfff8812092b83ac59204795fce25de9e Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Thu, 8 Nov 2018 22:10:31 +0300 Subject: [PATCH 043/374] [X11] Removed debug line --- src/Avalonia.X11/X11Window.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index 16907cfe1a..f4d62b268b 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -259,7 +259,6 @@ namespace Avalonia.X11 { var needEnqueue = (_configure == null); _configure = ev.ConfigureEvent; - Console.WriteLine($"{ev.ConfigureEvent.x} {ev.ConfigureEvent.y} {ev.ConfigureEvent.width} {ev.ConfigureEvent.height}"); if (needEnqueue) Dispatcher.UIThread.Post(() => { From 00bab51c700f2c08fc9c35aae07a53d17e301ed5 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Thu, 8 Nov 2018 22:10:53 +0300 Subject: [PATCH 044/374] Call XFlush after XMapWindow --- src/Avalonia.X11/X11Window.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index f4d62b268b..71c9c980f8 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -507,7 +507,11 @@ namespace Avalonia.X11 } - public void Show() => XMapWindow(_x11.Display, _handle); + public void Show() + { + XMapWindow(_x11.Display, _handle); + XFlush(_x11.Display); + } public void Hide() => XUnmapWindow(_x11.Display, _handle); From 4993e0d3225aa3b6ff91d01ff3f5490ff4cdfdd7 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Thu, 8 Nov 2018 22:33:09 +0300 Subject: [PATCH 045/374] [X11] Reference Avalonia package --- src/Avalonia.X11/Avalonia.X11.csproj | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Avalonia.X11/Avalonia.X11.csproj b/src/Avalonia.X11/Avalonia.X11.csproj index 3f61961571..62771d847b 100644 --- a/src/Avalonia.X11/Avalonia.X11.csproj +++ b/src/Avalonia.X11/Avalonia.X11.csproj @@ -6,8 +6,7 @@ - - + From 97faa23d384819ab982970d8ccd1617d44abcc2f Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Fri, 9 Nov 2018 12:22:22 +0300 Subject: [PATCH 046/374] [X11] Fixed references and update to new API --- .../ControlCatalog.NetCore.csproj | 4 +--- src/Avalonia.Desktop/Avalonia.Desktop.csproj | 2 +- src/Avalonia.X11/Avalonia.X11.csproj | 2 +- src/Avalonia.X11/X11Framebuffer.cs | 18 ++++++++---------- src/Avalonia.X11/X11Window.cs | 4 ++-- 5 files changed, 13 insertions(+), 17 deletions(-) diff --git a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj index 0c47c6728e..589f41c06b 100644 --- a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj +++ b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj @@ -6,12 +6,10 @@ - - - + diff --git a/src/Avalonia.Desktop/Avalonia.Desktop.csproj b/src/Avalonia.Desktop/Avalonia.Desktop.csproj index 3fc20150a7..52fee8a291 100644 --- a/src/Avalonia.Desktop/Avalonia.Desktop.csproj +++ b/src/Avalonia.Desktop/Avalonia.Desktop.csproj @@ -1,6 +1,6 @@ - netstandard2.0 + netstandard2.0 diff --git a/src/Avalonia.X11/Avalonia.X11.csproj b/src/Avalonia.X11/Avalonia.X11.csproj index 62771d847b..681eb0a2be 100644 --- a/src/Avalonia.X11/Avalonia.X11.csproj +++ b/src/Avalonia.X11/Avalonia.X11.csproj @@ -6,7 +6,7 @@ - + diff --git a/src/Avalonia.X11/X11Framebuffer.cs b/src/Avalonia.X11/X11Framebuffer.cs index 2d6955f47a..8e48a4089a 100644 --- a/src/Avalonia.X11/X11Framebuffer.cs +++ b/src/Avalonia.X11/X11Framebuffer.cs @@ -13,12 +13,11 @@ namespace Avalonia.X11 { _display = display; _xid = xid; - Width = width*factor; - Height = height*factor; - RowBytes = Width * 4; + Size = new PixelSize(width * factor, height * factor); + RowBytes = width * 4; Dpi = new Vector(96, 96) * factor; Format = PixelFormat.Bgra8888; - _blob = AvaloniaLocator.Current.GetService().AllocBlob(RowBytes * Height); + _blob = AvaloniaLocator.Current.GetService().AllocBlob(RowBytes * height); Address = _blob.Address; } @@ -26,8 +25,8 @@ namespace Avalonia.X11 { var image = new XImage(); int bitsPerPixel = 32; - image.width = Width; - image.height = Height; + image.width = Size.Width; + image.height = Size.Height; image.format = 2; //ZPixmap; image.data = Address; image.byte_order = 0;// LSBFirst; @@ -35,12 +34,12 @@ namespace Avalonia.X11 image.bitmap_bit_order = 0;// LSBFirst; image.bitmap_pad = bitsPerPixel; image.depth = 24; - image.bytes_per_line = RowBytes - Width * 4; + image.bytes_per_line = RowBytes - Size.Width * 4; image.bits_per_pixel = bitsPerPixel; XLockDisplay(_display); XInitImage(ref image); var gc = XCreateGC(_display, _xid, 0, IntPtr.Zero); - XPutImage(_display, _xid, gc, ref image, 0, 0, 0, 0, (uint) Width, (uint) Height); + XPutImage(_display, _xid, gc, ref image, 0, 0, 0, 0, (uint) Size.Width, (uint) Size.Height); XFreeGC(_display, gc); XSync(_display, true); XUnlockDisplay(_display); @@ -48,8 +47,7 @@ namespace Avalonia.X11 } public IntPtr Address { get; } - public int Width { get; } - public int Height { get; } + public PixelSize Size { get; } public int RowBytes { get; } public Vector Dpi { get; } public PixelFormat Format { get; } diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index 71c9c980f8..4b7b7fc8e1 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -116,7 +116,7 @@ namespace Avalonia.X11 } public IntPtr Handle { get; } - public System.Drawing.Size PixelSize + public PixelSize Size { get { @@ -126,7 +126,7 @@ namespace Avalonia.X11 XFlush(_display); XSync(_display, true); XUnlockDisplay(_display); - return new System.Drawing.Size(geo.width, geo.height); + return new PixelSize(geo.width, geo.height); } } From e184f8bfc44b98c7003daa571a8ad8402efa577c Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Fri, 9 Nov 2018 12:22:32 +0300 Subject: [PATCH 047/374] Fixed sln file --- Avalonia.sln | 28 ++-------------------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/Avalonia.sln b/Avalonia.sln index 6e265e7298..7f46379038 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -178,8 +178,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Skia.UnitTests", " EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.OpenGL", "src\Avalonia.OpenGL\Avalonia.OpenGL.csproj", "{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Native", "src\Avalonia.Native\Avalonia.Native.csproj", "{12A91A62-C064-42CA-9A8C-A1272F354388}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.DesktopRuntime", "src\Avalonia.DesktopRuntime\Avalonia.DesktopRuntime.csproj", "{878FEFE0-CD14-41CB-90B0-DBCB163E8F15}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Packages", "Packages", "{E870DCD7-F46A-498D-83FC-D0FD13E0A11C}" @@ -189,7 +187,9 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.X11", "src\Avalonia.X11\Avalonia.X11.csproj", "{212D02D5-C873-469A-8E78-4A6350EC4A1A}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlatformSanityChecks", "samples\PlatformSanityChecks\PlatformSanityChecks.csproj", "{8B5768BB-71F9-4E23-89B5-DDBA6458B856}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Native", "src\Avalonia.Native\Avalonia.Native.csproj", "{12A91A62-C064-42CA-9A8C-A1272F354388}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Desktop", "src\Avalonia.Desktop\Avalonia.Desktop.csproj", "{3C471044-3640-45E3-B1B2-16D2FF8399EE}" EndProject Global @@ -1643,30 +1643,6 @@ Global {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Release|iPhone.Build.0 = Release|Any CPU {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {8B5768BB-71F9-4E23-89B5-DDBA6458B856}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.AppStore|iPhone.Build.0 = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Debug|Any CPU.Build.0 = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Debug|iPhone.Build.0 = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Release|Any CPU.ActiveCfg = Release|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Release|Any CPU.Build.0 = Release|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Release|iPhone.ActiveCfg = Release|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Release|iPhone.Build.0 = Release|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU From 4bde2c464067cf77ad86b6a55f1ad16564663dbe Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Fri, 9 Nov 2018 12:26:04 +0300 Subject: [PATCH 048/374] [X11] Fixed references --- .../PlatformSanityChecks.csproj | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/samples/PlatformSanityChecks/PlatformSanityChecks.csproj b/samples/PlatformSanityChecks/PlatformSanityChecks.csproj index 00b5b10106..2adef5f64e 100644 --- a/samples/PlatformSanityChecks/PlatformSanityChecks.csproj +++ b/samples/PlatformSanityChecks/PlatformSanityChecks.csproj @@ -6,19 +6,8 @@ - - - + - - - - - - - - - From a29d74fb9891cc99605716c8d22a807893f30103 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Fri, 9 Nov 2018 17:26:05 +0300 Subject: [PATCH 049/374] [X11] Implemented clipboard support --- src/Avalonia.X11/Stubs.cs | 21 --- src/Avalonia.X11/X11Atoms.cs | 5 + src/Avalonia.X11/X11Clipboard.cs | 235 +++++++++++++++++++++++++++++++ src/Avalonia.X11/X11Platform.cs | 2 +- src/Avalonia.X11/XLib.cs | 13 ++ 5 files changed, 254 insertions(+), 22 deletions(-) create mode 100644 src/Avalonia.X11/X11Clipboard.cs diff --git a/src/Avalonia.X11/Stubs.cs b/src/Avalonia.X11/Stubs.cs index 6dda7b81ca..f70c7fee25 100644 --- a/src/Avalonia.X11/Stubs.cs +++ b/src/Avalonia.X11/Stubs.cs @@ -9,27 +9,6 @@ using Avalonia.Platform; namespace Avalonia.X11 { - class ClipboardStub : IClipboard - { - private string _text; - public Task GetTextAsync() - { - return Task.FromResult(_text); - } - - public Task SetTextAsync(string text) - { - _text = text; - return Task.CompletedTask; - } - - public Task ClearAsync() - { - _text = null; - return Task.CompletedTask; - } - } - class PlatformSettingsStub : IPlatformSettings { public Size DoubleClickSize { get; } = new Size(2, 2); diff --git a/src/Avalonia.X11/X11Atoms.cs b/src/Avalonia.X11/X11Atoms.cs index 51a4fc52f0..5c418c032b 100644 --- a/src/Avalonia.X11/X11Atoms.cs +++ b/src/Avalonia.X11/X11Atoms.cs @@ -172,11 +172,16 @@ namespace Avalonia.X11 { public readonly IntPtr _NET_WM_WINDOW_TYPE_DIALOG; public readonly IntPtr _NET_WM_WINDOW_TYPE_NORMAL; public readonly IntPtr CLIPBOARD; + public readonly IntPtr CLIPBOARD_MANAGER; + public readonly IntPtr SAVE_TARGETS; + public readonly IntPtr MULTIPLE; public readonly IntPtr PRIMARY; public readonly IntPtr OEMTEXT; public readonly IntPtr UNICODETEXT; public readonly IntPtr TARGETS; public readonly IntPtr UTF8_STRING; + public readonly IntPtr UTF16_STRING; + public readonly IntPtr ATOM_PAIR; public X11Atoms (IntPtr display) { diff --git a/src/Avalonia.X11/X11Clipboard.cs b/src/Avalonia.X11/X11Clipboard.cs new file mode 100644 index 0000000000..74d26d1d08 --- /dev/null +++ b/src/Avalonia.X11/X11Clipboard.cs @@ -0,0 +1,235 @@ +using System; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using Avalonia.Input.Platform; +using static Avalonia.X11.XLib; +namespace Avalonia.X11 +{ + class X11Clipboard : IClipboard + { + private readonly X11Info _x11; + private string _storedString; + private IntPtr _handle; + private TaskCompletionSource _requestedFormatsTcs; + private TaskCompletionSource _requestedTextTcs; + private readonly IntPtr[] _textAtoms; + private readonly IntPtr _avaloniaSaveTargetsAtom; + + public X11Clipboard(AvaloniaX11Platform platform) + { + _x11 = platform.Info; + _handle = XCreateSimpleWindow(_x11.Display, _x11.DefaultRootWindow, 0, 0, 1, 1, 0, IntPtr.Zero, IntPtr.Zero); + _avaloniaSaveTargetsAtom = XInternAtom(_x11.Display, "AVALONIA_SAVE_TARGETS_PROPERTY_ATOM", false); + platform.Windows[_handle] = OnEvent; + _textAtoms = new[] + { + _x11.Atoms.XA_STRING, + _x11.Atoms.OEMTEXT, + _x11.Atoms.UTF8_STRING, + _x11.Atoms.UTF16_STRING + }.Where(a => a != IntPtr.Zero).ToArray(); + } + + Encoding GetStringEncoding(IntPtr atom) + { + return (atom == _x11.Atoms.XA_STRING + || atom == _x11.Atoms.OEMTEXT) + ? Encoding.ASCII + : atom == _x11.Atoms.UTF8_STRING + ? Encoding.UTF8 + : atom == _x11.Atoms.UTF16_STRING + ? Encoding.Unicode + : null; + } + + private unsafe void OnEvent(XEvent ev) + { + if (ev.type == XEventName.SelectionRequest) + { + var sel = ev.SelectionRequestEvent; + var resp = new XEvent + { + SelectionEvent = + { + type = XEventName.SelectionNotify, + send_event = true, + display = _x11.Display, + selection = sel.selection, + target = sel.target, + requestor = sel.requestor, + time = sel.time, + property = IntPtr.Zero + } + }; + if (sel.selection == _x11.Atoms.CLIPBOARD) + { + resp.SelectionEvent.property = WriteTargetToProperty(sel.target, sel.requestor, sel.property); + } + + XSendEvent(_x11.Display, sel.requestor, false, new IntPtr((int)EventMask.NoEventMask), ref resp); + } + + IntPtr WriteTargetToProperty(IntPtr target, IntPtr window, IntPtr property) + { + Encoding textEnc; + if (target == _x11.Atoms.TARGETS) + { + var atoms = _textAtoms; + atoms = atoms.Concat(new[] {_x11.Atoms.TARGETS, _x11.Atoms.MULTIPLE}) + .ToArray(); + XChangeProperty(_x11.Display, window, property, + target, 32, PropertyMode.Replace, atoms, atoms.Length); + return property; + } + else if(target == _x11.Atoms.SAVE_TARGETS && _x11.Atoms.SAVE_TARGETS != IntPtr.Zero) + { + return property; + } + else if ((textEnc = GetStringEncoding(target)) != null) + { + + var data = textEnc.GetBytes(_storedString ?? ""); + fixed (void* pdata = data) + XChangeProperty(_x11.Display, window, property, target, 8, + PropertyMode.Replace, + pdata, data.Length); + return property; + } + else if (target == _x11.Atoms.MULTIPLE && _x11.Atoms.MULTIPLE != IntPtr.Zero) + { + XGetWindowProperty(_x11.Display, window, property, IntPtr.Zero, new IntPtr(0x7fffffff), false, + _x11.Atoms.ATOM_PAIR, out _, out var actualFormat, out var nitems, out _, out var prop); + if (nitems == IntPtr.Zero) + return IntPtr.Zero; + if (actualFormat == 32) + { + var data = (IntPtr*)prop.ToPointer(); + for (var c = 0; c < nitems.ToInt32(); c += 2) + { + var subTarget = data[c]; + var subProp = data[c + 1]; + var converted = WriteTargetToProperty(subTarget, window, subProp); + data[c + 1] = converted; + } + + XChangeProperty(_x11.Display, window, property, _x11.Atoms.ATOM_PAIR, 32, PropertyMode.Replace, + prop.ToPointer(), nitems.ToInt32()); + } + + XFree(prop); + + return property; + } + else + return IntPtr.Zero; + } + + if (ev.type == XEventName.SelectionNotify && ev.SelectionEvent.selection == _x11.Atoms.CLIPBOARD) + { + var sel = ev.SelectionEvent; + if (sel.property == IntPtr.Zero) + { + _requestedFormatsTcs?.TrySetResult(null); + _requestedTextTcs?.TrySetResult(null); + } + XGetWindowProperty(_x11.Display, _handle, sel.property, IntPtr.Zero, new IntPtr (0x7fffffff), true, (IntPtr)Atom.AnyPropertyType, + out var actualAtom, out var actualFormat, out var nitems, out var bytes_after, out var prop); + Encoding textEnc = null; + if (nitems == IntPtr.Zero) + { + _requestedFormatsTcs?.TrySetResult(null); + _requestedTextTcs?.TrySetResult(null); + } + else + { + if (sel.property == _x11.Atoms.TARGETS) + { + if (actualFormat != 32) + _requestedFormatsTcs?.TrySetResult(null); + else + { + var formats = new IntPtr[nitems.ToInt32()]; + Marshal.Copy(prop, formats, 0, formats.Length); + _requestedFormatsTcs?.TrySetResult(formats); + } + } + else if ((textEnc = GetStringEncoding(sel.property)) != null) + { + var text = textEnc.GetString((byte*)prop.ToPointer(), nitems.ToInt32()); + _requestedTextTcs?.TrySetResult(text); + } + } + + XFree(prop); + } + } + + Task SendFormatRequest() + { + if (_requestedFormatsTcs == null || _requestedFormatsTcs.Task.IsCompleted) + _requestedFormatsTcs = new TaskCompletionSource(); + XConvertSelection(_x11.Display, _x11.Atoms.CLIPBOARD, _x11.Atoms.TARGETS, _x11.Atoms.TARGETS, _handle, + IntPtr.Zero); + return _requestedFormatsTcs.Task; + } + + Task SendTextRequest(IntPtr format) + { + if (_requestedTextTcs == null || _requestedFormatsTcs.Task.IsCompleted) + _requestedTextTcs = new TaskCompletionSource(); + XConvertSelection(_x11.Display, _x11.Atoms.CLIPBOARD, format, format, _handle, IntPtr.Zero); + return _requestedTextTcs.Task; + } + + public async Task GetTextAsync() + { + if (XGetSelectionOwner(_x11.Display, _x11.Atoms.CLIPBOARD) == IntPtr.Zero) + return null; + var res = await SendFormatRequest(); + var target = _x11.Atoms.UTF8_STRING; + if (res != null) + { + var preferredFormats = new[] {_x11.Atoms.UTF16_STRING, _x11.Atoms.UTF8_STRING, _x11.Atoms.XA_STRING}; + foreach (var pf in preferredFormats) + if (res.Contains(pf)) + { + target = pf; + break; + } + } + + return await SendTextRequest(target); + } + + void StoreAtomsInClipboardManager(IntPtr[] atoms) + { + if (_x11.Atoms.CLIPBOARD_MANAGER != IntPtr.Zero && _x11.Atoms.SAVE_TARGETS != IntPtr.Zero) + { + var clipboardManager = XGetSelectionOwner(_x11.Display, _x11.Atoms.CLIPBOARD_MANAGER); + if (clipboardManager != IntPtr.Zero) + { + XChangeProperty(_x11.Display, _handle, _avaloniaSaveTargetsAtom, _x11.Atoms.XA_ATOM, 32, + PropertyMode.Replace, + atoms, atoms.Length); + XConvertSelection(_x11.Display, _x11.Atoms.CLIPBOARD_MANAGER, _x11.Atoms.SAVE_TARGETS, + _avaloniaSaveTargetsAtom, _handle, IntPtr.Zero); + } + } + } + + public Task SetTextAsync(string text) + { + _storedString = text; + XSetSelectionOwner(_x11.Display, _x11.Atoms.CLIPBOARD, _handle, IntPtr.Zero); + StoreAtomsInClipboardManager(_textAtoms); + return Task.CompletedTask; + } + + public Task ClearAsync() + { + return SetTextAsync(null); + } + } +} diff --git a/src/Avalonia.X11/X11Platform.cs b/src/Avalonia.X11/X11Platform.cs index b3ffc2a30b..cce5299843 100644 --- a/src/Avalonia.X11/X11Platform.cs +++ b/src/Avalonia.X11/X11Platform.cs @@ -37,7 +37,7 @@ namespace Avalonia.X11 .Bind().ToConstant(new PlatformHotkeyConfiguration(InputModifiers.Control)) .Bind().ToFunc(() => KeyboardDevice) .Bind().ToConstant(new X11CursorFactory(Display)) - .Bind().ToSingleton() + .Bind().ToConstant(new X11Clipboard(this)) .Bind().ToConstant(new PlatformSettingsStub()) .Bind().ToConstant(new SystemDialogsStub()) .Bind().ToConstant(new IconLoaderStub()); diff --git a/src/Avalonia.X11/XLib.cs b/src/Avalonia.X11/XLib.cs index 32f563b14b..7f9233c252 100644 --- a/src/Avalonia.X11/XLib.cs +++ b/src/Avalonia.X11/XLib.cs @@ -117,6 +117,19 @@ namespace Avalonia.X11 [DllImport(libX11)] public static extern int XInternAtoms(IntPtr display, string[] atom_names, int atom_count, bool only_if_exists, IntPtr[] atoms); + + [DllImport(libX11)] + public static extern IntPtr XGetAtomName(IntPtr display, IntPtr atom); + + public static string GetAtomName(IntPtr display, IntPtr atom) + { + var ptr = XGetAtomName(display, atom); + if (ptr == IntPtr.Zero) + return null; + var s = Marshal.PtrToStringAnsi(ptr); + XFree(ptr); + return s; + } [DllImport(libX11)] public static extern int XSetWMProtocols(IntPtr display, IntPtr window, IntPtr[] protocols, int count); From 5cd7c1f6f4a4cc663df6fa9abe9bbd6d1140922d Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Fri, 9 Nov 2018 21:44:45 +0300 Subject: [PATCH 050/374] Reworked dialogs for GTK/Win32 --- samples/ControlCatalog/Pages/DialogsPage.xaml | 4 -- .../ControlCatalog/Pages/DialogsPage.xaml.cs | 8 ++-- samples/ControlCatalog/Pages/MenuPage.xaml.cs | 2 +- src/Avalonia.Controls/Platform/IWindowImpl.cs | 5 +-- src/Avalonia.Controls/SystemDialog.cs | 31 ++++++++++----- src/Avalonia.Controls/Window.cs | 38 +++++++++---------- .../Remote/PreviewerWindowImpl.cs | 5 +-- src/Avalonia.DesignerSupport/Remote/Stubs.cs | 6 ++- src/Avalonia.Native/WindowImpl.cs | 4 +- src/Gtk/Avalonia.Gtk3/Interop/Native.cs | 4 ++ src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs | 2 +- src/Gtk/Avalonia.Gtk3/WindowImpl.cs | 13 +++++-- .../Interop/UnmanagedMethods.cs | 18 +++++++-- src/Windows/Avalonia.Win32/WindowImpl.cs | 25 ++++++++---- .../WindowTests.cs | 7 +++- 15 files changed, 108 insertions(+), 64 deletions(-) diff --git a/samples/ControlCatalog/Pages/DialogsPage.xaml b/samples/ControlCatalog/Pages/DialogsPage.xaml index 710d791f3a..23b14d009d 100644 --- a/samples/ControlCatalog/Pages/DialogsPage.xaml +++ b/samples/ControlCatalog/Pages/DialogsPage.xaml @@ -3,10 +3,6 @@ - - - Modal to window - diff --git a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs index 8b3e810f0a..f215bf9e64 100644 --- a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs +++ b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs @@ -31,12 +31,12 @@ namespace ControlCatalog.Pages }.ShowAsync(GetWindow()); }; this.FindControl