From 926062eb69b77566dbfdb85659d0f9e70475132c Mon Sep 17 00:00:00 2001 From: lytico Date: Mon, 6 Apr 2020 22:01:49 +0200 Subject: [PATCH] Samples: add PolarFixed-Sample --- .../Widgets/CustomWidgets/PolarFixed.cs | 196 ++++++++++++++++++ .../Sections/Widgets/PolarFixedSection.cs | 64 ++++++ 2 files changed, 260 insertions(+) create mode 100644 Source/Samples/Sections/Widgets/CustomWidgets/PolarFixed.cs create mode 100644 Source/Samples/Sections/Widgets/PolarFixedSection.cs diff --git a/Source/Samples/Sections/Widgets/CustomWidgets/PolarFixed.cs b/Source/Samples/Sections/Widgets/CustomWidgets/PolarFixed.cs new file mode 100644 index 000000000..6a09ae770 --- /dev/null +++ b/Source/Samples/Sections/Widgets/CustomWidgets/PolarFixed.cs @@ -0,0 +1,196 @@ +// adopted from: https://github.com/mono/gtk-sharp/commits/2.99.3/sample/PolarFixed.cs +// This is a completely pointless widget, but it shows how to subclass container... + +using System; +using System.Collections.Generic; +using Gtk; +using Gdk; + +namespace Samples +{ + class PolarFixed : Container + { + IList children; + + public PolarFixed() + { + children = new List(); + HasWindow = false; + } + + // The child properties object + public class PolarFixedChild : Container.ContainerChild + { + double theta; + uint r; + + public PolarFixedChild(PolarFixed parent, Widget child, double theta, uint r) : base(parent, child) + { + this.theta = theta; + this.r = r; + } + + // We call parent.QueueResize() from the property setters here so that you + // can move the widget around just by changing its child properties (just + // like with a native container class). + + public double Theta { + get { return theta; } + set { + theta = value; + parent.QueueResize(); + } + } + + public uint R { + get { return r; } + set { + r = value; + parent.QueueResize(); + } + } + } + + // Override the child properties accessor to return the right object from + // "children". + public override ContainerChild this[Widget w] { + get { + foreach (PolarFixedChild pfc in children) { + if (pfc.Child == w) + return pfc; + } + + return null; + } + } + + // Indicate the kind of children the container will accept. Most containers + // will accept any kind of child, so they should return Gtk.Widget.GType. + // The default is "GLib.GType.None", which technically means that no (new) + // children can be added to the container, though Container.Add does not + // enforce this. + protected override GLib.GType OnChildType() + { + return Gtk.Widget.GType; + } + + // Implement gtk_container_forall(), which is also used by + // Gtk.Container.Children and Gtk.Container.AllChildren. + protected override void ForAll(bool include_internals, Callback callback) + { + base.ForAll(include_internals, callback); + foreach (PolarFixedChild pfc in children) + callback(pfc.Child); + } + + // Invoked by Container.Add (w). It's good practice to have this do *something*, + // even if it's not something terribly useful. + protected override void OnAdded(Widget w) + { + Put(w, 0.0, 0); + } + + // our own adder method + public void Put(Widget w, double theta, uint r) + { + children.Add(new PolarFixedChild(this, w, theta, r)); + w.Parent = this; + QueueResize(); + } + + public void Move(Widget w, double theta, uint r) + { + PolarFixedChild pfc = (PolarFixedChild) this[w]; + if (pfc != null) { + pfc.Theta = theta; + pfc.R = r; + } + } + + // invoked by Container.Remove (w) + protected override void OnRemoved(Widget w) + { + PolarFixedChild pfc = (PolarFixedChild) this[w]; + if (pfc != null) { + pfc.Child.Unparent(); + children.Remove(pfc); + QueueResize(); + } + } + + // Handle size request + protected override void OnGetPreferredHeight(out int minimal_height, out int natural_height) + { + Requisition req = new Requisition(); + OnSizeRequested(ref req); + minimal_height = natural_height = req.Height; + } + + protected override void OnGetPreferredWidth(out int minimal_width, out int natural_width) + { + Requisition req = new Requisition(); + OnSizeRequested(ref req); + minimal_width = natural_width = req.Width; + } + + void OnSizeRequested(ref Requisition req) + { + int child_width, child_minwidth, child_height, child_minheight; + int x, y; + + req.Width = req.Height = 0; + foreach (PolarFixedChild pfc in children) { + // Recursively request the size of each child + pfc.Child.GetPreferredWidth(out child_minwidth, out child_width); + pfc.Child.GetPreferredHeight(out child_minheight, out child_height); + + // Figure out where we're going to put it + x = (int) (Math.Cos(pfc.Theta) * pfc.R) + child_width / 2; + y = (int) (Math.Sin(pfc.Theta) * pfc.R) + child_height / 2; + + // Update our own size request to fit it + if (req.Width < 2 * x) + req.Width = 2 * x; + if (req.Height < 2 * y) + req.Height = 2 * y; + } + + // Take Container.BorderWidth into account + req.Width += (int) (2 * BorderWidth); + req.Height += (int) (2 * BorderWidth); + } + + // Size allocation. Note that the allocation received may be smaller than what we + // requested. Some containers will take that into account by giving some or all + // of their children a smaller allocation than they requested. Other containers + // (like this one) just let their children get placed partly out-of-bounds if they + // aren't allocated enough room. + protected override void OnSizeAllocated(Rectangle allocation) + { + Requisition childReq, childMinReq; + int cx, cy, x, y; + + // This sets the "Allocation" property. For widgets that + // have a GdkWindow, it also calls GdkWindow.MoveResize() + base.OnSizeAllocated(allocation); + + // Figure out where the center of the grid will be + cx = allocation.X + (allocation.Width / 2); + cy = allocation.Y + (allocation.Height / 2); + + foreach (PolarFixedChild pfc in children) { + pfc.Child.GetPreferredSize(out childMinReq, out childReq); + + x = (int) (Math.Cos(pfc.Theta) * pfc.R) - childReq.Width / 2; + y = (int) (Math.Sin(pfc.Theta) * pfc.R) + childReq.Height / 2; + + allocation.X = cx + x; + allocation.Width = childReq.Width; + allocation.Y = cy - y; + allocation.Height = childReq.Height; + + pfc.Child.SizeAllocate(allocation); + } + } + } +} \ No newline at end of file diff --git a/Source/Samples/Sections/Widgets/PolarFixedSection.cs b/Source/Samples/Sections/Widgets/PolarFixedSection.cs new file mode 100644 index 000000000..668cfafe1 --- /dev/null +++ b/Source/Samples/Sections/Widgets/PolarFixedSection.cs @@ -0,0 +1,64 @@ +using System; +using Gtk; + +namespace Samples +{ + [Section(ContentType = typeof(PolarFixed), Category = Category.Widgets)] + class PolarFixedSection : ListSection + { + public PolarFixedSection() + { + AddItem(CreateClock()); + AddItem(CreateSpiral()); + } + + public (string, Widget) CreateClock() + { + uint r; + double theta; + + + // Clock + PolarFixed pf = new PolarFixed(); + + for (int hour = 1; hour <= 12; hour++) { + theta = (Math.PI / 2) - hour * (Math.PI / 6); + if (theta < 0) + theta += 2 * Math.PI; + + Label l = new Label("" + hour.ToString() + ""); + l.UseMarkup = true; + pf.Put(l, theta, 50); + } + + return ("Clock", pf); + } + + public (string, Widget) CreateSpiral() + { + uint r; + double theta; + + var pf = new PolarFixed(); + + + r = 0; + theta = 0.0; + + foreach (string id in Gtk.Stock.ListIds()) { + StockItem item = Gtk.Stock.Lookup(id); + if (item.Label == null) + continue; + var icon = Gtk.Image.NewFromIconName(item.StockId, IconSize.SmallToolbar); + + pf.Put(icon, theta, r); + + // Logarithmic spiral: r = a*e^(b*theta) + r += 1; + theta = 10 * Math.Log(10 * r); + } + + return ("Spiral", pf); + } + } +} \ No newline at end of file