Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/xwt.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--TestApps/Gtk3Test/Gtk3Test.csproj4
-rw-r--r--TestApps/GtkOnMacTest/GtkOnMacTest.csproj2
-rw-r--r--TestApps/GtkOnWindowsTest/GtkOnWindowsTest.csproj2
-rw-r--r--TestApps/GtkTest/GtkTest.csproj4
-rw-r--r--TestApps/MixedGtkMacTest/MixedGtkMacTest.csproj2
-rw-r--r--TestApps/Samples/MainWindow.cs2
-rw-r--r--TestApps/Samples/Samples.csproj28
-rw-r--r--TestApps/Samples/Samples/ListView1.cs10
-rw-r--r--TestApps/Samples/Samples/TreeViewCustomStore.cs292
-rw-r--r--TestApps/Samples/Samples/TreeViewEvents.cs286
-rw-r--r--TestApps/Samples/Samples/TreeViews.cs19
-rw-r--r--TestApps/Samples/icons/add-simple-16.pngbin0 -> 126 bytes
-rw-r--r--TestApps/Samples/icons/add-simple-16@2x.pngbin0 -> 130 bytes
-rw-r--r--TestApps/Samples/icons/add-simple-disabled-16.pngbin0 -> 126 bytes
-rw-r--r--TestApps/Samples/icons/add-simple-disabled-16@2x.pngbin0 -> 130 bytes
-rw-r--r--TestApps/Samples/icons/remove-simple-16.pngbin0 -> 183 bytes
-rw-r--r--TestApps/Samples/icons/remove-simple-16@2x.pngbin0 -> 214 bytes
-rw-r--r--TestApps/Samples/icons/remove-simple-disabled-16.pngbin0 -> 183 bytes
-rw-r--r--TestApps/Samples/icons/remove-simple-disabled-16@2x.pngbin0 -> 214 bytes
-rw-r--r--TestApps/WpfTest/WpfTest.csproj4
-rw-r--r--TestApps/XamMacTest/Info.plist2
-rw-r--r--TestApps/XamMacTest/Main.cs3
-rw-r--r--TestApps/XamMacTest/XamMacTest.csproj23
-rw-r--r--Testing/CoreTests/CoreTests.csproj2
-rw-r--r--Testing/GtkTestRunner.csproj3
-rw-r--r--Testing/GtkTests/PangoTests.cs87
-rw-r--r--Testing/MacTestRunner.csproj7
-rw-r--r--Testing/WpfTestRunner.csproj2
-rw-r--r--Xwt.Gtk.Mac/Xwt.Gtk.Mac.csproj2
-rw-r--r--Xwt.Gtk.Windows/Xwt.Gtk.Windows.csproj2
-rw-r--r--Xwt.Gtk/Xwt.Gtk.csproj2
-rw-r--r--Xwt.Gtk/Xwt.Gtk3.csproj2
-rw-r--r--Xwt.Gtk/Xwt.GtkBackend.CellViews/CellUtil.cs1
-rw-r--r--Xwt.Gtk/Xwt.GtkBackend.CellViews/CellViewBackend.cs18
-rw-r--r--Xwt.Gtk/Xwt.GtkBackend.CellViews/CustomCellRenderer.cs48
-rw-r--r--Xwt.Gtk/Xwt.GtkBackend/AccessibleBackend.cs1
-rw-r--r--Xwt.Gtk/Xwt.GtkBackend/BoxBackendGtk2.cs8
-rwxr-xr-xXwt.Gtk/Xwt.GtkBackend/ButtonBackend.cs11
-rw-r--r--Xwt.Gtk/Xwt.GtkBackend/ComboBoxBackend.cs4
-rw-r--r--Xwt.Gtk/Xwt.GtkBackend/CustomTreeModel.cs219
-rw-r--r--Xwt.Gtk/Xwt.GtkBackend/GtkInterop.cs37
-rw-r--r--Xwt.Gtk/Xwt.GtkBackend/GtkWorkarounds.cs4
-rw-r--r--Xwt.Gtk/Xwt.GtkBackend/MenuItemBackend.cs9
-rwxr-xr-xXwt.Gtk/Xwt.GtkBackend/RichTextViewBackend.cs5
-rw-r--r--Xwt.Gtk/Xwt.GtkBackend/TableViewBackend.cs82
-rw-r--r--Xwt.Gtk/Xwt.GtkBackend/TextLayoutBackendHandler.cs2
-rw-r--r--Xwt.Gtk/Xwt.GtkBackend/TreeStoreBackend.cs5
-rw-r--r--Xwt.Gtk/Xwt.GtkBackend/TreeViewBackend.cs97
-rw-r--r--Xwt.Gtk/Xwt.GtkBackend/WidgetBackend.cs12
-rw-r--r--Xwt.WPF/Xwt.WPF.csproj2
-rw-r--r--Xwt.WPF/Xwt.WPFBackend.CellViews/CanvasCellViewBackend.cs5
-rw-r--r--Xwt.WPF/Xwt.WPFBackend/ButtonBackend.cs12
-rw-r--r--Xwt.WPF/Xwt.WPFBackend/ExTreeView.cs14
-rw-r--r--Xwt.WPF/Xwt.WPFBackend/ExTreeViewItem.cs62
-rw-r--r--Xwt.WPF/Xwt.WPFBackend/ListDataSource.cs1
-rw-r--r--Xwt.WPF/Xwt.WPFBackend/MenuItemBackend.cs16
-rw-r--r--Xwt.WPF/Xwt.WPFBackend/TextEntryBackend.cs4
-rwxr-xr-xXwt.WPF/Xwt.WPFBackend/TreeStoreBackend.cs2
-rw-r--r--Xwt.WPF/Xwt.WPFBackend/TreeView.xaml66
-rw-r--r--Xwt.WPF/Xwt.WPFBackend/TreeViewBackend.cs22
-rw-r--r--Xwt.WPF/Xwt.WPFBackend/Util.cs15
-rw-r--r--Xwt.WPF/Xwt.WPFBackend/WidgetBackend.cs27
-rw-r--r--Xwt.XamMac/Xwt.Mac.CellViews/CanvasTableCell.cs150
-rw-r--r--Xwt.XamMac/Xwt.Mac.CellViews/CellUtil.cs133
-rw-r--r--Xwt.XamMac/Xwt.Mac.CellViews/CellViewBackend.cs75
-rw-r--r--Xwt.XamMac/Xwt.Mac.CellViews/CheckBoxTableCell.cs163
-rw-r--r--Xwt.XamMac/Xwt.Mac.CellViews/CompositeCell.cs354
-rw-r--r--Xwt.XamMac/Xwt.Mac.CellViews/ICellRenderer.cs7
-rw-r--r--Xwt.XamMac/Xwt.Mac.CellViews/ICellSource.cs6
-rw-r--r--Xwt.XamMac/Xwt.Mac.CellViews/ImageTableCell.cs137
-rw-r--r--Xwt.XamMac/Xwt.Mac.CellViews/RadioButtonTableCell.cs145
-rw-r--r--Xwt.XamMac/Xwt.Mac.CellViews/TextTableCell.cs133
-rw-r--r--Xwt.XamMac/Xwt.Mac/ButtonBackend.cs17
-rw-r--r--Xwt.XamMac/Xwt.Mac/ContextBackendHandler.cs6
-rw-r--r--Xwt.XamMac/Xwt.Mac/DialogBackend.cs17
-rw-r--r--Xwt.XamMac/Xwt.Mac/IViewObject.cs142
-rw-r--r--Xwt.XamMac/Xwt.Mac/LabelBackend.cs24
-rw-r--r--Xwt.XamMac/Xwt.Mac/ListBoxBackend.cs34
-rw-r--r--Xwt.XamMac/Xwt.Mac/ListViewBackend.cs115
-rw-r--r--Xwt.XamMac/Xwt.Mac/MenuItemBackend.cs12
-rw-r--r--Xwt.XamMac/Xwt.Mac/Messaging.cs18
-rw-r--r--Xwt.XamMac/Xwt.Mac/NSTableViewBackend.cs198
-rw-r--r--Xwt.XamMac/Xwt.Mac/OutlineViewBackend.cs215
-rw-r--r--Xwt.XamMac/Xwt.Mac/TableViewBackend.cs290
-rw-r--r--Xwt.XamMac/Xwt.Mac/TextEntryBackend.cs23
-rw-r--r--Xwt.XamMac/Xwt.Mac/TextLayoutBackendHandler.cs484
-rw-r--r--Xwt.XamMac/Xwt.Mac/TreeViewBackend.cs168
-rw-r--r--Xwt.XamMac/Xwt.Mac/Util.cs12
-rw-r--r--Xwt.XamMac/Xwt.Mac/ViewBackend.cs8
-rw-r--r--Xwt.XamMac/Xwt.Mac/WidgetView.cs129
-rw-r--r--Xwt.XamMac/Xwt.Mac/WindowBackend.cs40
-rw-r--r--Xwt.XamMac/Xwt.XamMac.csproj2
-rwxr-xr-xXwt/Xwt.Backends/IButtonBackend.cs1
-rw-r--r--Xwt/Xwt.Backends/ICanvasCellViewBackend.cs1
-rw-r--r--Xwt/Xwt.Backends/ICanvasCellViewFrontend.cs2
-rw-r--r--Xwt/Xwt.Backends/IMenuItemBackend.cs5
-rw-r--r--Xwt/Xwt.Backends/ITreeViewBackend.cs5
-rw-r--r--Xwt/Xwt.csproj2
-rwxr-xr-xXwt/Xwt/Button.cs5
-rw-r--r--Xwt/Xwt/CanvasCellView.cs24
-rw-r--r--Xwt/Xwt/CursorType.cs9
-rw-r--r--Xwt/Xwt/DataField.cs5
-rw-r--r--Xwt/Xwt/ITreeDataSource.cs28
-rw-r--r--Xwt/Xwt/ListStore.cs2
-rw-r--r--Xwt/Xwt/MenuItem.cs16
-rwxr-xr-xXwt/Xwt/Rectangle.cs28
-rw-r--r--Xwt/Xwt/SelectionMode.cs3
-rw-r--r--Xwt/Xwt/TreeStore.cs23
-rw-r--r--Xwt/Xwt/TreeView.cs30
110 files changed, 3803 insertions, 1248 deletions
diff --git a/.gitignore b/.gitignore
index 73531b66..87db769c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,3 +13,5 @@ obj
test-results
.nuget/
packages/
+.vs/
+
diff --git a/TestApps/Gtk3Test/Gtk3Test.csproj b/TestApps/Gtk3Test/Gtk3Test.csproj
index 243c6390..3267e869 100644
--- a/TestApps/Gtk3Test/Gtk3Test.csproj
+++ b/TestApps/Gtk3Test/Gtk3Test.csproj
@@ -17,6 +17,7 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>False</ConsolePause>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@@ -26,6 +27,7 @@
<WarningLevel>4</WarningLevel>
<ConsolePause>False</ConsolePause>
<DebugSymbols>true</DebugSymbols>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>True</DebugSymbols>
@@ -38,6 +40,7 @@
<CodeAnalysisIgnoreBuiltInRules>false</CodeAnalysisIgnoreBuiltInRules>
<WarningLevel>4</WarningLevel>
<Optimize>False</Optimize>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
@@ -51,6 +54,7 @@
<Optimize>true</Optimize>
<DebugSymbols>true</DebugSymbols>
<ConsolePause>false</ConsolePause>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
diff --git a/TestApps/GtkOnMacTest/GtkOnMacTest.csproj b/TestApps/GtkOnMacTest/GtkOnMacTest.csproj
index c700e379..7972aa33 100644
--- a/TestApps/GtkOnMacTest/GtkOnMacTest.csproj
+++ b/TestApps/GtkOnMacTest/GtkOnMacTest.csproj
@@ -18,6 +18,7 @@
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
<PlatformTarget>x64</PlatformTarget>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@@ -27,6 +28,7 @@
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
<DebugSymbols>true</DebugSymbols>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
diff --git a/TestApps/GtkOnWindowsTest/GtkOnWindowsTest.csproj b/TestApps/GtkOnWindowsTest/GtkOnWindowsTest.csproj
index 359c80d8..b4655bcd 100644
--- a/TestApps/GtkOnWindowsTest/GtkOnWindowsTest.csproj
+++ b/TestApps/GtkOnWindowsTest/GtkOnWindowsTest.csproj
@@ -18,6 +18,7 @@
<WarningLevel>4</WarningLevel>
<PlatformTarget>x86</PlatformTarget>
<ConsolePause>false</ConsolePause>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<DebugType>pdbonly</DebugType>
@@ -28,6 +29,7 @@
<PlatformTarget>x86</PlatformTarget>
<ConsolePause>false</ConsolePause>
<DebugSymbols>true</DebugSymbols>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
diff --git a/TestApps/GtkTest/GtkTest.csproj b/TestApps/GtkTest/GtkTest.csproj
index 67964760..0feba48a 100644
--- a/TestApps/GtkTest/GtkTest.csproj
+++ b/TestApps/GtkTest/GtkTest.csproj
@@ -17,6 +17,7 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>False</ConsolePause>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@@ -26,6 +27,7 @@
<WarningLevel>4</WarningLevel>
<ConsolePause>False</ConsolePause>
<DebugSymbols>true</DebugSymbols>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>True</DebugSymbols>
@@ -38,6 +40,7 @@
<CodeAnalysisIgnoreBuiltInRules>false</CodeAnalysisIgnoreBuiltInRules>
<WarningLevel>4</WarningLevel>
<Optimize>False</Optimize>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
@@ -51,6 +54,7 @@
<Optimize>true</Optimize>
<DebugSymbols>true</DebugSymbols>
<ConsolePause>false</ConsolePause>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
diff --git a/TestApps/MixedGtkMacTest/MixedGtkMacTest.csproj b/TestApps/MixedGtkMacTest/MixedGtkMacTest.csproj
index 2e699db6..a9226b6e 100644
--- a/TestApps/MixedGtkMacTest/MixedGtkMacTest.csproj
+++ b/TestApps/MixedGtkMacTest/MixedGtkMacTest.csproj
@@ -17,6 +17,7 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>False</ConsolePause>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@@ -26,6 +27,7 @@
<WarningLevel>4</WarningLevel>
<DebugSymbols>true</DebugSymbols>
<ConsolePause>false</ConsolePause>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
diff --git a/TestApps/Samples/MainWindow.cs b/TestApps/Samples/MainWindow.cs
index 7523dc47..2c9e873c 100644
--- a/TestApps/Samples/MainWindow.cs
+++ b/TestApps/Samples/MainWindow.cs
@@ -109,6 +109,8 @@ namespace Samples
AddSample (w, "Password Entry", typeof (PasswordEntries));
var treeview = AddSample (w, "TreeView", typeof(TreeViews));
AddSample (treeview, "Cell Bounds", typeof(TreeViewCellBounds));
+ AddSample (treeview, "Custom Data Source", typeof (TreeViewCustomStore));
+ AddSample (treeview, "Tree View Events", typeof (TreeViewEvents));
AddSample (w, "WebView", typeof(WebViewSample));
var n = AddSample (null, "Drawing", null);
diff --git a/TestApps/Samples/Samples.csproj b/TestApps/Samples/Samples.csproj
index 3bbe0b5a..01e07561 100644
--- a/TestApps/Samples/Samples.csproj
+++ b/TestApps/Samples/Samples.csproj
@@ -17,6 +17,7 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>False</ConsolePause>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@@ -26,6 +27,7 @@
<WarningLevel>4</WarningLevel>
<ConsolePause>False</ConsolePause>
<DebugSymbols>true</DebugSymbols>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
@@ -118,6 +120,8 @@
<Compile Include="Samples\FolderSelectorSample.cs" />
<Compile Include="Samples\ListViewCombos.cs" />
<Compile Include="Samples\PopupWindows.cs" />
+ <Compile Include="Samples\TreeViewCustomStore.cs" />
+ <Compile Include="Samples\TreeViewEvents.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
@@ -197,6 +201,30 @@
<Link>icons\zoom-in-16~sel%402x.png</Link>
<LogicalName>zoom-in-16~sel@2x.png</LogicalName>
</EmbeddedResource>
+ <EmbeddedResource Include="icons\add-simple-16.png">
+ <LogicalName>add-simple-16.png</LogicalName>
+ </EmbeddedResource>
+ <EmbeddedResource Include="icons\add-simple-16%402x.png">
+ <LogicalName>add-simple-16@2x.png</LogicalName>
+ </EmbeddedResource>
+ <EmbeddedResource Include="icons\remove-simple-16.png">
+ <LogicalName>remove-simple-16.png</LogicalName>
+ </EmbeddedResource>
+ <EmbeddedResource Include="icons\remove-simple-16%402x.png">
+ <LogicalName>remove-simple-16@2x.png</LogicalName>
+ </EmbeddedResource>
+ <EmbeddedResource Include="icons\remove-simple-disabled-16.png">
+ <LogicalName>remove-simple-disabled-16.png</LogicalName>
+ </EmbeddedResource>
+ <EmbeddedResource Include="icons\remove-simple-disabled-16%402x.png">
+ <LogicalName>remove-simple-disabled-16@2x.png</LogicalName>
+ </EmbeddedResource>
+ <EmbeddedResource Include="icons\add-simple-disabled-16.png">
+ <LogicalName>add-simple-disabled-16.png</LogicalName>
+ </EmbeddedResource>
+ <EmbeddedResource Include="icons\add-simple-disabled-16%402x.png">
+ <LogicalName>add-simple-disabled-16@2x.png</LogicalName>
+ </EmbeddedResource>
</ItemGroup>
<ProjectExtensions>
<MonoDevelop>
diff --git a/TestApps/Samples/Samples/ListView1.cs b/TestApps/Samples/Samples/ListView1.cs
index 646cfdf8..808e554d 100644
--- a/TestApps/Samples/Samples/ListView1.cs
+++ b/TestApps/Samples/Samples/ListView1.cs
@@ -172,10 +172,12 @@ namespace Samples
protected override void OnMouseMoved (MouseMovedEventArgs args)
{
- var data = GetValue (ValueField);
- data.Value = (int) (100 * ((args.X - Bounds.X) / Bounds.Width));
- data.YPos = args.Y - Bounds.Y;
- QueueDraw ();
+ if (Bounds.Contains (args.Position)) {
+ var data = GetValue (ValueField);
+ data.Value = Math.Min (100, (int)(100 * ((args.X - Bounds.X) / Bounds.Width)));
+ data.YPos = args.Y - Bounds.Y;
+ QueueDraw ();
+ }
base.OnMouseMoved (args);
}
diff --git a/TestApps/Samples/Samples/TreeViewCustomStore.cs b/TestApps/Samples/Samples/TreeViewCustomStore.cs
new file mode 100644
index 00000000..2572025b
--- /dev/null
+++ b/TestApps/Samples/Samples/TreeViewCustomStore.cs
@@ -0,0 +1,292 @@
+//
+// TreeViewCustomStore.cs
+//
+// Author:
+// Lluis Sanchez <llsan@microsoft.com>
+//
+// Copyright (c) 2018 Microsoft
+//
+// 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.
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Xwt;
+using Xwt.Drawing;
+
+namespace Samples
+{
+ public class TreeViewCustomStore: VBox
+ {
+ public TreeViewCustomStore ()
+ {
+ DataNode root = new DataNode ("Root",
+ new DataNode [] {
+ new DataNode ("One", new DataNode [] {
+ new DataNode ("Elephant"),
+ new DataNode ("Tiger")
+ }),
+ new DataNode ("Two", new DataNode [] {
+ new DataNode ("Monkey"),
+ new DataNode ("Lion")
+ }),
+ }
+ );
+ DataNode root2 = new DataNode ("Second Root",
+ new DataNode [] {
+ new DataNode ("One", new DataNode [] {
+ new DataNode ("Elephant"),
+ new DataNode ("Tiger")
+ }),
+ new DataNode ("Two", new DataNode [] {
+ new DataNode ("Monkey"),
+ new DataNode ("Lion")
+ }),
+ }
+ );
+
+ MyTreeSource source = new MyTreeSource ();
+ source.Add (root);
+ source.Add (root2);
+ source.Add (new InfiniteDataNode ("Infinite"));
+
+ TreeView tree = new TreeView ();
+
+ var col = new ListViewColumn ("Data");
+ col.Views.Add (new ImageCellView (source.ImageField));
+ col.Views.Add (new TextCellView (source.LabelField));
+ tree.Columns.Add (col);
+
+ PackStart (tree, true);
+
+ var hbox = new HBox ();
+ var add = new Button ("Add nodes");
+ var change = new Button ("Change nodes");
+ var remove = new Button ("Remove nodes");
+
+ hbox.PackStart (add);
+ hbox.PackStart (change);
+ hbox.PackStart (remove);
+ PackStart (hbox);
+
+ add.Clicked += delegate {
+ var node = new DataNode ("New child");
+ root.InsertChild (1, node);
+ tree.SelectRow (node);
+ };
+
+ remove.Clicked += delegate {
+ if (root.Children.Count > 0)
+ root.RemoveChild (root.Children [root.Children.Count - 1]);
+ };
+
+ change.Clicked += delegate {
+ var sel = (DataNode)tree.SelectedRow;
+ if (sel != null)
+ sel.Name = "Modified";
+ };
+
+ tree.DataSource = source;
+ }
+ }
+
+ class DataNode: TreePosition
+ {
+ protected List<DataNode> children;
+ public DataNode Parent { get; set; }
+ public virtual List<DataNode> Children => children;
+ string name;
+
+ public string Name {
+ get {
+ return name;
+ }
+
+ set {
+ name = value;
+ GetEventSink ()?.NotifyNodeChanged (this, -1);
+ }
+ }
+ Image image;
+
+ public Image Image {
+ get {
+ return image;
+ }
+
+ set {
+ image = value;
+ GetEventSink ()?.NotifyNodeChanged (this, -1);
+ }
+ }
+
+ public IEventSink EventSink { get; set; }
+
+ IEventSink GetEventSink ()
+ {
+ return EventSink ?? Parent?.GetEventSink ();
+ }
+
+ public DataNode (string name, params DataNode[] children)
+ {
+ Name = name;
+ if (children.Length > 0) {
+ this.children = children.ToList ();
+ foreach (var c in children)
+ c.Parent = this;
+ }
+ }
+
+ public void AddChild (DataNode node)
+ {
+ if (children == null)
+ children = new List<DataNode> ();
+ Children.Add (node);
+ node.Parent = this;
+ GetEventSink ()?.NotifyNodeAdded (node, Children.Count - 1);
+ }
+
+ public void InsertChild (int index, DataNode node)
+ {
+ if (children == null)
+ children = new List<DataNode> ();
+ Children.Insert (index, node);
+ node.Parent = this;
+ GetEventSink ()?.NotifyNodeAdded (node, index);
+ }
+
+ public void RemoveChild (DataNode node)
+ {
+ if (children != null) {
+ int i = children.IndexOf (node);
+ if (i != -1) {
+ children.RemoveAt (i);
+ GetEventSink ()?.NotifyNodeRemoved (this, i, node);
+ }
+ }
+ }
+
+ public override string ToString()
+ {
+ return "[" + Name + "]";
+ }
+ }
+
+ class InfiniteDataNode: DataNode
+ {
+ public InfiniteDataNode (string name, params DataNode [] children) : base (name, children)
+ {
+
+ }
+
+ public override List<DataNode> Children {
+ get {
+ if (children == null) {
+ AddChild (new InfiniteDataNode ("Child 1"));
+ AddChild (new InfiniteDataNode ("Child 2"));
+ }
+ return base.Children;
+ }
+ }
+ }
+
+ interface IEventSink
+ {
+ void NotifyNodeAdded (DataNode node, int childIndex);
+ void NotifyNodeRemoved (TreePosition parent, int childIndex, TreePosition child);
+ void NotifyNodeChanged (DataNode node, int childIndex);
+ }
+
+ class MyTreeSource : ITreeDataSource, IEventSink
+ {
+ public DataField<string> LabelField = new DataField<string> (0);
+ public DataField<Image> ImageField = new DataField<Image> (1);
+
+ List<DataNode> data = new List<DataNode> ();
+
+ public Type [] ColumnTypes => new [] { typeof (string), typeof (Image) };
+
+ public event EventHandler<TreeNodeEventArgs> NodeInserted;
+ public event EventHandler<TreeNodeChildEventArgs> NodeDeleted;
+ public event EventHandler<TreeNodeEventArgs> NodeChanged;
+ public event EventHandler<TreeNodeOrderEventArgs> NodesReordered;
+ public event EventHandler Cleared;
+
+ public void Add (DataNode node)
+ {
+ data.Add (node);
+ node.EventSink = this;
+ }
+
+ public TreePosition GetChild (TreePosition pos, int index)
+ {
+ var node = (DataNode)pos;
+ var list = node != null ? node.Children : data;
+ if (list == null || index >= list.Count)
+ return null;
+ return list [index];
+ }
+
+ public int GetChildrenCount (TreePosition pos)
+ {
+ var node = (DataNode)pos;
+ var list = node != null ? node.Children : data;
+ return list != null ? list.Count : 0;
+ }
+
+ public TreePosition GetParent (TreePosition pos)
+ {
+ var node = (DataNode)pos;
+ return node?.Parent;
+ }
+
+ public object GetValue (TreePosition pos, int column)
+ {
+ var node = (DataNode)pos;
+ if (column == 0)
+ return node.Name;
+ if (column == 1)
+ return node.Image;
+ return null;
+ }
+
+ public void SetValue (TreePosition pos, int column, object value)
+ {
+ var node = (DataNode)pos;
+ if (column == 0)
+ node.Name = (string) value;
+ if (column == 1)
+ node.Image = (Image) value;
+ }
+
+ void IEventSink.NotifyNodeAdded (DataNode node, int index)
+ {
+ NodeInserted?.Invoke (this, new TreeNodeEventArgs (node, index));
+ }
+
+ void IEventSink.NotifyNodeChanged (DataNode node, int index)
+ {
+ NodeChanged?.Invoke (this, new TreeNodeEventArgs (node, index));
+ }
+
+ void IEventSink.NotifyNodeRemoved (TreePosition parent, int childIndex, TreePosition child)
+ {
+ NodeDeleted?.Invoke (this, new TreeNodeChildEventArgs (parent, childIndex, child));
+ }
+ }
+}
diff --git a/TestApps/Samples/Samples/TreeViewEvents.cs b/TestApps/Samples/Samples/TreeViewEvents.cs
new file mode 100644
index 00000000..fb65bfee
--- /dev/null
+++ b/TestApps/Samples/Samples/TreeViewEvents.cs
@@ -0,0 +1,286 @@
+//
+// TreeViewEvents.cs
+//
+// Author:
+// Lluis Sanchez <llsan@microsoft.com>
+//
+// Copyright (c) 2018 Microsoft
+//
+// 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.
+using System;
+using System.Collections.Generic;
+using Xwt;
+using Xwt.Drawing;
+
+namespace Samples
+{
+ public class TreeViewEvents : VBox
+ {
+ DataField<TextNode> nodeField = new DataField<TextNode> ();
+
+ public TreeViewEvents ()
+ {
+ TreeStore store = new TreeStore (nodeField);
+ var tree = new TreeView (store);
+ tree.HeadersVisible = false;
+ tree.SelectionMode = SelectionMode.None;
+ tree.AnimationsEnabled = false;
+ tree.ButtonPressed += (s, e) =>
+ {
+ // disable internal selection/dragging logic, which might collide with the mouse handling in this example
+ if (e.Button == PointerButton.Left)
+ e.Handled = true;
+ };
+
+ var col = new ListViewColumn () { Expands = true };
+ var cellView = new ExpandableTextCellView ();
+ cellView.NodeField = nodeField;
+ col.Views.Add (cellView, true);
+ tree.Columns.Add (col);
+
+ var node = store.AddNode ().SetValue (nodeField, new TextNode ("Root 1"));
+ var child = node.AddChild ().SetValue (nodeField, new TextNode ("Very long text. Very long text. Very long text. Very long text. Very long text. Very long text. Very long text. Very long text."));
+ node = store.AddNode ().SetValue (nodeField, new TextNode ("Root 2"));
+ child = node.AddChild ().SetValue (nodeField, new TextNode ("Short text. Short text. Short text."));
+ child.AddChild ().SetValue (nodeField, new TextNode ("Very long text. Very long text. Very long text. Very long text. Very long text. Very long text. Very long text. Very long text."));
+
+
+ PackStart (tree, true);
+ }
+ }
+
+ class TextNode
+ {
+ public TextNode (string text)
+ {
+ Text = text;
+ }
+
+ public string Text { get; set; }
+ }
+
+ class ExpandableTextCellView : CanvasCellView
+ {
+ public IDataField<TextNode> NodeField { get; set; }
+
+ static Image addImage = Image.FromResource ("add-simple-16.png").WithSize (16);
+ static Image removeImage = Image.FromResource ("remove-simple-16.png").WithSize (16);
+ static Image addImageDisabled = Image.FromResource ("add-simple-disabled-16.png").WithSize (16);
+ static Image removeImageDisabled = Image.FromResource ("remove-simple-disabled-16.png").WithSize (16);
+
+ const double MoreLinkSpacing = 3;
+
+ Point pointerPosition;
+
+ class ViewStatus
+ {
+ public bool Expanded { get; set; }
+ public double LastRenderWidth;
+ public double LastCalculatedHeight;
+ }
+
+ // This could also be stored in the data store. In this example we keep it in
+ // an internal dictionary to clearly separate the data model from the view model.
+ // This is a simple implementation, it doesn't take into account that nodes could
+ // be removed
+ Dictionary<TextNode, ViewStatus> viewStatus = new Dictionary<TextNode, ViewStatus> ();
+
+ // Used to track the selection
+ int selectionStart;
+ int selectionEnd;
+ TextNode selectionRow;
+ bool dragging;
+
+ ViewStatus GetViewStatus (TextNode node)
+ {
+ ViewStatus status;
+ if (!viewStatus.TryGetValue (node, out status))
+ status = viewStatus [node] = new ViewStatus ();
+ return status;
+ }
+
+ protected override Size OnGetRequiredSize (SizeConstraint widthConstraint)
+ {
+ var node = GetValue (NodeField);
+ var status = GetViewStatus (node);
+
+ TextLayout layout = new TextLayout ();
+ layout.Text = node.Text;
+ var textSize = layout.GetSize ();
+
+ var maxWidth = widthConstraint.IsConstrained ? widthConstraint.AvailableSize : status.LastRenderWidth;
+
+ // When in expanded mode, the height of the row depends on the width. Since we don't know the width,
+ // let's use the last width that was used for rendering.
+
+ if (status.Expanded && maxWidth > 0 && textSize.Width > maxWidth) {
+ layout.Width = maxWidth - addImage.Width - MoreLinkSpacing;
+ textSize = layout.GetSize ();
+ }
+
+ status.LastCalculatedHeight = textSize.Height;
+
+ return new Size (30, textSize.Height);
+ }
+
+ protected override void OnDraw (Context ctx, Rectangle cellArea)
+ {
+ TextLayout layout = new TextLayout ();
+ var node = GetValue (NodeField);
+ var status = GetViewStatus (node);
+
+ // Store the width, it will be used for calculating height in OnGetRequiredSize() when in expanded mode.
+ status.LastRenderWidth = cellArea.Width;
+
+ layout.Text = node.Text;
+ var textSize = layout.GetSize ();
+
+ // Render the selection
+ if (selectionRow == node && selectionStart != selectionEnd)
+ layout.SetBackground (Colors.LightBlue, Math.Min (selectionStart, selectionEnd), Math.Abs (selectionEnd - selectionStart));
+
+ // Text doesn't fit. We need to render the expand icon
+
+ if (textSize.Width > cellArea.Width) {
+ layout.Width = Math.Max (1, cellArea.Width - addImage.Width - MoreLinkSpacing);
+ if (!status.Expanded)
+ layout.Trimming = TextTrimming.WordElipsis;
+ else
+ textSize = layout.GetSize (); // The height may have changed. We need the real height since we check it at the end of the method
+
+ // Draw the text
+
+ ctx.DrawTextLayout (layout, cellArea.X, cellArea.Y);
+
+ // Draw the image
+
+ var imageRect = new Rectangle (cellArea.X + layout.Width + MoreLinkSpacing, cellArea.Y, addImage.Width, addImage.Height);
+ bool hover = pointerPosition != Point.Zero && imageRect.Contains (pointerPosition);
+ Image icon;
+ if (status.Expanded)
+ icon = hover ? removeImage : removeImageDisabled;
+ else
+ icon = hover ? addImage : addImageDisabled;
+ ctx.DrawImage (icon, imageRect.X, imageRect.Y);
+ }
+ else {
+ ctx.DrawTextLayout (layout, cellArea.X, cellArea.Y);
+ }
+
+ // If the height required by the text is not the same as what was calculated in OnGetRequiredSize(), it means that
+ // the required height has changed. In that case call QueueResize(), so that OnGetRequiredSize() is called
+ // again and the row is properly resized.
+
+ if (status.Expanded && textSize.Height != status.LastCalculatedHeight)
+ QueueResize ();
+ }
+
+ void CalcLayout (out TextLayout layout, out Rectangle cellArea, out Rectangle expanderRect)
+ {
+ var node = GetValue (NodeField);
+ var status = GetViewStatus (node);
+ expanderRect = Rectangle.Zero;
+ cellArea = Bounds;
+ layout = new TextLayout ();
+ layout.Text = node.Text;
+ var textSize = layout.GetSize ();
+ if (textSize.Width > cellArea.Width) {
+ layout.Width = Math.Max (1, cellArea.Width - addImage.Width - MoreLinkSpacing);
+ if (!status.Expanded)
+ layout.Trimming = TextTrimming.WordElipsis;
+ var expanderX = cellArea.Right - addImage.Width;
+ if (expanderX > 0)
+ expanderRect = new Rectangle (expanderX, cellArea.Y, addImage.Width, addImage.Height);
+ }
+ }
+
+ protected override void OnMouseMoved (MouseMovedEventArgs args)
+ {
+ TextLayout layout;
+ Rectangle cellArea, expanderRect;
+ CalcLayout (out layout, out cellArea, out expanderRect);
+
+ if (expanderRect != Rectangle.Zero && expanderRect.Contains (args.Position)) {
+ pointerPosition = args.Position;
+ QueueDraw ();
+ } else if (pointerPosition != Point.Zero) {
+ pointerPosition = Point.Zero;
+ QueueDraw ();
+ }
+
+ var layoutSize = layout.GetSize ();
+ var insideText = new Rectangle (cellArea.TopLeft, layoutSize).Contains (args.Position);
+ var node = GetValue (NodeField);
+
+ if (dragging && insideText && selectionRow == node) {
+ var pos = layout.GetIndexFromCoordinates (args.Position.X - cellArea.X, args.Position.Y - cellArea.Y);
+ if (pos != -1) {
+ selectionEnd = pos;
+ QueueDraw ();
+ }
+ } else {
+ ParentWidget.Cursor = insideText ? CursorType.IBeam : CursorType.Arrow;
+ }
+ }
+
+ protected override void OnButtonPressed (ButtonEventArgs args)
+ {
+ TextLayout layout;
+ Rectangle cellArea, expanderRect;
+ CalcLayout (out layout, out cellArea, out expanderRect);
+
+ var node = GetValue (NodeField);
+ var status = GetViewStatus (node);
+
+ if (expanderRect != Rectangle.Zero && expanderRect.Contains (args.Position)) {
+ status.Expanded = !status.Expanded;
+ QueueResize ();
+ return;
+ }
+
+ var pos = layout.GetIndexFromCoordinates (args.Position.X - cellArea.X, args.Position.Y - cellArea.Y);
+ if (pos != -1) {
+ selectionStart = selectionEnd = pos;
+ selectionRow = node;
+ dragging = true;
+ } else
+ selectionRow = null;
+
+ QueueDraw ();
+
+ base.OnButtonPressed (args);
+ }
+
+ protected override void OnButtonReleased (ButtonEventArgs args)
+ {
+ if (dragging) {
+ dragging = false;
+ QueueDraw ();
+ }
+ base.OnButtonReleased (args);
+ }
+
+ protected override void OnMouseExited ()
+ {
+ pointerPosition = Point.Zero;
+ ParentWidget.Cursor = CursorType.Arrow;
+ base.OnMouseExited ();
+ }
+ }
+}
diff --git a/TestApps/Samples/Samples/TreeViews.cs b/TestApps/Samples/Samples/TreeViews.cs
index 67235596..1208a944 100644
--- a/TestApps/Samples/Samples/TreeViews.cs
+++ b/TestApps/Samples/Samples/TreeViews.cs
@@ -25,6 +25,7 @@
// THE SOFTWARE.
using System;
using Xwt;
+using Xwt.Drawing;
namespace Samples
{
@@ -147,6 +148,8 @@ namespace Samples
view.DragStarted += delegate(object sender, DragStartedEventArgs e) {
var val = store.GetNavigatorAt (view.SelectedRow).GetValue (text);
e.DragOperation.Data.AddValue (val);
+ var img = Image.FromResource(GetType(), "class.png");
+ e.DragOperation.SetDragImage(img, (int)img.Size.Width, (int)img.Size.Height);
e.DragOperation.Finished += delegate(object s, DragFinishedEventArgs args) {
Console.WriteLine ("D:" + args.DeleteSource);
};
@@ -168,6 +171,16 @@ namespace Samples
Console.WriteLine("Collapsed: " + val);
};
+ RadioButtonGroup group = new RadioButtonGroup ();
+ foreach (SelectionMode mode in Enum.GetValues(typeof (SelectionMode))) {
+ var radio = new RadioButton (mode.ToString ());
+ radio.Group = group;
+ radio.Activated += delegate {
+ view.SelectionMode = mode;
+ };
+ PackStart (radio);
+ }
+
int addCounter = 0;
view.KeyPressed += (sender, e) => {
if (e.Key == Key.Insert) {
@@ -205,6 +218,12 @@ namespace Samples
};
PackStart (removeButton);
+ Button clearButton = new Button("Clear");
+ clearButton.Clicked += delegate (object sender, EventArgs e) {
+ store.Clear();
+ };
+ PackStart(clearButton);
+
var label = new Label ();
PackStart (label);
diff --git a/TestApps/Samples/icons/add-simple-16.png b/TestApps/Samples/icons/add-simple-16.png
new file mode 100644
index 00000000..fc90983f
--- /dev/null
+++ b/TestApps/Samples/icons/add-simple-16.png
Binary files differ
diff --git a/TestApps/Samples/icons/add-simple-16@2x.png b/TestApps/Samples/icons/add-simple-16@2x.png
new file mode 100644
index 00000000..bd0de7a0
--- /dev/null
+++ b/TestApps/Samples/icons/add-simple-16@2x.png
Binary files differ
diff --git a/TestApps/Samples/icons/add-simple-disabled-16.png b/TestApps/Samples/icons/add-simple-disabled-16.png
new file mode 100644
index 00000000..b88861e2
--- /dev/null
+++ b/TestApps/Samples/icons/add-simple-disabled-16.png
Binary files differ
diff --git a/TestApps/Samples/icons/add-simple-disabled-16@2x.png b/TestApps/Samples/icons/add-simple-disabled-16@2x.png
new file mode 100644
index 00000000..566ffe5d
--- /dev/null
+++ b/TestApps/Samples/icons/add-simple-disabled-16@2x.png
Binary files differ
diff --git a/TestApps/Samples/icons/remove-simple-16.png b/TestApps/Samples/icons/remove-simple-16.png
new file mode 100644
index 00000000..96e1253c
--- /dev/null
+++ b/TestApps/Samples/icons/remove-simple-16.png
Binary files differ
diff --git a/TestApps/Samples/icons/remove-simple-16@2x.png b/TestApps/Samples/icons/remove-simple-16@2x.png
new file mode 100644
index 00000000..2c740fef
--- /dev/null
+++ b/TestApps/Samples/icons/remove-simple-16@2x.png
Binary files differ
diff --git a/TestApps/Samples/icons/remove-simple-disabled-16.png b/TestApps/Samples/icons/remove-simple-disabled-16.png
new file mode 100644
index 00000000..e37a8bfe
--- /dev/null
+++ b/TestApps/Samples/icons/remove-simple-disabled-16.png
Binary files differ
diff --git a/TestApps/Samples/icons/remove-simple-disabled-16@2x.png b/TestApps/Samples/icons/remove-simple-disabled-16@2x.png
new file mode 100644
index 00000000..679d0ca8
--- /dev/null
+++ b/TestApps/Samples/icons/remove-simple-disabled-16@2x.png
Binary files differ
diff --git a/TestApps/WpfTest/WpfTest.csproj b/TestApps/WpfTest/WpfTest.csproj
index 1a9d21e4..bf9fbd3b 100644
--- a/TestApps/WpfTest/WpfTest.csproj
+++ b/TestApps/WpfTest/WpfTest.csproj
@@ -23,6 +23,7 @@
<CodeAnalysisFailOnMissingRules>false</CodeAnalysisFailOnMissingRules>
<WarningLevel>4</WarningLevel>
<Optimize>False</Optimize>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
<OutputPath>bin\Release\</OutputPath>
@@ -34,6 +35,7 @@
<CodeAnalysisFailOnMissingRules>true</CodeAnalysisFailOnMissingRules>
<WarningLevel>4</WarningLevel>
<DebugSymbols>true</DebugSymbols>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>True</DebugSymbols>
@@ -47,6 +49,7 @@
<CodeAnalysisFailOnMissingRules>false</CodeAnalysisFailOnMissingRules>
<WarningLevel>4</WarningLevel>
<Optimize>False</Optimize>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
@@ -60,6 +63,7 @@
<CodeAnalysisFailOnMissingRules>true</CodeAnalysisFailOnMissingRules>
<WarningLevel>4</WarningLevel>
<DebugSymbols>true</DebugSymbols>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Xwt.WPF\Xwt.WPF.csproj">
diff --git a/TestApps/XamMacTest/Info.plist b/TestApps/XamMacTest/Info.plist
index deb645ae..b305a41c 100644
--- a/TestApps/XamMacTest/Info.plist
+++ b/TestApps/XamMacTest/Info.plist
@@ -9,7 +9,7 @@
<key>CFBundleVersion</key>
<string>1</string>
<key>LSMinimumSystemVersion</key>
- <string>10.6</string>
+ <string>10.7</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
diff --git a/TestApps/XamMacTest/Main.cs b/TestApps/XamMacTest/Main.cs
index 49fbbd4f..cd44c1a5 100644
--- a/TestApps/XamMacTest/Main.cs
+++ b/TestApps/XamMacTest/Main.cs
@@ -7,9 +7,6 @@ namespace MacTest
{
static void Main (string [] args)
{
- //FIXME: remove this once mmp summorts xammac
- ObjCRuntime.Dlfcn.dlopen ("/Library/Frameworks/Xamarin.Mac.framework/Versions/Current/lib/libxammac.dylib", 0);
-
App.Run (ToolkitType.XamMac);
}
}
diff --git a/TestApps/XamMacTest/XamMacTest.csproj b/TestApps/XamMacTest/XamMacTest.csproj
index e60d3b2e..eb578e3a 100644
--- a/TestApps/XamMacTest/XamMacTest.csproj
+++ b/TestApps/XamMacTest/XamMacTest.csproj
@@ -4,11 +4,12 @@
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{2B7FF081-FE53-42F7-9D5D-D4B38E548F94}</ProjectGuid>
- <ProjectTypeGuids>{948B3504-5B70-4649-8FE4-BDE1FB46EC69};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <ProjectTypeGuids>{A3F8F2AB-B479-4A4A-A458-A89E7DC349F1};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<OutputType>Exe</OutputType>
<RootNamespace>MacTest</RootNamespace>
<AssemblyName>MacTest</AssemblyName>
<SuppressXamMacMigration>True</SuppressXamMacMigration>
+ <UseXamMacFullFramework>true</UseXamMacFullFramework>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>True</DebugSymbols>
@@ -26,6 +27,8 @@
<UseSGen>False</UseSGen>
<UseRefCounting>false</UseRefCounting>
<Profiling>false</Profiling>
+ <AOTMode>None</AOTMode>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@@ -42,19 +45,17 @@
<UseRefCounting>false</UseRefCounting>
<Profiling>false</Profiling>
<DebugSymbols>true</DebugSymbols>
+ <AOTMode>None</AOTMode>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
- <Reference Include="Xamarin.Mac">
- <HintPath>\Library\Frameworks\Xamarin.Mac.framework\Versions\Current\lib\x86_64\full\Xamarin.Mac.dll</HintPath>
- </Reference>
+ <Reference Include="Xamarin.Mac" />
</ItemGroup>
<ItemGroup>
<Compile Include="Main.cs" />
</ItemGroup>
- <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
- <Import Project="$(MSBuildExtensionsPath)\Mono\MonoMac\v0.0\Mono.MonoMac.targets" />
<ItemGroup>
<None Include="Info.plist" />
</ItemGroup>
@@ -72,13 +73,5 @@
<Name>Xwt.XamMac</Name>
</ProjectReference>
</ItemGroup>
- <ProjectExtensions>
- <MonoDevelop>
- <Properties>
- <Policies>
- <DotNetNamingPolicy DirectoryNamespaceAssociation="None" ResourceNamePolicy="FileName" />
- </Policies>
- </Properties>
- </MonoDevelop>
- </ProjectExtensions>
+ <Import Project="$(MSBuildExtensionsPath)\Xamarin\Mac\Xamarin.Mac.CSharp.targets" />
</Project>
diff --git a/Testing/CoreTests/CoreTests.csproj b/Testing/CoreTests/CoreTests.csproj
index 99839e81..6b748674 100644
--- a/Testing/CoreTests/CoreTests.csproj
+++ b/Testing/CoreTests/CoreTests.csproj
@@ -17,12 +17,14 @@
<DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<Optimize>true</Optimize>
<OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
diff --git a/Testing/GtkTestRunner.csproj b/Testing/GtkTestRunner.csproj
index c9ebd83e..9b13958f 100644
--- a/Testing/GtkTestRunner.csproj
+++ b/Testing/GtkTestRunner.csproj
@@ -19,6 +19,7 @@
<WarningLevel>4</WarningLevel>
<ConsolePause>False</ConsolePause>
<PlatformTarget>x86</PlatformTarget>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@@ -29,6 +30,7 @@
<DebugSymbols>true</DebugSymbols>
<ConsolePause>false</ConsolePause>
<PlatformTarget>x86</PlatformTarget>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
@@ -111,6 +113,7 @@
<Compile Include="Tests\DialogTests.cs" />
<Compile Include="Tests\ScrollableWidgetTests.cs" />
<Compile Include="GtkTestRunner\GtkInit.cs" />
+ <Compile Include="GtkTests\PangoTests.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
diff --git a/Testing/GtkTests/PangoTests.cs b/Testing/GtkTests/PangoTests.cs
new file mode 100644
index 00000000..d2e4d575
--- /dev/null
+++ b/Testing/GtkTests/PangoTests.cs
@@ -0,0 +1,87 @@
+//
+// PangoTests.cs
+//
+// Author:
+// Vsevolod Kukol <sevoku@microsoft.com>
+//
+// Copyright (c) 2017 (c) Microsoft Corporation
+//
+// 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.
+using NUnit.Framework;
+using Xwt.GtkBackend;
+
+namespace Xwt
+{
+ [TestFixture]
+ public class PangoUtilTests
+ {
+ [Test]
+ public void TextIndexerAscii()
+ {
+ var str = "Test";
+ var indexer = new TextIndexer(str);
+
+ Assert.AreEqual(0, indexer.IndexToByteIndex(0));
+ Assert.AreEqual(0, indexer.ByteIndexToIndex(0));
+ Assert.AreEqual(1, indexer.IndexToByteIndex(1));
+ Assert.AreEqual(1, indexer.ByteIndexToIndex(1));
+ Assert.AreEqual(2, indexer.IndexToByteIndex(2));
+ Assert.AreEqual(2, indexer.ByteIndexToIndex(2));
+ Assert.AreEqual(3, indexer.IndexToByteIndex(3));
+ Assert.AreEqual(3, indexer.ByteIndexToIndex(3));
+ }
+
+ [Test]
+ public void TextIndexerUnicode()
+ {
+ var str = "バージョン";
+ var indexer = new TextIndexer(str);
+
+ Assert.AreEqual(0, indexer.IndexToByteIndex(0));
+ Assert.AreEqual(3, indexer.IndexToByteIndex(1));
+ Assert.AreEqual(6, indexer.IndexToByteIndex(2));
+
+ Assert.AreEqual(0, indexer.ByteIndexToIndex(0));
+ Assert.AreEqual(0, indexer.ByteIndexToIndex(1));
+ Assert.AreEqual(0, indexer.ByteIndexToIndex(2));
+ Assert.AreEqual(1, indexer.ByteIndexToIndex(3));
+ Assert.AreEqual(1, indexer.ByteIndexToIndex(4));
+ Assert.AreEqual(1, indexer.ByteIndexToIndex(5));
+ }
+
+ [Test]
+ public void TextIndexerMixed()
+ {
+ var str = "バAジョン";
+ var indexer = new TextIndexer(str);
+
+ Assert.AreEqual(0, indexer.IndexToByteIndex(0));
+ Assert.AreEqual(3, indexer.IndexToByteIndex(1));
+ Assert.AreEqual(4, indexer.IndexToByteIndex(2));
+
+ Assert.AreEqual(0, indexer.ByteIndexToIndex(0));
+ Assert.AreEqual(0, indexer.ByteIndexToIndex(1));
+ Assert.AreEqual(0, indexer.ByteIndexToIndex(2));
+ Assert.AreEqual(1, indexer.ByteIndexToIndex(3));
+ Assert.AreEqual(2, indexer.ByteIndexToIndex(4));
+ Assert.AreEqual(2, indexer.ByteIndexToIndex(5));
+ }
+ }
+}
+
diff --git a/Testing/MacTestRunner.csproj b/Testing/MacTestRunner.csproj
index 6e40b9b9..e25404e2 100644
--- a/Testing/MacTestRunner.csproj
+++ b/Testing/MacTestRunner.csproj
@@ -29,6 +29,10 @@
<CreatePackage>False</CreatePackage>
<UseRefCounting>false</UseRefCounting>
<Profiling>false</Profiling>
+ <HttpClientHandler>HttpClientHandler</HttpClientHandler>
+ <LinkMode>None</LinkMode>
+ <AOTMode>None</AOTMode>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@@ -47,6 +51,9 @@
<UseRefCounting>false</UseRefCounting>
<Profiling>false</Profiling>
<DebugSymbols>true</DebugSymbols>
+ <HttpClientHandler>HttpClientHandler</HttpClientHandler>
+ <AOTMode>None</AOTMode>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
diff --git a/Testing/WpfTestRunner.csproj b/Testing/WpfTestRunner.csproj
index 24f6d6e4..83f1b395 100644
--- a/Testing/WpfTestRunner.csproj
+++ b/Testing/WpfTestRunner.csproj
@@ -21,6 +21,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<PlatformTarget>x86</PlatformTarget>
@@ -31,6 +32,7 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DebugSymbols>true</DebugSymbols>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<ItemGroup>
<Reference Include="nunit-console-runner">
diff --git a/Xwt.Gtk.Mac/Xwt.Gtk.Mac.csproj b/Xwt.Gtk.Mac/Xwt.Gtk.Mac.csproj
index a31fd49d..7635fe3a 100644
--- a/Xwt.Gtk.Mac/Xwt.Gtk.Mac.csproj
+++ b/Xwt.Gtk.Mac/Xwt.Gtk.Mac.csproj
@@ -21,6 +21,7 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@@ -30,6 +31,7 @@
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
<DebugSymbols>true</DebugSymbols>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup>
<XamMacPath>\Library\Frameworks\Xamarin.Mac.framework\Versions\Current\lib\x86_64\full\Xamarin.Mac.dll</XamMacPath>
diff --git a/Xwt.Gtk.Windows/Xwt.Gtk.Windows.csproj b/Xwt.Gtk.Windows/Xwt.Gtk.Windows.csproj
index 3f3a2246..a5ce1ccb 100644
--- a/Xwt.Gtk.Windows/Xwt.Gtk.Windows.csproj
+++ b/Xwt.Gtk.Windows/Xwt.Gtk.Windows.csproj
@@ -19,6 +19,7 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@@ -28,6 +29,7 @@
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
<DebugSymbols>true</DebugSymbols>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<ItemGroup>
<Reference Include="gdk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f, processorArchitecture=MSIL" />
diff --git a/Xwt.Gtk/Xwt.Gtk.csproj b/Xwt.Gtk/Xwt.Gtk.csproj
index a4c35f3a..e090656d 100644
--- a/Xwt.Gtk/Xwt.Gtk.csproj
+++ b/Xwt.Gtk/Xwt.Gtk.csproj
@@ -23,6 +23,7 @@
<ConsolePause>False</ConsolePause>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@@ -34,6 +35,7 @@
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<DebugSymbols>true</DebugSymbols>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
diff --git a/Xwt.Gtk/Xwt.Gtk3.csproj b/Xwt.Gtk/Xwt.Gtk3.csproj
index 40fbee79..6801595c 100644
--- a/Xwt.Gtk/Xwt.Gtk3.csproj
+++ b/Xwt.Gtk/Xwt.Gtk3.csproj
@@ -21,6 +21,7 @@
<WarningLevel>4</WarningLevel>
<ConsolePause>False</ConsolePause>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@@ -32,6 +33,7 @@
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<DefineConstants>XWT_GTK3</DefineConstants>
<DebugSymbols>true</DebugSymbols>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
diff --git a/Xwt.Gtk/Xwt.GtkBackend.CellViews/CellUtil.cs b/Xwt.Gtk/Xwt.GtkBackend.CellViews/CellUtil.cs
index 2b6067cd..1bcf6911 100644
--- a/Xwt.Gtk/Xwt.GtkBackend.CellViews/CellUtil.cs
+++ b/Xwt.Gtk/Xwt.GtkBackend.CellViews/CellUtil.cs
@@ -158,6 +158,7 @@ namespace Xwt.GtkBackend
Gtk.Widget EventRootWidget { get; }
bool GetCellPosition (Gtk.CellRenderer r, int ex, int ey, out int cx, out int cy, out Gtk.TreeIter iter);
void QueueDraw (object target, Gtk.TreeIter iter);
+ void QueueResize (object target, Gtk.TreeIter iter);
TreeModel Model { get; }
Gtk.TreeIter PressedIter { get; set; }
CellViewBackend PressedCell { get; set; }
diff --git a/Xwt.Gtk/Xwt.GtkBackend.CellViews/CellViewBackend.cs b/Xwt.Gtk/Xwt.GtkBackend.CellViews/CellViewBackend.cs
index 62808a41..87fe116b 100644
--- a/Xwt.Gtk/Xwt.GtkBackend.CellViews/CellViewBackend.cs
+++ b/Xwt.Gtk/Xwt.GtkBackend.CellViews/CellViewBackend.cs
@@ -190,10 +190,11 @@ namespace Xwt.GtkBackend
if (!buttonReleaseSubscribed)
rendererTarget.EventRootWidget.ButtonReleaseEvent -= HandleButtonReleaseEvent;
- var rect = rendererTarget.GetCellBounds (target, CellRenderer, iter);
+ var rect = rendererTarget.GetCellBackgroundBounds (target, CellRenderer, iter);
if (captured || rect.Contains (cx, cy)) {
ApplicationContext.InvokeUserCode (delegate {
LoadData (rendererTarget.Model, iter);
+ SetCurrentEventRow ();
var a = new ButtonEventArgs {
X = args.Event.X,
Y = args.Event.Y,
@@ -212,7 +213,7 @@ namespace Xwt.GtkBackend
int cx, cy;
Gtk.TreeIter iter;
if (rendererTarget.GetCellPosition (CellRenderer, (int)args.Event.X, (int)args.Event.Y, out cx, out cy, out iter)) {
- var rect = rendererTarget.GetCellBounds (target, CellRenderer, iter);
+ var rect = rendererTarget.GetCellBackgroundBounds (target, CellRenderer, iter);
if (rect.Contains (cx, cy)) {
rendererTarget.PressedIter = iter;
rendererTarget.PressedCell = this;
@@ -226,6 +227,7 @@ namespace Xwt.GtkBackend
}
ApplicationContext.InvokeUserCode (delegate {
LoadData (rendererTarget.Model, iter);
+ SetCurrentEventRow ();
var a = new ButtonEventArgs {
X = args.Event.X,
Y = args.Event.Y,
@@ -246,13 +248,14 @@ namespace Xwt.GtkBackend
int cx, cy;
Gtk.TreeIter iter;
if (rendererTarget.GetCellPosition (CellRenderer, (int)args.Event.X, (int)args.Event.Y, out cx, out cy, out iter)) {
- var rect = rendererTarget.GetCellBounds (target, CellRenderer, iter);
+ var rect = rendererTarget.GetCellBackgroundBounds (target, CellRenderer, iter);
if (rect.Contains (cx, cy)) {
if (enabledEvents.HasFlag (WidgetEvent.MouseMoved))
ApplicationContext.InvokeUserCode (delegate {
LoadData (rendererTarget.Model, iter);
- EventSink.OnMouseMoved (new MouseMovedEventArgs (args.Event.Time, cx, cy));
+ SetCurrentEventRow ();
+ EventSink.OnMouseMoved (new MouseMovedEventArgs (args.Event.Time, cx, cy));
});
if (!mouseInsideCell) {
@@ -260,6 +263,7 @@ namespace Xwt.GtkBackend
if (enabledEvents.HasFlag (WidgetEvent.MouseEntered))
ApplicationContext.InvokeUserCode (delegate {
LoadData (rendererTarget.Model, iter);
+ SetCurrentEventRow ();
EventSink.OnMouseEntered ();
});
}
@@ -268,6 +272,7 @@ namespace Xwt.GtkBackend
if (enabledEvents.HasFlag (WidgetEvent.MouseExited))
ApplicationContext.InvokeUserCode (delegate {
LoadData (rendererTarget.Model, iter);
+ SetCurrentEventRow ();
EventSink.OnMouseExited ();
});
}
@@ -361,6 +366,11 @@ namespace Xwt.GtkBackend
{
rendererTarget.QueueDraw (target, CurrentIter);
}
+
+ public void QueueResize ()
+ {
+ rendererTarget.QueueResize (target, CurrentIter);
+ }
}
}
diff --git a/Xwt.Gtk/Xwt.GtkBackend.CellViews/CustomCellRenderer.cs b/Xwt.Gtk/Xwt.GtkBackend.CellViews/CustomCellRenderer.cs
index 2304625c..22670f38 100644
--- a/Xwt.Gtk/Xwt.GtkBackend.CellViews/CustomCellRenderer.cs
+++ b/Xwt.Gtk/Xwt.GtkBackend.CellViews/CustomCellRenderer.cs
@@ -38,7 +38,8 @@ namespace Xwt.GtkBackend
bool isSelected;
bool hasFocus;
bool isPrelit;
- bool isDrawing;
+ TreeIter lastIter;
+ bool shown;
public override void Initialize (ICellViewFrontend cellView, ICellRendererTarget rendererTarget, object target)
{
@@ -57,17 +58,30 @@ namespace Xwt.GtkBackend
hasFocus = (flags & CellRendererState.Focused) != 0;
isPrelit = (flags & CellRendererState.Prelit) != 0;
- isDrawing = true;
+ // Gtk will rerender the cell on every status change, hence we can expect the values
+ // set here to be valid until the geometry or any other status of the cell and tree changes.
+ // Setting shown=true ensures that those values are always reused instead if queried
+ // from the parent tree after the cell has been rendered.
+ shown = true;
+ }
+
+ protected override void OnLoadData ()
+ {
+ if (!CurrentIter.Equals (lastIter)) {
+ // if the current iter has changed all cached values from the last draw are invalid
+ shown = false;
+ lastIter = CurrentIter;
+ }
+ base.OnLoadData ();
}
internal void EndDrawing ()
{
- isDrawing = false;
}
public override Rectangle CellBounds {
get {
- if (isDrawing)
+ if (shown)
return cellArea;
return base.CellBounds;
}
@@ -75,7 +89,7 @@ namespace Xwt.GtkBackend
public override Rectangle BackgroundBounds {
get {
- if (isDrawing)
+ if (shown)
return backgroundArea;
return base.BackgroundBounds;
}
@@ -83,7 +97,7 @@ namespace Xwt.GtkBackend
public override bool Selected {
get {
- if (isDrawing)
+ if (shown)
return isSelected;
return base.Selected;
}
@@ -91,7 +105,7 @@ namespace Xwt.GtkBackend
public override bool HasFocus {
get {
- if (isDrawing)
+ if (shown)
return hasFocus;
return base.HasFocus;
}
@@ -99,7 +113,7 @@ namespace Xwt.GtkBackend
public bool IsHighlighted {
get {
- if (isDrawing)
+ if (shown)
return isPrelit;
return false;
}
@@ -113,10 +127,25 @@ namespace Xwt.GtkBackend
protected override void OnRender (Cairo.Context cr, Gtk.Widget widget, Gdk.Rectangle background_area, Gdk.Rectangle cell_area, CellRendererState flags)
{
+ int wx, wy, dx = 0, dy = 0;
+ var tree = widget as Gtk.TreeView;
+ // Tree coordinates must be converted to widget coordinates,
+ // otherwise custom cell bounds will have an offset to the parent widget
+ if (tree != null) {
+ tree.ConvertBinWindowToWidgetCoords (cell_area.X, cell_area.Y, out wx, out wy);
+ dx = wx - cell_area.X;
+ dy = wy - cell_area.Y;
+ cell_area.X += dx;
+ background_area.X += dx;
+ cell_area.Y += dy;
+ background_area.Y += dy;
+ }
+
Parent.StartDrawing (new Rectangle (background_area.X, background_area.Y, background_area.Width, background_area.Height), new Rectangle (cell_area.X, cell_area.Y, cell_area.Width, cell_area.Height), flags);
CellView.ApplicationContext.InvokeUserCode (delegate {
CairoContextBackend ctx = new CairoContextBackend (Util.GetScaleFactor (widget));
ctx.Context = cr;
+ ctx.Context.Translate(-dx, -dy);
using (ctx) {
CellView.Draw (ctx, new Rectangle (cell_area.X, cell_area.Y, cell_area.Width, cell_area.Height));
}
@@ -127,8 +156,9 @@ namespace Xwt.GtkBackend
protected override void OnGetSize (Gtk.Widget widget, ref Gdk.Rectangle cell_area, out int x_offset, out int y_offset, out int width, out int height)
{
Size size = new Size ();
+ var widthConstraint = cell_area.Width > 0 ? SizeConstraint.WithSize(cell_area.Width) : SizeConstraint.Unconstrained;
CellView.ApplicationContext.InvokeUserCode (delegate {
- size = CellView.GetRequiredSize ();
+ size = CellView.GetRequiredSize (widthConstraint);
});
width = (int) size.Width;
height = (int) size.Height;
diff --git a/Xwt.Gtk/Xwt.GtkBackend/AccessibleBackend.cs b/Xwt.Gtk/Xwt.GtkBackend/AccessibleBackend.cs
index 711cea70..6c68fbd6 100644
--- a/Xwt.Gtk/Xwt.GtkBackend/AccessibleBackend.cs
+++ b/Xwt.Gtk/Xwt.GtkBackend/AccessibleBackend.cs
@@ -135,7 +135,6 @@ namespace Xwt.GtkBackend
}
set {
if (widget.Accessible is AtkValue) {
- GLib.Value val = GLib.Value.Empty;
(widget.Accessible as AtkValue)?.SetCurrentValue (new GLib.Value (value));
} else if (widget.Accessible is AtkEditableText) {
var atkText = (widget.Accessible as AtkEditableText);
diff --git a/Xwt.Gtk/Xwt.GtkBackend/BoxBackendGtk2.cs b/Xwt.Gtk/Xwt.GtkBackend/BoxBackendGtk2.cs
index 9f12f4c1..fd4ccb84 100644
--- a/Xwt.Gtk/Xwt.GtkBackend/BoxBackendGtk2.cs
+++ b/Xwt.Gtk/Xwt.GtkBackend/BoxBackendGtk2.cs
@@ -24,6 +24,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
+using System.Linq;
namespace Xwt.GtkBackend
{
@@ -34,6 +35,13 @@ namespace Xwt.GtkBackend
if (!IsReallocating)
QueueResize ();
}
+
+ protected override void OnSizeRequested (ref Gtk.Requisition requisition)
+ {
+ base.OnSizeRequested (ref requisition);
+ foreach (var c in children.Keys.ToArray ())
+ c.SizeRequest ();
+ }
}
}
diff --git a/Xwt.Gtk/Xwt.GtkBackend/ButtonBackend.cs b/Xwt.Gtk/Xwt.GtkBackend/ButtonBackend.cs
index 2c055645..8bb0514b 100755
--- a/Xwt.Gtk/Xwt.GtkBackend/ButtonBackend.cs
+++ b/Xwt.Gtk/Xwt.GtkBackend/ButtonBackend.cs
@@ -47,8 +47,12 @@ namespace Xwt.GtkBackend
{
NeedsEventBox = false;
Widget = new Gtk.Button ();
+ Widget.Realized += (o, arg) =>
+ {
+ if (Widget.IsRealized && Widget.CanDefault)
+ Widget.GrabDefault();
+ };
base.Widget.Show ();
-
}
protected new Gtk.Button Widget {
@@ -83,6 +87,11 @@ namespace Xwt.GtkBackend
}
}
+ public virtual bool IsDefault {
+ get { return Widget.CanDefault; }
+ set { Widget.CanDefault = value; }
+ }
+
public override object Font {
get {
return base.Font;
diff --git a/Xwt.Gtk/Xwt.GtkBackend/ComboBoxBackend.cs b/Xwt.Gtk/Xwt.GtkBackend/ComboBoxBackend.cs
index dfed802e..1f00511a 100644
--- a/Xwt.Gtk/Xwt.GtkBackend/ComboBoxBackend.cs
+++ b/Xwt.Gtk/Xwt.GtkBackend/ComboBoxBackend.cs
@@ -184,6 +184,10 @@ namespace Xwt.GtkBackend
{
}
+ public void QueueResize (object target, Gtk.TreeIter iter)
+ {
+ }
+
#endregion
}
}
diff --git a/Xwt.Gtk/Xwt.GtkBackend/CustomTreeModel.cs b/Xwt.Gtk/Xwt.GtkBackend/CustomTreeModel.cs
index b7e5fdb4..8753ec62 100644
--- a/Xwt.Gtk/Xwt.GtkBackend/CustomTreeModel.cs
+++ b/Xwt.Gtk/Xwt.GtkBackend/CustomTreeModel.cs
@@ -33,15 +33,25 @@ using TreeModelImplementor = Gtk.ITreeModelImplementor;
namespace Xwt.GtkBackend
{
- public class CustomTreeModel: TreeModelImplementor
+ public class CustomTreeModel: GLib.Object, TreeModelImplementor
{
ITreeDataSource source;
- Dictionary<GCHandle,TreePosition> nodeHash = new Dictionary<GCHandle,TreePosition> ();
- Dictionary<TreePosition,GCHandle> handleHash = new Dictionary<TreePosition,GCHandle> ();
+ Dictionary<TreePosition,NodeData> handleHash = new Dictionary<TreePosition,NodeData> ();
Type[] colTypes;
Gtk.TreeModelAdapter adapter;
-
+ int stamp;
+
+ /// <summary>
+ /// Stores information about the index of a noda and the GCHandle
+ /// used by the native reference
+ /// </summary>
+ class NodeData
+ {
+ public GCHandle Handle;
+ public int Index;
+ }
+
public CustomTreeModel (ITreeDataSource source)
{
this.source = source;
@@ -57,55 +67,125 @@ namespace Xwt.GtkBackend
public Gtk.TreeModelAdapter Store {
get { return adapter; }
}
-
- public IntPtr Handle {
- get {
- return IntPtr.Zero;
- }
- }
-
- Gtk.TreeIter IterFromNode (TreePosition node)
+
+ public Gtk.TreeIter IterFromNode (TreePosition node, int index = -1)
{
- GCHandle gch;
- if (!handleHash.TryGetValue (node, out gch)) {
- gch = GCHandle.Alloc (node);
- handleHash [node] = gch;
- nodeHash [gch] = node;
+ // The returned iter will have a reference to the TreePosition
+ // through a GCHandle. At the same time we store a NodeData
+ // object to a hashtable, which contains the GCHandle and
+ // the index of the node
+
+ NodeData data;
+ if (!handleHash.TryGetValue (node, out data)) {
+ // It is the first time the node is referenced.
+ // Store it in handleHash.
+ handleHash [node] = data = new NodeData {
+ Handle = GCHandle.Alloc (node),
+ Index = index
+ };
}
+
+ // If the index of the node changed, update it in its NodeData
+ if (index != -1 && index != data.Index)
+ data.Index = index;
+
Gtk.TreeIter result = Gtk.TreeIter.Zero;
- result.UserData = (IntPtr)gch;
+ result.UserData = (IntPtr)data.Handle;
+ result.Stamp = stamp;
return result;
}
+
+ int GetCachedIndex (TreePosition node)
+ {
+ // Gets the index of a node, or -1 if that information is not available
+ NodeData data;
+ if (handleHash.TryGetValue (node, out data))
+ return data.Index;
+ return -1;
+ }
- TreePosition NodeFromIter (Gtk.TreeIter iter)
+ void CacheIndex (TreePosition node, int index)
{
- TreePosition node;
- GCHandle gch = (GCHandle)iter.UserData;
- nodeHash.TryGetValue (gch, out node);
- return node;
+ // Stores the index of a node
+ NodeData data;
+ if (handleHash.TryGetValue (node, out data))
+ data.Index = index;
}
+ public bool NodeFromIter (Gtk.TreeIter iter, out TreePosition pos)
+ {
+ if (iter.UserData == IntPtr.Zero) {
+ pos = null;
+ return true;
+ }
+ if (iter.Stamp != stamp) {
+ // The iter has been invalidated
+ pos = null;
+ return false;
+ }
+ GCHandle gch = GCHandle.FromIntPtr (iter.UserData);
+ pos = (TreePosition) gch.Target;
+ return true;
+ }
+
#region TreeModelImplementor implementation
void HandleNodesReordered (object sender, TreeNodeOrderEventArgs e)
{
- Gtk.TreeIter it = IterFromNode (e.Node);
- adapter.EmitRowsReordered (GetPath (it), it, e.ChildrenOrder);
+ // Don't increase the stamp because no nodes have been removed, so all iters are still valid
+ // Update the cached indexes for all the children that have chanfed
+ for (int n = 0; n < e.ChildrenOrder.Length; n++) {
+ if (e.ChildrenOrder [n] != n) {
+ var child = source.GetChild (e.Node, n);
+ if (child != null)
+ CacheIndex (child, n);
+ }
+ }
+ adapter.EmitRowsReordered (GetPath (e.Node), IterFromNode (e.Node), e.ChildrenOrder);
}
void HandleNodeInserted (object sender, TreeNodeEventArgs e)
{
- Gtk.TreeIter it = IterFromNode (e.Node);
- adapter.EmitRowInserted (GetPath (it), it);
+ // Don't increase the stamp because no nodes have been removed, so all iters are still valid
+ // Update the cached indexes
+ var parent = source.GetParent (e.Node);
+ var count = source.GetChildrenCount (parent);
+ for (int n = e.ChildIndex + 1; n < count; n++) {
+ var child = source.GetChild (parent, n);
+ if (child != null)
+ CacheIndex (child, n);
+ }
+ var iter = IterFromNode (e.Node, e.ChildIndex);
+ adapter.EmitRowInserted (GetPath (e.Node), iter);
}
void HandleNodeDeleted (object sender, TreeNodeChildEventArgs e)
{
+ if (e.Node != null && !handleHash.ContainsKey (e.Node))
+ return;
+
+ NodeData data;
+ if (e.Child != null && handleHash.TryGetValue (e.Child, out data)) {
+ // Increase the model stamp since the node is gone and there may
+ // be iters referencing that node. Increasing the stamp will
+ // invalidate all those nodes
+ stamp++;
+ data.Handle.Free ();
+ handleHash.Remove (e.Child);
+
+ // Update the indexes of the node following the deleted one
+ var count = source.GetChildrenCount (e.Node);
+ for (int n = e.ChildIndex; n < count; n++) {
+ var child = source.GetChild (e.Node, n);
+ if (child != null)
+ CacheIndex (child, n);
+ }
+ }
+
if (e.Node == null) {
adapter.EmitRowDeleted (new Gtk.TreePath (new int[] { e.ChildIndex }));
} else {
- Gtk.TreeIter it = IterFromNode (e.Node);
- var p = GetPath (it);
+ var p = GetPath (e.Node);
int[] idx = new int [p.Indices.Length + 1];
p.Indices.CopyTo (idx, 0);
idx [idx.Length - 1] = e.ChildIndex;
@@ -115,8 +195,8 @@ namespace Xwt.GtkBackend
void HandleNodeChanged (object sender, TreeNodeEventArgs e)
{
- Gtk.TreeIter it = IterFromNode (e.Node);
- adapter.EmitRowChanged (GetPath (it), it);
+ if (handleHash.ContainsKey (e.Node))
+ adapter.EmitRowChanged (GetPath (e.Node), IterFromNode (e.Node));
}
public GLib.GType GetColumnType (int index)
@@ -136,13 +216,20 @@ namespace Xwt.GtkBackend
if (pos == null)
return false;
}
- iter = IterFromNode (pos);
+ iter = IterFromNode (pos, indices[indices.Length - 1]);
return true;
}
public Gtk.TreePath GetPath (Gtk.TreeIter iter)
{
- TreePosition pos = NodeFromIter (iter);
+ TreePosition pos;
+ if (NodeFromIter (iter, out pos))
+ return GetPath (pos);
+ return Gtk.TreePath.NewFirst ();
+ }
+
+ public Gtk.TreePath GetPath (TreePosition pos)
+ {
List<int> idx = new List<int> ();
while (pos != null) {
var parent = source.GetParent (pos);
@@ -155,34 +242,49 @@ namespace Xwt.GtkBackend
int GetIndex (TreePosition parent, TreePosition pos)
{
+ var res = GetCachedIndex (pos);
+ if (res != -1)
+ return res;
+
int n = 0;
do {
var c = source.GetChild (parent, n);
if (c == null)
return -1;
- if (c == pos || c.Equals(pos))
+ if (c == pos || c.Equals (pos)) {
+ CacheIndex (pos, n);
return n;
+ }
n++;
} while (true);
}
public void GetValue (Gtk.TreeIter iter, int column, ref GLib.Value value)
{
- TreePosition pos = NodeFromIter (iter);
+ TreePosition pos;
+ if (!NodeFromIter (iter, out pos)) {
+ value = GLib.Value.Empty;
+ return;
+ }
var v = source.GetValue (pos, column);
- value = new GLib.Value (v);
+ if (v == null)
+ value = GLib.Value.Empty;
+ else
+ value = new GLib.Value (v);
}
public bool IterNext (ref Gtk.TreeIter iter)
{
- TreePosition pos = NodeFromIter (iter);
+ TreePosition pos;
+ if (!NodeFromIter (iter, out pos))
+ return false;
TreePosition parent = source.GetParent (pos);
int i = GetIndex (parent, pos);
- if (source.GetChildrenCount (parent) >= i)
+ if (i == -1)
return false;
pos = source.GetChild (parent, i + 1);
if (pos != null) {
- iter = IterFromNode (pos);
+ iter = IterFromNode (pos, i + 1);
return true;
} else
return false;
@@ -191,12 +293,14 @@ namespace Xwt.GtkBackend
#if XWT_GTK3
public bool IterPrevious (ref Gtk.TreeIter iter)
{
- TreePosition pos = NodeFromIter (iter);
+ TreePosition pos;
+ if (!NodeFromIter (iter, out pos))
+ return false;
TreePosition parent = source.GetParent (pos);
int i = GetIndex (parent, pos);
pos = source.GetChild (parent, i - 1);
if (pos != null) {
- iter = IterFromNode (pos);
+ iter = IterFromNode (pos, i - 1);
return true;
} else
return false;
@@ -205,11 +309,13 @@ namespace Xwt.GtkBackend
public bool IterChildren (out Gtk.TreeIter iter, Gtk.TreeIter parent)
{
- iter = Gtk.TreeIter.Zero;
- TreePosition pos = NodeFromIter (parent);
+ iter = parent;
+ TreePosition pos;
+ if (!NodeFromIter (parent, out pos))
+ return false;
pos = source.GetChild (pos, 0);
if (pos != null) {
- iter = IterFromNode (pos);
+ iter = IterFromNode (pos, 0);
return true;
} else
return false;
@@ -217,23 +323,29 @@ namespace Xwt.GtkBackend
public bool IterHasChild (Gtk.TreeIter iter)
{
- TreePosition pos = NodeFromIter (iter);
+ TreePosition pos;
+ if (!NodeFromIter (iter, out pos))
+ return false;
return source.GetChildrenCount (pos) != 0;
}
public int IterNChildren (Gtk.TreeIter iter)
{
- TreePosition pos = NodeFromIter (iter);
+ TreePosition pos;
+ if (!NodeFromIter (iter, out pos))
+ return 0;
return source.GetChildrenCount (pos);
}
public bool IterNthChild (out Gtk.TreeIter iter, Gtk.TreeIter parent, int n)
{
- iter = Gtk.TreeIter.Zero;
- TreePosition pos = NodeFromIter (parent);
+ iter = parent;
+ TreePosition pos;
+ if (!NodeFromIter (parent, out pos))
+ return false;
pos = source.GetChild (pos, n);
if (pos != null) {
- iter = IterFromNode (pos);
+ iter = IterFromNode (pos, n);
return true;
} else
return false;
@@ -241,9 +353,12 @@ namespace Xwt.GtkBackend
public bool IterParent (out Gtk.TreeIter iter, Gtk.TreeIter child)
{
- iter = Gtk.TreeIter.Zero;
- TreePosition pos = NodeFromIter (iter);
- pos = source.GetParent (pos);
+ iter = child;
+ TreePosition pos;
+ if (!NodeFromIter (iter, out pos))
+ return false;
+ if (pos != null)
+ pos = source.GetParent (pos);
if (pos != null) {
iter = IterFromNode (pos);
return true;
diff --git a/Xwt.Gtk/Xwt.GtkBackend/GtkInterop.cs b/Xwt.Gtk/Xwt.GtkBackend/GtkInterop.cs
index 7aceea0d..edcf3030 100644
--- a/Xwt.Gtk/Xwt.GtkBackend/GtkInterop.cs
+++ b/Xwt.Gtk/Xwt.GtkBackend/GtkInterop.cs
@@ -228,10 +228,12 @@ namespace Xwt.GtkBackend
}
}
- internal class TextIndexer
+ public class TextIndexer
{
- int[] indexToByteIndex;
- int[] byteIndexToIndex;
+ static readonly List<int> emptyList = new List<int> ();
+ static readonly int [] emptyArray = new int [0];
+ int [] indexToByteIndex;
+ List<int> byteIndexToIndex;
public TextIndexer (string text)
{
@@ -244,7 +246,7 @@ namespace Xwt.GtkBackend
// if the index exceeds the byte index range, return the last byte index + 1
// telling pango to span the attribute to the end of the string
// this happens if the string contains multibyte characters
- return indexToByteIndex[i-1] + 1;
+ return indexToByteIndex[indexToByteIndex.Length-1] + 1;
return indexToByteIndex[i];
}
@@ -255,24 +257,27 @@ namespace Xwt.GtkBackend
public void SetupTables (string text)
{
- if (text == null) {
- this.indexToByteIndex = new int[0];
- this.byteIndexToIndex = new int[0];
+ if (string.IsNullOrEmpty (text)) {
+ this.indexToByteIndex = emptyArray;
+ this.byteIndexToIndex = emptyList;
return;
}
- var arr = text.ToCharArray ();
int byteIndex = 0;
- int[] indexToByteIndex = new int[arr.Length];
- var byteIndexToIndex = new List<int> ();
- for (int i = 0; i < arr.Length; i++) {
- indexToByteIndex[i] = byteIndex;
- byteIndex += System.Text.Encoding.UTF8.GetByteCount (arr, i, 1);
- while (byteIndexToIndex.Count < byteIndex)
- byteIndexToIndex.Add (i);
+ int [] indexToByteIndex = new int [text.Length];
+ var byteIndexToIndex = new System.Collections.Generic.List<int> (text.Length);
+ unsafe {
+ fixed (char* p = text) {
+ for (int i = 0; i < text.Length; i++) {
+ indexToByteIndex[i] = byteIndex;
+ byteIndex += System.Text.Encoding.UTF8.GetByteCount (p + i, 1);
+ while (byteIndexToIndex.Count < byteIndex)
+ byteIndexToIndex.Add (i);
+ }
+ }
}
this.indexToByteIndex = indexToByteIndex;
- this.byteIndexToIndex = byteIndexToIndex.ToArray ();
+ this.byteIndexToIndex = byteIndexToIndex;
}
}
}
diff --git a/Xwt.Gtk/Xwt.GtkBackend/GtkWorkarounds.cs b/Xwt.Gtk/Xwt.GtkBackend/GtkWorkarounds.cs
index 3808b2fa..9247d858 100644
--- a/Xwt.Gtk/Xwt.GtkBackend/GtkWorkarounds.cs
+++ b/Xwt.Gtk/Xwt.GtkBackend/GtkWorkarounds.cs
@@ -554,11 +554,11 @@ namespace Xwt.GtkBackend
//introduced in GTK 2.20
[DllImport (GtkInterop.LIBGDK, CallingConvention = CallingConvention.Cdecl)]
- extern static bool gdk_keymap_add_virtual_modifiers (IntPtr keymap, ref Gdk.ModifierType state);
+ extern static void gdk_keymap_add_virtual_modifiers (IntPtr keymap, ref Gdk.ModifierType state);
//Custom patch in Mono Mac w/GTK+ 2.24.8+
[DllImport (GtkInterop.LIBGDK, CallingConvention = CallingConvention.Cdecl)]
- extern static bool gdk_quartz_set_fix_modifiers (bool fix);
+ extern static void gdk_quartz_set_fix_modifiers (bool fix);
static Gdk.Keymap keymap = Gdk.Keymap.Default;
static Dictionary<ulong,MappedKeys> mappedKeys = new Dictionary<ulong,MappedKeys> ();
diff --git a/Xwt.Gtk/Xwt.GtkBackend/MenuItemBackend.cs b/Xwt.Gtk/Xwt.GtkBackend/MenuItemBackend.cs
index 960e114e..c7f6ee05 100644
--- a/Xwt.Gtk/Xwt.GtkBackend/MenuItemBackend.cs
+++ b/Xwt.Gtk/Xwt.GtkBackend/MenuItemBackend.cs
@@ -126,6 +126,15 @@ namespace Xwt.GtkBackend
}
}
+ public string TooltipText {
+ get {
+ return item.TooltipText;
+ }
+ set {
+ item.TooltipText = value;
+ }
+ }
+
public bool UseMnemonic {
get { return label.UseUnderline; }
set { label.UseUnderline = value; }
diff --git a/Xwt.Gtk/Xwt.GtkBackend/RichTextViewBackend.cs b/Xwt.Gtk/Xwt.GtkBackend/RichTextViewBackend.cs
index f85d6b79..88977cbd 100755
--- a/Xwt.Gtk/Xwt.GtkBackend/RichTextViewBackend.cs
+++ b/Xwt.Gtk/Xwt.GtkBackend/RichTextViewBackend.cs
@@ -584,7 +584,10 @@ namespace Xwt.GtkBackend
{
if (!IsRealized)
return;
- var color = (Gdk.Color) StyleGetProperty ("link-color");
+ var objColor = StyleGetProperty ("link-color");
+ var color = Gdk.Color.Zero;
+ if (objColor != null)
+ color = (Gdk.Color) objColor;
if (color.Equals (Gdk.Color.Zero))
color = Toolkit.CurrentEngine.Defaults.FallbackLinkColor.ToGtkValue ();
if (Buffer != null)
diff --git a/Xwt.Gtk/Xwt.GtkBackend/TableViewBackend.cs b/Xwt.Gtk/Xwt.GtkBackend/TableViewBackend.cs
index 72de4f73..3e16979e 100644
--- a/Xwt.Gtk/Xwt.GtkBackend/TableViewBackend.cs
+++ b/Xwt.Gtk/Xwt.GtkBackend/TableViewBackend.cs
@@ -29,6 +29,7 @@ using Xwt.Backends;
using Gtk;
using System.Collections.Generic;
using System.Linq;
+using Gdk;
#if XWT_GTK3
using TreeModel = Gtk.ITreeModel;
#endif
@@ -238,6 +239,7 @@ namespace Xwt.GtkBackend
public void SetSelectionMode (SelectionMode mode)
{
switch (mode) {
+ case SelectionMode.None: Widget.Selection.Mode = Gtk.SelectionMode.None; break;
case SelectionMode.Single: Widget.Selection.Mode = Gtk.SelectionMode.Single; break;
case SelectionMode.Multiple: Widget.Selection.Mode = Gtk.SelectionMode.Multiple; break;
}
@@ -463,19 +465,36 @@ namespace Xwt.GtkBackend
{
Gtk.TreeViewColumn col;
Gtk.TreePath path;
- int cellx, celly;
+ int _cellx, _celly;
cx = cy = 0;
it = Gtk.TreeIter.Zero;
- if (!Widget.GetPathAtPos (ex, ey, out path, out col, out cellx, out celly))
+ if (!Widget.GetPathAtPos (ex, ey, out path, out col, out _cellx, out _celly))
return false;
- if (!Widget.Model.GetIterFromString (out it, path.ToString ()))
+ if (!Widget.Model.GetIter (out it, path))
return false;
- int sp, w;
- if (col.CellGetPosition (r, out sp, out w)) {
- if (cellx >= sp && cellx < sp + w) {
+ var cellArea = Widget.GetCellArea (path, col);
+ var cellx = ex - cellArea.X;
+
+ var renderers = col.GetCellRenderers ();
+ int i = Array.IndexOf (renderers, r);
+
+ int rendererX, rendererWidth;
+ if (col.CellGetPosition (r, out rendererX, out rendererWidth)) {
+ if (i < renderers.Length - 1) {
+ int nextX, _w;
+ // The width returned by CellGetPosition is not reliable. Calculate the width
+ // by getting the position of the next renderer.
+ if (col.CellGetPosition (renderers [i + 1], out nextX, out _w))
+ rendererWidth = nextX - rendererX;
+ } else {
+ // Last renderer of the column. Its width is what's left in the cell area.
+ rendererWidth = cellArea.Width - rendererX;
+ }
+
+ if (cellx >= rendererX && cellx < rendererX + rendererWidth) {
Widget.ConvertBinWindowToWidgetCoords (ex, ey, out cx, out cy);
return true;
}
@@ -492,16 +511,26 @@ namespace Xwt.GtkBackend
Widget.QueueDrawArea (x, y, r.Width, r.Height);
}
+ public void QueueResize (object target, Gtk.TreeIter iter)
+ {
+ var path = Widget.Model.GetPath (iter);
+ Widget.Model.EmitRowChanged (path, iter);
+ }
+
#endregion
}
class CustomTreeView: Gtk.TreeView
{
WidgetBackend backend;
-
+ TreePath delayedSelection;
+ TreeViewColumn delayedSelectionColumn;
+
public CustomTreeView (WidgetBackend b)
{
backend = b;
+ base.DragBegin += (_, __) =>
+ delayedSelection = null;
}
static CustomTreeView ()
@@ -514,6 +543,44 @@ namespace Xwt.GtkBackend
GtkWorkarounds.RemoveKeyBindingFromClass (Gtk.TreeView.GType, Gdk.Key.BackSpace, Gdk.ModifierType.None);
}
+ protected override bool OnButtonPressEvent (EventButton evnt)
+ {
+ if (Selection.Mode == Gtk.SelectionMode.Multiple) {
+ // If we are clicking on already selected row, delay the selection until we are certain that
+ // the user is not starting a DragDrop operation.
+ // This is needed to allow user to drag multiple selected rows.
+ TreePath treePath;
+ TreeViewColumn column;
+ GetPathAtPos ((int)evnt.X, (int)evnt.Y, out treePath, out column);
+
+ var ctrlShiftMask = (evnt.State & (Gdk.ModifierType.ShiftMask | Gdk.ModifierType.ControlMask | Gdk.ModifierType.Mod2Mask));
+ if (treePath != null && evnt.Button == 1 && this.Selection.PathIsSelected (treePath) && this.Selection.CountSelectedRows() > 1 && ctrlShiftMask == 0) {
+ delayedSelection = treePath;
+ delayedSelectionColumn = column;
+ Selection.SelectFunction = (_, __, ___, ____) => false;
+ var result = false;
+ try {
+ result = base.OnButtonPressEvent (evnt);
+ } finally {
+ Selection.SelectFunction = (_, __, ___, ____) => true;
+ }
+ return result;
+ }
+ }
+ return base.OnButtonPressEvent (evnt);
+ }
+
+ protected override bool OnButtonReleaseEvent (EventButton evnt)
+ {
+ // Now, if mouse hadn't moved, we are certain that this was just a click. Proceed as usual.
+ if (delayedSelection != null) {
+ SetCursor (delayedSelection, delayedSelectionColumn, false);
+ delayedSelection = null;
+ delayedSelectionColumn = null;
+ }
+ return base.OnButtonReleaseEvent (evnt);
+ }
+
protected override void OnDragDataDelete (Gdk.DragContext context)
{
// This method is override to avoid the default implementation
@@ -523,4 +590,3 @@ namespace Xwt.GtkBackend
}
}
}
-
diff --git a/Xwt.Gtk/Xwt.GtkBackend/TextLayoutBackendHandler.cs b/Xwt.Gtk/Xwt.GtkBackend/TextLayoutBackendHandler.cs
index 842e94c8..eedfa976 100644
--- a/Xwt.Gtk/Xwt.GtkBackend/TextLayoutBackendHandler.cs
+++ b/Xwt.Gtk/Xwt.GtkBackend/TextLayoutBackendHandler.cs
@@ -198,7 +198,7 @@ namespace Xwt.GtkBackend
{
var tl = (PangoBackend) backend;
int index, trailing;
- tl.Layout.XyToIndex ((int)x, (int)y, out index, out trailing);
+ tl.Layout.XyToIndex (Pango.Units.FromPixels ((int)x), Pango.Units.FromPixels ((int)y), out index, out trailing);
return tl.TextIndexer.ByteIndexToIndex (index);
}
diff --git a/Xwt.Gtk/Xwt.GtkBackend/TreeStoreBackend.cs b/Xwt.Gtk/Xwt.GtkBackend/TreeStoreBackend.cs
index fba2ba64..efdbe50e 100644
--- a/Xwt.Gtk/Xwt.GtkBackend/TreeStoreBackend.cs
+++ b/Xwt.Gtk/Xwt.GtkBackend/TreeStoreBackend.cs
@@ -71,6 +71,7 @@ namespace Xwt.GtkBackend
public event EventHandler<TreeNodeChildEventArgs> NodeDeleted;
public event EventHandler<TreeNodeEventArgs> NodeChanged;
public event EventHandler<TreeNodeOrderEventArgs> NodesReordered;
+ public event EventHandler Cleared;
IterPos GetIterPos (TreePosition pos)
{
@@ -86,6 +87,8 @@ namespace Xwt.GtkBackend
{
version++;
Tree.Clear ();
+ if (Cleared != null)
+ Cleared (this, EventArgs.Empty);
}
public TreePosition GetChild (TreePosition pos, int index)
@@ -212,7 +215,7 @@ namespace Xwt.GtkBackend
IterPos tpos = GetIterPos (pos);
Gtk.TreeIter it = tpos.Iter;
var delPath = Tree.GetPath (it);
- var eventArgs = new TreeNodeChildEventArgs (GetParent (tpos), delPath.Indices[delPath.Indices.Length - 1]);
+ var eventArgs = new TreeNodeChildEventArgs (GetParent (tpos), delPath.Indices[delPath.Indices.Length - 1], pos);
Tree.Remove (ref it);
if (NodeDeleted != null)
NodeDeleted (this, eventArgs);
diff --git a/Xwt.Gtk/Xwt.GtkBackend/TreeViewBackend.cs b/Xwt.Gtk/Xwt.GtkBackend/TreeViewBackend.cs
index 6783094c..b43324e3 100644
--- a/Xwt.Gtk/Xwt.GtkBackend/TreeViewBackend.cs
+++ b/Xwt.Gtk/Xwt.GtkBackend/TreeViewBackend.cs
@@ -34,6 +34,7 @@ namespace Xwt.GtkBackend
{
Gtk.TreePath autoExpandPath;
uint expandTimer;
+ CustomTreeModel customModel;
protected new ITreeViewEventSink EventSink {
get { return (ITreeViewEventSink)base.EventSink; }
@@ -91,9 +92,9 @@ namespace Xwt.GtkBackend
{
Gtk.TreeIter it;
if (Widget.Model.GetIter (out it, args.Path)) {
- CurrentEventRow = new IterPos (-1, it);
+ CurrentEventRow = GetPositionFromIter (-1, it);
ApplicationContext.InvokeUserCode (delegate {
- EventSink.OnRowExpanded (new IterPos (-1, it));
+ EventSink.OnRowExpanded (GetPositionFromIter (-1, it));
});
}
}
@@ -102,9 +103,9 @@ namespace Xwt.GtkBackend
{
Gtk.TreeIter it;
if (Widget.Model.GetIter (out it, args.Path)) {
- CurrentEventRow = new IterPos (-1, it);
+ CurrentEventRow = GetPositionFromIter (-1, it);
ApplicationContext.InvokeUserCode (delegate {
- EventSink.OnRowExpanding (new IterPos (-1, it));
+ EventSink.OnRowExpanding (GetPositionFromIter (-1, it));
});
}
}
@@ -113,9 +114,9 @@ namespace Xwt.GtkBackend
{
Gtk.TreeIter it;
if (Widget.Model.GetIter (out it, args.Path)) {
- CurrentEventRow = new IterPos (-1, it);
+ CurrentEventRow = GetPositionFromIter (-1, it);
ApplicationContext.InvokeUserCode (delegate {
- EventSink.OnRowCollapsed (new IterPos (-1, it));
+ EventSink.OnRowCollapsed (GetPositionFromIter (-1, it));
});
}
}
@@ -124,9 +125,9 @@ namespace Xwt.GtkBackend
{
Gtk.TreeIter it;
if (Widget.Model.GetIter (out it, args.Path)) {
- CurrentEventRow = new IterPos (-1, it);
+ CurrentEventRow = GetPositionFromIter (-1, it);
ApplicationContext.InvokeUserCode (delegate {
- EventSink.OnRowCollapsing (new IterPos (-1, it));
+ EventSink.OnRowCollapsing (GetPositionFromIter (-1, it));
});
}
}
@@ -135,9 +136,9 @@ namespace Xwt.GtkBackend
{
Gtk.TreeIter it;
if (Widget.Model.GetIter (out it, args.Path)) {
- CurrentEventRow = new IterPos (-1, it);
+ CurrentEventRow = GetPositionFromIter (-1, it);
ApplicationContext.InvokeUserCode (delegate {
- EventSink.OnRowActivated (new IterPos (-1, it));
+ EventSink.OnRowActivated (GetPositionFromIter (-1, it));
});
}
}
@@ -195,21 +196,24 @@ namespace Xwt.GtkBackend
public void SetSource (ITreeDataSource source, IBackend sourceBackend)
{
TreeStoreBackend b = sourceBackend as TreeStoreBackend;
+ customModel = null;
if (b == null) {
- CustomTreeModel model = new CustomTreeModel (source);
- Widget.Model = model.Store;
+ customModel = new CustomTreeModel (source);
+ Widget.Model = customModel.Store;
} else
Widget.Model = b.Store;
}
+ public bool AnimationsEnabled { get; set; }
+
public TreePosition[] SelectedRows {
get {
var rows = Widget.Selection.GetSelectedRows ();
- IterPos[] sel = new IterPos [rows.Length];
+ TreePosition[] sel = new TreePosition [rows.Length];
for (int i = 0; i < rows.Length; i++) {
Gtk.TreeIter it;
Widget.Model.GetIter (out it, rows[i]);
- sel[i] = new IterPos (-1, it);
+ sel[i] = GetPositionFromIter (-1, it);
}
return sel;
}
@@ -223,13 +227,13 @@ namespace Xwt.GtkBackend
Gtk.TreeIter it;
if (path != null && Widget.Model.GetIter (out it, path))
- return new IterPos (-1, it);
+ return GetPositionFromIter (-1, it);
return null;
}
set {
Gtk.TreePath path = new Gtk.TreePath(new [] { int.MaxValue }); // set invalid path to unfocus
if (value != null)
- path = Widget.Model.GetPath (((IterPos)value).Iter);
+ path = Widget.Model.GetPath (GetIterFromPosition (value));
Widget.SetCursor (path, null, false);
}
}
@@ -241,44 +245,53 @@ namespace Xwt.GtkBackend
public void SelectRow (TreePosition pos)
{
- Widget.Selection.SelectIter (((IterPos)pos).Iter);
+ Widget.Selection.SelectIter (GetIterFromPosition (pos));
}
public void UnselectRow (TreePosition pos)
{
- Widget.Selection.UnselectIter (((IterPos)pos).Iter);
+ Widget.Selection.UnselectIter (GetIterFromPosition (pos));
}
public bool IsRowSelected (TreePosition pos)
{
- return Widget.Selection.IterIsSelected (((IterPos)pos).Iter);
+ return Widget.Selection.IterIsSelected (GetIterFromPosition (pos));
}
public bool IsRowExpanded (TreePosition pos)
{
- return Widget.GetRowExpanded (Widget.Model.GetPath (((IterPos)pos).Iter));
+ return Widget.GetRowExpanded (Widget.Model.GetPath (GetIterFromPosition (pos)));
}
public void ExpandRow (TreePosition pos, bool expandedChildren)
{
- Widget.ExpandRow (Widget.Model.GetPath (((IterPos)pos).Iter), expandedChildren);
+ Widget.ExpandRow (Widget.Model.GetPath (GetIterFromPosition (pos)), expandedChildren);
}
public void CollapseRow (TreePosition pos)
{
- Widget.CollapseRow (Widget.Model.GetPath (((IterPos)pos).Iter));
+ Widget.CollapseRow (Widget.Model.GetPath (GetIterFromPosition (pos)));
}
public void ScrollToRow (TreePosition pos)
{
- ScrollToRow (((IterPos)pos).Iter);
+ ScrollToRow (GetIterFromPosition (pos));
}
public void ExpandToRow (TreePosition pos)
{
- Widget.ExpandToPath (Widget.Model.GetPath (((IterPos)pos).Iter));
+ Widget.ExpandToPath (Widget.Model.GetPath (GetIterFromPosition (pos)));
}
-
+
+ public bool BorderVisible {
+ get {
+ return ScrolledWindow.ShadowType != Gtk.ShadowType.None;
+ }
+ set {
+ ScrolledWindow.ShadowType = value ? Gtk.ShadowType.In : Gtk.ShadowType.None;
+ }
+ }
+
public bool HeadersVisible {
get {
return Widget.HeadersVisible;
@@ -287,6 +300,15 @@ namespace Xwt.GtkBackend
Widget.HeadersVisible = value;
}
}
+
+ public bool UseAlternatingRowColors {
+ get {
+ return Widget.RulesHint;
+ }
+ set {
+ Widget.RulesHint = value;
+ }
+ }
public bool GetDropTargetRow (double x, double y, out RowDropPosition pos, out TreePosition nodePosition)
{
@@ -300,7 +322,7 @@ namespace Xwt.GtkBackend
Gtk.TreeIter it;
Widget.Model.GetIter (out it, path);
- nodePosition = new IterPos (-1, it);
+ nodePosition = GetPositionFromIter (-1, it);
switch (tpos) {
case Gtk.TreeViewDropPosition.After: pos = RowDropPosition.After; break;
case Gtk.TreeViewDropPosition.Before: pos = RowDropPosition.Before; break;
@@ -315,7 +337,7 @@ namespace Xwt.GtkBackend
if (path != null) {
Gtk.TreeIter iter;
Widget.Model.GetIter (out iter, path);
- return new IterPos (-1, iter);
+ return GetPositionFromIter (-1, iter);
}
return null;
}
@@ -324,7 +346,7 @@ namespace Xwt.GtkBackend
{
var col = GetCellColumn (cell);
var cr = GetCellRenderer (cell);
- Gtk.TreeIter iter = ((IterPos)pos).Iter;
+ Gtk.TreeIter iter = GetIterFromPosition (pos);
var rect = includeMargin ? ((ICellRendererTarget)this).GetCellBackgroundBounds (col, cr, iter) : ((ICellRendererTarget)this).GetCellBounds (col, cr, iter);
return rect;
@@ -332,7 +354,7 @@ namespace Xwt.GtkBackend
public Rectangle GetRowBounds (TreePosition pos, bool includeMargin)
{
- Gtk.TreeIter iter = ((IterPos)pos).Iter;
+ Gtk.TreeIter iter = GetIterFromPosition (pos);
Rectangle rect = includeMargin ? GetRowBackgroundBounds (iter) : GetRowBounds (iter);
return rect;
}
@@ -351,6 +373,23 @@ namespace Xwt.GtkBackend
CurrentEventRow = toggledItem;
}
+
+ TreePosition GetPositionFromIter (int treeVersion, Gtk.TreeIter iter)
+ {
+ if (customModel != null) {
+ TreePosition pos;
+ customModel.NodeFromIter (iter, out pos);
+ return pos;
+ }
+ return new IterPos (treeVersion, iter);
+ }
+
+ Gtk.TreeIter GetIterFromPosition (TreePosition position)
+ {
+ if (customModel != null)
+ return customModel.IterFromNode (position);
+ return ((IterPos)position).Iter;
+ }
}
}
diff --git a/Xwt.Gtk/Xwt.GtkBackend/WidgetBackend.cs b/Xwt.Gtk/Xwt.GtkBackend/WidgetBackend.cs
index c78649ed..97a8f983 100644
--- a/Xwt.Gtk/Xwt.GtkBackend/WidgetBackend.cs
+++ b/Xwt.Gtk/Xwt.GtkBackend/WidgetBackend.cs
@@ -192,6 +192,8 @@ namespace Xwt.GtkBackend
ctype = Gdk.CursorType.Crosshair;
else if (cursor == CursorType.Hand)
ctype = Gdk.CursorType.Hand1;
+ else if (cursor == CursorType.Hand2 || cursor == CursorType.DragCopy)
+ ctype = Gdk.CursorType.Hand2;
else if (cursor == CursorType.IBeam)
ctype = Gdk.CursorType.Xterm;
else if (cursor == CursorType.ResizeDown)
@@ -206,6 +208,16 @@ namespace Xwt.GtkBackend
ctype = Gdk.CursorType.SbHDoubleArrow;
else if (cursor == CursorType.ResizeUpDown)
ctype = Gdk.CursorType.SbVDoubleArrow;
+ else if (cursor == CursorType.ResizeNE)
+ ctype = Gdk.CursorType.TopRightCorner;
+ else if (cursor == CursorType.ResizeNW)
+ ctype = Gdk.CursorType.TopLeftCorner;
+ else if (cursor == CursorType.ResizeSE)
+ ctype = Gdk.CursorType.BottomRightCorner;
+ else if (cursor == CursorType.ResizeSW)
+ ctype = Gdk.CursorType.BottomLeftCorner;
+ else if (cursor == CursorType.NotAllowed)
+ ctype = Gdk.CursorType.XCursor;
else if (cursor == CursorType.Move)
ctype = Gdk.CursorType.Fleur;
else if (cursor == CursorType.Wait)
diff --git a/Xwt.WPF/Xwt.WPF.csproj b/Xwt.WPF/Xwt.WPF.csproj
index 29498b6b..3661be48 100644
--- a/Xwt.WPF/Xwt.WPF.csproj
+++ b/Xwt.WPF/Xwt.WPF.csproj
@@ -19,6 +19,7 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>False</ConsolePause>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@@ -28,6 +29,7 @@
<WarningLevel>4</WarningLevel>
<ConsolePause>False</ConsolePause>
<DebugSymbols>true</DebugSymbols>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup>
<SignAssembly>True</SignAssembly>
diff --git a/Xwt.WPF/Xwt.WPFBackend.CellViews/CanvasCellViewBackend.cs b/Xwt.WPF/Xwt.WPFBackend.CellViews/CanvasCellViewBackend.cs
index b6dc8dcb..99339464 100644
--- a/Xwt.WPF/Xwt.WPFBackend.CellViews/CanvasCellViewBackend.cs
+++ b/Xwt.WPF/Xwt.WPFBackend.CellViews/CanvasCellViewBackend.cs
@@ -14,6 +14,11 @@ namespace Xwt.WPFBackend
CurrentElement.InvalidateVisual ();
}
+ public void QueueResize ()
+ {
+ CurrentElement.InvalidateVisual ();
+ }
+
public bool IsHighlighted {
get {
return false;
diff --git a/Xwt.WPF/Xwt.WPFBackend/ButtonBackend.cs b/Xwt.WPF/Xwt.WPFBackend/ButtonBackend.cs
index 067c86a5..3f1d1583 100644
--- a/Xwt.WPF/Xwt.WPFBackend/ButtonBackend.cs
+++ b/Xwt.WPF/Xwt.WPFBackend/ButtonBackend.cs
@@ -135,6 +135,18 @@ namespace Xwt.WPFBackend
set { Button.Foreground = ResPool.GetSolidBrush (value.ToWpfColor()); }
}
+ bool isDefault;
+ public virtual bool IsDefault {
+ get { return (Button as SWC.Button)?.IsDefault ?? isDefault; }
+ set {
+ var button = Button as SWC.Button;
+ if (button != null)
+ button.IsDefault = value;
+ else
+ isDefault = value; // just cache the value
+ }
+ }
+
public override void EnableEvent (object eventId)
{
base.EnableEvent (eventId);
diff --git a/Xwt.WPF/Xwt.WPFBackend/ExTreeView.cs b/Xwt.WPF/Xwt.WPFBackend/ExTreeView.cs
index 2abaa221..3af650e3 100644
--- a/Xwt.WPF/Xwt.WPFBackend/ExTreeView.cs
+++ b/Xwt.WPF/Xwt.WPFBackend/ExTreeView.cs
@@ -250,7 +250,7 @@ namespace Xwt.WPFBackend
HashSet<object> oldItems = (e.OldItems != null)
? new HashSet<object> (e.OldItems.Cast<object> ())
: new HashSet<object> ();
-
+
TraverseTree ((o, ti) => {
if (newItems.Remove (o))
ti.IsSelected = true;
@@ -260,10 +260,6 @@ namespace Xwt.WPFBackend
return (newItems.Count + oldItems.Count > 0);
});
}
-
- if (!changeActive) {
- shiftStart = GetTreeItem (SelectedItems [0]);
- }
break;
}
@@ -328,8 +324,12 @@ namespace Xwt.WPFBackend
foreach (var forEachItem in GetItemsBetween(shiftStart, shiftEnd))
SelectedItems.Remove(forEachItem.DataContext);
shiftEnd = item;
- foreach (var forEachItem in GetItemsBetween(shiftStart, shiftEnd))
- SelectedItems.Add(forEachItem.DataContext);
+ if (this.SelectionMode == SWC.SelectionMode.Single) {
+ SelectedItems.Add(item.DataContext);
+ } else {
+ foreach (var forEachItem in GetItemsBetween(shiftStart, shiftEnd))
+ SelectedItems.Add(forEachItem.DataContext);
+ }
}
else
{
diff --git a/Xwt.WPF/Xwt.WPFBackend/ExTreeViewItem.cs b/Xwt.WPF/Xwt.WPFBackend/ExTreeViewItem.cs
index 3939dedb..4ca5c95e 100644
--- a/Xwt.WPF/Xwt.WPFBackend/ExTreeViewItem.cs
+++ b/Xwt.WPF/Xwt.WPFBackend/ExTreeViewItem.cs
@@ -35,6 +35,8 @@ using Xwt.Backends;
namespace Xwt.WPFBackend
{
+ using Keyboard = System.Windows.Input.Keyboard;
+
public class ExTreeViewItem
: TreeViewItem
{
@@ -125,15 +127,47 @@ namespace Xwt.WPFBackend
protected override DependencyObject GetContainerForItemOverride()
{
return new ExTreeViewItem (this.view);
- }
-
- protected override void OnMouseLeftButtonDown (MouseButtonEventArgs e) {
- view.SelectItem(this);
- e.Handled = true;
- base.OnMouseLeftButtonDown(e);
- }
-
- protected override void OnMouseDoubleClick(MouseButtonEventArgs e)
+ }
+
+ protected override void OnMouseLeftButtonDown (MouseButtonEventArgs e)
+ {
+ var args = e.ToXwtButtonArgs (view.Backend.Widget);
+ view.Backend.Context.InvokeUserCode (delegate () {
+ view.Backend.EventSink.OnButtonPressed (args);
+ });
+ if (args.Handled) {
+ e.Handled = true;
+ return;
+ }
+
+ if (!view.SelectedItems.Contains (this.DataContext) || CtrlPressed)
+ view.SelectItem (this);
+ view.Backend.WidgetMouseDownForDragHandler (this, e);
+
+ e.Handled = true;
+ base.OnMouseLeftButtonDown (e);
+ }
+
+ protected override void OnMouseLeftButtonUp (MouseButtonEventArgs e)
+ {
+ var args = e.ToXwtButtonArgs (view.Backend.Widget);
+ view.Backend.Context.InvokeUserCode (delegate () {
+ view.Backend.EventSink.OnButtonReleased (args);
+ });
+ if (args.Handled) {
+ e.Handled = true;
+ return;
+ }
+
+ if (view.SelectedItems.Contains (this.DataContext) && !CtrlPressed)
+ view.SelectItem (this);
+ view.Backend.WidgetMouseUpForDragHandler(this, e);
+
+ e.Handled = true;
+ base.OnMouseLeftButtonUp (e);
+ }
+
+ protected override void OnMouseDoubleClick (MouseButtonEventArgs e)
{
if ((view.Backend as TreeViewBackend)?.RowActivatedEventEnabled == true && IsSelected)
{
@@ -194,6 +228,14 @@ namespace Xwt.WPFBackend
{
BringIntoView();
//We can't allow TreeViewItem(our base class) to get this message(OnGotFocus) because it will also select this item which we don't want
- }
+ }
+
+ private bool CtrlPressed
+ {
+ get
+ {
+ return Keyboard.IsKeyDown (WKey.RightCtrl) || Keyboard.IsKeyDown (WKey.LeftCtrl);
+ }
+ }
}
}
diff --git a/Xwt.WPF/Xwt.WPFBackend/ListDataSource.cs b/Xwt.WPF/Xwt.WPFBackend/ListDataSource.cs
index 18a98e6b..86a0c32b 100644
--- a/Xwt.WPF/Xwt.WPFBackend/ListDataSource.cs
+++ b/Xwt.WPF/Xwt.WPFBackend/ListDataSource.cs
@@ -99,7 +99,6 @@ namespace Xwt.WPFBackend
if (row == this.rows.Count)
return AddRow ();
- row--;
this.rows.Insert (row, new ValuesContainer (this.columnTypes.Length));
OnRowInserted (new ListRowEventArgs (row));
diff --git a/Xwt.WPF/Xwt.WPFBackend/MenuItemBackend.cs b/Xwt.WPF/Xwt.WPFBackend/MenuItemBackend.cs
index 49ffae4f..f8c93877 100644
--- a/Xwt.WPF/Xwt.WPFBackend/MenuItemBackend.cs
+++ b/Xwt.WPF/Xwt.WPFBackend/MenuItemBackend.cs
@@ -31,6 +31,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
+using System.Windows.Controls;
using SWC = System.Windows.Controls;
using SWMI = System.Windows.Media.Imaging;
using Xwt.Backends;
@@ -98,6 +99,21 @@ namespace Xwt.WPFBackend
}
}
+ public string TooltipText
+ {
+ get { return menuItem.ToolTip == null ? null : ((ToolTip)menuItem.ToolTip).Content.ToString(); }
+ set
+ {
+ var tp = menuItem.ToolTip as ToolTip;
+ if (tp == null)
+ menuItem.ToolTip = tp = new ToolTip();
+ tp.Content = value ?? string.Empty;
+ ToolTipService.SetIsEnabled(menuItem, value != null);
+ if (tp.IsOpen && value == null)
+ tp.IsOpen = false;
+ }
+ }
+
public bool UseMnemonic {
get { return useMnemonic; }
set
diff --git a/Xwt.WPF/Xwt.WPFBackend/TextEntryBackend.cs b/Xwt.WPF/Xwt.WPFBackend/TextEntryBackend.cs
index 8cecd6c1..02ec5470 100644
--- a/Xwt.WPF/Xwt.WPFBackend/TextEntryBackend.cs
+++ b/Xwt.WPF/Xwt.WPFBackend/TextEntryBackend.cs
@@ -90,7 +90,7 @@ namespace Xwt.WPFBackend
public bool ReadOnly
{
get { return TextBox.IsReadOnly; }
- set { TextBox.IsReadOnly = true; }
+ set { TextBox.IsReadOnly = value; }
}
public bool ShowFrame
@@ -235,4 +235,4 @@ namespace Xwt.WPFBackend
Context.InvokeUserCode (EventSink.OnSelectionChanged);
}
}
-} \ No newline at end of file
+}
diff --git a/Xwt.WPF/Xwt.WPFBackend/TreeStoreBackend.cs b/Xwt.WPF/Xwt.WPFBackend/TreeStoreBackend.cs
index d4c5df13..81b12736 100755
--- a/Xwt.WPF/Xwt.WPFBackend/TreeStoreBackend.cs
+++ b/Xwt.WPF/Xwt.WPFBackend/TreeStoreBackend.cs
@@ -49,6 +49,7 @@ namespace Xwt.WPFBackend
public event EventHandler<TreeNodeChildEventArgs> NodeDeleted;
public event EventHandler<TreeNodeEventArgs> NodeChanged;
public event EventHandler<TreeNodeOrderEventArgs> NodesReordered;
+ public event EventHandler Cleared;
public Type[] ColumnTypes
{
@@ -181,6 +182,7 @@ namespace Xwt.WPFBackend
public void Clear ()
{
this.topNodes.Clear();
+ Cleared?.Invoke(this, EventArgs.Empty);
}
public IEnumerator GetEnumerator ()
diff --git a/Xwt.WPF/Xwt.WPFBackend/TreeView.xaml b/Xwt.WPF/Xwt.WPFBackend/TreeView.xaml
index 84053ccc..72f823db 100644
--- a/Xwt.WPF/Xwt.WPFBackend/TreeView.xaml
+++ b/Xwt.WPF/Xwt.WPFBackend/TreeView.xaml
@@ -1,4 +1,4 @@
-<ResourceDictionary
+<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:Xwt.WPFBackend;assembly=Xwt.WPF">
@@ -9,17 +9,17 @@
<Setter.Value>
<ControlTemplate TargetType="l:ExTreeViewItem">
<StackPanel>
- <Border Name="HBorder" Background="{TemplateBinding Background}" BorderBrush="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" BorderThickness="1" Padding="{TemplateBinding Padding}">
+ <Border Name="HBorder" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}">
<GridViewRowPresenter x:Name="PART_Header" Content="{TemplateBinding Header}" Columns="{Binding Path=View.Columns, RelativeSource={RelativeSource AncestorType={x:Type l:ExTreeView}}}" />
</Border>
<ItemsPresenter x:Name="ItemsHost" />
</StackPanel>
-
+
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="False">
<Setter TargetName="ItemsHost" Property="Visibility" Value="Collapsed" />
</Trigger>
-
+
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="HasHeader" Value="False" />
@@ -27,7 +27,7 @@
</MultiTrigger.Conditions>
<Setter TargetName="PART_Header" Property="MinWidth" Value="75" />
</MultiTrigger>
-
+
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="HasHeader" Value="False" />
@@ -35,36 +35,40 @@
</MultiTrigger.Conditions>
<Setter Property="MinHeight" Value="19" />
</MultiTrigger>
-
- <Trigger Property="IsSelected" Value="True">
- <Setter TargetName="HBorder" Property="Background" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" />
- <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}" />
- </Trigger>
-
- <MultiTrigger>
- <MultiTrigger.Conditions>
- <Condition Property="IsSelected" Value="True" />
- <Condition Property="IsSelectionActive" Value="False" />
- </MultiTrigger.Conditions>
- <Setter TargetName="HBorder" Property="Background" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" />
- <Setter TargetName="HBorder" Property="BorderBrush" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" />
- <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
- </MultiTrigger>
-
- <Trigger Property="IsEnabled" Value="False">
- <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
- </Trigger>
- <MultiTrigger>
- <MultiTrigger.Conditions>
- <Condition Property="IsSelected" Value="False" />
- <Condition Property="IsFocused" Value="False" />
- </MultiTrigger.Conditions>
- <Setter TargetName="HBorder" Property="BorderBrush" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Background}" />
- </MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
+
+ <Setter Property="BorderBrush" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" />
+ <Setter Property="BorderThickness" Value="1" />
+ <Style.Triggers>
+ <Trigger Property="IsSelected" Value="True">
+ <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" />
+ <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}" />
+ </Trigger>
+
+ <MultiTrigger>
+ <MultiTrigger.Conditions>
+ <Condition Property="IsSelected" Value="True" />
+ <Condition Property="IsSelectionActive" Value="False" />
+ </MultiTrigger.Conditions>
+ <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" />
+ <Setter Property="BorderBrush" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" />
+ <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
+ </MultiTrigger>
+
+ <Trigger Property="IsEnabled" Value="False">
+ <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
+ </Trigger>
+ <MultiTrigger>
+ <MultiTrigger.Conditions>
+ <Condition Property="IsSelected" Value="False" />
+ <Condition Property="IsFocused" Value="False" />
+ </MultiTrigger.Conditions>
+ <Setter Property="BorderBrush" Value="{Binding RelativeSource={RelativeSource Self}, Path=Background}" />
+ </MultiTrigger>
+ </Style.Triggers>
</Style>
<Style TargetType="{x:Type l:ExTreeView}">
diff --git a/Xwt.WPF/Xwt.WPFBackend/TreeViewBackend.cs b/Xwt.WPF/Xwt.WPFBackend/TreeViewBackend.cs
index 37147670..6ee8e43d 100644
--- a/Xwt.WPF/Xwt.WPFBackend/TreeViewBackend.cs
+++ b/Xwt.WPF/Xwt.WPFBackend/TreeViewBackend.cs
@@ -1,4 +1,4 @@
-//
+//
// TreeViewBackend.cs
//
// Author:
@@ -116,6 +116,15 @@ namespace Xwt.WPFBackend
}
}
+ public bool BorderVisible {
+ get {
+ return Tree.BorderThickness.Left != 0;
+ }
+ set {
+ Tree.BorderThickness = value ? new Thickness (1) : new Thickness (0);
+ }
+ }
+
private bool headersVisible = true;
public bool HeadersVisible {
get { return this.headersVisible; }
@@ -144,6 +153,17 @@ namespace Xwt.WPFBackend
}
}
+ public bool UseAlternatingRowColors {
+ get {
+ return Tree.AlternationCount == 2;
+ }
+ set {
+ Tree.AlternationCount = value ? 2 : 0;
+ }
+ }
+
+ public bool AnimationsEnabled { get; set; }
+
public void SelectRow (TreePosition pos)
{
Tree.SelectedItems.Add (pos);
diff --git a/Xwt.WPF/Xwt.WPFBackend/Util.cs b/Xwt.WPF/Xwt.WPFBackend/Util.cs
index a678ba4d..0e9ca565 100644
--- a/Xwt.WPF/Xwt.WPFBackend/Util.cs
+++ b/Xwt.WPF/Xwt.WPFBackend/Util.cs
@@ -90,6 +90,21 @@ namespace Xwt.WPFBackend
return null;
}
+
+ /// <summary>
+ /// Get the the parent System.Windows.Window. If that fails for whatever reason (which can happen if the WPF
+ /// visual tree isn't rooted with a System.Windows.Window, like in the case where it's rooted in a WinForms
+ /// component), then fallback to returning the WPF MainWindow.
+ /// <param name="element">WPF element</param>
+ /// <returns>ancestor System.Windows.Window or MainWindow</returns>
+ public static System.Windows.Window GetParentOrMainWindow (this FrameworkElement element)
+ {
+ System.Windows.Window parentWindow = GetParentWindow (element);
+ if (parentWindow != null)
+ return parentWindow;
+
+ return System.Windows.Application.Current.MainWindow;
+ }
}
class XwtWin32Window : System.Windows.Forms.IWin32Window
diff --git a/Xwt.WPF/Xwt.WPFBackend/WidgetBackend.cs b/Xwt.WPF/Xwt.WPFBackend/WidgetBackend.cs
index 3033dea5..39f28bf9 100644
--- a/Xwt.WPF/Xwt.WPFBackend/WidgetBackend.cs
+++ b/Xwt.WPF/Xwt.WPFBackend/WidgetBackend.cs
@@ -439,7 +439,7 @@ namespace Xwt.WPFBackend
Widget.Cursor = Cursors.Arrow;
else if (cursor == CursorType.Crosshair)
Widget.Cursor = Cursors.Cross;
- else if (cursor == CursorType.Hand)
+ else if (cursor == CursorType.Hand || cursor == CursorType.Hand2)
Widget.Cursor = Cursors.Hand;
else if (cursor == CursorType.IBeam)
Widget.Cursor = Cursors.IBeam;
@@ -455,6 +455,10 @@ namespace Xwt.WPFBackend
Widget.Cursor = Cursors.SizeWE;
else if (cursor == CursorType.ResizeLeftRight)
widget.Cursor = Cursors.SizeWE;
+ else if (cursor == CursorType.ResizeNE || cursor == CursorType.ResizeSW)
+ widget.Cursor = Cursors.SizeNESW;
+ else if (cursor == CursorType.ResizeNW || cursor == CursorType.ResizeSE)
+ widget.Cursor = Cursors.SizeNWSE;
else if (cursor == CursorType.Move)
widget.Cursor = Cursors.SizeAll;
else if (cursor == CursorType.Wait)
@@ -463,6 +467,8 @@ namespace Xwt.WPFBackend
widget.Cursor = Cursors.Help;
else if (cursor == CursorType.Invisible)
widget.Cursor = Cursors.None;
+ else if (cursor == CursorType.NotAllowed)
+ widget.Cursor = Cursors.No;
else
Widget.Cursor = Cursors.Arrow;
}
@@ -645,6 +651,11 @@ namespace Xwt.WPFBackend
e.Handled = true;
}
+ internal void WidgetMouseDownForDragHandler(object o, MouseButtonEventArgs e)
+ {
+ SetupDragRect(e);
+ }
+
void WidgetMouseUpHandler (object o, MouseButtonEventArgs e)
{
var args = e.ToXwtButtonArgs (Widget);
@@ -684,6 +695,11 @@ namespace Xwt.WPFBackend
return Widget.GetParentWindow ();
}
+ private SW.Window GetParentOrMainWindow ()
+ {
+ return Widget.GetParentOrMainWindow ();
+ }
+
public void DragStart (DragStartData data)
{
if (data.Data == null)
@@ -692,7 +708,7 @@ namespace Xwt.WPFBackend
DataObject dataObj = data.Data.ToDataObject();
if (data.ImageBackend != null) {
- AdornedWindow = GetParentWindow ();
+ AdornedWindow = GetParentOrMainWindow ();
AdornedWindow.AllowDrop = true;
var e = (UIElement)AdornedWindow.Content;
@@ -742,6 +758,7 @@ namespace Xwt.WPFBackend
DragDropInfo.TargetTypes = types == null ? new TransferDataType [0] : types;
Widget.MouseUp += WidgetMouseUpForDragHandler;
Widget.MouseMove += WidgetMouseMoveForDragHandler;
+ Widget.MouseDown += WidgetMouseDownForDragHandler;
}
private void SetupDragRect (MouseEventArgs e)
@@ -752,7 +769,7 @@ namespace Xwt.WPFBackend
DragDropInfo.DragRect = new Rect (loc.X - width / 2, loc.Y - height / 2, width, height);
}
- void WidgetMouseUpForDragHandler (object o, EventArgs e)
+ internal void WidgetMouseUpForDragHandler (object o, EventArgs e)
{
DragDropInfo.DragRect = Rect.Empty;
}
@@ -765,7 +782,7 @@ namespace Xwt.WPFBackend
return;
if (DragDropInfo.DragRect.IsEmpty)
- SetupDragRect (e);
+ return;
if (DragDropInfo.DragRect.Contains (e.GetPosition (Widget)))
return;
@@ -858,7 +875,7 @@ namespace Xwt.WPFBackend
void WidgetDragOverHandler (object sender, System.Windows.DragEventArgs e)
{
if (Adorner != null) {
- var w = GetParentWindow ();
+ var w = GetParentOrMainWindow ();
var v = (UIElement)w.Content;
if (w != AdornedWindow) {
diff --git a/Xwt.XamMac/Xwt.Mac.CellViews/CanvasTableCell.cs b/Xwt.XamMac/Xwt.Mac.CellViews/CanvasTableCell.cs
index c3703f17..573a3342 100644
--- a/Xwt.XamMac/Xwt.Mac.CellViews/CanvasTableCell.cs
+++ b/Xwt.XamMac/Xwt.Mac.CellViews/CanvasTableCell.cs
@@ -30,19 +30,19 @@ using Xwt.Backends;
namespace Xwt.Mac
{
- class CanvasTableCell: NSCell, ICellRenderer
+ class CanvasTableCell: NSView, ICanvasCellRenderer
{
- bool visible = true;
+ NSTrackingArea trackingArea;
- public CanvasTableCell (IntPtr p): base (p)
- {
- }
+ public CompositeCell CellContainer { get; set; }
- public CanvasTableCell ()
- {
- }
+ public CellViewBackend Backend { get; set; }
- public CompositeCell CellContainer { get; set; }
+ public NSView CellView { get { return this; } }
+
+ // Since 10.12 or 10.13 views inside tables flip on data reload.
+ // IsFlipped enforces the correct orientation of the layer.
+ public override bool IsFlipped { get { return true; } }
public void CopyFrom (object other)
{
@@ -52,46 +52,132 @@ namespace Xwt.Mac
public void Fill ()
{
- visible = Frontend.Visible;
+ Hidden = !Frontend.Visible;
}
ICanvasCellViewFrontend Frontend {
get { return (ICanvasCellViewFrontend) Backend.Frontend; }
}
- public CellViewBackend Backend { get; set; }
-
+ public override CGSize FittingSize {
+ get {
+ var size = CGSize.Empty;
+ Frontend.ApplicationContext.InvokeUserCode (delegate {
+ var s = Frontend.GetRequiredSize (SizeConstraint.Unconstrained);
+ size = new CGSize ((nfloat)s.Width, (nfloat)s.Height);
+ });
+ return size;
+ }
+ }
- public override CGSize CellSizeForBounds (CGRect bounds)
+ public Size GetRequiredSize(SizeConstraint widthConstraint)
{
- if (!visible)
- return CGSize.Empty;
- var size = new CGSize ();
+ var size = Size.Zero;
Frontend.ApplicationContext.InvokeUserCode (delegate {
- var s = Frontend.GetRequiredSize ();
- size = new CGSize ((nfloat)s.Width, (nfloat)s.Height);
+ size = Frontend.GetRequiredSize (widthConstraint);
});
- if (size.Width > bounds.Width)
- size.Width = bounds.Width;
- if (size.Height > bounds.Height)
- size.Height = bounds.Height;
return size;
}
- public override void DrawInteriorWithFrame (CGRect cellFrame, NSView inView)
+ public override void DrawRect (CGRect dirtyRect)
{
- if (!visible)
- return;
- CGContext ctx = NSGraphicsContext.CurrentContext.GraphicsPort;
-
- var backend = new CGContextBackend {
- Context = ctx,
- InverseViewTransform = ctx.GetCTM ().Invert ()
- };
+ Backend.Load (this);
Frontend.ApplicationContext.InvokeUserCode (delegate {
- Frontend.Draw (backend, new Rectangle (cellFrame.X, cellFrame.Y, cellFrame.Width, cellFrame.Height));
+ CGContext ctx = NSGraphicsContext.CurrentContext.GraphicsPort;
+
+ var backend = new CGContextBackend {
+ Context = ctx,
+ InverseViewTransform = ctx.GetCTM ().Invert ()
+ };
+ var bounds = Backend.CellBounds;
+ backend.Context.ClipToRect (dirtyRect);
+ backend.Context.TranslateCTM ((nfloat)(-bounds.X), (nfloat)(-bounds.Y));
+ Frontend.Draw (backend, new Rectangle (bounds.X, bounds.Y, bounds.Width, bounds.Height));
});
}
+
+ public override void UpdateTrackingAreas ()
+ {
+ if (trackingArea != null) {
+ RemoveTrackingArea (trackingArea);
+ trackingArea.Dispose ();
+ }
+ var options = NSTrackingAreaOptions.MouseMoved | NSTrackingAreaOptions.ActiveInKeyWindow | NSTrackingAreaOptions.MouseEnteredAndExited;
+ trackingArea = new NSTrackingArea (Bounds, options, this, null);
+ AddTrackingArea (trackingArea);
+ }
+
+ public override void RightMouseDown (NSEvent theEvent)
+ {
+ if (!this.HandleMouseDown (theEvent))
+ base.RightMouseDown (theEvent);
+ }
+
+ public override void RightMouseUp (NSEvent theEvent)
+ {
+ if (!this.HandleMouseUp (theEvent))
+ base.RightMouseUp (theEvent);
+ }
+
+ public override void MouseDown (NSEvent theEvent)
+ {
+ if (!this.HandleMouseDown (theEvent))
+ base.MouseDown (theEvent);
+ }
+
+ public override void MouseUp (NSEvent theEvent)
+ {
+ if (!this.HandleMouseUp (theEvent))
+ base.MouseUp (theEvent);
+ }
+
+ public override void OtherMouseDown (NSEvent theEvent)
+ {
+ if (!this.HandleMouseDown (theEvent))
+ base.OtherMouseDown (theEvent);
+ }
+
+ public override void OtherMouseUp (NSEvent theEvent)
+ {
+ if (!this.HandleMouseUp (theEvent))
+ base.OtherMouseUp (theEvent);
+ }
+
+ public override void MouseEntered (NSEvent theEvent)
+ {
+ this.HandleMouseEntered (theEvent);
+ base.MouseEntered (theEvent);
+ }
+
+ public override void MouseExited (NSEvent theEvent)
+ {
+ this.HandleMouseExited (theEvent);
+ base.MouseExited (theEvent);
+ }
+
+ public override void MouseMoved (NSEvent theEvent)
+ {
+ if (!this.HandleMouseMoved (theEvent))
+ base.MouseMoved (theEvent);
+ }
+
+ public override void MouseDragged (NSEvent theEvent)
+ {
+ if (!this.HandleMouseMoved (theEvent))
+ base.MouseDragged (theEvent);
+ }
+
+ public override void KeyDown (NSEvent theEvent)
+ {
+ if (!this.HandleKeyDown (theEvent))
+ base.KeyDown (theEvent);
+ }
+
+ public override void KeyUp (NSEvent theEvent)
+ {
+ if (!this.HandleKeyUp (theEvent))
+ base.KeyUp (theEvent);
+ }
}
}
diff --git a/Xwt.XamMac/Xwt.Mac.CellViews/CellUtil.cs b/Xwt.XamMac/Xwt.Mac.CellViews/CellUtil.cs
index 759c2b65..8134e62b 100644
--- a/Xwt.XamMac/Xwt.Mac.CellViews/CellUtil.cs
+++ b/Xwt.XamMac/Xwt.Mac.CellViews/CellUtil.cs
@@ -27,21 +27,29 @@
using System;
using System.Collections.Generic;
using AppKit;
+using CoreGraphics;
using Xwt.Backends;
namespace Xwt.Mac
{
static class CellUtil
{
- public static NSCell CreateCell (ApplicationContext context, NSTableView table, ICellSource source, ICollection<CellView> cells, int column)
+ public static CompositeCell CreateCellView (ApplicationContext context, ICellSource source, ICollection<CellView> cells, int column)
{
- CompositeCell c = new CompositeCell (context, Orientation.Horizontal, source);
+ CompositeCell c = new CompositeCell (context, source);
foreach (var cell in cells)
- c.AddCell ((ICellRenderer) CreateCell (table, c, cell, column));
+ c.AddCell ((ICellRenderer) CreateCellView (source, cell, column));
return c;
}
+
+ public static void UpdateCellView (CompositeCell cellView, ICellSource source, ICollection<CellView> cells, int column)
+ {
+ cellView.ClearCells ();
+ foreach (var cell in cells)
+ cellView.AddCell ((ICellRenderer) CreateCellView (source, cell, column));
+ }
- static NSCell CreateCell (NSTableView table, CompositeCell source, CellView cell, int column)
+ static NSView CreateCellView (ICellSource source, CellView cell, int column)
{
ICellRenderer cr = null;
@@ -57,10 +65,121 @@ namespace Xwt.Mac
cr = new RadioButtonTableCell ();
else
throw new NotImplementedException ();
- cr.Backend = new CellViewBackend (table, column);
ICellViewFrontend fr = cell;
- fr.AttachBackend (null, cr.Backend);
- return (NSCell)cr;
+ CellViewBackend backend = null;
+ try {
+ //FIXME: although the cell views are based on XwtComponent, they don't implement
+ // the dynamic registration based backend creation and there is no way to
+ // identify whether the frontend has already a valid backend.
+ backend = cell.GetBackend () as CellViewBackend;
+ } catch (InvalidOperationException) { }
+
+ if (backend == null) {
+ cr.Backend = new CellViewBackend (source.TableView, column);
+ fr.AttachBackend ((source as ViewBackend).Frontend, cr.Backend);
+ } else
+ cr.Backend = backend;
+ return (NSView)cr;
+ }
+
+ public static bool HandleMouseDown (this ICellRenderer cell, NSEvent theEvent)
+ {
+ if (cell.Backend.GetIsEventEnabled (WidgetEvent.ButtonPressed)) {
+ CGPoint p = cell.CellView.ConvertPointFromEvent (theEvent);
+ if (cell.CellView.Bounds.Contains (p)) {
+ cell.Backend.Load (cell);
+ cell.CellContainer.SetCurrentEventRow ();
+ var offset = cell.Backend.CellBounds.Location;
+ ButtonEventArgs args = new ButtonEventArgs {
+ X = p.X + offset.X,
+ Y = p.Y + offset.Y,
+ Button = theEvent.GetPointerButton(),
+ MultiplePress = (int)theEvent.ClickCount
+ };
+ cell.Backend.Context.InvokeUserCode (() => cell.Backend.EventSink.OnButtonPressed (args));
+ return args.Handled;
+ }
+ }
+ return false;
+ }
+
+ public static bool HandleMouseUp (this ICellRenderer cell, NSEvent theEvent)
+ {
+ if (cell.Backend.GetIsEventEnabled (WidgetEvent.ButtonReleased)) {
+ CGPoint p = cell.CellView.ConvertPointFromEvent (theEvent);
+ if (cell.CellView.Bounds.Contains (p)) {
+ cell.Backend.Load (cell);
+ cell.CellContainer.SetCurrentEventRow ();
+ var offset = cell.Backend.CellBounds.Location;
+ ButtonEventArgs args = new ButtonEventArgs {
+ X = p.X + offset.X,
+ Y = p.Y + offset.Y,
+ Button = theEvent.GetPointerButton (),
+ MultiplePress = (int)theEvent.ClickCount
+ };
+ cell.Backend.Context.InvokeUserCode (() => cell.Backend.EventSink.OnButtonReleased (args));
+ return args.Handled;
+ }
+ }
+ return false;
+ }
+
+ public static void HandleMouseEntered (this ICellRenderer cell, NSEvent theEvent)
+ {
+ if (cell.Backend.GetIsEventEnabled (WidgetEvent.MouseEntered)) {
+ cell.Backend.Load (cell);
+ cell.CellContainer.SetCurrentEventRow ();
+ cell.Backend.Context.InvokeUserCode (cell.Backend.EventSink.OnMouseEntered);
+ }
+ }
+
+ public static void HandleMouseExited (this ICellRenderer cell, NSEvent theEvent)
+ {
+ if (cell.Backend.GetIsEventEnabled (WidgetEvent.MouseExited)) {
+ cell.Backend.Load (cell);
+ cell.CellContainer.SetCurrentEventRow ();
+ cell.Backend.Context.InvokeUserCode (cell.Backend.EventSink.OnMouseExited);
+ }
+ }
+
+ public static bool HandleMouseMoved (this ICellRenderer cell, NSEvent theEvent)
+ {
+ if (cell.Backend.GetIsEventEnabled (WidgetEvent.MouseMoved)) {
+ CGPoint p = cell.CellView.ConvertPointFromEvent (theEvent);
+ if (cell.CellView.Bounds.Contains (p)) {
+ cell.Backend.Load (cell);
+ cell.CellContainer.SetCurrentEventRow ();
+ var offset = cell.Backend.CellBounds.Location;
+ MouseMovedEventArgs args = new MouseMovedEventArgs ((long)TimeSpan.FromSeconds (theEvent.Timestamp).TotalMilliseconds, p.X + offset.X, p.Y + offset.Y);
+ cell.Backend.Context.InvokeUserCode (() => cell.Backend.EventSink.OnMouseMoved (args));
+ return args.Handled;
+ }
+ }
+ return false;
+ }
+
+ public static bool HandleKeyDown (this ICellRenderer cell, NSEvent theEvent)
+ {
+ if (cell.Backend.GetIsEventEnabled (WidgetEvent.KeyPressed)) {
+ cell.Backend.Load (cell);
+ cell.CellContainer.SetCurrentEventRow ();
+ var keyArgs = theEvent.ToXwtKeyEventArgs ();
+ cell.Backend.Context.InvokeUserCode (() => cell.Backend.EventSink.OnKeyPressed (keyArgs));
+ return keyArgs.Handled;
+ }
+ return false;
+ }
+
+ public static bool HandleKeyUp (this ICellRenderer cell, NSEvent theEvent)
+ {
+ if (cell.Backend.GetIsEventEnabled (WidgetEvent.KeyReleased)) {
+ cell.Backend.Load (cell);
+ cell.CellContainer.SetCurrentEventRow ();
+ var keyArgs = theEvent.ToXwtKeyEventArgs ();
+ cell.Backend.Context.InvokeUserCode (() => cell.Backend.EventSink.OnKeyReleased (keyArgs));
+ return keyArgs.Handled;
+ }
+ return false;
}
}
}
diff --git a/Xwt.XamMac/Xwt.Mac.CellViews/CellViewBackend.cs b/Xwt.XamMac/Xwt.Mac.CellViews/CellViewBackend.cs
index b7b3d9a6..6418facf 100644
--- a/Xwt.XamMac/Xwt.Mac.CellViews/CellViewBackend.cs
+++ b/Xwt.XamMac/Xwt.Mac.CellViews/CellViewBackend.cs
@@ -30,79 +30,92 @@ namespace Xwt.Mac
{
public class CellViewBackend: ICellViewBackend, ICanvasCellViewBackend
{
- NSTableView table;
- int column;
+ WidgetEvent enabledEvents;
public CellViewBackend (NSTableView table, int column)
{
- this.table = table;
- this.column = column;
+ Table = table;
+ Column = column;
}
- public ICellViewFrontend Frontend { get; private set; }
-
public virtual void InitializeBackend (object frontend, ApplicationContext context)
{
Frontend = (ICellViewFrontend)frontend;
+ Context = context;
}
- public NSCell CurrentCell { get; set; }
+ public ICellViewFrontend Frontend { get; private set; }
- public int Column {
- get {
- return column;
- }
- }
+ public ICellViewEventSink EventSink { get; private set; }
- public int CurrentRow { get; set; }
+ public ApplicationContext Context { get; private set; }
- internal ITablePosition CurrentPosition { get; set; }
+ internal NSView CurrentCellView { get; private set; }
+ public int Column { get; private set; }
+
+ public NSTableView Table { get; internal set; }
+
+ internal ITablePosition CurrentPosition { get; private set; }
+
+ internal void Load (ICellRenderer cell)
+ {
+ CurrentCellView = (NSView)cell;
+ CurrentPosition = cell.CellContainer.TablePosition;
+ EventSink = Frontend.Load (cell.CellContainer);
+ }
+
public virtual void EnableEvent (object eventId)
{
+ if (eventId is WidgetEvent)
+ enabledEvents |= (WidgetEvent)eventId;
}
-
+
public virtual void DisableEvent (object eventId)
{
+ if (eventId is WidgetEvent)
+ enabledEvents &= ~(WidgetEvent)eventId;
+ }
+
+ public bool GetIsEventEnabled (WidgetEvent eventId)
+ {
+ return enabledEvents.HasFlag (eventId);
}
public void QueueDraw ()
{
- // nothing to be done here, NSTableView should handle this
+ CurrentCellView.NeedsDisplay = true;
+ }
+
+ public void QueueResize ()
+ {
+ CurrentCellView.NeedsDisplay = true;
+ ((ICellRenderer)CurrentCellView).CellContainer.InvalidateRowHeight ();
}
public Rectangle CellBounds {
get {
- if (CurrentPosition is TableRow) {
- var r = table.GetCellFrame (column, ((TableRow)CurrentPosition).Row);
- r = ((ICellRenderer)CurrentCell).CellContainer.GetCellRect (r, CurrentCell);
- return new Rectangle (r.X, r.Y, r.Width, r.Height);
- }
- return Rectangle.Zero;
+ return CurrentCellView.ConvertRectToView (CurrentCellView.Frame, Table).ToXwtRect ();
}
}
public Rectangle BackgroundBounds {
get {
- // TODO
- return CellBounds;
+ return CurrentCellView.ConvertRectToView (CurrentCellView.Frame, ((ICellRenderer)CurrentCellView).CellContainer.Superview).ToXwtRect ();
}
}
public bool Selected {
get {
- if (CurrentPosition is TableRow) {
- return table.IsRowSelected (((TableRow)CurrentPosition).Row);
- }
- // TODO
- return false;
+ if (CurrentPosition is TableRow)
+ return Table.IsRowSelected (((TableRow)CurrentPosition).Row);
+ return Table.IsRowSelected (Table.RowForView (CurrentCellView));
}
}
public bool HasFocus {
get {
- // TODO
- return false;
+ return CurrentCellView?.Window != null && CurrentCellView.Window.FirstResponder == CurrentCellView;
}
}
diff --git a/Xwt.XamMac/Xwt.Mac.CellViews/CheckBoxTableCell.cs b/Xwt.XamMac/Xwt.Mac.CellViews/CheckBoxTableCell.cs
index f41c3ab6..2a00812f 100644
--- a/Xwt.XamMac/Xwt.Mac.CellViews/CheckBoxTableCell.cs
+++ b/Xwt.XamMac/Xwt.Mac.CellViews/CheckBoxTableCell.cs
@@ -25,35 +25,65 @@
// THE SOFTWARE.
using System;
using AppKit;
+using CoreGraphics;
+using Foundation;
using Xwt.Backends;
namespace Xwt.Mac
{
- class CheckBoxTableCell: NSButtonCell, ICellRenderer
+ class CheckBoxTableCell: NSButton, ICellRenderer
{
- bool visible = true;
+ NSTrackingArea trackingArea;
public CheckBoxTableCell ()
{
SetButtonType (NSButtonType.Switch);
Activated += HandleActivated;
- Title = "";
+ Title = string.Empty;
+ }
+
+ public override NSCellStateValue State {
+ get {
+ return base.State;
+ }
+ set {
+ // don't let Cocoa set the state for us
+ }
}
void HandleActivated (object sender, EventArgs e)
{
+ Backend.Load (this);
var cellView = Frontend;
+ var nextState = State; // store new state internally set by Cocoa
+ base.State = cellView.State.ToMacState (); // reset state to previous state from store
CellContainer.SetCurrentEventRow ();
- if (cellView.Editable && !cellView.RaiseToggled () && (cellView.StateField != null || cellView.ActiveField != null)) {
+ if (!cellView.RaiseToggled ()) {
if (cellView.StateField != null)
- CellContainer.SetValue (cellView.StateField, State.ToXwtState ());
+ CellContainer.SetValue (cellView.StateField, nextState.ToXwtState ());
else if (cellView.ActiveField != null)
- CellContainer.SetValue (cellView.ActiveField, State != NSCellStateValue.Off);
+ CellContainer.SetValue (cellView.ActiveField, nextState != NSCellStateValue.Off);
}
}
- public CheckBoxTableCell (IntPtr p): base (p)
+ NSCellStateValue GetNextState ()
{
+ if (!AllowsMixedState) {
+ switch (State) {
+ case NSCellStateValue.Off:
+ case NSCellStateValue.Mixed:
+ return NSCellStateValue.On;
+ default: return NSCellStateValue.Off;
+ }
+ } else {
+ switch (State) {
+ case NSCellStateValue.Off:
+ return NSCellStateValue.Mixed;
+ case NSCellStateValue.Mixed:
+ return NSCellStateValue.On;
+ default: return NSCellStateValue.Off;
+ }
+ }
}
ICheckBoxCellViewFrontend Frontend {
@@ -64,26 +94,36 @@ namespace Xwt.Mac
public CompositeCell CellContainer { get; set; }
+ public NSView CellView { get { return this; } }
+
public void Fill ()
{
var cellView = Frontend;
AllowsMixedState = cellView.AllowMixed || cellView.State == CheckBoxState.Mixed;
- State = cellView.State.ToMacState ();
- Editable = cellView.Editable;
- visible = cellView.Visible;
+ base.State = cellView.State.ToMacState ();
+ Enabled = cellView.Editable;
+ Hidden = !cellView.Visible;
}
-
- public override CoreGraphics.CGSize CellSizeForBounds (CoreGraphics.CGRect bounds)
- {
- if (visible)
- return base.CellSizeForBounds (bounds);
- return CoreGraphics.CGSize.Empty;
+
+ public virtual NSBackgroundStyle BackgroundStyle {
+ [Export ("backgroundStyle")]
+ get {
+ return Cell.BackgroundStyle;
+ }
+ [Export ("setBackgroundStyle:")]
+ set {
+ Cell.BackgroundStyle = value;
+ }
}
- public override void DrawInteriorWithFrame (CoreGraphics.CGRect cellFrame, NSView inView)
- {
- if (visible)
- base.DrawInteriorWithFrame (cellFrame, inView);
+ static CGSize defaultSize = CGSize.Empty;
+ public override CGSize FittingSize {
+ get {
+ // CheckBox has always the same size, measure it only once
+ if (defaultSize.IsEmpty)
+ defaultSize = base.FittingSize;
+ return defaultSize;
+ }
}
public void CopyFrom (object other)
@@ -91,6 +131,89 @@ namespace Xwt.Mac
var ob = (CheckBoxTableCell)other;
Backend = ob.Backend;
}
+
+ public override void UpdateTrackingAreas ()
+ {
+ if (trackingArea != null) {
+ RemoveTrackingArea (trackingArea);
+ trackingArea.Dispose ();
+ }
+ var options = NSTrackingAreaOptions.MouseMoved | NSTrackingAreaOptions.ActiveInKeyWindow | NSTrackingAreaOptions.MouseEnteredAndExited;
+ trackingArea = new NSTrackingArea (Bounds, options, this, null);
+ AddTrackingArea (trackingArea);
+ }
+
+ public override void RightMouseDown (NSEvent theEvent)
+ {
+ if (!this.HandleMouseDown (theEvent))
+ base.RightMouseDown (theEvent);
+ }
+
+ public override void RightMouseUp (NSEvent theEvent)
+ {
+ if (!this.HandleMouseUp (theEvent))
+ base.RightMouseUp (theEvent);
+ }
+
+ public override void MouseDown (NSEvent theEvent)
+ {
+ if (!this.HandleMouseDown (theEvent))
+ base.MouseDown (theEvent);
+ }
+
+ public override void MouseUp (NSEvent theEvent)
+ {
+ if (!this.HandleMouseUp (theEvent))
+ base.MouseUp (theEvent);
+ }
+
+ public override void OtherMouseDown (NSEvent theEvent)
+ {
+ if (!this.HandleMouseDown (theEvent))
+ base.OtherMouseDown (theEvent);
+ }
+
+ public override void OtherMouseUp (NSEvent theEvent)
+ {
+ if (!this.HandleMouseUp (theEvent))
+ base.OtherMouseUp (theEvent);
+ }
+
+ public override void MouseEntered (NSEvent theEvent)
+ {
+ this.HandleMouseEntered (theEvent);
+ base.MouseEntered (theEvent);
+ }
+
+ public override void MouseExited (NSEvent theEvent)
+ {
+ this.HandleMouseExited (theEvent);
+ base.MouseExited (theEvent);
+ }
+
+ public override void MouseMoved (NSEvent theEvent)
+ {
+ if (!this.HandleMouseMoved (theEvent))
+ base.MouseMoved (theEvent);
+ }
+
+ public override void MouseDragged (NSEvent theEvent)
+ {
+ if (!this.HandleMouseMoved (theEvent))
+ base.MouseDragged (theEvent);
+ }
+
+ public override void KeyDown (NSEvent theEvent)
+ {
+ if (!this.HandleKeyDown (theEvent))
+ base.KeyDown (theEvent);
+ }
+
+ public override void KeyUp (NSEvent theEvent)
+ {
+ if (!this.HandleKeyUp (theEvent))
+ base.KeyUp (theEvent);
+ }
}
}
diff --git a/Xwt.XamMac/Xwt.Mac.CellViews/CompositeCell.cs b/Xwt.XamMac/Xwt.Mac.CellViews/CompositeCell.cs
index 18849fff..1502aefe 100644
--- a/Xwt.XamMac/Xwt.Mac.CellViews/CompositeCell.cs
+++ b/Xwt.XamMac/Xwt.Mac.CellViews/CompositeCell.cs
@@ -24,24 +24,22 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-
using System;
using System.Collections.Generic;
using System.Linq;
using AppKit;
using CoreGraphics;
using Foundation;
+using ObjCRuntime;
using Xwt.Backends;
namespace Xwt.Mac
{
- class CompositeCell: NSCell, ICopiableObject, ICellDataSource
+ class CompositeCell : NSView, ICopiableObject, ICellDataSource, INSCopying
{
ICellSource source;
NSObject val;
List<ICellRenderer> cells = new List<ICellRenderer> ();
- Orientation direction;
- NSCell trackingCell;
ITablePosition tablePosition;
ApplicationContext context;
@@ -51,16 +49,19 @@ namespace Xwt.Mac
}
}
- public CompositeCell (ApplicationContext context, Orientation dir, ICellSource source)
+ public CompositeCell (ApplicationContext context, ICellSource source)
{
if (source == null)
- throw new ArgumentNullException ("source");
- direction = dir;
+ throw new ArgumentNullException (nameof (source));
this.context = context;
this.source = source;
}
-
- public CompositeCell (IntPtr p): base (p)
+
+ public CompositeCell (IntPtr p) : base (p)
+ {
+ }
+
+ CompositeCell ()
{
}
@@ -87,11 +88,37 @@ namespace Xwt.Mac
source.SetCurrentEventRow (tablePosition.Position);
}
- public override NSObject Copy (NSZone zone)
+ bool recalculatingHeight = false;
+ public void InvalidateRowHeight ()
+ {
+ if (tablePosition != null && !recalculatingHeight) {
+ recalculatingHeight = true;
+ source.InvalidateRowHeight (tablePosition.Position);
+ recalculatingHeight = false;
+ }
+ }
+
+ public double GetRequiredHeightForWidth (double width)
+ {
+ Fill (false);
+ double height = 0;
+ foreach (var c in GetCells (new CGSize (width, -1)))
+ height = Math.Max (height, c.Frame.Height);
+ return height;
+ }
+
+ public override NSObject Copy ()
+ {
+ var ob = (ICopiableObject)base.Copy ();
+ ob.CopyFrom (this);
+ return (NSObject)ob;
+ }
+
+ NSObject INSCopying.Copy (NSZone zone)
{
- var ob = (ICopiableObject) base.Copy (zone);
+ var ob = (ICopiableObject)new CompositeCell ();
ob.CopyFrom (this);
- return (NSObject) ob;
+ return (NSObject)ob;
}
void ICopiableObject.CopyFrom (object other)
@@ -99,33 +126,31 @@ namespace Xwt.Mac
var ob = (CompositeCell)other;
if (ob.source == null)
throw new ArgumentException ("Cannot copy from a CompositeCell with a null `source`");
+ Identifier = ob.Identifier;
context = ob.context;
source = ob.source;
- val = ob.val;
- tablePosition = ob.tablePosition;
- direction = ob.direction;
- trackingCell = ob.trackingCell;
cells = new List<ICellRenderer> ();
foreach (var c in ob.cells) {
- var copy = (ICellRenderer) Activator.CreateInstance (c.GetType ());
+ var copy = (ICellRenderer)Activator.CreateInstance (c.GetType ());
copy.CopyFrom (c);
AddCell (copy);
}
if (tablePosition != null)
- Fill ();
+ Fill (false);
}
-
- public override NSObject ObjectValue {
+
+ public virtual NSObject ObjectValue {
+ [Export ("objectValue")]
get {
return val;
}
+ [Export ("setObjectValue:")]
set {
val = value;
if (val is ITablePosition) {
- tablePosition = (ITablePosition) val;
+ tablePosition = (ITablePosition)val;
Fill ();
- }
- else if (val is NSNumber) {
+ } else if (val is NSNumber) {
tablePosition = new TableRow () {
Row = ((NSNumber)val).Int32Value
};
@@ -135,202 +160,156 @@ namespace Xwt.Mac
}
}
- public override bool IsOpaque {
- get {
- var b = base.IsOpaque;
- return true;
- }
+ internal ITablePosition TablePosition {
+ get { return tablePosition; }
}
-
+
public void AddCell (ICellRenderer cell)
{
cell.CellContainer = this;
cells.Add (cell);
+ AddSubview ((NSView)cell);
}
-
- public void Fill ()
+
+ public void ClearCells ()
+ {
+ foreach (NSView cell in cells) {
+ cell.RemoveFromSuperview ();
+ }
+ cells.Clear ();
+ }
+
+ public override CGRect Frame {
+ get { return base.Frame; }
+ set {
+ var oldSize = base.Frame.Size;
+ base.Frame = value;
+ if (oldSize != value.Size && tablePosition != null) {
+ Fill(false);
+ double height = 0;
+ foreach (var c in GetCells (value.Size)) {
+ c.Cell.Frame = c.Frame;
+ c.Cell.NeedsDisplay = true;
+ height = Math.Max (height, c.Frame.Height);
+ }
+ if (Math.Abs(value.Height - height) > double.Epsilon)
+ InvalidateRowHeight ();
+
+ }
+ }
+ }
+
+ public void Fill (bool reallocateCells = true)
{
foreach (var c in cells) {
- c.Backend.CurrentCell = (NSCell) c;
- c.Backend.CurrentPosition = tablePosition;
- c.Backend.Frontend.Load (this);
+ c.Backend.Load (c);
c.Fill ();
}
+ if (!reallocateCells || Frame.IsEmpty)
+ return;
- var s = CellSize;
+ foreach (var c in GetCells (Frame.Size)) {
+ c.Cell.Frame = c.Frame;
+ c.Cell.NeedsDisplay = true;
+ }
}
- IEnumerable<ICellRenderer> VisibleCells {
- get { return cells.Where (c => c.Backend.Frontend.Visible); }
+ public NSView GetCellViewForBackend (ICellViewBackend backend)
+ {
+ return cells.FirstOrDefault (c => c.Backend == backend) as NSView;
}
CGSize CalcSize ()
{
nfloat w = 0;
nfloat h = 0;
- foreach (NSCell c in VisibleCells) {
- var s = c.CellSize;
- if (direction == Orientation.Horizontal) {
- w += s.Width;
- if (s.Height > h)
- h = s.Height;
- } else {
- h += s.Height;
- if (s.Width > w)
- w = s.Width;
- }
+ foreach (var cell in cells) {
+ if (!cell.Backend.Frontend.Visible)
+ continue;
+ var c = (NSView)cell;
+ var s = c.FittingSize;
+ w += s.Width;
+ if (s.Height > h)
+ h = s.Height;
}
return new CGSize (w, h);
}
- public override CGSize CellSizeForBounds (CGRect bounds)
- {
- return CalcSize ();
- }
-
- public override NSBackgroundStyle BackgroundStyle {
+ public override CGSize FittingSize {
get {
- return base.BackgroundStyle;
- }
- set {
- base.BackgroundStyle = value;
- foreach (NSCell c in cells)
- c.BackgroundStyle = value;
+ return CalcSize ();
}
}
-
- public override NSCellStateValue State {
- get {
- return base.State;
- }
- set {
- base.State = value;
- foreach (NSCell c in cells)
- c.State = value;
- }
- }
-
- public override bool Highlighted {
+
+ static readonly Selector selSetBackgroundStyle = new Selector ("setBackgroundStyle:");
+
+ NSBackgroundStyle backgroundStyle;
+
+ public virtual NSBackgroundStyle BackgroundStyle {
+ [Export ("backgroundStyle")]
get {
- return base.Highlighted;
+ return backgroundStyle;
}
+ [Export ("setBackgroundStyle:")]
set {
- base.Highlighted = value;
- foreach (NSCell c in cells)
- c.Highlighted = value;
- }
- }
-
- public override void DrawInteriorWithFrame (CGRect cellFrame, NSView inView)
- {
- // FIXME: although ObjectValue seems to be set and Fill called correctly,
- // the table flickers without an additional Fill call, especially
- // on expansion/collapsing with partially hidden cells (row wise).
- // Cocoa seems to be resetting some NSCell bits, which may be
- // related to the deprecated NSCell mode.
- if (tablePosition != null)
- Fill ();
- CGContext ctx = NSGraphicsContext.CurrentContext.GraphicsPort;
- ctx.SaveState ();
- ctx.AddRect (cellFrame);
- ctx.Clip ();
- foreach (CellPos cp in GetCells(cellFrame))
- cp.Cell.DrawInteriorWithFrame (cp.Frame, inView);
- ctx.RestoreState ();
- }
-
- public override void Highlight (bool flag, CGRect withFrame, NSView inView)
- {
- foreach (CellPos cp in GetCells(withFrame)) {
- cp.Cell.Highlight (flag, cp.Frame, inView);
+ backgroundStyle = value;
+ foreach (NSView cell in cells)
+ if (cell.RespondsToSelector (selSetBackgroundStyle)) {
+ if (IntPtr.Size == 8)
+ Messaging.void_objc_msgSend_Int64 (cell.Handle, selSetBackgroundStyle.Handle, (long)value);
+ else
+ Messaging.void_objc_msgSend_int (cell.Handle, selSetBackgroundStyle.Handle, (int)value);
+ } else
+ cell.NeedsDisplay = true;
}
}
- public override NSCellHit HitTest (NSEvent forEvent, CGRect inRect, NSView ofView)
+ List <CellPos> GetCells (CGSize cellSize)
{
- foreach (CellPos cp in GetCells(inRect)) {
- var h = cp.Cell.HitTest (forEvent, cp.Frame, ofView);
- if (h != NSCellHit.None)
- return h;
- }
- return NSCellHit.None;
- }
+ int nexpands = 0;
+ double requiredSize = 0;
+ double availableSize = cellSize.Width;
- public override bool TrackMouse (NSEvent theEvent, CGRect cellFrame, NSView controlView, bool untilMouseUp)
- {
- var c = GetHitCell (theEvent, cellFrame, controlView);
- if (c != null)
- return c.Cell.TrackMouse (theEvent, c.Frame, controlView, untilMouseUp);
- else
- return base.TrackMouse (theEvent, cellFrame, controlView, untilMouseUp);
- }
+ var cellFrames = new List<CellPos> (cells.Count);
- public CGRect GetCellRect (CGRect cellFrame, NSCell cell)
- {
- foreach (var c in GetCells (cellFrame)) {
- if (c.Cell == cell)
- return c.Frame;
+ // Get the natural size of each child
+ foreach (var cell in cells) {
+ if (!cell.Backend.Frontend.Visible)
+ continue;
+ var cellPos = new CellPos { Cell = (NSView)cell, Frame = CGRect.Empty };
+ cellFrames.Add (cellPos);
+ var size = cellPos.Cell.FittingSize;
+ cellPos.Frame.Width = size.Width;
+ requiredSize += size.Width;
+ if (cell.Backend.Frontend.Expands)
+ nexpands++;
}
- return CGRect.Empty;
- }
- CellPos GetHitCell (NSEvent theEvent, CGRect cellFrame, NSView controlView)
- {
- foreach (CellPos cp in GetCells(cellFrame)) {
- var h = cp.Cell.HitTest (theEvent, cp.Frame, controlView);
- if (h != NSCellHit.None)
- return cp;
- }
- return null;
- }
-
- IEnumerable<CellPos> GetCells (CGRect cellFrame)
- {
- if (direction == Orientation.Horizontal) {
-
- int nexpands = 0;
- double requiredSize = 0;
- double availableSize = cellFrame.Width;
-
- var sizes = new Dictionary<ICellRenderer, double> ();
-
- // Get the natural size of each child
- foreach (var bp in VisibleCells) {
- var s = ((NSCell)bp).CellSize;
- sizes [bp] = s.Width;
- requiredSize += s.Width;
- if (bp.Backend.Frontend.Expands)
- nexpands++;
- }
-
- double remaining = availableSize - requiredSize;
- if (remaining > 0) {
- var expandRemaining = new SizeSplitter (remaining, nexpands);
- foreach (var bp in VisibleCells) {
- if (bp.Backend.Frontend.Expands)
- sizes [bp] += (nfloat)expandRemaining.NextSizePart ();
- }
+ double remaining = availableSize - requiredSize;
+ if (remaining > 0) {
+ var expandRemaining = new SizeSplitter (remaining, nexpands);
+ foreach (var cellFrame in cellFrames) {
+ if (((ICellRenderer)cellFrame.Cell).Backend.Frontend.Expands)
+ cellFrame.Frame.Width += (nfloat)expandRemaining.NextSizePart ();
}
+ }
- double x = cellFrame.X;
- foreach (var s in sizes) {
- yield return new CellPos () { Cell = (NSCell)s.Key, Frame = new CGRect (x, cellFrame.Y, s.Value, cellFrame.Height) };
- x += s.Value;
- }
- } else {
- nfloat y = cellFrame.Y;
- foreach (NSCell c in VisibleCells) {
- var s = c.CellSize;
- var f = new CGRect (cellFrame.X, y, s.Width, cellFrame.Height);
- y += s.Height;
- yield return new CellPos () { Cell = c, Frame = f };
- }
+ double x = 0;
+ foreach (var cellFrame in cellFrames) {
+ var width = cellFrame.Frame.Width;
+ var canvas = cellFrame.Cell as ICanvasCellRenderer;
+ var height = (canvas != null) ? canvas.GetRequiredSize (SizeConstraint.WithSize (width)).Height : cellFrame.Cell.FittingSize.Height;
+ // y-align only if the cell has a valid height, otherwise we're just recalculating the required size
+ var y = cellSize.Height > 0 ? (cellSize.Height - height) / 2 : 0;
+ cellFrame.Frame = new CGRect (x, y, width, height);
+ x += width;
}
+ return cellFrames;
}
-
+
class CellPos
{
- public NSCell Cell;
+ public NSView Cell;
public CGRect Frame;
}
@@ -354,10 +333,29 @@ namespace Xwt.Mac
{
rem--;
return part + 1;
- }
- else
+ } else
return part;
}
}
+
+ bool isDisposed;
+
+ public bool IsDisposed {
+ get {
+ try {
+ // Cocoa may dispose the native view in NSView based table mode
+ // in this case Handle and SuperHandle will become Zero.
+ return isDisposed || Handle == IntPtr.Zero || SuperHandle == IntPtr.Zero;
+ } catch {
+ return true;
+ }
+ }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ isDisposed = true;
+ base.Dispose(disposing);
+ }
}
}
diff --git a/Xwt.XamMac/Xwt.Mac.CellViews/ICellRenderer.cs b/Xwt.XamMac/Xwt.Mac.CellViews/ICellRenderer.cs
index e40cec63..82499dc4 100644
--- a/Xwt.XamMac/Xwt.Mac.CellViews/ICellRenderer.cs
+++ b/Xwt.XamMac/Xwt.Mac.CellViews/ICellRenderer.cs
@@ -24,6 +24,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+using AppKit;
namespace Xwt.Mac
{
@@ -31,8 +32,14 @@ namespace Xwt.Mac
{
CellViewBackend Backend { get; set; }
CompositeCell CellContainer { get; set; }
+ NSView CellView { get; }
void Fill ();
}
+
+ interface ICanvasCellRenderer : ICellRenderer
+ {
+ Size GetRequiredSize (SizeConstraint widthConstraint);
+ }
interface ITablePosition
{
diff --git a/Xwt.XamMac/Xwt.Mac.CellViews/ICellSource.cs b/Xwt.XamMac/Xwt.Mac.CellViews/ICellSource.cs
index 95c51a7b..85355f00 100644
--- a/Xwt.XamMac/Xwt.Mac.CellViews/ICellSource.cs
+++ b/Xwt.XamMac/Xwt.Mac.CellViews/ICellSource.cs
@@ -25,6 +25,8 @@
// THE SOFTWARE.
using System;
+using System.Collections.Generic;
+using AppKit;
namespace Xwt.Mac
{
@@ -33,6 +35,8 @@ namespace Xwt.Mac
object GetValue (object pos, int nField);
void SetValue (object pos, int nField, object value);
void SetCurrentEventRow (object pos);
- nfloat RowHeight { get; set; }
+ void InvalidateRowHeight (object pos);
+ List<NSTableColumn> Columns { get; }
+ NSTableView TableView { get; }
}
}
diff --git a/Xwt.XamMac/Xwt.Mac.CellViews/ImageTableCell.cs b/Xwt.XamMac/Xwt.Mac.CellViews/ImageTableCell.cs
index cffd6fe9..5efb8374 100644
--- a/Xwt.XamMac/Xwt.Mac.CellViews/ImageTableCell.cs
+++ b/Xwt.XamMac/Xwt.Mac.CellViews/ImageTableCell.cs
@@ -1,4 +1,4 @@
-//
+//
// ImageTableCell.cs
//
// Author:
@@ -24,67 +24,138 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-using System;
+using System;
using AppKit;
using CoreGraphics;
using Xwt.Backends;
namespace Xwt.Mac
{
- class ImageTableCell: NSImageCell, ICellRenderer
+ class ImageTableCell : NSImageView, ICellRenderer
{
- bool visible = true;
+ NSTrackingArea trackingArea;
- public ImageTableCell ()
- {
- }
-
- public ImageTableCell (IntPtr p): base (p)
- {
- }
-
IImageCellViewFrontend Frontend {
- get { return (IImageCellViewFrontend) Backend.Frontend; }
+ get { return (IImageCellViewFrontend)Backend.Frontend; }
}
public CellViewBackend Backend { get; set; }
public CompositeCell CellContainer { get; set; }
+ public NSView CellView { get { return this; } }
+
public void Fill ()
{
- ObjectValue = Frontend.Image.ToImageDescription (CellContainer.Context).ToNSImage ();
- visible = Frontend.Visible;
+ if (Frontend.Image != null) {
+ Image = Frontend.Image.ToImageDescription (Backend.Context).ToNSImage ();
+ SetFrameSize (Image.Size);
+ } else
+ SetFrameSize (CoreGraphics.CGSize.Empty);
+ Hidden = !Frontend.Visible;
}
-
- public override CGSize CellSize {
+
+ public override CoreGraphics.CGSize FittingSize {
get {
- NSImage img = ObjectValue as NSImage;
- if (img != null)
- return img.Size;
- else
- return base.CellSize;
+ if (Image == null)
+ return CGSize.Empty;
+ return Image.Size;
}
}
- public override CGSize CellSizeForBounds (CGRect bounds)
+ public override void SizeToFit()
{
- if (visible)
- return base.CellSizeForBounds (bounds);
- return CGSize.Empty;
+ if (Frame.Size.IsEmpty && Image != null)
+ SetFrameSize (Image.Size);
}
- public override void DrawInteriorWithFrame (CGRect cellFrame, NSView inView)
- {
- if (visible)
- base.DrawInteriorWithFrame (cellFrame, inView);
- }
-
public void CopyFrom (object other)
{
var ob = (ImageTableCell)other;
Backend = ob.Backend;
}
+
+ public override void UpdateTrackingAreas ()
+ {
+ if (trackingArea != null) {
+ RemoveTrackingArea (trackingArea);
+ trackingArea.Dispose ();
+ }
+ var options = NSTrackingAreaOptions.MouseMoved | NSTrackingAreaOptions.ActiveInKeyWindow | NSTrackingAreaOptions.MouseEnteredAndExited;
+ trackingArea = new NSTrackingArea (Bounds, options, this, null);
+ AddTrackingArea (trackingArea);
+ }
+
+ public override void RightMouseDown (NSEvent theEvent)
+ {
+ if (!this.HandleMouseDown (theEvent))
+ base.RightMouseDown (theEvent);
+ }
+
+ public override void RightMouseUp (NSEvent theEvent)
+ {
+ if (!this.HandleMouseUp (theEvent))
+ base.RightMouseUp (theEvent);
+ }
+
+ public override void MouseDown (NSEvent theEvent)
+ {
+ if (!this.HandleMouseDown (theEvent))
+ base.MouseDown (theEvent);
+ }
+
+ public override void MouseUp (NSEvent theEvent)
+ {
+ if (!this.HandleMouseUp (theEvent))
+ base.MouseUp (theEvent);
+ }
+
+ public override void OtherMouseDown (NSEvent theEvent)
+ {
+ if (!this.HandleMouseDown (theEvent))
+ base.OtherMouseDown (theEvent);
+ }
+
+ public override void OtherMouseUp (NSEvent theEvent)
+ {
+ if (!this.HandleMouseUp (theEvent))
+ base.OtherMouseUp (theEvent);
+ }
+
+ public override void MouseEntered (NSEvent theEvent)
+ {
+ this.HandleMouseEntered (theEvent);
+ base.MouseEntered (theEvent);
+ }
+
+ public override void MouseExited (NSEvent theEvent)
+ {
+ this.HandleMouseExited (theEvent);
+ base.MouseExited (theEvent);
+ }
+
+ public override void MouseMoved (NSEvent theEvent)
+ {
+ if (!this.HandleMouseMoved (theEvent))
+ base.MouseMoved (theEvent);
+ }
+
+ public override void MouseDragged (NSEvent theEvent)
+ {
+ if (!this.HandleMouseMoved (theEvent))
+ base.MouseDragged (theEvent);
+ }
+
+ public override void KeyDown (NSEvent theEvent)
+ {
+ if (!this.HandleKeyDown (theEvent))
+ base.KeyDown (theEvent);
+ }
+
+ public override void KeyUp (NSEvent theEvent)
+ {
+ if (!this.HandleKeyUp (theEvent))
+ base.KeyUp (theEvent);
+ }
}
}
-
diff --git a/Xwt.XamMac/Xwt.Mac.CellViews/RadioButtonTableCell.cs b/Xwt.XamMac/Xwt.Mac.CellViews/RadioButtonTableCell.cs
index 9a08f28b..f98b005b 100644
--- a/Xwt.XamMac/Xwt.Mac.CellViews/RadioButtonTableCell.cs
+++ b/Xwt.XamMac/Xwt.Mac.CellViews/RadioButtonTableCell.cs
@@ -25,27 +25,21 @@
// THE SOFTWARE.
using System;
using Xwt.Backends;
-
-#if MONOMAC
-using nint = System.Int32;
-using nfloat = System.Single;
-using MonoMac.AppKit;
-#else
using AppKit;
-#endif
+using CoreGraphics;
namespace Xwt.Mac
{
- class RadioButtonTableCell: NSButtonCell, ICellRenderer
+ class RadioButtonTableCell: NSButton, ICellRenderer
{
- bool visible = true;
+ NSTrackingArea trackingArea;
public RadioButtonTableCell ()
{
SetButtonType (NSButtonType.Radio);
AllowsMixedState = false;
Activated += HandleActivated;
- Title = "";
+ Title = string.Empty;
}
public override NSCellStateValue State {
@@ -53,30 +47,19 @@ namespace Xwt.Mac
return base.State;
}
set {
- if (base.State != value)
- stateChanging = true;
- base.State = value;
+ // don't let Cocoa set the state for us
}
}
- bool stateChanging;
void HandleActivated (object sender, EventArgs e)
{
- if (State == NSCellStateValue.On && stateChanging) {
- var cellView = Frontend;
- CellContainer.SetCurrentEventRow ();
- Frontend.Load (CellContainer);
- if (cellView.Editable && !cellView.RaiseToggled ()) {
- if (cellView.ActiveField != null)
- CellContainer.SetValue (cellView.ActiveField, State != NSCellStateValue.Off);
- } else
- base.State = NSCellStateValue.Off;
+ Backend.Load (this);
+ var cellView = Frontend;
+ CellContainer.SetCurrentEventRow ();
+ if (!cellView.RaiseToggled ()) {
+ if (cellView.ActiveField != null)
+ CellContainer.SetValue (cellView.ActiveField, true);
}
- stateChanging = false;
- }
-
- public RadioButtonTableCell (IntPtr p): base (p)
- {
}
IRadioButtonCellViewFrontend Frontend {
@@ -87,31 +70,113 @@ namespace Xwt.Mac
public CompositeCell CellContainer { get; set; }
+ public NSView CellView { get { return this; } }
+
+ static CGSize defaultSize = CGSize.Empty;
+ public override CGSize FittingSize {
+ get {
+ // Radio NSButton has always the same size, measure it only once
+ if (defaultSize.IsEmpty)
+ defaultSize = base.FittingSize;
+ return defaultSize;
+ }
+ }
+
public void Fill ()
{
var cellView = Frontend;
base.State = cellView.Active ? NSCellStateValue.On : NSCellStateValue.Off;
- Editable = cellView.Editable;
- visible = cellView.Visible;
+ Enabled = cellView.Editable;
+ Hidden = !cellView.Visible;
}
- public override CoreGraphics.CGSize CellSizeForBounds (CoreGraphics.CGRect bounds)
+ public void CopyFrom (object other)
{
- if (visible)
- return base.CellSizeForBounds (bounds);
- return CoreGraphics.CGSize.Empty;
+ var ob = (RadioButtonTableCell)other;
+ Backend = ob.Backend;
}
- public override void DrawInteriorWithFrame (CoreGraphics.CGRect cellFrame, NSView inView)
+ public override void UpdateTrackingAreas ()
{
- if (visible)
- base.DrawInteriorWithFrame (cellFrame, inView);
+ if (trackingArea != null) {
+ RemoveTrackingArea (trackingArea);
+ trackingArea.Dispose ();
+ }
+ var options = NSTrackingAreaOptions.MouseMoved | NSTrackingAreaOptions.ActiveInKeyWindow | NSTrackingAreaOptions.MouseEnteredAndExited;
+ trackingArea = new NSTrackingArea (Bounds, options, this, null);
+ AddTrackingArea (trackingArea);
}
- public void CopyFrom (object other)
+ public override void RightMouseDown (NSEvent theEvent)
{
- var ob = (RadioButtonTableCell)other;
- Backend = ob.Backend;
+ if (!this.HandleMouseDown (theEvent))
+ base.RightMouseDown (theEvent);
+ }
+
+ public override void RightMouseUp (NSEvent theEvent)
+ {
+ if (!this.HandleMouseUp (theEvent))
+ base.RightMouseUp (theEvent);
+ }
+
+ public override void MouseDown (NSEvent theEvent)
+ {
+ if (!this.HandleMouseDown (theEvent))
+ base.MouseDown (theEvent);
+ }
+
+ public override void MouseUp (NSEvent theEvent)
+ {
+ if (!this.HandleMouseUp (theEvent))
+ base.MouseUp (theEvent);
+ }
+
+ public override void OtherMouseDown (NSEvent theEvent)
+ {
+ if (!this.HandleMouseDown (theEvent))
+ base.OtherMouseDown (theEvent);
+ }
+
+ public override void OtherMouseUp (NSEvent theEvent)
+ {
+ if (!this.HandleMouseUp (theEvent))
+ base.OtherMouseUp (theEvent);
+ }
+
+ public override void MouseEntered (NSEvent theEvent)
+ {
+ this.HandleMouseEntered (theEvent);
+ base.MouseEntered (theEvent);
+ }
+
+ public override void MouseExited (NSEvent theEvent)
+ {
+ this.HandleMouseExited (theEvent);
+ base.MouseExited (theEvent);
+ }
+
+ public override void MouseMoved (NSEvent theEvent)
+ {
+ if (!this.HandleMouseMoved (theEvent))
+ base.MouseMoved (theEvent);
+ }
+
+ public override void MouseDragged (NSEvent theEvent)
+ {
+ if (!this.HandleMouseMoved (theEvent))
+ base.MouseDragged (theEvent);
+ }
+
+ public override void KeyDown (NSEvent theEvent)
+ {
+ if (!this.HandleKeyDown (theEvent))
+ base.KeyDown (theEvent);
+ }
+
+ public override void KeyUp (NSEvent theEvent)
+ {
+ if (!this.HandleKeyUp (theEvent))
+ base.KeyUp (theEvent);
}
}
}
diff --git a/Xwt.XamMac/Xwt.Mac.CellViews/TextTableCell.cs b/Xwt.XamMac/Xwt.Mac.CellViews/TextTableCell.cs
index f51e763a..0d79d44a 100644
--- a/Xwt.XamMac/Xwt.Mac.CellViews/TextTableCell.cs
+++ b/Xwt.XamMac/Xwt.Mac.CellViews/TextTableCell.cs
@@ -24,61 +24,148 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-
-using System;
using AppKit;
+using Foundation;
using Xwt.Backends;
namespace Xwt.Mac
{
- class TextTableCell: NSCell, ICellRenderer
+ class TextTableCell : NSTextField, ICellRenderer
{
- bool visible = true;
-
-
- public TextTableCell (): base ("")
- {
- Wraps = false;
- }
-
- public TextTableCell (IntPtr p): base (p)
+ NSTrackingArea trackingArea;
+ public TextTableCell ()
{
+ Editable = false;
+ Bezeled = false;
+ DrawsBackground = false;
}
ITextCellViewFrontend Frontend {
get { return (ITextCellViewFrontend) Backend.Frontend; }
}
+
+ public override bool AllowsVibrancy {
+ get {
+ // we don't support vibrancy
+ if (EffectiveAppearance.AllowsVibrancy)
+ return false;
+ return base.AllowsVibrancy;
+ }
+ }
public CellViewBackend Backend { get; set; }
public CompositeCell CellContainer { get; set; }
+ public NSView CellView { get { return this; } }
+
public void Fill ()
{
if (Frontend.Markup != null)
AttributedStringValue = FormattedText.FromMarkup (Frontend.Markup).ToAttributedString ();
else
StringValue = Frontend.Text ?? "";
- visible = Frontend.Visible;
+ Hidden = !Frontend.Visible;
+ }
+
+ public virtual NSBackgroundStyle BackgroundStyle {
+ [Export ("backgroundStyle")]
+ get {
+ return Cell.BackgroundStyle;
+ }
+ [Export ("setBackgroundStyle:")]
+ set {
+ Cell.BackgroundStyle = value;
+ }
}
- public override CoreGraphics.CGSize CellSizeForBounds (CoreGraphics.CGRect bounds)
+ public void CopyFrom (object other)
{
- if (visible)
- return base.CellSizeForBounds (bounds);
- return CoreGraphics.CGSize.Empty;
+ var ob = (TextTableCell)other;
+ Backend = ob.Backend;
}
- public override void DrawInteriorWithFrame (CoreGraphics.CGRect cellFrame, NSView inView)
+ public override void UpdateTrackingAreas ()
{
- if (visible)
- base.DrawInteriorWithFrame (cellFrame, inView);
+ if (trackingArea != null) {
+ RemoveTrackingArea (trackingArea);
+ trackingArea.Dispose ();
+ }
+ var options = NSTrackingAreaOptions.MouseMoved | NSTrackingAreaOptions.ActiveInKeyWindow | NSTrackingAreaOptions.MouseEnteredAndExited;
+ trackingArea = new NSTrackingArea (Bounds, options, this, null);
+ AddTrackingArea (trackingArea);
}
- public void CopyFrom (object other)
+ public override void RightMouseDown (NSEvent theEvent)
{
- var ob = (TextTableCell)other;
- Backend = ob.Backend;
+ if (!this.HandleMouseDown (theEvent))
+ base.RightMouseDown (theEvent);
+ }
+
+ public override void RightMouseUp (NSEvent theEvent)
+ {
+ if (!this.HandleMouseUp (theEvent))
+ base.RightMouseUp (theEvent);
+ }
+
+ public override void MouseDown (NSEvent theEvent)
+ {
+ if (!this.HandleMouseDown (theEvent))
+ base.MouseDown (theEvent);
+ }
+
+ public override void MouseUp (NSEvent theEvent)
+ {
+ if (!this.HandleMouseUp (theEvent))
+ base.MouseUp (theEvent);
+ }
+
+ public override void OtherMouseDown (NSEvent theEvent)
+ {
+ if (!this.HandleMouseDown (theEvent))
+ base.OtherMouseDown (theEvent);
+ }
+
+ public override void OtherMouseUp (NSEvent theEvent)
+ {
+ if (!this.HandleMouseUp (theEvent))
+ base.OtherMouseUp (theEvent);
+ }
+
+ public override void MouseEntered (NSEvent theEvent)
+ {
+ this.HandleMouseEntered (theEvent);
+ base.MouseEntered (theEvent);
+ }
+
+ public override void MouseExited (NSEvent theEvent)
+ {
+ this.HandleMouseExited (theEvent);
+ base.MouseExited (theEvent);
+ }
+
+ public override void MouseMoved (NSEvent theEvent)
+ {
+ if (!this.HandleMouseMoved (theEvent))
+ base.MouseMoved (theEvent);
+ }
+
+ public override void MouseDragged (NSEvent theEvent)
+ {
+ if (!this.HandleMouseMoved (theEvent))
+ base.MouseDragged (theEvent);
+ }
+
+ public override void KeyDown (NSEvent theEvent)
+ {
+ if (!this.HandleKeyDown (theEvent))
+ base.KeyDown (theEvent);
+ }
+
+ public override void KeyUp (NSEvent theEvent)
+ {
+ if (!this.HandleKeyUp (theEvent))
+ base.KeyUp (theEvent);
}
}
}
diff --git a/Xwt.XamMac/Xwt.Mac/ButtonBackend.cs b/Xwt.XamMac/Xwt.Mac/ButtonBackend.cs
index 12a1b235..119f5d9b 100644
--- a/Xwt.XamMac/Xwt.Mac/ButtonBackend.cs
+++ b/Xwt.XamMac/Xwt.Mac/ButtonBackend.cs
@@ -143,6 +143,17 @@ namespace Xwt.Mac
break;
}
}
+ bool isDefault;
+ public bool IsDefault {
+ get { return isDefault; }
+ set {
+ isDefault = value;
+ if (Widget.Window != null && Widget.Window.DefaultButtonCell != Widget.Cell)
+ Widget.Window.DefaultButtonCell = Widget.Cell;
+ }
+ }
+
+
#endregion
@@ -223,6 +234,12 @@ namespace Xwt.Mac
{
}
+ public override void ViewDidMoveToWindow ()
+ {
+ if ((Backend as ButtonBackend)?.IsDefault == true && Window != null)
+ Window.DefaultButtonCell = Cell;
+ }
+
void OnActivatedInternal ()
{
if (ActivatedInternal == null)
diff --git a/Xwt.XamMac/Xwt.Mac/ContextBackendHandler.cs b/Xwt.XamMac/Xwt.Mac/ContextBackendHandler.cs
index b459f884..70f9f640 100644
--- a/Xwt.XamMac/Xwt.Mac/ContextBackendHandler.cs
+++ b/Xwt.XamMac/Xwt.Mac/ContextBackendHandler.cs
@@ -47,6 +47,7 @@ namespace Xwt.Mac
{
public object Pattern;
public double GlobalAlpha = 1;
+ public CGColor GlobalColor = null;
public ContextStatus Previous;
}
@@ -67,6 +68,7 @@ namespace Xwt.Mac
ct.CurrentStatus = new ContextStatus {
Pattern = ct.CurrentStatus.Pattern,
GlobalAlpha = ct.CurrentStatus.GlobalAlpha,
+ GlobalColor = ct.CurrentStatus.GlobalColor,
Previous = ct.CurrentStatus,
};
}
@@ -217,6 +219,8 @@ namespace Xwt.Mac
{
CGContextBackend gc = (CGContextBackend)backend;
gc.CurrentStatus.Pattern = null;
+ // Store the current color for TextLayout using NSLayoutManager
+ gc.CurrentStatus.GlobalColor = color.ToCGColor ();
CGContext ctx = gc.Context;
ctx.SetFillColorSpace (Util.DeviceRGBColorSpace);
ctx.SetStrokeColorSpace (Util.DeviceRGBColorSpace);
@@ -285,7 +289,7 @@ namespace Xwt.Mac
CGContext ctx = ((CGContextBackend)backend).Context;
SetupContextForDrawing (ctx);
var li = ApplicationContext.Toolkit.GetSafeBackend (layout);
- MacTextLayoutBackendHandler.Draw (ctx, li, x, y);
+ MacTextLayoutBackendHandler.Draw ((CGContextBackend)backend, li, x, y);
}
public override void DrawImage (object backend, ImageDescription img, double x, double y)
diff --git a/Xwt.XamMac/Xwt.Mac/DialogBackend.cs b/Xwt.XamMac/Xwt.Mac/DialogBackend.cs
index 189c6be9..0d19d4cb 100644
--- a/Xwt.XamMac/Xwt.Mac/DialogBackend.cs
+++ b/Xwt.XamMac/Xwt.Mac/DialogBackend.cs
@@ -162,25 +162,8 @@ namespace Xwt.Mac
public void RunLoop (IWindowFrameBackend parent)
{
- if (parent != null)
- StyleMask &= ~NSWindowStyle.Miniaturizable;
- else
- StyleMask |= NSWindowStyle.Miniaturizable;
Visible = true;
modalSessionRunning = true;
- var win = parent as NSWindow ?? ApplicationContext.Toolkit.GetNativeWindow (parent) as NSWindow;
- if (win != null) {
- win.AddChildWindow (this, NSWindowOrderingMode.Above);
- // always use NSWindow for alignment when running in guest mode and
- // don't rely on AddChildWindow to position the window correctly
- if (!(parent is WindowBackend)) {
- var parentBounds = MacDesktopBackend.ToDesktopRect (win.ContentRectFor (win.Frame));
- var bounds = ((IWindowFrameBackend)this).Bounds;
- bounds.X = parentBounds.Center.X - (Frame.Width / 2);
- bounds.Y = parentBounds.Center.Y - (Frame.Height / 2);
- ((IWindowFrameBackend)this).Bounds = bounds;
- }
- }
NSApplication.SharedApplication.RunModalForWindow (this);
}
diff --git a/Xwt.XamMac/Xwt.Mac/IViewObject.cs b/Xwt.XamMac/Xwt.Mac/IViewObject.cs
index d2d35057..febd3282 100644
--- a/Xwt.XamMac/Xwt.Mac/IViewObject.cs
+++ b/Xwt.XamMac/Xwt.Mac/IViewObject.cs
@@ -25,6 +25,8 @@
// THE SOFTWARE.
using AppKit;
+using CoreGraphics;
+using System;
namespace Xwt.Mac
{
@@ -33,5 +35,145 @@ namespace Xwt.Mac
NSView View { get; }
ViewBackend Backend { get; set; }
}
+
+ public static class IViewObjectExtensions
+ {
+ public static void UpdateEventTrackingArea (this IViewObject view, ref NSTrackingArea replaceArea)
+ {
+ if (view == null)
+ throw new ArgumentNullException (nameof (view));
+ if (view.View == null)
+ throw new InvalidOperationException ();
+ if (replaceArea != null) {
+ view.View.RemoveTrackingArea (replaceArea);
+ replaceArea.Dispose ();
+ }
+ CGRect viewBounds = view.View.Bounds;
+ var options = NSTrackingAreaOptions.MouseMoved | NSTrackingAreaOptions.ActiveInKeyWindow | NSTrackingAreaOptions.MouseEnteredAndExited;
+ var trackingArea = new NSTrackingArea (viewBounds, options, view.View, null);
+ view.View.AddTrackingArea (trackingArea);
+ }
+
+ public static bool HandleMouseDown (this IViewObject view, NSEvent theEvent)
+ {
+ if (view == null)
+ throw new ArgumentNullException (nameof (view));
+ if (theEvent == null)
+ throw new ArgumentNullException (nameof (theEvent));
+ if (view.View == null)
+ throw new InvalidOperationException ();
+ CGPoint p = view.View.ConvertPointFromEvent (theEvent);
+ if (!view.View.Bounds.Contains (p))
+ return false;
+ ButtonEventArgs args = new ButtonEventArgs ();
+ args.X = p.X;
+ args.Y = p.Y;
+ args.Button = theEvent.GetPointerButton ();
+ args.IsContextMenuTrigger = theEvent.TriggersContextMenu ();
+ args.MultiplePress = (int)theEvent.ClickCount;
+ view.Backend.ApplicationContext.InvokeUserCode (delegate
+ {
+ view.Backend.EventSink.OnButtonPressed (args);
+ });
+ return args.Handled;
+ }
+
+ public static bool HandleMouseUp (this IViewObject view, NSEvent theEvent)
+ {
+ if (view == null)
+ throw new ArgumentNullException (nameof (view));
+ if (theEvent == null)
+ throw new ArgumentNullException (nameof (theEvent));
+ if (view.View == null)
+ throw new InvalidOperationException ();
+ CGPoint p = view.View.ConvertPointFromEvent (theEvent);
+ if (!view.View.Bounds.Contains (p))
+ return false;
+ ButtonEventArgs args = new ButtonEventArgs ();
+ args.X = p.X;
+ args.Y = p.Y;
+ args.Button = theEvent.GetPointerButton ();
+ args.MultiplePress = (int)theEvent.ClickCount;
+ view.Backend.ApplicationContext.InvokeUserCode (delegate {
+ view.Backend.EventSink.OnButtonReleased (args);
+ });
+ return args.Handled;
+ }
+
+ public static void HandleMouseEntered (this IViewObject view, NSEvent theEvent)
+ {
+ if (view == null)
+ throw new ArgumentNullException (nameof (view));
+ view.Backend.ApplicationContext.InvokeUserCode (view.Backend.EventSink.OnMouseEntered);
+ }
+
+ public static void HandleMouseExited (this IViewObject view, NSEvent theEvent)
+ {
+ if (view == null)
+ throw new ArgumentNullException (nameof (view));
+ view.Backend.ApplicationContext.InvokeUserCode (view.Backend.EventSink.OnMouseExited);
+ }
+
+ public static bool HandleMouseMoved (this IViewObject view, NSEvent theEvent)
+ {
+ if (view == null)
+ throw new ArgumentNullException (nameof (view));
+ if (theEvent == null)
+ throw new ArgumentNullException (nameof (theEvent));
+ if (view.View == null)
+ throw new InvalidOperationException ();
+ CGPoint p = view.View.ConvertPointFromEvent (theEvent);
+ if (!view.View.Bounds.Contains (p))
+ return false;
+ MouseMovedEventArgs args = new MouseMovedEventArgs ((long)TimeSpan.FromSeconds (theEvent.Timestamp).TotalMilliseconds, p.X, p.Y);
+ view.Backend.ApplicationContext.InvokeUserCode (delegate {
+ view.Backend.EventSink.OnMouseMoved (args);
+ });
+ return args.Handled;
+ }
+
+ public static bool HandleKeyDown (this IViewObject view, NSEvent theEvent)
+ {
+ if (view == null)
+ throw new ArgumentNullException (nameof (view));
+ if (theEvent == null)
+ throw new ArgumentNullException (nameof (theEvent));
+ if (view.View == null)
+ throw new InvalidOperationException ();
+
+ var keyArgs = theEvent.ToXwtKeyEventArgs ();
+ view.Backend.ApplicationContext.InvokeUserCode (delegate {
+ view.Backend.EventSink.OnKeyPressed (keyArgs);
+ });
+ if (keyArgs.Handled)
+ return true;
+
+ var textArgs = new TextInputEventArgs (theEvent.Characters);
+ if (!String.IsNullOrEmpty (theEvent.Characters))
+ view.Backend.ApplicationContext.InvokeUserCode (delegate {
+ view.Backend.EventSink.OnTextInput (textArgs);
+ });
+ if (textArgs.Handled)
+ return true;
+
+ return false;
+ }
+
+ public static bool HandleKeyUp (this IViewObject view, NSEvent theEvent)
+ {
+ if (view == null)
+ throw new ArgumentNullException (nameof (view));
+ if (theEvent == null)
+ throw new ArgumentNullException (nameof (theEvent));
+ if (view.View == null)
+ throw new InvalidOperationException ();
+
+ var keyArgs = theEvent.ToXwtKeyEventArgs ();
+ view.Backend.ApplicationContext.InvokeUserCode (delegate {
+ view.Backend.EventSink.OnKeyReleased (keyArgs);
+ });
+ return keyArgs.Handled;
+ }
+ }
}
diff --git a/Xwt.XamMac/Xwt.Mac/LabelBackend.cs b/Xwt.XamMac/Xwt.Mac/LabelBackend.cs
index cab91899..ba87df67 100644
--- a/Xwt.XamMac/Xwt.Mac/LabelBackend.cs
+++ b/Xwt.XamMac/Xwt.Mac/LabelBackend.cs
@@ -271,6 +271,30 @@ namespace Xwt.Mac
{
CGColor bgColor;
+ public CustomTextFieldCell ()
+ {
+ }
+
+ protected CustomTextFieldCell (IntPtr ptr) : base (ptr)
+ {
+ }
+
+ /// <summary>
+ /// Like what happens for the ios designer, AppKit can sometimes clone the native `NSTextFieldCell` using the Copy (NSZone)
+ /// method. We *need* to ensure we can create a new managed wrapper for the cloned native object so we need the IntPtr
+ /// constructor. NOTE: By keeping this override in managed we ensure the new wrapper C# object is created ~immediately,
+ /// which makes it easier to debug issues.
+ /// </summary>
+ /// <returns>The copy.</returns>
+ /// <param name="zone">Zone.</param>
+ public override NSObject Copy(NSZone zone)
+ {
+ // Don't remove this override because the comment on this explains why we need this!
+ var newCell = (CustomTextFieldCell)base.Copy(zone);
+ newCell.bgColor = bgColor;
+ return newCell;
+ }
+
public void SetBackgroundColor (CGColor c)
{
bgColor = c;
diff --git a/Xwt.XamMac/Xwt.Mac/ListBoxBackend.cs b/Xwt.XamMac/Xwt.Mac/ListBoxBackend.cs
index aad70347..d2b9b65d 100644
--- a/Xwt.XamMac/Xwt.Mac/ListBoxBackend.cs
+++ b/Xwt.XamMac/Xwt.Mac/ListBoxBackend.cs
@@ -32,7 +32,7 @@ namespace Xwt.Mac
{
public class ListBoxBackend: ListViewBackend, IListBoxBackend
{
- ListViewColumn column = new ListViewColumn ();
+ readonly ListViewColumn column = new ListViewColumn { Expands = true };
NSTableColumn columnHandle;
public ListBoxBackend ()
@@ -64,38 +64,6 @@ namespace Xwt.Mac
column.Views.Add (v);
UpdateColumn (column, columnHandle, ListViewColumnChange.Cells);
}
-
- public override void SetSource (IListDataSource source, IBackend sourceBackend)
- {
- base.SetSource (source, sourceBackend);
-
- source.RowInserted += HandleColumnSizeChanged;
- source.RowDeleted += HandleColumnSizeChanged;
- source.RowChanged += HandleColumnSizeChanged;
- ResetColumnSize (source);
- }
-
- void HandleColumnSizeChanged (object sender, ListRowEventArgs e)
- {
- var source = (IListDataSource)sender;
- ResetColumnSize (source);
- }
-
- void ResetColumnSize (IListDataSource source)
- {
- // Calculate size of column
- // This is how Apple implements it; unfortunately, they don't expose this functionality in the API.
- // https://developer.apple.com/library/mac/documentation/Cocoa/Reference/NSTableViewDelegate_Protocol/index.html#//apple_ref/occ/intfm/NSTableViewDelegate/tableView:sizeToFitWidthOfColumn:
- nfloat w = 0;
- for (var row = 0; row < source.RowCount; row++) {
- using (var cell = Table.GetCell (0, row)) {
- var size = cell.CellSize;
- w = (nfloat)Math.Max (w, size.Width);
- }
- }
- columnHandle.MinWidth = (nfloat)Math.Ceiling (w);
- columnHandle.Width = (nfloat)Math.Ceiling (w);
- }
}
}
diff --git a/Xwt.XamMac/Xwt.Mac/ListViewBackend.cs b/Xwt.XamMac/Xwt.Mac/ListViewBackend.cs
index c93d90b5..0566e606 100644
--- a/Xwt.XamMac/Xwt.Mac/ListViewBackend.cs
+++ b/Xwt.XamMac/Xwt.Mac/ListViewBackend.cs
@@ -28,6 +28,8 @@
// THE SOFTWARE.
using System;
+using System.Collections.Generic;
+using System.Linq;
using AppKit;
using CoreGraphics;
using Foundation;
@@ -37,12 +39,67 @@ namespace Xwt.Mac
{
public class ListViewBackend: TableViewBackend<NSTableView, IListViewEventSink>, IListViewBackend
{
+ class ListDelegate: NSTableViewDelegate
+ {
+ public ListViewBackend Backend;
+
+ public override nfloat GetRowHeight (NSTableView tableView, nint row)
+ {
+ // GetView() and GetRowView() can't be used here, hence we use cached
+ // sizes calculated by the backend or calcuate the height using the template view
+ nfloat height = Backend.RowHeights[(int)row];
+ if (height <= -1)
+ height = Backend.RowHeights [(int)row] = Backend.CalcRowHeight (row, false);
+ return height;
+ }
+
+ public override NSTableRowView CoreGetRowView (NSTableView tableView, nint row)
+ {
+ return tableView.GetRowView (row, false) ?? new TableRowView ();
+ }
+
+ public override NSView GetViewForItem (NSTableView tableView, NSTableColumn tableColumn, nint row)
+ {
+ var col = tableColumn as TableColumn;
+ var cell = tableView.MakeView (tableColumn.Identifier, this) as CompositeCell;
+ if (cell == null)
+ cell = col.CreateNewView ();
+ cell.ObjectValue = NSNumber.FromNInt (row);
+ return cell;
+ }
+
+ public override nfloat GetSizeToFitColumnWidth (NSTableView tableView, nint column)
+ {
+ var tableColumn = Backend.Columns[(int)column] as TableColumn;
+ var width = tableColumn.HeaderCell.CellSize.Width;
+
+ CompositeCell templateCell = null;
+ for (int i = 0; i < tableView.RowCount; i++) {
+ var cellView = tableView.GetView (column, i, false) as CompositeCell;
+ if (cellView == null) { // use template for invisible rows
+ cellView = templateCell ?? (templateCell = (tableColumn as TableColumn)?.DataView?.Copy () as CompositeCell);
+ if (cellView != null)
+ cellView.ObjectValue = NSNumber.FromInt32 (i);
+ }
+ width = (nfloat)Math.Max (width, cellView.FittingSize.Width);
+ }
+ return width;
+ }
+
+ public override NSIndexSet GetSelectionIndexes(NSTableView tableView, NSIndexSet proposedSelectionIndexes)
+ {
+ return Backend.SelectionMode != SelectionMode.None ? proposedSelectionIndexes : new NSIndexSet();
+ }
+ }
+
IListDataSource source;
ListSource tsource;
protected override NSTableView CreateView ()
{
- return new NSTableViewBackend (EventSink, ApplicationContext);
+ var listView = new NSTableViewBackend (this);
+ listView.Delegate = new ListDelegate { Backend = this };
+ return listView;
}
protected override string SelectionChangeEventName {
@@ -87,16 +144,64 @@ namespace Xwt.Mac
public virtual void SetSource (IListDataSource source, IBackend sourceBackend)
{
this.source = source;
+
+ RowHeights = new List<nfloat> ();
+ for (int i = 0; i < source.RowCount; i++)
+ RowHeights.Add (-1);
+
tsource = new ListSource (source);
Table.DataSource = tsource;
//TODO: Reloading single rows would be slightly more efficient.
// According to NSTableView.ReloadData() documentation,
// only the visible rows are reloaded.
- source.RowInserted += (sender, e) => Table.ReloadData();
- source.RowDeleted += (sender, e) => Table.ReloadData();
- source.RowChanged += (sender, e) => Table.ReloadData (NSIndexSet.FromIndex (e.Row), NSIndexSet.FromNSRange (new NSRange(0, Table.ColumnCount - 1)));
- source.RowsReordered += (sender, e) => Table.ReloadData();
+ source.RowInserted += (sender, e) => {
+ RowHeights.Insert (e.Row, -1);
+ Table.ReloadData ();
+ };
+ source.RowDeleted += (sender, e) => {
+ RowHeights.RemoveAt (e.Row);
+ Table.ReloadData ();
+ };
+ source.RowChanged += (sender, e) => {
+ UpdateRowHeight (e.Row);
+ Table.ReloadData (NSIndexSet.FromIndex (e.Row), NSIndexSet.FromNSRange (new NSRange (0, Table.ColumnCount)));
+ };
+ source.RowsReordered += (sender, e) => { RowHeights.Clear (); Table.ReloadData (); };
+ }
+
+ public override void InvalidateRowHeight (object pos)
+ {
+ UpdateRowHeight((int)pos);
+ }
+
+ List<nfloat> RowHeights = new List<nfloat> ();
+ bool updatingRowHeight;
+ public void UpdateRowHeight (nint row)
+ {
+ if (updatingRowHeight)
+ return;
+ RowHeights[(int)row] = CalcRowHeight (row);
+ Table.NoteHeightOfRowsWithIndexesChanged (NSIndexSet.FromIndex (row));
+ }
+
+ nfloat CalcRowHeight (nint row, bool tryReuse = true)
+ {
+ updatingRowHeight = true;
+ var height = Table.RowHeight;
+
+ for (int i = 0; i < Columns.Count; i++) {
+ CompositeCell cell = tryReuse ? Table.GetView (i, row, false) as CompositeCell : null;
+ if (cell == null) {
+ cell = (Columns [i] as TableColumn)?.DataView as CompositeCell;
+ cell.ObjectValue = NSNumber.FromNInt (row);
+ height = (nfloat)Math.Max (height, cell.FittingSize.Height);
+ } else {
+ height = (nfloat)Math.Max (height, cell.GetRequiredHeightForWidth (cell.Frame.Width));
+ }
+ }
+ updatingRowHeight = false;
+ return height;
}
public int[] SelectedRows {
diff --git a/Xwt.XamMac/Xwt.Mac/MenuItemBackend.cs b/Xwt.XamMac/Xwt.Mac/MenuItemBackend.cs
index 062775c3..0c5f9f28 100644
--- a/Xwt.XamMac/Xwt.Mac/MenuItemBackend.cs
+++ b/Xwt.XamMac/Xwt.Mac/MenuItemBackend.cs
@@ -80,6 +80,18 @@ namespace Xwt.Mac
}
}
+ public string TooltipText
+ {
+ get
+ {
+ return item.ToolTip;
+ }
+ set
+ {
+ item.ToolTip = value;
+ }
+ }
+
public bool UseMnemonic
{
get
diff --git a/Xwt.XamMac/Xwt.Mac/Messaging.cs b/Xwt.XamMac/Xwt.Mac/Messaging.cs
index 8f6a20ca..bad8ab8b 100644
--- a/Xwt.XamMac/Xwt.Mac/Messaging.cs
+++ b/Xwt.XamMac/Xwt.Mac/Messaging.cs
@@ -57,5 +57,23 @@ namespace Xwt.Mac
[DllImport (LIBOBJC_DYLIB, EntryPoint="objc_msgSend")]
public static extern void void_objc_msgSend_bool (IntPtr handle, IntPtr sel, bool a1);
+
+ //[DllImport (LIBOBJC_DYLIB, EntryPoint = "objc_msgSend")]
+ //public static extern long Int64_objc_msgSend (IntPtr receiver, IntPtr selector);
+
+ //[DllImport (LIBOBJC_DYLIB, EntryPoint = "objc_msgSend")]
+ //public static extern int int_objc_msgSend (IntPtr receiver, IntPtr selector);
+
+ [DllImport (LIBOBJC_DYLIB, EntryPoint = "objc_msgSend")]
+ public static extern void void_objc_msgSend_int (IntPtr receiver, IntPtr selector, int arg1);
+
+ [DllImport (LIBOBJC_DYLIB, EntryPoint = "objc_msgSend")]
+ public static extern void void_objc_msgSend_Int64 (IntPtr receiver, IntPtr selector, long arg1);
+
+ //[DllImport (LIBOBJC_DYLIB, EntryPoint = "objc_msgSendSuper")]
+ //public static extern void void_objc_msgSendSuper_int (IntPtr receiver, IntPtr selector, int arg1);
+
+ //[DllImport (LIBOBJC_DYLIB, EntryPoint = "objc_msgSendSuper")]
+ //public static extern void void_objc_msgSendSuper_Int64 (IntPtr receiver, IntPtr selector, long arg1);
}
}
diff --git a/Xwt.XamMac/Xwt.Mac/NSTableViewBackend.cs b/Xwt.XamMac/Xwt.Mac/NSTableViewBackend.cs
index ad3b7ab3..c83d0d77 100644
--- a/Xwt.XamMac/Xwt.Mac/NSTableViewBackend.cs
+++ b/Xwt.XamMac/Xwt.Mac/NSTableViewBackend.cs
@@ -32,29 +32,14 @@ using AppKit;
using CoreGraphics;
using Foundation;
using Xwt.Backends;
+using ObjCRuntime;
namespace Xwt.Mac
{
- public class NSTableViewBackend : NSTableView
+ public class NSTableViewBackend : NSTableView, IViewObject
{
- IWidgetEventSink eventSink;
- protected ApplicationContext context;
NSTrackingArea trackingArea; // Captures Mouse Entered, Exited, and Moved events
- class ListDelegate: NSTableViewDelegate
- {
- public override nfloat GetRowHeight (NSTableView tableView, nint row)
- {
- var height = tableView.RowHeight;
- for (int i = 0; i < tableView.ColumnCount; i++) {
- var cell = tableView.GetCell (i, row);
- if (cell != null)
- height = (nfloat) Math.Max (height, cell.CellSize.Height);
- }
- return height;
- }
- }
-
public override NSObject WeakDataSource {
get { return base.WeakDataSource; }
set {
@@ -71,39 +56,29 @@ namespace Xwt.Mac
internal void AutosizeColumns ()
{
+ if (DataSource == null || RowCount == 0)
+ return;
var columns = TableColumns ();
- foreach (var col in columns)
- AutosizeColumn (col);
- if (columns.Any (c => c.ResizingMask.HasFlag (NSTableColumnResizing.Autoresizing)))
+ if (columns.Length == 1 && columns[0].ResizingMask.HasFlag (NSTableColumnResizing.Autoresizing))
+ return;
+ var needsSizeToFit = false;
+ for (nint i = 0; i < columns.Length; i++) {
+ AutosizeColumn (columns[i], i);
+ needsSizeToFit |= columns[i].ResizingMask.HasFlag (NSTableColumnResizing.Autoresizing);
+ }
+ if (needsSizeToFit)
SizeToFit ();
}
- void AutosizeColumn (NSTableColumn tableColumn)
+ void AutosizeColumn (NSTableColumn tableColumn, nint colIndex)
{
- var column = IndexOfColumn (tableColumn);
-
- var s = tableColumn.HeaderCell.CellSize;
+ var contentWidth = tableColumn.HeaderCell.CellSize.Width;
if (!tableColumn.ResizingMask.HasFlag (NSTableColumnResizing.UserResizingMask)) {
- for (int i = 0; i < base.RowCount; i++)
- {
- var cell = base.GetCell (column, i);
- s.Width = (nfloat)Math.Max (s.Width, cell.CellSize.Width);
- }
+ contentWidth = Delegate.GetSizeToFitColumnWidth (this, colIndex);
if (!tableColumn.ResizingMask.HasFlag (NSTableColumnResizing.Autoresizing))
- tableColumn.Width = s.Width;
+ tableColumn.Width = contentWidth;
}
- tableColumn.MinWidth = s.Width;
- }
-
- nint IndexOfColumn (NSTableColumn tableColumn)
- {
- nint icol = -1;
- foreach (var col in TableColumns ()) {
- icol++;
- if (col == tableColumn)
- return icol;
- }
- return icol;
+ tableColumn.MinWidth = contentWidth;
}
public override void ReloadData ()
@@ -123,148 +98,115 @@ namespace Xwt.Mac
{
if (!columnResizeQueued) {
columnResizeQueued = true;
- Application.MainLoop.QueueExitAction (delegate {
+ (Backend.ApplicationContext.Toolkit.GetSafeBackend (Backend.ApplicationContext.Toolkit) as ToolkitEngineBackend).InvokeBeforeMainLoop (delegate {
columnResizeQueued = false;
AutosizeColumns ();
});
}
}
- public NSTableViewBackend(IWidgetEventSink eventSink, ApplicationContext context) {
- this.context = context;
- this.eventSink = eventSink;
- this.Delegate = new ListDelegate ();
+ public NSTableViewBackend (ListViewBackend viewBackend) {
+ Backend = viewBackend;
AllowsColumnReordering = false;
}
-
public ViewBackend Backend { get; set; }
- public NSTableView View {
+ public NSView View {
get { return this; }
}
+ public override void ResetCursorRects ()
+ {
+ base.ResetCursorRects ();
+ if (Backend.Cursor != null)
+ AddCursorRect (Bounds, Backend.Cursor);
+ }
+
public override void UpdateTrackingAreas ()
{
- if (trackingArea != null) {
- RemoveTrackingArea (trackingArea);
- trackingArea.Dispose ();
- }
- CGRect viewBounds = this.Bounds;
- var options = NSTrackingAreaOptions.MouseMoved | NSTrackingAreaOptions.ActiveInKeyWindow | NSTrackingAreaOptions.MouseEnteredAndExited;
- trackingArea = new NSTrackingArea (viewBounds, options, this, null);
- AddTrackingArea (trackingArea);
+ this.UpdateEventTrackingArea (ref trackingArea);
}
public override void RightMouseDown (NSEvent theEvent)
{
- base.RightMouseDown (theEvent);
- var p = ConvertPointFromView (theEvent.LocationInWindow, null);
- ButtonEventArgs args = new ButtonEventArgs ();
- args.X = p.X;
- args.Y = p.Y;
- args.Button = PointerButton.Right;
- args.IsContextMenuTrigger = theEvent.TriggersContextMenu ();
- context.InvokeUserCode (delegate {
- eventSink.OnButtonPressed (args);
- });
+ if (!this.HandleMouseDown (theEvent))
+ base.RightMouseDown (theEvent);
}
public override void RightMouseUp (NSEvent theEvent)
{
- base.RightMouseUp (theEvent);
- var p = ConvertPointFromView (theEvent.LocationInWindow, null);
- ButtonEventArgs args = new ButtonEventArgs ();
- args.X = p.X;
- args.Y = p.Y;
- args.Button = PointerButton.Right;
- context.InvokeUserCode (delegate {
- eventSink.OnButtonReleased (args);
- });
+ if (!this.HandleMouseUp (theEvent))
+ base.RightMouseUp (theEvent);
}
public override void MouseDown (NSEvent theEvent)
{
- base.MouseDown (theEvent);
- var p = ConvertPointFromView (theEvent.LocationInWindow, null);
- ButtonEventArgs args = new ButtonEventArgs ();
- args.X = p.X;
- args.Y = p.Y;
- args.Button = PointerButton.Left;
- args.IsContextMenuTrigger = theEvent.TriggersContextMenu ();
- context.InvokeUserCode (delegate {
- eventSink.OnButtonPressed (args);
- });
+ if (!this.HandleMouseDown (theEvent))
+ base.MouseDown (theEvent);
}
public override void MouseUp (NSEvent theEvent)
{
- base.MouseUp (theEvent);
- var p = ConvertPointFromView (theEvent.LocationInWindow, null);
- ButtonEventArgs args = new ButtonEventArgs ();
- args.X = p.X;
- args.Y = p.Y;
- args.Button = (PointerButton) (int)theEvent.ButtonNumber + 1;
- context.InvokeUserCode (delegate {
- eventSink.OnButtonReleased (args);
- });
+ if (!this.HandleMouseUp (theEvent))
+ base.MouseUp (theEvent);
+ }
+
+ public override void OtherMouseDown (NSEvent theEvent)
+ {
+ if (!this.HandleMouseDown (theEvent))
+ base.OtherMouseDown (theEvent);
+ }
+
+ public override void OtherMouseUp (NSEvent theEvent)
+ {
+ if (!this.HandleMouseUp (theEvent))
+ base.OtherMouseUp (theEvent);
}
public override void MouseEntered (NSEvent theEvent)
{
- context.InvokeUserCode (eventSink.OnMouseEntered);
+ this.HandleMouseEntered (theEvent);
}
public override void MouseExited (NSEvent theEvent)
{
- context.InvokeUserCode (eventSink.OnMouseExited);
+ this.HandleMouseExited (theEvent);
}
public override void MouseMoved (NSEvent theEvent)
{
- var p = ConvertPointFromView (theEvent.LocationInWindow, null);
- MouseMovedEventArgs args = new MouseMovedEventArgs ((long) TimeSpan.FromSeconds (theEvent.Timestamp).TotalMilliseconds, p.X, p.Y);
- context.InvokeUserCode (delegate {
- eventSink.OnMouseMoved (args);
- });
+ if (!this.HandleMouseMoved (theEvent))
+ base.MouseMoved (theEvent);
}
- public override void MouseDragged (NSEvent theEvent)
+ public override void RightMouseDragged (NSEvent theEvent)
{
- var p = ConvertPointFromView (theEvent.LocationInWindow, null);
- MouseMovedEventArgs args = new MouseMovedEventArgs ((long) TimeSpan.FromSeconds (theEvent.Timestamp).TotalMilliseconds, p.X, p.Y);
- context.InvokeUserCode (delegate {
- eventSink.OnMouseMoved (args);
- });
+ if (!this.HandleMouseMoved (theEvent))
+ base.RightMouseDragged (theEvent);
}
- public override void KeyDown (NSEvent theEvent)
+ public override void MouseDragged (NSEvent theEvent)
{
- var keyArgs = theEvent.ToXwtKeyEventArgs ();
- context.InvokeUserCode (delegate {
- eventSink.OnKeyPressed (keyArgs);
- });
- if (keyArgs.Handled)
- return;
+ if (!this.HandleMouseMoved (theEvent))
+ base.MouseDragged (theEvent);
+ }
- var textArgs = new TextInputEventArgs (theEvent.Characters);
- if (!String.IsNullOrEmpty(theEvent.Characters))
- context.InvokeUserCode (delegate {
- eventSink.OnTextInput (textArgs);
- });
- if (textArgs.Handled)
- return;
+ public override void OtherMouseDragged (NSEvent theEvent)
+ {
+ if (!this.HandleMouseMoved (theEvent))
+ base.OtherMouseDragged (theEvent);
+ }
- base.KeyDown (theEvent);
+ public override void KeyDown (NSEvent theEvent)
+ {
+ if (!this.HandleKeyDown (theEvent))
+ base.KeyDown (theEvent);
}
public override void KeyUp (NSEvent theEvent)
{
- var keyArgs = theEvent.ToXwtKeyEventArgs ();
- context.InvokeUserCode (delegate {
- eventSink.OnKeyReleased (keyArgs);
- });
- if (!keyArgs.Handled)
+ if (!this.HandleKeyUp (theEvent))
base.KeyUp (theEvent);
}
}
diff --git a/Xwt.XamMac/Xwt.Mac/OutlineViewBackend.cs b/Xwt.XamMac/Xwt.Mac/OutlineViewBackend.cs
index df44f836..8684ded9 100644
--- a/Xwt.XamMac/Xwt.Mac/OutlineViewBackend.cs
+++ b/Xwt.XamMac/Xwt.Mac/OutlineViewBackend.cs
@@ -34,23 +34,22 @@ using Xwt.Backends;
namespace Xwt.Mac
{
- public class OutlineViewBackend : NSOutlineView
+ public class OutlineViewBackend : NSOutlineView, IViewObject
{
- ITreeViewEventSink eventSink;
- protected ApplicationContext context;
NSTrackingArea trackingArea;
- public OutlineViewBackend (ITreeViewEventSink eventSink, ApplicationContext context)
+ public OutlineViewBackend (TreeViewBackend viewBackend)
{
- this.context = context;
- this.eventSink = eventSink;
+ Backend = viewBackend;
AllowsColumnReordering = false;
}
- public NSOutlineView View {
+ public NSView View {
get { return this; }
}
+ public ViewBackend Backend { get; set; }
+
public override NSObject WeakDataSource {
get { return base.WeakDataSource; }
set {
@@ -59,6 +58,12 @@ namespace Xwt.Mac
}
}
+ bool animationsEnabled = true;
+ public bool AnimationsEnabled {
+ get { return animationsEnabled; }
+ set { animationsEnabled = value; }
+ }
+
public override void AddColumn (NSTableColumn tableColumn)
{
base.AddColumn (tableColumn);
@@ -67,70 +72,84 @@ namespace Xwt.Mac
internal void AutosizeColumns ()
{
+ if (DataSource == null || RowCount == 0)
+ return;
var columns = TableColumns ();
- foreach (var col in columns)
- AutosizeColumn (col);
- if (columns.Any (c => c.ResizingMask.HasFlag (NSTableColumnResizing.Autoresizing)))
+ if (columns.Length == 1 && columns[0].ResizingMask.HasFlag (NSTableColumnResizing.Autoresizing))
+ return;
+ var needsSizeToFit = false;
+ for (nint i = 0; i < columns.Length; i++) {
+ AutosizeColumn (columns[i], i);
+ needsSizeToFit |= columns[i].ResizingMask.HasFlag (NSTableColumnResizing.Autoresizing);
+ }
+ if (needsSizeToFit)
SizeToFit ();
}
- void AutosizeColumn (NSTableColumn tableColumn)
+ void AutosizeColumn (NSTableColumn tableColumn, nint colIndex)
{
- var column = IndexOfColumn (tableColumn);
-
- var s = tableColumn.HeaderCell.CellSize;
+ var contentWidth = tableColumn.HeaderCell.CellSize.Width;
if (!tableColumn.ResizingMask.HasFlag (NSTableColumnResizing.UserResizingMask)) {
- for (int i = 0; i < base.RowCount; i++) {
- var cell = GetCell (column, i);
- if (column == 0)
- { // first column contains expanders
- var f = GetCellFrame (column, i);
- s.Width = (nfloat)Math.Max (s.Width, f.X + cell.CellSize.Width);
- }
- else
- s.Width = (nfloat)Math.Max (s.Width, cell.CellSize.Width);
- }
+ contentWidth = Delegate.GetSizeToFitColumnWidth (this, colIndex);
if (!tableColumn.ResizingMask.HasFlag (NSTableColumnResizing.Autoresizing))
- tableColumn.Width = s.Width;
+ tableColumn.Width = contentWidth;
}
- tableColumn.MinWidth = s.Width;
- }
-
- nint IndexOfColumn (NSTableColumn tableColumn)
- {
- nint icol = -1;
- foreach (var col in TableColumns ()) {
- icol++;
- if (col == tableColumn)
- return icol;
- }
- return icol;
+ tableColumn.MinWidth = contentWidth;
}
public override void ExpandItem (NSObject item)
{
+ BeginExpandCollapseAnimation ();
base.ExpandItem (item);
+ EndExpandCollapseAnimation ();
QueueColumnResize ();
}
public override void ExpandItem (NSObject item, bool expandChildren)
{
+ BeginExpandCollapseAnimation ();
base.ExpandItem (item, expandChildren);
+ EndExpandCollapseAnimation ();
QueueColumnResize ();
}
public override void CollapseItem (NSObject item)
{
+ BeginExpandCollapseAnimation ();
base.CollapseItem (item);
+ EndExpandCollapseAnimation ();
QueueColumnResize ();
}
public override void CollapseItem (NSObject item, bool collapseChildren)
{
+ BeginExpandCollapseAnimation ();
base.CollapseItem (item, collapseChildren);
+ EndExpandCollapseAnimation ();
QueueColumnResize ();
}
+ public override void NoteHeightOfRowsWithIndexesChanged(NSIndexSet indexSet)
+ {
+ BeginExpandCollapseAnimation();
+ base.NoteHeightOfRowsWithIndexesChanged(indexSet);
+ EndExpandCollapseAnimation();
+ }
+
+ void BeginExpandCollapseAnimation ()
+ {
+ if (!AnimationsEnabled) {
+ NSAnimationContext.BeginGrouping ();
+ NSAnimationContext.CurrentContext.Duration = 0;
+ }
+ }
+
+ void EndExpandCollapseAnimation ()
+ {
+ if (!AnimationsEnabled)
+ NSAnimationContext.EndGrouping ();
+ }
+
public override void ReloadData ()
{
base.ReloadData ();
@@ -160,109 +179,105 @@ namespace Xwt.Mac
{
if (!columnResizeQueued) {
columnResizeQueued = true;
- Application.MainLoop.QueueExitAction (delegate {
+ (Backend.ApplicationContext.Toolkit.GetSafeBackend (Backend.ApplicationContext.Toolkit) as ToolkitEngineBackend).InvokeBeforeMainLoop (delegate {
columnResizeQueued = false;
AutosizeColumns ();
});
}
}
+ public override void ResetCursorRects ()
+ {
+ base.ResetCursorRects ();
+ if (Backend.Cursor != null)
+ AddCursorRect (Bounds, Backend.Cursor);
+ }
+
public override void UpdateTrackingAreas ()
{
- if (trackingArea != null) {
- RemoveTrackingArea (trackingArea);
- trackingArea.Dispose ();
- }
- var viewBounds = this.Bounds;
- var options = NSTrackingAreaOptions.MouseMoved | NSTrackingAreaOptions.ActiveInKeyWindow | NSTrackingAreaOptions.MouseEnteredAndExited;
- trackingArea = new NSTrackingArea (viewBounds, options, this, null);
- AddTrackingArea (trackingArea);
+ this.UpdateEventTrackingArea (ref trackingArea);
}
public override void RightMouseDown (NSEvent theEvent)
{
- base.RightMouseUp (theEvent);
- var p = ConvertPointFromView (theEvent.LocationInWindow, null);
- ButtonEventArgs args = new ButtonEventArgs ();
- args.X = p.X;
- args.Y = p.Y;
- args.Button = PointerButton.Right;
- args.IsContextMenuTrigger = theEvent.TriggersContextMenu ();
- context.InvokeUserCode (delegate {
- eventSink.OnButtonPressed (args);
- });
+ if (!this.HandleMouseDown (theEvent))
+ base.RightMouseDown (theEvent);
}
public override void RightMouseUp (NSEvent theEvent)
{
- base.RightMouseUp (theEvent);
- var p = ConvertPointFromView (theEvent.LocationInWindow, null);
- ButtonEventArgs args = new ButtonEventArgs ();
- args.X = p.X;
- args.Y = p.Y;
- args.Button = PointerButton.Right;
- context.InvokeUserCode (delegate {
- eventSink.OnButtonReleased (args);
- });
+ if (!this.HandleMouseUp (theEvent))
+ base.RightMouseUp (theEvent);
}
public override void MouseDown (NSEvent theEvent)
{
- base.MouseDown (theEvent);
- var p = ConvertPointFromView (theEvent.LocationInWindow, null);
- ButtonEventArgs args = new ButtonEventArgs ();
- args.X = p.X;
- args.Y = p.Y;
- args.Button = PointerButton.Left;
- args.IsContextMenuTrigger = theEvent.TriggersContextMenu ();
- context.InvokeUserCode (delegate {
- eventSink.OnButtonPressed (args);
- });
+ if (!this.HandleMouseDown (theEvent))
+ base.MouseDown (theEvent);
}
public override void MouseUp (NSEvent theEvent)
{
- base.MouseUp (theEvent);
- var p = ConvertPointFromView (theEvent.LocationInWindow, null);
- ButtonEventArgs args = new ButtonEventArgs ();
- args.X = p.X;
- args.Y = p.Y;
- args.Button = (PointerButton) (int) theEvent.ButtonNumber + 1;
- context.InvokeUserCode (delegate {
- eventSink.OnButtonReleased (args);
- });
+ if (!this.HandleMouseUp (theEvent))
+ base.MouseUp (theEvent);
+ }
+
+ public override void OtherMouseDown (NSEvent theEvent)
+ {
+ if (!this.HandleMouseDown (theEvent))
+ base.OtherMouseDown (theEvent);
+ }
+
+ public override void OtherMouseUp (NSEvent theEvent)
+ {
+ if (!this.HandleMouseUp (theEvent))
+ base.OtherMouseUp (theEvent);
}
public override void MouseEntered (NSEvent theEvent)
{
- base.MouseEntered (theEvent);
- context.InvokeUserCode (eventSink.OnMouseEntered);
+ this.HandleMouseEntered (theEvent);
}
public override void MouseExited (NSEvent theEvent)
{
- base.MouseExited (theEvent);
- context.InvokeUserCode (eventSink.OnMouseExited);
+ this.HandleMouseExited (theEvent);
}
public override void MouseMoved (NSEvent theEvent)
{
- base.MouseMoved (theEvent);
- var p = ConvertPointFromView (theEvent.LocationInWindow, null);
- MouseMovedEventArgs args = new MouseMovedEventArgs ((long) TimeSpan.FromSeconds (theEvent.Timestamp).TotalMilliseconds, p.X, p.Y);
- context.InvokeUserCode (delegate {
- eventSink.OnMouseMoved (args);
- });
+ if (!this.HandleMouseMoved (theEvent))
+ base.MouseMoved (theEvent);
+ }
+
+ public override void RightMouseDragged (NSEvent theEvent)
+ {
+ if (!this.HandleMouseMoved (theEvent))
+ base.RightMouseDragged (theEvent);
}
public override void MouseDragged (NSEvent theEvent)
{
- base.MouseDragged (theEvent);
- var p = ConvertPointFromView (theEvent.LocationInWindow, null);
- MouseMovedEventArgs args = new MouseMovedEventArgs ((long) TimeSpan.FromSeconds (theEvent.Timestamp).TotalMilliseconds, p.X, p.Y);
- context.InvokeUserCode (delegate {
- eventSink.OnMouseMoved (args);
- });
+ if (!this.HandleMouseMoved (theEvent))
+ base.MouseDragged (theEvent);
+ }
+
+ public override void OtherMouseDragged (NSEvent theEvent)
+ {
+ if (!this.HandleMouseMoved (theEvent))
+ base.OtherMouseDragged (theEvent);
+ }
+
+ public override void KeyDown (NSEvent theEvent)
+ {
+ if (!this.HandleKeyDown (theEvent))
+ base.KeyDown (theEvent);
+ }
+
+ public override void KeyUp (NSEvent theEvent)
+ {
+ if (!this.HandleKeyUp (theEvent))
+ base.KeyUp (theEvent);
}
}
} \ No newline at end of file
diff --git a/Xwt.XamMac/Xwt.Mac/TableViewBackend.cs b/Xwt.XamMac/Xwt.Mac/TableViewBackend.cs
index 726f8cd1..9a4dda4f 100644
--- a/Xwt.XamMac/Xwt.Mac/TableViewBackend.cs
+++ b/Xwt.XamMac/Xwt.Mac/TableViewBackend.cs
@@ -27,8 +27,10 @@
using System;
using System.Collections.Generic;
using AppKit;
+using CoreGraphics;
using Foundation;
using Xwt.Backends;
+using Xwt.Drawing;
namespace Xwt.Mac
{
@@ -40,11 +42,17 @@ namespace Xwt.Mac
ScrollView scroll;
NSObject selChangeObserver;
NormalClipView clipView;
-
- public TableViewBackend ()
- {
+
+ NSTableView ICellSource.TableView { get { return Table; } }
+
+ List<NSTableColumn> ICellSource.Columns {
+ get { return cols; }
}
-
+
+ protected List<NSTableColumn> Columns {
+ get { return cols; }
+ }
+
public override void Initialize ()
{
Table = CreateView ();
@@ -173,36 +181,23 @@ namespace Xwt.Mac
{
ApplicationContext.InvokeUserCode (EventSink.OnSelectionChanged);
}
+
+ public SelectionMode SelectionMode { get; private set; }
public void SetSelectionMode (SelectionMode mode)
{
+ SelectionMode = mode;
Table.AllowsMultipleSelection = mode == SelectionMode.Multiple;
+ if (mode == SelectionMode.None && Table.SelectedRowCount > 0)
+ UnselectAll ();
}
public virtual NSTableColumn AddColumn (ListViewColumn col)
{
- var tcol = new NSTableColumn ();
- tcol.Editable = true;
+ var tcol = new TableColumn (ApplicationContext, this, Table);
cols.Add (tcol);
- var c = CellUtil.CreateCell (ApplicationContext, Table, this, col.Views, cols.Count - 1);
- tcol.DataCell = c;
+ tcol.UpdateColumn (col);
Table.AddColumn (tcol);
- var hc = new NSTableHeaderCell ();
- hc.Title = col.Title ?? "";
- tcol.HeaderCell = hc;
- tcol.HeaderCell.Alignment = col.Alignment.ToNSTextAlignment();
-
-
- if (col.CanResize)
- tcol.ResizingMask |= NSTableColumnResizing.UserResizingMask;
- else
- tcol.ResizingMask &= ~NSTableColumnResizing.UserResizingMask;
- if (col.Expands)
- tcol.ResizingMask |= NSTableColumnResizing.Autoresizing;
- else
- tcol.ResizingMask &= ~NSTableColumnResizing.Autoresizing;
- tcol.SizeToFit();
- Widget.InvalidateIntrinsicContentSize ();
return tcol;
}
object IColumnContainerBackend.AddColumn (ListViewColumn col)
@@ -212,72 +207,41 @@ namespace Xwt.Mac
public void RemoveColumn (ListViewColumn col, object handle)
{
- Table.RemoveColumn ((NSTableColumn)handle);
+ var tcol = (NSTableColumn)handle;
+ cols.Remove (tcol);
+ Table.RemoveColumn (tcol);
}
public void UpdateColumn (ListViewColumn col, object handle, ListViewColumnChange change)
{
- NSTableColumn tcol = (NSTableColumn) handle;
-
- switch (change) {
- case ListViewColumnChange.CanResize:
- if (col.CanResize)
- tcol.ResizingMask |= NSTableColumnResizing.UserResizingMask;
- else
- tcol.ResizingMask &= ~NSTableColumnResizing.UserResizingMask;
- break;
- case ListViewColumnChange.Expanding:
- if (col.Expands)
- tcol.ResizingMask |= NSTableColumnResizing.Autoresizing;
- else
- tcol.ResizingMask &= ~NSTableColumnResizing.Autoresizing;
- break;
- case ListViewColumnChange.Cells:
- var c = CellUtil.CreateCell(ApplicationContext, Table, this, col.Views, cols.IndexOf(tcol));
- c.Alignment = col.Alignment.ToNSTextAlignment();
- tcol.DataCell = c;
- break;
- case ListViewColumnChange.Title:
- tcol.HeaderCell.Title = col.Title ?? string.Empty;
- if (!col.CanResize)
- tcol.SizeToFit();
- break;
- case ListViewColumnChange.Alignment:
- tcol.HeaderCell.Alignment = col.Alignment.ToNSTextAlignment();
- break;
- }
+ var tcol = handle as TableColumn;
+ if (tcol != null)
+ tcol.UpdateColumn (col, change);
}
public Rectangle GetCellBounds (int row, CellView cell, bool includeMargin)
{
+ var rect = Rectangle.Zero;
var cellBackend = cell.GetBackend () as CellViewBackend;
- var r = Table.GetCellFrame (cellBackend.Column, row);
- var container = Table.GetCell (cellBackend.Column, row) as CompositeCell;
- r = container.GetCellRect (r, (NSCell)cellBackend.CurrentCell);
- r.Y -= scroll.DocumentVisibleRect.Y;
- r.X -= scroll.DocumentVisibleRect.X;
- if (HeadersVisible)
- r.Y += Table.HeaderView.Frame.Height;
- return new Rectangle (r.X, r.Y, r.Width, r.Height);
+ var container = Table.GetView (cellBackend.Column, row, false) as CompositeCell;
+ if (container != null) {
+ var cellView = container.GetCellViewForBackend (cellBackend);
+ rect = cellView.ConvertRectToView (new CGRect (CGPoint.Empty, cellView.Frame.Size), Table).ToXwtRect ();
+ rect.Y -= scroll.DocumentVisibleRect.Y;
+ rect.X -= scroll.DocumentVisibleRect.X;
+ }
+ return rect;
}
public Rectangle GetRowBounds (int row, bool includeMargin)
{
var rect = Rectangle.Zero;
- var columns = Table.TableColumns ();
-
- for (int i = 0; i < columns.Length; i++)
- {
- var r = Table.GetCellFrame (i, row);
- if (rect == Rectangle.Zero)
- rect = new Rectangle (r.X, r.Y, r.Width, r.Height);
- else
- rect = rect.Union (new Rectangle (r.X, r.Y, r.Width, r.Height));
+ var rowView = Table.GetRowView (row, false);
+ if (rowView != null) {
+ rect = rowView.Frame.ToXwtRect ();
+ rect.Y -= scroll.DocumentVisibleRect.Y;
+ rect.X -= scroll.DocumentVisibleRect.X;
}
- rect.Y -= scroll.DocumentVisibleRect.Y;
- rect.X -= scroll.DocumentVisibleRect.X;
- if (HeadersVisible)
- rect.Y += Table.HeaderView.Frame.Height;
return rect;
}
@@ -307,11 +271,8 @@ namespace Xwt.Mac
public abstract void SetCurrentEventRow (object pos);
- nfloat ICellSource.RowHeight {
- get { return Table.RowHeight; }
- set { Table.RowHeight = value; }
- }
-
+ public abstract void InvalidateRowHeight (object pos);
+
public bool BorderVisible {
get { return scroll.BorderType == NSBorderType.BezelBorder;}
set {
@@ -319,6 +280,11 @@ namespace Xwt.Mac
}
}
+ public bool UseAlternatingRowColors {
+ get { return Table.UsesAlternatingRowBackgroundColors; }
+ set { Table.UsesAlternatingRowBackgroundColors = value; }
+ }
+
public bool HeadersVisible {
get {
return Table.HeaderView != null;
@@ -338,6 +304,170 @@ namespace Xwt.Mac
get { return Table.GridStyleMask.ToXwtValue (); }
set { Table.GridStyleMask = value.ToMacValue (); }
}
+
+ public override Color BackgroundColor
+ {
+ get { return Table.BackgroundColor.ToXwtColor (); }
+ set { Table.BackgroundColor = value.ToNSColor (); }
+ }
+ }
+
+ class TableColumn : NSTableColumn
+ {
+ readonly ICellSource backend;
+ readonly ApplicationContext context;
+
+ public CompositeCell DataView { get; private set; }
+
+ List<WeakReference> CachedViews = new List<WeakReference> ();
+
+ public TableColumn (ApplicationContext context, ICellSource backend, NSTableView table)
+ {
+ this.context = context;
+ Identifier = GetHashCode ().ToString (); // this is used to identify cached views
+ this.backend = backend;
+ TableView = table;
+ }
+
+ public CompositeCell CreateNewView ()
+ {
+ CleanViewCache ();
+ var view = DataView.Copy () as CompositeCell;
+ view.Identifier = Identifier;
+ // Cocoa will manage the native views in the background and eventually dispose
+ // them without letting us know. In order to keep track of the active views
+ // we store a weak ref for each view to not disturb the internal Cocoa caching login.
+ CachedViews.Add (new WeakReference (view));
+ return view;
+ }
+
+ void UpdateCachedViews (ICollection<CellView> cells)
+ {
+ if (CachedViews.Count == 0)
+ return;
+ var col = backend.Columns.IndexOf (this);
+ foreach (var cached in CachedViews) {
+ // update only the alive and not disposed views
+ if (cached.IsAlive) {
+ var view = cached.Target as CompositeCell;
+ if (view?.IsDisposed == false)
+ CellUtil.UpdateCellView (view, backend, cells, col);
+ }
+ }
+
+ CleanViewCache ();
+ }
+
+ void CleanViewCache ()
+ {
+ // remove any GCd and/or disposed views
+ CachedViews.RemoveAll (c => {
+ if (!c.IsAlive)
+ return true;
+ var cell = c.Target as CompositeCell;
+ if (cell == null || cell.IsDisposed)
+ return true;
+ return false;
+ });
+ }
+
+ public void UpdateColumn (ListViewColumn col)
+ {
+ Editable = true;
+ var hc = new NSTableHeaderCell {
+ Title = col.Title ?? string.Empty
+ };
+ HeaderCell = hc;
+ HeaderCell.Alignment = col.Alignment.ToNSTextAlignment ();
+
+ DataView = CellUtil.CreateCellView (context, backend, col.Views, backend.Columns.IndexOf (this));
+ DataView.Identifier = Identifier;
+ UpdateCachedViews (col.Views);
+
+ if (col.CanResize)
+ ResizingMask |= NSTableColumnResizing.UserResizingMask;
+ else
+ ResizingMask &= ~NSTableColumnResizing.UserResizingMask;
+ if (col.Expands)
+ ResizingMask |= NSTableColumnResizing.Autoresizing;
+ else
+ ResizingMask &= ~NSTableColumnResizing.Autoresizing;
+ SizeToFit ();
+ TableView?.InvalidateIntrinsicContentSize ();
+ }
+
+ public void UpdateColumn (ListViewColumn col, ListViewColumnChange change)
+ {
+ if (TableView == null)
+ throw new InvalidOperationException ("Add the column to a table first");
+ switch (change) {
+ case ListViewColumnChange.CanResize:
+ if (col.CanResize)
+ ResizingMask |= NSTableColumnResizing.UserResizingMask;
+ else
+ ResizingMask &= ~NSTableColumnResizing.UserResizingMask;
+ break;
+ case ListViewColumnChange.Expanding:
+ if (col.Expands)
+ ResizingMask |= NSTableColumnResizing.Autoresizing;
+ else
+ ResizingMask &= ~NSTableColumnResizing.Autoresizing;
+ break;
+ case ListViewColumnChange.Cells:
+ DataView = CellUtil.CreateCellView (context, backend, col.Views, backend.Columns.IndexOf (this));
+ DataView.Identifier = Identifier;
+ UpdateCachedViews (col.Views);
+ TableView.ReloadData ();
+ break;
+ case ListViewColumnChange.Title:
+ HeaderCell.Title = col.Title ?? string.Empty;
+ if (!col.CanResize)
+ SizeToFit ();
+ break;
+ case ListViewColumnChange.Alignment:
+ HeaderCell.Alignment = col.Alignment.ToNSTextAlignment ();
+ break;
+ }
+ }
+ }
+
+ class TableRowView : NSTableRowView
+ {
+
+ public override bool Selected {
+ get {
+ return base.Selected;
+ }
+ set {
+ base.Selected = value;
+ // the first time NSTableView is presented the background
+ // may be drawn already and it will not be redrawn even
+ // if Selection has been changed.
+ NeedsDisplay = true;
+ }
+ }
+
+ public override void DrawSelection (CGRect dirtyRect)
+ {
+ if (EffectiveAppearance.Name == NSAppearance.NameVibrantDark &&
+ SelectionHighlightStyle != NSTableViewSelectionHighlightStyle.None) {
+ (Selected ? NSColor.AlternateSelectedControl : BackgroundColor).SetFill ();
+ var path = NSBezierPath.FromRect (dirtyRect);
+ path.Fill ();
+ } else
+ base.DrawSelection (dirtyRect);
+ }
+
+ public override void DrawBackground (CGRect dirtyRect)
+ {
+ if (Selected && EffectiveAppearance.Name == NSAppearance.NameVibrantDark &&
+ SelectionHighlightStyle != NSTableViewSelectionHighlightStyle.None) {
+ (Selected ? NSColor.AlternateSelectedControl : BackgroundColor).SetFill ();
+ var path = NSBezierPath.FromRect (dirtyRect);
+ path.Fill ();
+ } else
+ base.DrawBackground (dirtyRect);
+ }
}
class ScrollView: NSScrollView, IViewObject
diff --git a/Xwt.XamMac/Xwt.Mac/TextEntryBackend.cs b/Xwt.XamMac/Xwt.Mac/TextEntryBackend.cs
index e6d81734..fe11c70f 100644
--- a/Xwt.XamMac/Xwt.Mac/TextEntryBackend.cs
+++ b/Xwt.XamMac/Xwt.Mac/TextEntryBackend.cs
@@ -356,6 +356,29 @@ namespace Xwt.Mac
}
+ protected CustomCell(IntPtr ptr) : base(ptr)
+ {
+ }
+
+ /// <summary>
+ /// Like what happens for the ios designer, AppKit can sometimes clone the native `NSTextFieldCell` using the Copy (NSZone)
+ /// method. We *need* to ensure we can create a new managed wrapper for the cloned native object so we need the IntPtr
+ /// constructor. NOTE: By keeping this override in managed we ensure the new wrapper C# object is created ~immediately,
+ /// which makes it easier to debug issues.
+ /// </summary>
+ /// <returns>The copy.</returns>
+ /// <param name="zone">Zone.</param>
+ public override NSObject Copy(NSZone zone)
+ {
+ // Don't remove this override because the comment on this explains why we need this!
+ var newCell = (CustomCell)base.Copy(zone);
+ newCell.editor = editor;
+ newCell.selChangeObserver = selChangeObserver;
+ newCell.Context = Context;
+ newCell.EventSink = EventSink;
+ return newCell;
+ }
+
public override NSTextView FieldEditorForView (NSView aControlView)
{
if (editor == null) {
diff --git a/Xwt.XamMac/Xwt.Mac/TextLayoutBackendHandler.cs b/Xwt.XamMac/Xwt.Mac/TextLayoutBackendHandler.cs
index 918d23af..cd4fd5a1 100644
--- a/Xwt.XamMac/Xwt.Mac/TextLayoutBackendHandler.cs
+++ b/Xwt.XamMac/Xwt.Mac/TextLayoutBackendHandler.cs
@@ -26,6 +26,7 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using AppKit;
using CoreGraphics;
using CoreText;
@@ -37,19 +38,291 @@ namespace Xwt.Mac
{
public class MacTextLayoutBackendHandler: TextLayoutBackendHandler
{
- class LayoutInfo
+ class LayoutInfo : IDisposable
{
- public string Text = String.Empty;
- public NSFont Font;
- public float? Width, Height;
- public TextTrimming TextTrimming;
- public Alignment TextAlignment;
- readonly public List<TextAttribute> Attributes = new List<TextAttribute> ();
- readonly public ApplicationContext ApplicationContext;
+ string text = String.Empty;
+ NSFont font;
+ float? width, height;
+ TextTrimming textTrimming;
+ Alignment textAlignment;
+ readonly public List<TextAttribute> Attributes;
+ readonly ApplicationContext ApplicationContext;
+ readonly NSTextStorage TextStorage;
+ readonly NSTextContainer TextContainer;
+
+ public string Text
+ {
+ get { return text; }
+ set {
+ text = value;
+ Attributes.Clear ();
+ ResetAttributes ();
+ }
+ }
+
+ public NSFont Font
+ {
+ get { return font; }
+ set {
+ font = value;
+ ResetAttributes ();
+ }
+ }
+
+ public TextTrimming TextTrimming
+ {
+ get { return textTrimming; }
+ set {
+ textTrimming = value;
+ ResetAttributes ();
+ }
+ }
+
+ public Alignment TextAlignment
+ {
+ get { return textAlignment; }
+ set
+ {
+ textAlignment = value;
+ ResetAttributes ();
+ }
+ }
+
+ public float? Width
+ {
+ get { return width; }
+ set
+ {
+ width = value;
+ TextContainer.Size = new CGSize (value ?? double.MaxValue, TextContainer.Size.Height);
+ }
+ }
+
+ public float? Height
+ {
+ get { return height; }
+ set
+ {
+ height = value;
+ TextContainer.Size = new CGSize (TextContainer.Size.Width, value ?? double.MaxValue);
+ }
+ }
public LayoutInfo (ApplicationContext actx)
{
ApplicationContext = actx;
+ Attributes = new List<TextAttribute> ();
+ TextStorage = new NSTextStorage ();
+ TextContainer = new NSTextContainer {
+ LineFragmentPadding = 0.0f
+ };
+ }
+
+ public void AddAttribute (TextAttribute attribute)
+ {
+ Attributes.Add (attribute);
+ AddAttributeInternal (attribute);
+ }
+
+ public void ClearAttributes ()
+ {
+ Attributes.Clear ();
+ ResetAttributes ();
+ }
+
+ void ResetAttributes (NSColor foregroundColorOverride = null)
+ {
+ // clear user attributes
+ TextStorage.SetAttributes (new NSDictionary (), new NSRange (0, TextStorage.Length));
+
+ var r = new NSRange (0, Text.Length);
+
+ TextStorage.SetString (new NSAttributedString (Text));
+ TextStorage.SetAlignment (TextAlignment.ToNSTextAlignment (), r);
+
+ if (Font != null)
+ // set a global font
+ TextStorage.AddAttribute (NSStringAttributeKey.Font, Font, r);
+
+ // paragraph style
+ TextContainer.LineBreakMode = TextTrimming == TextTrimming.WordElipsis ? NSLineBreakMode.TruncatingTail : NSLineBreakMode.ByWordWrapping;
+ var pstyle = NSParagraphStyle.DefaultParagraphStyle.MutableCopy () as NSMutableParagraphStyle;
+ pstyle.Alignment = TextAlignment.ToNSTextAlignment ();
+ if (TextTrimming == TextTrimming.WordElipsis)
+ pstyle.LineBreakMode = NSLineBreakMode.TruncatingTail;
+ TextStorage.AddAttribute (NSStringAttributeKey.ParagraphStyle, pstyle, r);
+
+ // set foreground color override
+ if (foregroundColorOverride != null)
+ TextStorage.AddAttribute(NSStringAttributeKey.ForegroundColor, foregroundColorOverride, r);
+
+ // restore user attributes
+ foreach (var att in Attributes)
+ AddAttributeInternal (att);
+ }
+
+ void AddAttributeInternal (TextAttribute attribute)
+ {
+ var r = new NSRange (attribute.StartIndex, attribute.Count);
+
+ if (attribute is BackgroundTextAttribute)
+ {
+ var xa = (BackgroundTextAttribute)attribute;
+ TextStorage.AddAttribute (NSStringAttributeKey.BackgroundColor, xa.Color.ToNSColor (), r);
+ }
+ else if (attribute is ColorTextAttribute)
+ {
+ var xa = (ColorTextAttribute)attribute;
+ TextStorage.AddAttribute (NSStringAttributeKey.ForegroundColor, xa.Color.ToNSColor (), r);
+ }
+ else if (attribute is UnderlineTextAttribute)
+ {
+ var xa = (UnderlineTextAttribute)attribute;
+ var style = xa.Underline ? NSUnderlineStyle.Single : NSUnderlineStyle.None;
+ TextStorage.AddAttribute(NSStringAttributeKey.UnderlineStyle, NSNumber.FromInt32 ((int)style), r);
+ }
+ else if (attribute is FontStyleTextAttribute)
+ {
+ var xa = (FontStyleTextAttribute)attribute;
+ if (xa.Style == FontStyle.Italic)
+ {
+ TextStorage.ApplyFontTraits (NSFontTraitMask.Italic, r);
+ }
+ else if (xa.Style == FontStyle.Oblique)
+ {
+ // copy Pango.Style.Oblique behaviour (25% skew)
+ TextStorage.AddAttribute (NSStringAttributeKey.Obliqueness, NSNumber.FromFloat ((float)0.25), r);
+ }
+ else
+ {
+ TextStorage.RemoveAttribute (NSStringAttributeKey.Obliqueness, r);
+ TextStorage.ApplyFontTraits (NSFontTraitMask.Unitalic, r);
+ }
+ }
+ else if (attribute is FontWeightTextAttribute)
+ {
+ var xa = (FontWeightTextAttribute)attribute;
+ NSRange er;
+ // get the effective font to modify for the given range
+ var ft = TextStorage.GetAttribute (NSStringAttributeKey.Font, attribute.StartIndex, out er, r) as NSFont;
+ ft = ft.WithWeight (xa.Weight);
+ TextStorage.AddAttribute (NSStringAttributeKey.Font, ft, r);
+ }
+ else if (attribute is LinkTextAttribute)
+ {
+ TextStorage.AddAttribute (NSStringAttributeKey.ForegroundColor, Toolkit.CurrentEngine.Defaults.FallbackLinkColor.ToNSColor (), r);
+ TextStorage.AddAttribute (NSStringAttributeKey.UnderlineStyle, NSNumber.FromInt32 ((int)NSUnderlineStyle.Single), r);
+ }
+ else if (attribute is StrikethroughTextAttribute)
+ {
+ var xa = (StrikethroughTextAttribute)attribute;
+ var style = xa.Strikethrough ? NSUnderlineStyle.Single : NSUnderlineStyle.None;
+ TextStorage.AddAttribute (NSStringAttributeKey.StrikethroughStyle, NSNumber.FromInt32 ((int)style), r);
+ }
+ else if (attribute is FontTextAttribute)
+ {
+ var xa = (FontTextAttribute)attribute;
+ var nf = ((FontData)ApplicationContext.Toolkit.GetSafeBackend (xa.Font)).Font;
+ TextStorage.AddAttribute (NSStringAttributeKey.Font, nf, r);
+ }
+ }
+
+ public void Draw (CGContext ctx, CGColor foregroundColor, double x, double y)
+ {
+ bool tempForegroundSet = false;
+ // if no color attribute is set for the whole string,
+ // NSLayoutManager will use the default control foreground color.
+ // To override the default color we need to apply the current CGContext stroke color
+ // before all other attributes are set, otherwise it will remove all other foreground colors.
+ if (foregroundColor != null && !Attributes.Any (a => a is ColorTextAttribute && a.StartIndex == 0 && a.Count == Text.Length))
+ {
+ // FIXME: we need to find a better way to accomplish this without the need to reset all attributes.
+ ResetAttributes(NSColor.FromCGColor(foregroundColor));
+ tempForegroundSet = true;
+ }
+
+ ctx.SaveState ();
+ NSGraphicsContext.GlobalSaveGraphicsState ();
+ var nsContext = NSGraphicsContext.FromCGContext (ctx, true);
+ NSGraphicsContext.CurrentContext = nsContext;
+
+ using (var TextLayout = new NSLayoutManager ())
+ {
+ TextLayout.AddTextContainer (TextContainer);
+ TextStorage.AddLayoutManager (TextLayout);
+
+ TextLayout.DrawBackgroundForGlyphRange (new NSRange(0, Text.Length), new CGPoint (x, y));
+ TextLayout.DrawGlyphsForGlyphRange (new NSRange(0, Text.Length), new CGPoint (x, y));
+ TextStorage.RemoveLayoutManager (TextLayout);
+ TextLayout.RemoveTextContainer (0);
+ }
+
+ // reset foreground color change
+ if (tempForegroundSet)
+ ResetAttributes();
+
+ NSGraphicsContext.GlobalRestoreGraphicsState ();
+ ctx.RestoreState ();
+ }
+
+ public CGSize GetSize ()
+ {
+ using (var TextLayout = new NSLayoutManager ())
+ {
+ TextLayout.AddTextContainer (TextContainer);
+ TextStorage.AddLayoutManager (TextLayout);
+ TextLayout.GlyphRangeForBoundingRect (new CGRect (CGPoint.Empty, TextContainer.Size), TextContainer);
+ var s = TextLayout.GetUsedRectForTextContainer (TextContainer);
+ TextStorage.RemoveLayoutManager (TextLayout);
+ TextLayout.RemoveTextContainer (0);
+ return s.Size;
+ }
+ }
+
+ public double GetBaseLine ()
+ {
+ using (var line = new CTLine (TextStorage))
+ {
+ nfloat ascent, descent, leading;
+ line.GetTypographicBounds (out ascent, out descent, out leading);
+ return ascent;
+ }
+ }
+
+ public nuint GetIndexFromCoordinates (double x, double y)
+ {
+ using (var TextLayout = new NSLayoutManager ())
+ {
+ TextLayout.AddTextContainer (TextContainer);
+ TextStorage.AddLayoutManager (TextLayout);
+ TextLayout.GlyphRangeForBoundingRect (new CGRect (CGPoint.Empty, TextContainer.Size), TextContainer);
+ nfloat fraction = 0;
+ var index = TextLayout.CharacterIndexForPoint (new CGPoint (x, y), TextContainer, ref fraction);
+ TextStorage.RemoveLayoutManager (TextLayout);
+ TextLayout.RemoveTextContainer (0);
+ return index;
+ }
+ }
+
+ public CGPoint GetCoordinateFromIndex(int index)
+ {
+ using (var TextLayout = new NSLayoutManager ())
+ {
+ TextLayout.AddTextContainer (TextContainer);
+ TextStorage.AddLayoutManager (TextLayout);
+ TextLayout.GlyphRangeForBoundingRect (new CGRect (CGPoint.Empty, TextContainer.Size), TextContainer);
+ var glyphIndex = TextLayout.GlyphIndexForCharacterAtIndex (index);
+ var p = TextLayout.LocationForGlyphAtIndex ((nint)glyphIndex);
+ TextStorage.RemoveLayoutManager (TextLayout);
+ TextLayout.RemoveTextContainer (0);
+ return p;
+ }
+ }
+
+ public void Dispose ()
+ {
+ TextStorage.Dispose ();
+ TextContainer.Dispose ();
}
}
@@ -64,7 +337,7 @@ namespace Xwt.Mac
li.Text = text == null ? String.Empty : text.Replace ("\r\n", "\n");
}
- public override void SetFont (object backend, Xwt.Drawing.Font font)
+ public override void SetFont (object backend, Font font)
{
LayoutInfo li = (LayoutInfo)backend;
li.Font = ((FontData)ApplicationContext.Toolkit.GetSafeBackend (font)).Font;
@@ -82,10 +355,10 @@ namespace Xwt.Mac
li.Height = value < 0 ? null : (float?)value;
}
- public override void SetTrimming (object backend, TextTrimming value)
+ public override void SetTrimming (object backend, TextTrimming textTrimming)
{
LayoutInfo li = (LayoutInfo)backend;
- li.TextTrimming = value;
+ li.TextTrimming = textTrimming;
}
public override void SetAlignment (object backend, Alignment alignment)
@@ -97,53 +370,13 @@ namespace Xwt.Mac
public override Size GetSize (object backend)
{
LayoutInfo li = (LayoutInfo)backend;
- using (CTFrame frame = CreateFrame (li)) {
- if (frame == null)
- return Size.Zero;
-
- Size result = Size.Zero;
- CTLine [] lines = frame.GetLines ();
- nfloat lineHeight = li.Font.Ascender - li.Font.Descender + li.Font.Leading;
-
- CTLine ellipsis = null;
- bool ellipsize = li.Width.HasValue && li.TextTrimming == TextTrimming.WordElipsis;
- if (ellipsize)
- ellipsis = new CTLine (CreateAttributedString (li, "..."));
-
- // try to approximate Pango's layout
- foreach (var line in lines) {
- var l = line;
- if (ellipsize) { // we need to create a new CTLine here because the framesetter already truncated the text for the line
- l = new CTLine (CreateAttributedString (li, li.Text.Substring ((int)line.StringRange.Location)))
- .GetTruncatedLine (li.Width.Value, CTLineTruncation.End, ellipsis);
- line.Dispose ();
- }
-
- result.Width = Math.Max (result.Width, GetLineWidth (l));
- result.Height += lineHeight;
-
- // clean up after ourselves as we go
- l.Dispose ();
- }
-
- // CoreText throws away trailing line breaks..
- if (li.Text.EndsWith ("\n"))
- result.Height += lineHeight;
-
- result.Width = Math.Ceiling (result.Width);
- result.Height = Math.Ceiling (result.Height);
- return result;
- }
+ return li.GetSize().ToXwtSize();
}
public override double GetBaseline (object backend)
{
LayoutInfo li = (LayoutInfo)backend;
- using (var line = new CTLine (CreateAttributedString (li))) {
- nfloat ascent, descent, leading;
- line.GetTypographicBounds (out ascent, out descent, out leading);
- return (double)ascent;
- }
+ return li.GetBaseLine();
}
public override double GetMeanline (object backend)
@@ -151,167 +384,40 @@ namespace Xwt.Mac
LayoutInfo li = (LayoutInfo)backend;
return GetBaseline (backend) - li.Font.XHeight / 2;
}
-
- static CTFrame CreateFrame (LayoutInfo li)
- {
- if (string.IsNullOrEmpty (li.Text))
- return null;
-
- using (CTFramesetter framesetter = new CTFramesetter (CreateAttributedString (li))) {
- CGPath path = new CGPath ();
- bool ellipsize = li.Width.HasValue && li.TextTrimming == TextTrimming.WordElipsis;
- path.AddRect (new CGRect (0, 0, li.Width.HasValue && !ellipsize ? li.Width.Value : float.MaxValue, li.Height ?? float.MaxValue));
-
- return framesetter.GetFrame (new NSRange (0, li.Text.Length), path, null);
- }
- }
-
- static NSAttributedString CreateAttributedString (LayoutInfo li, string overrideText = null)
- {
- if (overrideText != null || li.Attributes.Count == 0)
- return CreateAttributedString (overrideText ?? li.Text, li.Font);
-
- var ns = new NSMutableAttributedString (li.Text);
- ns.BeginEditing ();
- var r = new NSRange (0, li.Text.Length);
- if (li.Font != null)
- ns.AddAttribute (CTStringAttributeKey.Font, li.Font, r);
- ns.AddAttribute (CTStringAttributeKey.ForegroundColorFromContext, new NSNumber (true), r);
-
- foreach (var att in li.Attributes) {
- r = new NSRange (att.StartIndex, att.Count);
- if (att is BackgroundTextAttribute) {
- var xa = (BackgroundTextAttribute)att;
- ns.AddAttribute (CTStringAttributeKey.BackgroundColor, xa.Color.ToNSColor (), r);
- } else if (att is ColorTextAttribute) {
- var xa = (ColorTextAttribute)att;
- // FIXME: CTStringAttributeKey.ForegroundColor has no effect
- ns.AddAttribute (CTStringAttributeKey.ForegroundColor, xa.Color.ToNSColor (), r);
- } else if (att is UnderlineTextAttribute) {
- var xa = (UnderlineTextAttribute)att;
- var style = xa.Underline ? CTUnderlineStyle.Single : CTUnderlineStyle.None;
- ns.AddAttribute (CTStringAttributeKey.UnderlineStyle, NSNumber.FromInt32 ((int)style), r);
- } else if (att is FontStyleTextAttribute) {
- var xa = (FontStyleTextAttribute)att;
- if (xa.Style == FontStyle.Italic) {
- ns.ApplyFontTraits (NSFontTraitMask.Italic, r);
- } else if (xa.Style == FontStyle.Oblique) {
- // FIXME: CoreText has no Obliqueness support
- } else {
- // FIXME: CoreText has no Obliqueness support
- ns.ApplyFontTraits (NSFontTraitMask.Unitalic, r);
- }
- } else if (att is FontWeightTextAttribute) {
- var xa = (FontWeightTextAttribute)att;
- NSRange er;
- // get the effective font to modify for the given range
- var ft = ns.GetAttribute (CTStringAttributeKey.Font, att.StartIndex, out er, r) as NSFont;
- ft = ft.WithWeight (xa.Weight);
- ns.AddAttribute (CTStringAttributeKey.Font, ft, r);
- } else if (att is LinkTextAttribute) {
- ns.AddAttribute (CTStringAttributeKey.ForegroundColor, Toolkit.CurrentEngine.Defaults.FallbackLinkColor.ToNSColor (), r);
- ns.AddAttribute (CTStringAttributeKey.UnderlineStyle, NSNumber.FromInt32 ((int)CTUnderlineStyle.Single), r);
- } else if (att is StrikethroughTextAttribute) {
- //FIXME: CoreText has no Strikethrough support
- } else if (att is FontTextAttribute) {
- var xa = (FontTextAttribute)att;
- var nf = ((FontData)li.ApplicationContext.Toolkit.GetSafeBackend (xa.Font)).Font;
- ns.AddAttribute (CTStringAttributeKey.Font, nf, r);
- }
- }
-
- ns.EndEditing ();
- return ns;
- }
-
- static NSAttributedString CreateAttributedString (string text, NSFont font)
- {
- NSDictionary dict;
- if (font != null) {
- dict = NSDictionary.FromObjectsAndKeys (
- new object [] { font, new NSNumber (true) },
- new object [] { CTStringAttributeKey.Font, CTStringAttributeKey.ForegroundColorFromContext }
- );
- } else {
- dict = NSDictionary.FromObjectsAndKeys (
- new object [] { new NSNumber (true) },
- new object [] { CTStringAttributeKey.ForegroundColorFromContext }
- );
- }
- return new NSAttributedString (text, dict);
- }
- internal static void Draw (CGContext ctx, object layout, double x, double y)
+ internal static void Draw (CGContextBackend ctx, object layout, double x, double y)
{
LayoutInfo li = (LayoutInfo)layout;
- using (CTFrame frame = CreateFrame (li)) {
- if (frame == null)
- return;
-
- CTLine ellipsis = null;
- bool ellipsize = li.Width.HasValue && li.TextTrimming == TextTrimming.WordElipsis;
- if (ellipsize)
- ellipsis = new CTLine (CreateAttributedString (li, "..."));
-
- nfloat lineHeight = li.Font.Ascender - li.Font.Descender + li.Font.Leading;
-
- ctx.SaveState ();
- ctx.TextMatrix = CGAffineTransform.MakeScale (1f, -1f);
- ctx.TranslateCTM ((float)x, (float)y + li.Font.Ascender);
- foreach (var line in frame.GetLines ()) {
- ctx.TextPosition = CGPoint.Empty;
- // Determine final line
- var ln = line;
- if (ellipsize) {
- // we need to create a new CTLine here because the framesetter already truncated the text for the line
- ln = new CTLine (CreateAttributedString (li, li.Text.Substring ((int)line.StringRange.Location)))
- .GetTruncatedLine (li.Width.Value, CTLineTruncation.End, ellipsis);
- line.Dispose ();
- } else if (li.Width.HasValue && li.TextAlignment != Alignment.Start) {
- var tx = li.Width.Value - GetLineWidth (ln);
- if (li.TextAlignment == Alignment.Center)
- tx /= 2d;
- ctx.TextPosition = new CGPoint ((nfloat)tx, 0);
- }
- ln.Draw (ctx);
- ctx.TranslateCTM (0, lineHeight);
- ln.Dispose ();
- }
- ctx.RestoreState ();
- }
+ li.Draw (ctx.Context, ctx.CurrentStatus.GlobalColor, x, y);
}
public override void AddAttribute (object backend, TextAttribute attribute)
{
LayoutInfo li = (LayoutInfo)backend;
- li.Attributes.Add (attribute);
+ li.AddAttribute (attribute);
}
public override void ClearAttributes (object backend)
{
LayoutInfo li = (LayoutInfo)backend;
- li.Attributes.Clear ();
+ li.ClearAttributes ();
}
public override int GetIndexFromCoordinates (object backend, double x, double y)
{
- return 0;
+ LayoutInfo li = (LayoutInfo)backend;
+ return (int)li.GetIndexFromCoordinates (x, y);
}
public override Point GetCoordinateFromIndex (object backend, int index)
{
- return new Point (0,0);
+ LayoutInfo li = (LayoutInfo)backend;
+ return li.GetCoordinateFromIndex (index).ToXwtPoint ();
}
public override void Dispose (object backend)
{
- // nothing
- }
-
- static double GetLineWidth (CTLine l)
- {
- // Pango does not consider trailing whitespace in its size
- return l.GetTypographicBounds () - l.TrailingWhitespaceWidth;
+ ((LayoutInfo)backend).Dispose ();
}
}
}
diff --git a/Xwt.XamMac/Xwt.Mac/TreeViewBackend.cs b/Xwt.XamMac/Xwt.Mac/TreeViewBackend.cs
index cd2a9db5..7c6be830 100644
--- a/Xwt.XamMac/Xwt.Mac/TreeViewBackend.cs
+++ b/Xwt.XamMac/Xwt.Mac/TreeViewBackend.cs
@@ -64,27 +64,78 @@ namespace Xwt.Mac
public override nfloat GetRowHeight (NSOutlineView outlineView, NSObject item)
{
- var height = outlineView.RowHeight;
- var row = outlineView.RowForItem (item);
- for (int i = 0; i < outlineView.ColumnCount; i++) {
- var cell = outlineView.GetCell (i, row);
- if (cell != null)
- height = (nfloat) Math.Max (height, cell.CellSize.Height);
- }
+ nfloat height;
+ var treeItem = (TreeItem)item;
+ if (!Backend.RowHeights.TryGetValue (treeItem, out height) || height <= 0)
+ height = Backend.RowHeights [treeItem] = Backend.CalcRowHeight (treeItem, false);
+
return height;
}
+
+ public override NSTableRowView RowViewForItem (NSOutlineView outlineView, NSObject item)
+ {
+ return outlineView.GetRowView (outlineView.RowForItem (item), false) ?? new TableRowView ();
+ }
+
+ public override NSView GetView (NSOutlineView outlineView, NSTableColumn tableColumn, NSObject item)
+ {
+ var col = tableColumn as TableColumn;
+ var cell = outlineView.MakeView (tableColumn.Identifier, this) as CompositeCell;
+ if (cell == null)
+ cell = col.CreateNewView ();
+ if (cell.ObjectValue != item)
+ cell.ObjectValue = item;
+ return cell;
+ }
+
+ public override nfloat GetSizeToFitColumnWidth (NSOutlineView outlineView, nint column)
+ {
+ var tableColumn = Backend.Columns[(int)column] as TableColumn;
+ var width = tableColumn.HeaderCell.CellSize.Width;
+
+ CompositeCell templateCell = null;
+ for (int i = 0; i < outlineView.RowCount; i++) {
+ var cellView = outlineView.GetView (column, i, false) as CompositeCell;
+ if (cellView == null) { // use template for invisible rows
+ cellView = templateCell ?? (templateCell = (tableColumn as TableColumn)?.DataView?.Copy () as CompositeCell);
+ if (cellView != null)
+ cellView.ObjectValue = outlineView.ItemAtRow (i);
+ }
+ if (cellView != null) {
+ if (column == 0) // first column contains expanders
+ width = (nfloat)Math.Max (width, cellView.Frame.X + cellView.FittingSize.Width);
+ else
+ width = (nfloat)Math.Max (width, cellView.FittingSize.Width);
+ }
+ }
+ return width;
+ }
+
+ public override NSIndexSet GetSelectionIndexes(NSOutlineView outlineView, NSIndexSet proposedSelectionIndexes)
+ {
+ return Backend.SelectionMode != SelectionMode.None ? proposedSelectionIndexes : new NSIndexSet();
+ }
}
- NSOutlineView Tree {
- get { return (NSOutlineView) Table; }
+ OutlineViewBackend Tree {
+ get { return (OutlineViewBackend) Table; }
}
protected override NSTableView CreateView ()
{
- var t = new OutlineViewBackend (EventSink, ApplicationContext);
+ var t = new OutlineViewBackend (this);
t.Delegate = new TreeDelegate () { Backend = this };
return t;
}
+
+ public bool AnimationsEnabled {
+ get {
+ return Tree.AnimationsEnabled;
+ }
+ set {
+ Tree.AnimationsEnabled = value;
+ }
+ }
protected override string SelectionChangeEventName {
get { return "NSOutlineViewSelectionDidChangeNotification"; }
@@ -103,13 +154,37 @@ namespace Xwt.Mac
public void SetSource (ITreeDataSource source, IBackend sourceBackend)
{
this.source = source;
+ RowHeights.Clear ();
tsource = new TreeSource (source);
Tree.DataSource = tsource;
- source.NodeInserted += (sender, e) => Tree.ReloadItem (tsource.GetItem (source.GetParent(e.Node)), true);
- source.NodeDeleted += (sender, e) => Tree.ReloadItem (tsource.GetItem (e.Node), true);
- source.NodeChanged += (sender, e) => Tree.ReloadItem (tsource.GetItem (e.Node), false);
- source.NodesReordered += (sender, e) => Tree.ReloadItem (tsource.GetItem (e.Node), true);
+ source.NodeInserted += (sender, e) => {
+ var parent = tsource.GetItem (source.GetParent (e.Node));
+ Tree.ReloadItem (parent, parent == null || Tree.IsItemExpanded (parent));
+ };
+ source.NodeDeleted += (sender, e) => {
+ var parent = tsource.GetItem (e.Node);
+ var item = tsource.GetItem(e.Child);
+ if (item != null)
+ RowHeights.Remove (null);
+ Tree.ReloadItem (parent, parent == null || Tree.IsItemExpanded (parent));
+ };
+ source.NodeChanged += (sender, e) => {
+ var item = tsource.GetItem (e.Node);
+ if (item != null) {
+ Tree.ReloadItem (item, false);
+ UpdateRowHeight (item);
+ }
+ };
+ source.NodesReordered += (sender, e) => {
+ var parent = tsource.GetItem (e.Node);
+ Tree.ReloadItem (parent, parent == null || Tree.IsItemExpanded (parent));
+ };
+ source.Cleared += (sender, e) =>
+ {
+ Tree.ReloadData ();
+ RowHeights.Clear ();
+ };
}
public override object GetValue (object pos, int nField)
@@ -121,6 +196,48 @@ namespace Xwt.Mac
{
source.SetValue ((TreePosition)pos, nField, value);
}
+
+ public override void InvalidateRowHeight (object pos)
+ {
+ UpdateRowHeight (tsource.GetItem((TreePosition)pos));
+ }
+
+ Dictionary<TreeItem, nfloat> RowHeights = new Dictionary<TreeItem, nfloat> ();
+ bool updatingRowHeight;
+
+ void UpdateRowHeight (TreeItem pos)
+ {
+ if (updatingRowHeight)
+ return;
+ var row = Tree.RowForItem (pos);
+ if (row >= 0) {
+ // calculate new height now by reusing the visible cell to avoid using the template cell with unnecessary data reloads
+ // NOTE: cell reusing is not supported in Delegate.GetRowHeight and would require an other data reload to the template cell
+ RowHeights[pos] = CalcRowHeight (pos);
+ Table.NoteHeightOfRowsWithIndexesChanged (NSIndexSet.FromIndex (row));
+ } else // Invalidate the height, to force recalculation in Delegate.GetRowHeight
+ RowHeights[pos] = -1;
+ }
+
+ nfloat CalcRowHeight (TreeItem pos, bool tryReuse = true)
+ {
+ updatingRowHeight = true;
+ var height = Table.RowHeight;
+ var row = Tree.RowForItem (pos);
+
+ for (int i = 0; i < Columns.Count; i++) {
+ CompositeCell cell = tryReuse && row >= 0 ? Tree.GetView (i, row, false) as CompositeCell : null;
+ if (cell == null) {
+ cell = (Columns [i] as TableColumn)?.DataView as CompositeCell;
+ cell.ObjectValue = pos;
+ height = (nfloat)Math.Max (height, cell.FittingSize.Height);
+ } else {
+ height = (nfloat)Math.Max (height, cell.GetRequiredHeightForWidth (cell.Frame.Width));
+ }
+ }
+ updatingRowHeight = false;
+ return height;
+ }
public TreePosition[] SelectedRows {
get {
@@ -143,6 +260,7 @@ namespace Xwt.Mac
}
set {
SelectRow (value);
+ ScrollToRow (value);
}
}
@@ -201,12 +319,19 @@ namespace Xwt.Mac
public void ExpandToRow (TreePosition pos)
{
var p = source.GetParent (pos);
+ if (p == null)
+ return;
+ var s = new Stack<TreePosition> ();
while (p != null) {
- var it = tsource.GetItem (p);
+ s.Push (p);
+ p = source.GetParent (p);
+ }
+
+ while (s.Count > 0) {
+ var it = tsource.GetItem (s.Pop ());
if (it == null)
break;
Tree.ExpandItem (it, false);
- p = source.GetParent (p);
}
}
@@ -295,10 +420,9 @@ namespace Xwt.Mac
class TreeSource: NSOutlineViewDataSource
{
ITreeDataSource source;
-
- // TODO: remove unused positions
+
Dictionary<TreePosition,TreeItem> items = new Dictionary<TreePosition, TreeItem> ();
-
+
public TreeSource (ITreeDataSource source)
{
this.source = source;
@@ -307,6 +431,12 @@ namespace Xwt.Mac
if (!items.ContainsKey (e.Node))
items.Add (e.Node, new TreeItem { Position = e.Node });
};
+ source.NodeDeleted += (sender, e) => {
+ items.Remove (e.Child);
+ };
+ source.Cleared += (sender, e) => {
+ items.Clear ();
+ };
}
public TreeItem GetItem (TreePosition pos)
diff --git a/Xwt.XamMac/Xwt.Mac/Util.cs b/Xwt.XamMac/Xwt.Mac/Util.cs
index ce8774b9..aa9f1862 100644
--- a/Xwt.XamMac/Xwt.Mac/Util.cs
+++ b/Xwt.XamMac/Xwt.Mac/Util.cs
@@ -567,6 +567,18 @@ namespace Xwt.Mac
return view.ConvertPointFromView(point, null);
}
+ public static PointerButton GetPointerButton (this NSEvent theEvent)
+ {
+ switch (theEvent.ButtonNumber) {
+ case 0: return PointerButton.Left;
+ case 1: return PointerButton.Right;
+ case 2: return PointerButton.Middle;
+ case 3: return PointerButton.ExtendedButton1;
+ case 4: return PointerButton.ExtendedButton2;
+ }
+ return (PointerButton)0;
+ }
+
public static Accessibility.Role GetXwtRole (INSAccessibility widget)
{
var r = widget.AccessibilityRole;
diff --git a/Xwt.XamMac/Xwt.Mac/ViewBackend.cs b/Xwt.XamMac/Xwt.Mac/ViewBackend.cs
index 88e24791..026c5510 100644
--- a/Xwt.XamMac/Xwt.Mac/ViewBackend.cs
+++ b/Xwt.XamMac/Xwt.Mac/ViewBackend.cs
@@ -222,6 +222,8 @@ namespace Xwt.Mac
Cursor = NSCursor.CrosshairCursor;
else if (cursor == CursorType.Hand)
Cursor = NSCursor.OpenHandCursor;
+ else if (cursor == CursorType.Hand2)
+ Cursor = NSCursor.PointingHandCursor;
else if (cursor == CursorType.IBeam)
Cursor = NSCursor.IBeamCursor;
else if (cursor == CursorType.ResizeDown)
@@ -241,8 +243,14 @@ namespace Xwt.Mac
Cursor = NSCursor.ArrowCursor;
else if (cursor == CursorType.Move)
Cursor = NSCursor.ClosedHandCursor;
+ else if (cursor == CursorType.DragCopy)
+ Cursor = NSCursor.DragCopyCursor;
+ else if (cursor == CursorType.NotAllowed)
+ Cursor = NSCursor.OperationNotAllowedCursor;
else
Cursor = NSCursor.ArrowCursor;
+ // immediately invalidate cursor rects, if the view is visible
+ ViewObject?.View?.Window?.InvalidateCursorRectsForView(ViewObject.View);
}
~ViewBackend ()
diff --git a/Xwt.XamMac/Xwt.Mac/WidgetView.cs b/Xwt.XamMac/Xwt.Mac/WidgetView.cs
index 269e32d4..e047d0df 100644
--- a/Xwt.XamMac/Xwt.Mac/WidgetView.cs
+++ b/Xwt.XamMac/Xwt.Mac/WidgetView.cs
@@ -82,133 +82,88 @@ namespace Xwt.Mac
public override void UpdateTrackingAreas ()
{
- if (trackingArea != null) {
- RemoveTrackingArea (trackingArea);
- trackingArea.Dispose ();
- }
- CGRect viewBounds = this.Bounds;
- var options = NSTrackingAreaOptions.MouseMoved | NSTrackingAreaOptions.ActiveInKeyWindow | NSTrackingAreaOptions.MouseEnteredAndExited;
- trackingArea = new NSTrackingArea (viewBounds, options, this, null);
- AddTrackingArea (trackingArea);
+ this.UpdateEventTrackingArea (ref trackingArea);
}
public override void RightMouseDown (NSEvent theEvent)
{
- CGPoint p = this.ConvertPointFromEvent(theEvent);
- if (!Bounds.Contains(p))
- return;
- ButtonEventArgs args = new ButtonEventArgs ();
- args.X = p.X;
- args.Y = p.Y;
- args.Button = PointerButton.Right;
- args.IsContextMenuTrigger = theEvent.TriggersContextMenu ();
- context.InvokeUserCode (delegate {
- eventSink.OnButtonPressed (args);
- });
+ if (!this.HandleMouseDown (theEvent))
+ base.RightMouseDown (theEvent);
}
public override void RightMouseUp (NSEvent theEvent)
{
- CGPoint p = this.ConvertPointFromEvent(theEvent);
- if (!Bounds.Contains(p))
- return;
- ButtonEventArgs args = new ButtonEventArgs ();
- args.X = p.X;
- args.Y = p.Y;
- args.Button = PointerButton.Right;
- context.InvokeUserCode (delegate {
- eventSink.OnButtonReleased (args);
- });
+ if (!this.HandleMouseUp (theEvent))
+ base.RightMouseUp (theEvent);
}
public override void MouseDown (NSEvent theEvent)
{
- CGPoint p = this.ConvertPointFromEvent(theEvent);
- if (!Bounds.Contains(p))
- return;
- ButtonEventArgs args = new ButtonEventArgs ();
- args.X = p.X;
- args.Y = p.Y;
- args.Button = PointerButton.Left;
- args.IsContextMenuTrigger = theEvent.TriggersContextMenu ();
- context.InvokeUserCode (delegate {
- eventSink.OnButtonPressed (args);
- });
+ if (!this.HandleMouseDown (theEvent))
+ base.MouseDown (theEvent);
}
public override void MouseUp (NSEvent theEvent)
{
- CGPoint p = this.ConvertPointFromEvent(theEvent);
- if (!Bounds.Contains(p))
- return;
- ButtonEventArgs args = new ButtonEventArgs ();
- args.X = p.X;
- args.Y = p.Y;
- args.Button = (PointerButton) (int)theEvent.ButtonNumber + 1;
- context.InvokeUserCode (delegate {
- eventSink.OnButtonReleased (args);
- });
+ if (!this.HandleMouseUp (theEvent))
+ base.MouseUp (theEvent);
+ }
+
+ public override void OtherMouseDown (NSEvent theEvent)
+ {
+ if (!this.HandleMouseDown (theEvent))
+ base.OtherMouseDown (theEvent);
+ }
+
+ public override void OtherMouseUp (NSEvent theEvent)
+ {
+ if (!this.HandleMouseUp (theEvent))
+ base.OtherMouseUp (theEvent);
}
public override void MouseEntered (NSEvent theEvent)
{
- context.InvokeUserCode (eventSink.OnMouseEntered);
+ this.HandleMouseEntered (theEvent);
}
public override void MouseExited (NSEvent theEvent)
{
- context.InvokeUserCode (eventSink.OnMouseExited);
+ this.HandleMouseExited (theEvent);
}
public override void MouseMoved (NSEvent theEvent)
{
- CGPoint p = this.ConvertPointFromEvent(theEvent);
- if (!Bounds.Contains(p))
- return;
- MouseMovedEventArgs args = new MouseMovedEventArgs ((long) TimeSpan.FromSeconds (theEvent.Timestamp).TotalMilliseconds, p.X, p.Y);
- context.InvokeUserCode (delegate {
- eventSink.OnMouseMoved (args);
- });
+ if (!this.HandleMouseMoved (theEvent))
+ base.MouseMoved (theEvent);
}
- public override void MouseDragged (NSEvent theEvent)
+ public override void RightMouseDragged (NSEvent theEvent)
{
- CGPoint p = this.ConvertPointFromEvent(theEvent);
- if (!Bounds.Contains(p))
- return;
- MouseMovedEventArgs args = new MouseMovedEventArgs ((long) TimeSpan.FromSeconds (theEvent.Timestamp).TotalMilliseconds, p.X, p.Y);
- context.InvokeUserCode (delegate {
- eventSink.OnMouseMoved (args);
- });
+ if (!this.HandleMouseMoved (theEvent))
+ base.RightMouseDragged (theEvent);
}
- public override void KeyDown (NSEvent theEvent)
+ public override void MouseDragged (NSEvent theEvent)
{
- var keyArgs = theEvent.ToXwtKeyEventArgs ();
- context.InvokeUserCode (delegate {
- eventSink.OnKeyPressed (keyArgs);
- });
- if (keyArgs.Handled)
- return;
+ if (!this.HandleMouseMoved (theEvent))
+ base.MouseDragged (theEvent);
+ }
- var textArgs = new TextInputEventArgs (theEvent.Characters);
- if (!String.IsNullOrEmpty(theEvent.Characters))
- context.InvokeUserCode (delegate {
- eventSink.OnTextInput (textArgs);
- });
- if (textArgs.Handled)
- return;
+ public override void OtherMouseDragged (NSEvent theEvent)
+ {
+ if (!this.HandleMouseMoved (theEvent))
+ base.OtherMouseDragged (theEvent);
+ }
- base.KeyDown (theEvent);
+ public override void KeyDown (NSEvent theEvent)
+ {
+ if (!this.HandleKeyDown (theEvent))
+ base.KeyDown (theEvent);
}
public override void KeyUp (NSEvent theEvent)
{
- var keyArgs = theEvent.ToXwtKeyEventArgs ();
- context.InvokeUserCode (delegate {
- eventSink.OnKeyReleased (keyArgs);
- });
- if (!keyArgs.Handled)
+ if (!this.HandleKeyUp (theEvent))
base.KeyUp (theEvent);
}
diff --git a/Xwt.XamMac/Xwt.Mac/WindowBackend.cs b/Xwt.XamMac/Xwt.Mac/WindowBackend.cs
index 350aed24..29c1762c 100644
--- a/Xwt.XamMac/Xwt.Mac/WindowBackend.cs
+++ b/Xwt.XamMac/Xwt.Mac/WindowBackend.cs
@@ -29,6 +29,7 @@
// THE SOFTWARE.
using System;
+using System.Linq;
using AppKit;
using CoreGraphics;
using Foundation;
@@ -110,11 +111,27 @@ namespace Xwt.Mac
internal void InternalShow ()
{
MakeKeyAndOrderFront (MacEngine.App);
+ if (ParentWindow != null)
+ {
+ if (!ParentWindow.ChildWindows.Contains(this))
+ ParentWindow.AddChildWindow(this, NSWindowOrderingMode.Above);
+
+ // always use NSWindow for alignment when running in guest mode and
+ // don't rely on AddChildWindow to position the window correctly
+ if (frontend.InitialLocation == WindowLocation.CenterParent && !(ParentWindow is WindowBackend))
+ {
+ var parentBounds = MacDesktopBackend.ToDesktopRect(ParentWindow.ContentRectFor(ParentWindow.Frame));
+ var bounds = ((IWindowFrameBackend)this).Bounds;
+ bounds.X = parentBounds.Center.X - (Frame.Width / 2);
+ bounds.Y = parentBounds.Center.Y - (Frame.Height / 2);
+ ((IWindowFrameBackend)this).Bounds = bounds;
+ }
+ }
}
public void Present ()
{
- MakeKeyAndOrderFront (MacEngine.App);
+ InternalShow();
}
public bool Visible {
@@ -238,6 +255,8 @@ namespace Xwt.Mac
PerformClose(this);
else
Close ();
+ if (ParentWindow != null)
+ ParentWindow.RemoveChildWindow(this);
return closePerformed;
}
@@ -378,8 +397,21 @@ namespace Xwt.Mac
void IWindowFrameBackend.SetTransientFor (IWindowFrameBackend window)
{
- // Generally, TransientFor is used to implement dialog, we reproduce the assumption here
- Level = window == null ? NSWindowLevel.Normal : NSWindowLevel.ModalPanel;
+ if (!((IWindowFrameBackend)this).ShowInTaskbar)
+ StyleMask &= ~NSWindowStyle.Miniaturizable;
+
+ var win = window as NSWindow ?? ApplicationContext.Toolkit.GetNativeWindow(window) as NSWindow;
+
+ if (ParentWindow != win) {
+ // remove from the previous parent
+ if (ParentWindow != null)
+ ParentWindow.RemoveChildWindow(this);
+
+ ParentWindow = win;
+ // A window must be visible to be added to a parent. See InternalShow().
+ if (Visible)
+ ParentWindow.AddChildWindow(this, NSWindowOrderingMode.Above);
+ }
}
bool IWindowFrameBackend.Resizable {
@@ -528,7 +560,7 @@ namespace Xwt.Mac
if (child != null) {
frame.X += (nfloat) frontend.Padding.Left;
frame.Width -= (nfloat) (frontend.Padding.HorizontalSpacing);
- frame.Y += (nfloat) frontend.Padding.Top;
+ frame.Y += (nfloat) (childView.IsFlipped ? frontend.Padding.Bottom : frontend.Padding.Top);
frame.Height -= (nfloat) (frontend.Padding.VerticalSpacing);
childView.Frame = frame;
}
diff --git a/Xwt.XamMac/Xwt.XamMac.csproj b/Xwt.XamMac/Xwt.XamMac.csproj
index d56701cb..01bb962d 100644
--- a/Xwt.XamMac/Xwt.XamMac.csproj
+++ b/Xwt.XamMac/Xwt.XamMac.csproj
@@ -21,6 +21,7 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@@ -30,6 +31,7 @@
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<DebugSymbols>true</DebugSymbols>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup>
<XamMacPath>\Library\Frameworks\Xamarin.Mac.framework\Versions\Current\lib\x86_64\full\Xamarin.Mac.dll</XamMacPath>
diff --git a/Xwt/Xwt.Backends/IButtonBackend.cs b/Xwt/Xwt.Backends/IButtonBackend.cs
index 13ca8cde..5dabf47e 100755
--- a/Xwt/Xwt.Backends/IButtonBackend.cs
+++ b/Xwt/Xwt.Backends/IButtonBackend.cs
@@ -32,6 +32,7 @@ namespace Xwt.Backends
public interface IButtonBackend: IWidgetBackend
{
Color LabelColor { get; set; }
+ bool IsDefault { get; set; }
void SetButtonStyle (ButtonStyle style);
void SetButtonType (ButtonType type);
void SetContent (string label, bool useMnemonic, ImageDescription image, ContentPosition position);
diff --git a/Xwt/Xwt.Backends/ICanvasCellViewBackend.cs b/Xwt/Xwt.Backends/ICanvasCellViewBackend.cs
index 4bb56cdc..345add6a 100644
--- a/Xwt/Xwt.Backends/ICanvasCellViewBackend.cs
+++ b/Xwt/Xwt.Backends/ICanvasCellViewBackend.cs
@@ -30,6 +30,7 @@ namespace Xwt.Backends
public interface ICanvasCellViewBackend: ICellViewBackend
{
void QueueDraw ();
+ void QueueResize ();
bool IsHighlighted { get; }
}
}
diff --git a/Xwt/Xwt.Backends/ICanvasCellViewFrontend.cs b/Xwt/Xwt.Backends/ICanvasCellViewFrontend.cs
index 9609f44d..85949233 100644
--- a/Xwt/Xwt.Backends/ICanvasCellViewFrontend.cs
+++ b/Xwt/Xwt.Backends/ICanvasCellViewFrontend.cs
@@ -33,7 +33,9 @@ namespace Xwt.Backends
ApplicationContext ApplicationContext { get; }
void Draw (object ctxBackend, Rectangle cellArea);
Rectangle GetDrawingAreaForBounds (Rectangle cellBounds);
+ [Obsolete("Use GetRequiredSize (SizeConstraint)")]
Size GetRequiredSize ();
+ Size GetRequiredSize (SizeConstraint widthConstraint);
}
public class CellViewStatus
diff --git a/Xwt/Xwt.Backends/IMenuItemBackend.cs b/Xwt/Xwt.Backends/IMenuItemBackend.cs
index 399c7ff1..1b2c47cb 100644
--- a/Xwt/Xwt.Backends/IMenuItemBackend.cs
+++ b/Xwt/Xwt.Backends/IMenuItemBackend.cs
@@ -51,6 +51,11 @@ namespace Xwt.Backends
string Label { get; set; }
/// <summary>
+ /// Gets or sets the tooltip text for the menu item.
+ /// </summary>
+ string TooltipText { get; set; }
+
+ /// <summary>
/// Gets or sets a value indicating whether this <see cref="Xwt.Backends.IMenuItemBackend"/> use a mnemonic.
/// </summary>
/// <value><c>true</c> if it use a mnemonic; otherwise, <c>false</c>.</value>
diff --git a/Xwt/Xwt.Backends/ITreeViewBackend.cs b/Xwt/Xwt.Backends/ITreeViewBackend.cs
index 392e4784..84276e8e 100644
--- a/Xwt/Xwt.Backends/ITreeViewBackend.cs
+++ b/Xwt/Xwt.Backends/ITreeViewBackend.cs
@@ -43,9 +43,12 @@ namespace Xwt.Backends
void CollapseRow (TreePosition pos);
void ScrollToRow (TreePosition pos);
void ExpandToRow (TreePosition pos);
-
+
+ bool BorderVisible { get; set; }
bool HeadersVisible { get; set; }
GridLines GridLinesVisible { get; set; }
+ bool UseAlternatingRowColors { get; set; }
+ bool AnimationsEnabled { get; set; }
bool GetDropTargetRow (double x, double y, out RowDropPosition pos, out TreePosition nodePosition);
diff --git a/Xwt/Xwt.csproj b/Xwt/Xwt.csproj
index 93763315..95d1caf4 100644
--- a/Xwt/Xwt.csproj
+++ b/Xwt/Xwt.csproj
@@ -22,6 +22,7 @@
<WarningLevel>4</WarningLevel>
<ConsolePause>False</ConsolePause>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@@ -32,6 +33,7 @@
<ConsolePause>False</ConsolePause>
<DebugSymbols>true</DebugSymbols>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+ <LangVersion>6</LangVersion>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
diff --git a/Xwt/Xwt/Button.cs b/Xwt/Xwt/Button.cs
index 6e64113c..c523aed6 100755
--- a/Xwt/Xwt/Button.cs
+++ b/Xwt/Xwt/Button.cs
@@ -167,6 +167,11 @@ namespace Xwt
get { return Backend.LabelColor; }
set { Backend.LabelColor = value; }
}
+
+ public bool IsDefault {
+ get { return Backend.IsDefault; }
+ set { Backend.IsDefault = value; }
+ }
protected virtual void OnClicked (EventArgs e)
{
diff --git a/Xwt/Xwt/CanvasCellView.cs b/Xwt/Xwt/CanvasCellView.cs
index 8e6e58dc..4989f162 100644
--- a/Xwt/Xwt/CanvasCellView.cs
+++ b/Xwt/Xwt/CanvasCellView.cs
@@ -41,6 +41,15 @@ namespace Xwt
}
/// <summary>
+ /// Signals that the size of the cell may have changed, and the row
+ /// that contains it may need to be resized
+ /// </summary>
+ protected void QueueResize ()
+ {
+ ((ICanvasCellViewBackend)BackendHost.Backend).QueueResize ();
+ }
+
+ /// <summary>
/// Called when the cell needs to be redrawn
/// </summary>
/// <param name='ctx'>
@@ -55,10 +64,18 @@ namespace Xwt
return cellBounds;
}
+ [Obsolete("Use OnGetRequiredSize (SizeConstraint widthConstraint)")]
protected virtual Size OnGetRequiredSize ()
{
return new Size ();
}
+
+ protected virtual Size OnGetRequiredSize (SizeConstraint widthConstraint)
+ {
+ #pragma warning disable 618
+ return OnGetRequiredSize ();
+ #pragma warning restore 618
+ }
#region ICanvasCellRenderer implementation
@@ -77,7 +94,12 @@ namespace Xwt
Size ICanvasCellViewFrontend.GetRequiredSize ()
{
- return OnGetRequiredSize ();
+ return OnGetRequiredSize (SizeConstraint.Unconstrained);
+ }
+
+ Size ICanvasCellViewFrontend.GetRequiredSize (SizeConstraint widthConstraint)
+ {
+ return OnGetRequiredSize (widthConstraint);
}
ApplicationContext ICanvasCellViewFrontend.ApplicationContext {
diff --git a/Xwt/Xwt/CursorType.cs b/Xwt/Xwt/CursorType.cs
index 22286862..f08658da 100644
--- a/Xwt/Xwt/CursorType.cs
+++ b/Xwt/Xwt/CursorType.cs
@@ -53,11 +53,18 @@ namespace Xwt
public static readonly CursorType ResizeUp = new CursorType ("ResizeUp");
public static readonly CursorType ResizeDown = new CursorType ("ResizeDown");
public static readonly CursorType ResizeUpDown = new CursorType ("ResizeUpDown");
+ public static readonly CursorType ResizeNE = new CursorType("ResizeNE");
+ public static readonly CursorType ResizeNW = new CursorType("ResizeNW");
+ public static readonly CursorType ResizeSE = new CursorType("ResizeSE");
+ public static readonly CursorType ResizeSW = new CursorType("ResizeSW");
public static readonly CursorType Hand = new CursorType ("Hand");
+ public static readonly CursorType Hand2 = new CursorType("Hand2");
public static readonly CursorType Move = new CursorType ("Move");
public static readonly CursorType Wait = new CursorType ("Watch");
public static readonly CursorType Help = new CursorType ("Help");
public static readonly CursorType Invisible = new CursorType ("Invisible");
+ public static readonly CursorType DragCopy = new CursorType("DragCopy");
+ public static readonly CursorType NotAllowed = new CursorType("NotAllowed");
class CursorTypeValueConverter: TypeConverter
@@ -98,6 +105,8 @@ namespace Xwt
return ct;
}
}
+
+ public override string ToString() => id;
}
}
diff --git a/Xwt/Xwt/DataField.cs b/Xwt/Xwt/DataField.cs
index 39c1859d..29e60396 100644
--- a/Xwt/Xwt/DataField.cs
+++ b/Xwt/Xwt/DataField.cs
@@ -50,6 +50,11 @@ namespace Xwt
Index = -1;
}
+ public DataField (int index)
+ {
+ Index = index;
+ }
+
public int Index { get; private set; }
void IDataFieldInternal.SetIndex (int index)
diff --git a/Xwt/Xwt/ITreeDataSource.cs b/Xwt/Xwt/ITreeDataSource.cs
index a19a81ec..3a6739db 100644
--- a/Xwt/Xwt/ITreeDataSource.cs
+++ b/Xwt/Xwt/ITreeDataSource.cs
@@ -44,29 +44,47 @@ namespace Xwt
event EventHandler<TreeNodeChildEventArgs> NodeDeleted;
event EventHandler<TreeNodeEventArgs> NodeChanged;
event EventHandler<TreeNodeOrderEventArgs> NodesReordered;
+ event EventHandler Cleared;
}
public class TreeNodeEventArgs: EventArgs
{
- public TreeNodeEventArgs (TreePosition node)
+ public TreeNodeEventArgs (TreePosition node): this (node, -1)
+ {
+ }
+
+ public TreeNodeEventArgs (TreePosition node, int childIndex)
{
Node = node;
+ ChildIndex = childIndex;
}
public TreePosition Node {
get;
private set;
}
+
+ public int ChildIndex {
+ get;
+ private set;
+ }
}
public class TreeNodeChildEventArgs: TreeNodeEventArgs
{
- public TreeNodeChildEventArgs (TreePosition parent, int childIndex): base (parent)
+ [Obsolete ("Use the constructor that takes the child object")]
+ public TreeNodeChildEventArgs (TreePosition parent, int childIndex): base (parent, childIndex)
{
- ChildIndex = childIndex;
+ }
+
+#pragma warning disable CS0618 // Type or member is obsolete
+ public TreeNodeChildEventArgs (TreePosition parent, int childIndex, TreePosition child): this (parent, childIndex)
+#pragma warning restore CS0618 // Type or member is obsolete
+ {
+ Child = child;
}
- public int ChildIndex {
+ public TreePosition Child {
get;
private set;
}
@@ -74,7 +92,7 @@ namespace Xwt
public class TreeNodeOrderEventArgs: TreeNodeEventArgs
{
- public TreeNodeOrderEventArgs (TreePosition parentNode, int[] childrenOrder): base (parentNode)
+ public TreeNodeOrderEventArgs (TreePosition parentNode, int[] childrenOrder): base (parentNode, -1)
{
ChildrenOrder = childrenOrder;
}
diff --git a/Xwt/Xwt/ListStore.cs b/Xwt/Xwt/ListStore.cs
index 275db6c8..4b246acf 100644
--- a/Xwt/Xwt/ListStore.cs
+++ b/Xwt/Xwt/ListStore.cs
@@ -387,7 +387,7 @@ namespace Xwt
{
int count = list.Count;
list.Clear ();
- for (int n=0; n<count; n++) {
+ for (int n=count-1; n>=0; n--) {
if (RowDeleted != null)
RowDeleted (this, new ListRowEventArgs (n));
}
diff --git a/Xwt/Xwt/MenuItem.cs b/Xwt/Xwt/MenuItem.cs
index ad430029..8947f08b 100644
--- a/Xwt/Xwt/MenuItem.cs
+++ b/Xwt/Xwt/MenuItem.cs
@@ -107,6 +107,22 @@ namespace Xwt
}
/// <summary>
+ /// Gets or sets the tooltip text.
+ /// </summary>
+ /// <value>The tooltip text.</value>
+ [DefaultValue("")]
+ public string TooltipText
+ {
+ get { return Backend.TooltipText ?? ""; }
+ set
+ {
+ if (IsSeparator)
+ throw new NotSupportedException();
+ Backend.TooltipText = value;
+ }
+ }
+
+ /// <summary>
/// Gets or sets a value indicating whether this <see cref="Xwt.Button"/> uses a mnemonic.
/// </summary>
/// <value><c>true</c> if it uses a mnemonic; otherwise, <c>false</c>.</value>
diff --git a/Xwt/Xwt/Rectangle.cs b/Xwt/Xwt/Rectangle.cs
index dc07fc92..17fee72f 100755
--- a/Xwt/Xwt/Rectangle.cs
+++ b/Xwt/Xwt/Rectangle.cs
@@ -56,6 +56,14 @@ namespace Xwt
public Rectangle (Point loc, Size sz) : this (loc.X, loc.Y, sz.Width, sz.Height) {}
+ public Rectangle (Point point1, Point point2)
+ {
+ X = Math.Min (point1.X, point2.X);
+ Y = Math.Min (point1.Y, point2.Y);
+ Width = Math.Max (Math.Max (point1.X, point2.X) - X, 0);
+ Height = Math.Max (Math.Max (point1.Y, point2.Y) - Y, 0);
+ }
+
public static Rectangle FromLTRB (double left, double top, double right, double bottom)
{
return new Rectangle (left, top, right - left, bottom - top);
@@ -168,6 +176,26 @@ namespace Xwt
get { return X; }
set { X = value; }
}
+ public Point TopLeft {
+ get {
+ return new Point (Left, Top);
+ }
+ }
+ public Point TopRight {
+ get {
+ return new Point (Right, Top);
+ }
+ }
+ public Point BottomLeft {
+ get {
+ return new Point (Left, Bottom);
+ }
+ }
+ public Point BottomRight {
+ get {
+ return new Point (Right, Bottom);
+ }
+ }
public bool IsEmpty {
get { return (Width <= 0) || (Height <= 0); }
diff --git a/Xwt/Xwt/SelectionMode.cs b/Xwt/Xwt/SelectionMode.cs
index a9698fbf..9bde31be 100644
--- a/Xwt/Xwt/SelectionMode.cs
+++ b/Xwt/Xwt/SelectionMode.cs
@@ -31,7 +31,8 @@ namespace Xwt
public enum SelectionMode
{
Single,
- Multiple
+ Multiple,
+ None
}
}
diff --git a/Xwt/Xwt/TreeStore.cs b/Xwt/Xwt/TreeStore.cs
index b9200f8c..ccdc918b 100644
--- a/Xwt/Xwt/TreeStore.cs
+++ b/Xwt/Xwt/TreeStore.cs
@@ -167,6 +167,11 @@ namespace Xwt
add { Backend.NodesReordered += value; }
remove { Backend.NodesReordered -= value; }
}
+ event EventHandler ITreeDataSource.Cleared
+ {
+ add { Backend.Cleared += value; }
+ remove { Backend.Cleared -= value; }
+ }
TreePosition ITreeDataSource.GetChild (TreePosition pos, int index)
{
@@ -243,6 +248,7 @@ namespace Xwt
public event EventHandler<TreeNodeChildEventArgs> NodeDeleted;
public event EventHandler<TreeNodeEventArgs> NodeChanged;
public event EventHandler<TreeNodeOrderEventArgs> NodesReordered;
+ public event EventHandler Cleared;
public void InitializeBackend (object frontend, ApplicationContext context)
{
@@ -256,6 +262,8 @@ namespace Xwt
public void Clear ()
{
rootNodes.Clear ();
+ if (Cleared != null)
+ Cleared (this, EventArgs.Empty);
}
NodePosition GetPosition (TreePosition pos)
@@ -288,7 +296,7 @@ namespace Xwt
}
node.Data [column] = value;
if (NodeChanged != null)
- NodeChanged (this, new TreeNodeEventArgs (pos));
+ NodeChanged (this, new TreeNodeEventArgs (pos, n.NodeIndex));
}
public object GetValue (TreePosition pos, int column)
@@ -359,7 +367,7 @@ namespace Xwt
var node = new NodePosition () { ParentList = np.ParentList, NodeId = nn.NodeId, NodeIndex = np.NodeIndex - 1, StoreVersion = version };
if (NodeInserted != null)
- NodeInserted (this, new TreeNodeEventArgs (node));
+ NodeInserted (this, new TreeNodeEventArgs (node, node.NodeIndex));
return node;
}
@@ -377,7 +385,7 @@ namespace Xwt
var node = new NodePosition () { ParentList = np.ParentList, NodeId = nn.NodeId, NodeIndex = np.NodeIndex + 1, StoreVersion = version };
if (NodeInserted != null)
- NodeInserted (this, new TreeNodeEventArgs (node));
+ NodeInserted (this, new TreeNodeEventArgs (node, node.NodeIndex));
return node;
}
@@ -410,19 +418,24 @@ namespace Xwt
var node = new NodePosition () { ParentList = list, NodeId = nn.NodeId, NodeIndex = list.Count - 1, StoreVersion = version };
if (NodeInserted != null)
- NodeInserted (this, new TreeNodeEventArgs (node));
+ NodeInserted (this, new TreeNodeEventArgs (node, node.NodeIndex));
return node;
}
public void Remove (TreePosition pos)
{
+ // Remove all child nodes in reverse order and notify client of each removed child.
+ // This allows clients to keep track of all removed nodes, before the current node
+ // will be removed and invalidated.
+ for (int i = GetChildrenCount (pos) - 1; i >= 0 ; i--)
+ Remove (GetChild (pos, i));
NodePosition np = GetPosition (pos);
np.ParentList.RemoveAt (np.NodeIndex);
var parent = np.ParentList.Parent;
var index = np.NodeIndex;
version++;
if (NodeDeleted != null)
- NodeDeleted (this, new TreeNodeChildEventArgs (parent, index));
+ NodeDeleted (this, new TreeNodeChildEventArgs (parent, index, pos));
}
public TreePosition GetParent (TreePosition pos)
diff --git a/Xwt/Xwt/TreeView.cs b/Xwt/Xwt/TreeView.cs
index e4ba98e3..1710b3a4 100644
--- a/Xwt/Xwt/TreeView.cs
+++ b/Xwt/Xwt/TreeView.cs
@@ -125,7 +125,17 @@ namespace Xwt
ITreeViewBackend Backend {
get { return (ITreeViewBackend) BackendHost.Backend; }
}
-
+
+ public bool BorderVisible {
+ get { return Backend.BorderVisible; }
+ set { Backend.BorderVisible = value; }
+ }
+
+ public bool UseAlternatingRowColors {
+ get { return Backend.UseAlternatingRowColors; }
+ set { Backend.UseAlternatingRowColors = value; }
+ }
+
public ScrollPolicy VerticalScrollPolicy {
get { return Backend.VerticalScrollPolicy; }
set { Backend.VerticalScrollPolicy = value; }
@@ -204,6 +214,24 @@ namespace Xwt
get { return Backend.GridLinesVisible; }
set { Backend.GridLinesVisible = value; }
}
+
+ /// <summary>
+ /// Gets or sets a value indicating whether native animations are enabled.
+ /// </summary>
+ /// <value><c>true</c> if animations enabled; otherwise, <c>false</c>.</value>
+ /// <remarks>
+ /// This property controls native TreeView animations (like slow expanding collapsing rows)
+ /// only and is not related to <see cref="T:Xwt.Motion.Animation"/>.
+ /// </remarks>
+ public bool AnimationsEnabled
+ {
+ get {
+ return Backend.AnimationsEnabled;
+ }
+ set {
+ Backend.AnimationsEnabled = value;
+ }
+ }
/// <summary>
/// Gets or sets the selection mode.