node.vala 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. /********************************************************************
  2. # Copyright 2014-2019 Daniel 'grindhold' Brendle
  3. #
  4. # This file is part of libgtkflow.
  5. #
  6. # libgtkflow is free software: you can redistribute it and/or
  7. # modify it under the terms of the GNU Lesser General Public License
  8. # as published by the Free Software Foundation, either
  9. # version 3 of the License, or (at your option) any later
  10. # version.
  11. #
  12. # libgtkflow is distributed in the hope that it will be
  13. # useful, but WITHOUT ANY WARRANTY; without even the implied
  14. # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  15. # PURPOSE. See the GNU Lesser General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU Lesser General Public
  18. # License along with libgtkflow.
  19. # If not, see http://www.gnu.org/licenses/.
  20. *********************************************************************/
  21. /**
  22. * Flowgraphs for Gtk
  23. */
  24. namespace GtkFlow {
  25. /**
  26. * Represents an element that can generate, process or receive data
  27. * This is done by adding Sources and Sinks to it. The inner logic of
  28. * The node can be represented towards the user as arbitrary Gtk widget.
  29. */
  30. private class Node : Gtk.Container {
  31. // Determines the space between the title and the first dock (y-axis)
  32. // as well as the space between the title and the close-button if any (x-axis)
  33. public GFlow.Node gnode {public get; private set; default = null;}
  34. public NodeView? node_view {get; set; default=null;}
  35. internal bool selected {get; set; default=false;}
  36. private NodeRenderer? _node_renderer = null;
  37. public NodeRenderer? node_renderer {
  38. get {return this._node_renderer;}
  39. set {
  40. if (this._node_renderer != null) {
  41. this._node_renderer.size_changed.disconnect(this.size_changed_callback);
  42. this._node_renderer.child_redraw.disconnect(this.child_redraw_callback);
  43. }
  44. this._node_renderer = value;
  45. this._node_renderer.size_changed.connect(this.size_changed_callback);
  46. this._node_renderer.child_redraw.connect(this.child_redraw_callback);
  47. }
  48. }
  49. private void size_changed_callback() {
  50. this.render();
  51. }
  52. public Cairo.Context? current_cairo_ctx {get; set; default=null;}
  53. private void child_redraw_callback(Gtk.Widget w) {
  54. if (this.current_cairo_ctx == null) {
  55. warning("Child Redraw: No context to draw on");
  56. return;
  57. }
  58. Cairo.Context? cr = this.current_cairo_ctx;
  59. Gtk.Allocation node_alloc, child_alloc;
  60. this.get_allocation(out node_alloc);
  61. w.get_allocation(out child_alloc);
  62. cr.save();
  63. cr.translate(node_alloc.x + child_alloc.x, node_alloc.y + child_alloc.y);
  64. w.draw(cr);
  65. cr.restore();
  66. }
  67. private List<DockRenderer?> dock_renderers = new List<DockRenderer?>();
  68. private List<Gtk.Widget> childlist = new List<Gtk.Widget>();
  69. private HashTable<weak Gtk.Widget, ulong> childlist_alloc_handles = new HashTable<weak Gtk.Widget, ulong>(direct_hash, direct_equal);
  70. public Node (GFlow.Node n) {
  71. this.gnode = n;
  72. this.node_renderer = new DefaultNodeRenderer(this);
  73. foreach (GFlow.Dock d in this.gnode.get_sources())
  74. this.register_dock(d);
  75. foreach (GFlow.Dock d in this.gnode.get_sinks())
  76. this.register_dock(d);
  77. this.gnode.source_added.connect((s)=>{this.register_dock(s);});
  78. this.gnode.sink_added.connect((s)=>{this.register_dock(s);});
  79. this.gnode.source_removed.connect((s)=>{this.unregister_dock(s);});
  80. this.gnode.sink_removed.connect((s)=>{this.unregister_dock(s);});
  81. this.node_renderer.update_name_layout(this.gnode.name);
  82. this.gnode.notify["name"].connect(()=>{
  83. this.node_renderer.update_name_layout(this.gnode.name);
  84. });
  85. this.set_border_width(this.node_renderer.resize_handle_size);
  86. this.show_all();
  87. }
  88. public void render() {
  89. this.recalculate_size();
  90. if (this.node_view != null) {
  91. this.node_view.queue_draw();
  92. }
  93. }
  94. public void render_all() {
  95. if (this.node_renderer != null)
  96. this.node_renderer.update_name_layout(this.gnode.name);
  97. foreach(DockRenderer dr in this.dock_renderers)
  98. if (dr != null) {
  99. bool show_types = this.node_view != null ? this.node_view.show_types : false;
  100. dr.update_name_layout(show_types);
  101. }
  102. }
  103. private void register_dock(GFlow.Dock d) {
  104. DefaultDockRenderer dr = new DefaultDockRenderer(this, d);
  105. dr.size_changed.connect(()=>{this.render();});
  106. d.notify["name"].connect(()=>{
  107. dr.update_name_layout(this.node_view != null ? this.node_view.show_types : false);
  108. });
  109. d.notify["typename"].connect(()=>{
  110. dr.update_name_layout(this.node_view != null ? this.node_view.show_types : false);
  111. });
  112. d.changed.connect(()=>{this.render();});
  113. this.dock_renderers.append(dr);
  114. dr.update_name_layout(this.node_view != null ? this.node_view.show_types : false);
  115. if (this.get_realized())
  116. this.render();
  117. }
  118. public DockRenderer? get_dock_renderer(GFlow.Dock d) {
  119. foreach (DockRenderer dock_renderer in this.dock_renderers) {
  120. if (dock_renderer.get_dock() == d)
  121. return dock_renderer;
  122. }
  123. return null;
  124. }
  125. private void unregister_dock(GFlow.Dock d) {
  126. DockRenderer? dr = this.get_dock_renderer(d);
  127. if (dr != null) {
  128. this.dock_renderers.remove_link(
  129. this.dock_renderers.find_custom(dr, (x,y)=>{return (int)(x!=y);})
  130. );
  131. //m.free();
  132. }
  133. if (this.get_realized())
  134. this.render();
  135. }
  136. public Node.with_child(GFlow.Node n, Gtk.Widget c) {
  137. this(n);
  138. this.add(c);
  139. this.show_all();
  140. }
  141. public void on_child_size_allocate(Gtk.Allocation _) {
  142. Gtk.Allocation alloc;
  143. this.get_allocation(out alloc);
  144. this.size_allocate(alloc);
  145. this.node_view.queue_draw();
  146. }
  147. public new void size_allocate(Gtk.Allocation alloc) {
  148. if (!this.get_visible() && !this.is_toplevel())
  149. return;
  150. int mw = (int)this.node_renderer.get_min_width(
  151. this.dock_renderers, this.childlist,
  152. (int)this.get_border_width()
  153. );
  154. int mh = (int)this.node_renderer.get_min_height(
  155. this.dock_renderers, this.childlist,
  156. (int)this.get_border_width()
  157. );
  158. if (alloc.width < mw)
  159. alloc.width = mw;
  160. if (alloc.height < mh)
  161. alloc.height = mh;
  162. (this as Gtk.Widget).size_allocate(alloc);
  163. }
  164. public void set_position(int x, int y) {
  165. Gtk.Allocation alloc;
  166. this.get_allocation(out alloc);
  167. alloc.x = x;
  168. alloc.y = y;
  169. this.size_allocate(alloc);
  170. this.node_view.queue_draw();
  171. }
  172. public unowned Gdk.Point get_position() {
  173. Gtk.Allocation alloc;
  174. this.get_allocation(out alloc);
  175. Gdk.Point pos = Gdk.Point();
  176. pos.x = alloc.x;
  177. pos.y = alloc.y;
  178. return pos;
  179. }
  180. public unowned List<DockRenderer> get_dock_renderers() {
  181. return this.dock_renderers;
  182. }
  183. public override void forall_internal(bool include_internals, Gtk.Callback c) {
  184. foreach (Gtk.Widget child in this.childlist) {
  185. c(child);
  186. }
  187. }
  188. public override void add(Gtk.Widget w) {
  189. w.set_parent(this);
  190. this.childlist.append(w);
  191. ulong handle = w.size_allocate.connect(this.on_child_size_allocate);
  192. this.childlist_alloc_handles.set(w, handle);
  193. if (this.get_realized())
  194. this.render();
  195. }
  196. public override void remove(Gtk.Widget w) {
  197. w.unparent();
  198. ulong handle = this.childlist_alloc_handles.get(w);
  199. w.disconnect(handle);
  200. this.childlist_alloc_handles.remove(w);
  201. this.childlist.remove_link(
  202. this.childlist.find_custom(w, (x,y)=>{return (int)(x!=y);})
  203. );
  204. }
  205. public unowned List<Gtk.Widget> get_childlist() {
  206. return this.childlist;
  207. }
  208. public new void set_border_width(uint border_width) {
  209. int nr = this.node_renderer.resize_handle_size;
  210. if (border_width < nr) {
  211. warning("Cannot set border width smaller than %d", nr);
  212. return;
  213. }
  214. base.set_border_width(border_width);
  215. if (this.get_realized())
  216. this.render();
  217. }
  218. public override void realize() {
  219. this.recalculate_size();
  220. Gtk.Allocation alloc;
  221. this.get_allocation(out alloc);
  222. var attr = Gdk.WindowAttr();
  223. attr.window_type = Gdk.WindowType.CHILD;
  224. attr.x = alloc.x;
  225. attr.y = alloc.y;
  226. attr.width = alloc.width;
  227. attr.height = alloc.height;
  228. attr.visual = this.get_visual();
  229. attr.event_mask = this.get_events();
  230. Gdk.WindowAttributesType mask = Gdk.WindowAttributesType.X
  231. | Gdk.WindowAttributesType.X
  232. | Gdk.WindowAttributesType.VISUAL;
  233. var window = new Gdk.Window(this.get_parent_window(), attr, mask);
  234. this.set_window(window);
  235. this.register_window(window);
  236. this.set_realized(true);
  237. this.render();
  238. }
  239. /**
  240. * Checks if the node needs to be resized in order to fill the minimum
  241. * size requirements
  242. */
  243. public void recalculate_size() {
  244. Gtk.Allocation alloc;
  245. this.get_allocation(out alloc);
  246. uint mw = this.node_renderer.get_min_width(
  247. this.dock_renderers, this.childlist,
  248. (int) this.get_border_width()
  249. );
  250. uint mh = this.node_renderer.get_min_height(
  251. this.dock_renderers, this.childlist,
  252. (int) this.get_border_width()
  253. );
  254. if (mw > alloc.width)
  255. alloc.width = (int)mw;
  256. if (mh > alloc.height)
  257. alloc.height = (int)mh;
  258. this.size_allocate(alloc);
  259. }
  260. }
  261. public struct NodeProperties {
  262. bool editable;
  263. bool deletable;
  264. bool resizable;
  265. bool selected;
  266. }
  267. }