diff options
Diffstat (limited to 'Xwt.Gtk')
19 files changed, 442 insertions, 125 deletions
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) |