default_node_renderer.vala 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  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. namespace GtkFlow {
  22. public class DefaultNodeRenderer : NodeRenderer {
  23. private Pango.Layout layout;
  24. internal DefaultNodeRenderer(Node n) {
  25. this.layout = (new Gtk.Label("")).create_pango_layout("");
  26. }
  27. public override void update_name_layout(string name) {
  28. this.layout.set_markup("<b>%s</b>".printf(name),-1);
  29. this.size_changed();
  30. }
  31. private uint get_title_line_height() {
  32. // FIXME: this is a bad solution. it should not happen in the first place
  33. // probably related to the remaining Pango-CRITICALs. but it works.
  34. if (this.layout == null)
  35. return 25;
  36. int width, height;
  37. this.layout.get_pixel_size(out width, out height);
  38. return (uint)Math.fmax(height, delete_btn_size) + title_spacing;
  39. }
  40. /**
  41. * Returns the minimum height this node has to have
  42. */
  43. public override uint get_min_height(List<DockRenderer> dock_renderers,
  44. List<Gtk.Widget> children,
  45. int border_width) {
  46. uint mh = border_width*2;
  47. mh += this.get_title_line_height();
  48. foreach (DockRenderer dock_renderer in dock_renderers) {
  49. mh += dock_renderer.get_min_height();
  50. }
  51. Gtk.Widget child = children.nth_data(0);
  52. if (child != null) {
  53. int child_height, _;
  54. child.get_preferred_height(out child_height, out _);
  55. mh += child_height;
  56. }
  57. return mh;
  58. }
  59. /**
  60. * Returns the minimum width this node has to have
  61. */
  62. public override uint get_min_width(List<DockRenderer> dock_renderers,
  63. List<Gtk.Widget> children,
  64. int border_width) {
  65. uint mw = 0;
  66. int t = 0;
  67. if (this.layout.get_text() != "") {
  68. int width, height;
  69. this.layout.get_pixel_size(out width, out height);
  70. mw = width + title_spacing + delete_btn_size;
  71. }
  72. foreach (DockRenderer dr in dock_renderers) {
  73. t = dr.get_min_width();
  74. if (t > mw)
  75. mw = t;
  76. }
  77. Gtk.Widget child = children.nth_data(0);
  78. if (child != null) {
  79. int child_width, _;
  80. child.get_preferred_width(out child_width, out _);
  81. if (child_width > mw)
  82. mw = child_width;
  83. }
  84. return mw + border_width*2;
  85. }
  86. /**
  87. * Returns true if the point is on the close-button of the node
  88. */
  89. public override bool is_on_closebutton(Gdk.Point p,
  90. Gtk.Allocation alloc,
  91. uint border_width) {
  92. int x = p.x;
  93. int y = p.y;
  94. int x_left, x_right, y_top, y_bot;
  95. if ((this.get_style().get_state() & Gtk.StateFlags.DIR_LTR) > 0 ){
  96. x_left = alloc.x + alloc.width - delete_btn_size
  97. - (int)border_width;
  98. x_right = x_left + delete_btn_size;
  99. y_top = alloc.y + (int)border_width;
  100. y_bot = y_top + delete_btn_size;
  101. } else {
  102. x_left = alloc.x + (int)border_width;
  103. x_right = x_left + delete_btn_size;
  104. y_top = alloc.y + (int)border_width;
  105. y_bot = y_top + delete_btn_size;
  106. }
  107. return x > x_left && x < x_right && y > y_top && y < y_bot;
  108. }
  109. /**
  110. * Returns true if the point is in the resize-drag area
  111. */
  112. public override bool is_on_resize_handle(Gdk.Point p,
  113. Gtk.Allocation alloc,
  114. uint border_width) {
  115. int x = p.x;
  116. int y = p.y;
  117. int x_right, x_left, y_bot, y_top;
  118. if ((this.get_style().get_state() & Gtk.StateFlags.DIR_LTR) > 0 ){
  119. x_right = alloc.x + alloc.width;
  120. x_left = x_right - resize_handle_size;
  121. y_bot = alloc.y + alloc.height;
  122. y_top = y_bot - resize_handle_size;
  123. } else {
  124. x_left = alloc.x;
  125. x_right = x_left + resize_handle_size;
  126. y_bot = alloc.y + alloc.height;
  127. y_top = y_bot - resize_handle_size;
  128. }
  129. return x > x_left && x < x_right && y > y_top && y < y_bot;
  130. }
  131. /**
  132. * Returns the position of the given dock.
  133. * This is obviously bullshit. GFlow.Docks should be able to know
  134. * their own position
  135. */
  136. public override bool get_dock_position(GFlow.Dock d,
  137. List<DockRenderer> dock_renderers,
  138. int border_width,
  139. Gtk.Allocation alloc,
  140. out int x,
  141. out int y) {
  142. int i = 0;
  143. x = y = 0;
  144. uint title_offset = this.get_title_line_height();
  145. foreach(DockRenderer dock_renderer in dock_renderers) {
  146. GFlow.Dock s = dock_renderer.get_dock();
  147. if (s == d) {
  148. if ((this.get_style().get_state() & Gtk.StateFlags.DIR_LTR) > 0 ){
  149. if (s is GFlow.Sink) {
  150. x += alloc.x + border_width
  151. + dock_renderer.dockpoint_height/2;
  152. y += alloc.y + border_width + (int)title_offset
  153. + dock_renderer.dockpoint_height/2 + i
  154. * dock_renderer.get_min_height();
  155. return true;
  156. } else if (s is GFlow.Source) {
  157. x += alloc.x - border_width
  158. + alloc.width - dock_renderer.dockpoint_height/2;
  159. y += alloc.y + border_width + (int)title_offset
  160. + dock_renderer.dockpoint_height/2 + i
  161. * dock_renderer.get_min_height();
  162. return true;
  163. }
  164. } else {
  165. if (s is GFlow.Sink) {
  166. x += alloc.x - border_width
  167. + alloc.width - dock_renderer.dockpoint_height/2;
  168. y += alloc.y + border_width + (int)title_offset
  169. + dock_renderer.dockpoint_height/2 + i
  170. * dock_renderer.get_min_height();
  171. return true;
  172. } else if (s is GFlow.Source) {
  173. x += alloc.x + border_width
  174. + dock_renderer.dockpoint_height/2;
  175. y += alloc.y + border_width + (int)title_offset
  176. + dock_renderer.dockpoint_height/2 + i
  177. * dock_renderer.get_min_height();
  178. return true;
  179. }
  180. }
  181. }
  182. i++;
  183. }
  184. return false;
  185. }
  186. /**
  187. * Determines whether the mousepointer is hovering over a dock on this node
  188. */
  189. public override GFlow.Dock? get_dock_on_position(Gdk.Point p,
  190. List<DockRenderer> dock_renderers,
  191. uint border_width,
  192. Gtk.Allocation alloc ) {
  193. int x = p.x;
  194. int y = p.y;
  195. int i = 0;
  196. int dock_x, dock_y, mh;
  197. uint title_offset;
  198. title_offset = this.get_title_line_height();
  199. foreach (DockRenderer dock_renderer in dock_renderers) {
  200. GFlow.Dock s = dock_renderer.get_dock();
  201. mh = dock_renderer.get_min_height();
  202. if ((this.get_style().get_state() & Gtk.StateFlags.DIR_LTR) > 0 ){
  203. if (s is GFlow.Sink) {
  204. dock_x = alloc.x + (int)border_width;
  205. dock_y = alloc.y + (int)border_width + (int)title_offset
  206. + i * mh;
  207. if (x > dock_x && x < dock_x + dock_renderer.dockpoint_height
  208. && y > dock_y && y < dock_y + dock_renderer.dockpoint_height )
  209. return s;
  210. } else if (s is GFlow.Source) {
  211. dock_x = alloc.x + alloc.width
  212. - (int)border_width
  213. - dock_renderer.dockpoint_height;
  214. dock_y = alloc.y + (int)border_width + (int)title_offset
  215. + i * mh;
  216. if (x > dock_x && x < dock_x + dock_renderer.dockpoint_height
  217. && y > dock_y && y < dock_y + dock_renderer.dockpoint_height )
  218. return s;
  219. }
  220. } else {
  221. if (s is GFlow.Sink) {
  222. dock_x = alloc.x + alloc.width
  223. - (int)border_width
  224. - dock_renderer.dockpoint_height;
  225. dock_y = alloc.y + (int)border_width + (int)title_offset
  226. + i * mh;
  227. if (x > dock_x && x < dock_x + dock_renderer.dockpoint_height
  228. && y > dock_y && y < dock_y + dock_renderer.dockpoint_height )
  229. return s;
  230. } else if (s is GFlow.Source) {
  231. dock_x = alloc.x + (int)border_width;
  232. dock_y = alloc.y + (int)border_width + (int)title_offset
  233. + i * mh;
  234. if (x > dock_x && x < dock_x + dock_renderer.dockpoint_height
  235. && y > dock_y && y < dock_y + dock_renderer.dockpoint_height )
  236. return s;
  237. }
  238. }
  239. i++;
  240. }
  241. return null;
  242. }
  243. /**
  244. * Returns a Gtk.StyleContext matching a given selector
  245. */
  246. private Gtk.StyleContext get_style() {
  247. var b = new Gtk.Button();
  248. return b.get_style_context();
  249. }
  250. /**
  251. * Draw this node on the given cairo context
  252. */
  253. public override void draw_node(Gtk.Widget w, Cairo.Context cr,
  254. Gtk.Allocation alloc,
  255. List<DockRenderer> dock_renderers,
  256. List<Gtk.Widget> children,
  257. int border_width,
  258. NodeProperties node_properties) {
  259. bool editable = node_properties.editable;
  260. bool deletable = node_properties.deletable;
  261. bool resizable = node_properties.resizable;
  262. bool selected = node_properties.selected;
  263. var sc = this.get_style();
  264. sc.save();
  265. sc.render_background(cr, alloc.x, alloc.y, alloc.width, alloc.height);
  266. sc.render_frame(cr, alloc.x, alloc.y, alloc.width, alloc.height);
  267. sc.restore();
  268. int y_offset = 0;
  269. if (this.layout.get_text() != "") {
  270. sc.save();
  271. cr.save();
  272. sc.add_class(Gtk.STYLE_CLASS_BUTTON);
  273. Gdk.RGBA col = sc.get_color(Gtk.StateFlags.NORMAL);
  274. cr.set_source_rgba(col.red,col.green,col.blue,col.alpha);
  275. if ((this.get_style().get_state() & Gtk.StateFlags.DIR_LTR) > 0 ){
  276. cr.move_to(alloc.x + border_width,
  277. alloc.y + (int) border_width + y_offset);
  278. } else {
  279. cr.move_to(alloc.x + 2*border_width + delete_btn_size,
  280. alloc.y + (int) border_width + y_offset);
  281. }
  282. Pango.cairo_show_layout(cr, this.layout);
  283. cr.restore();
  284. sc.restore();
  285. }
  286. if (editable && deletable) {
  287. Gtk.IconTheme it = Gtk.IconTheme.get_default();
  288. try {
  289. cr.save();
  290. Gdk.Pixbuf icon_pix = it.load_icon("edit-delete", delete_btn_size, 0);
  291. if ((this.get_style().get_state() & Gtk.StateFlags.DIR_LTR) > 0 ){
  292. Gdk.cairo_set_source_pixbuf(
  293. cr, icon_pix,
  294. alloc.x+alloc.width-delete_btn_size-border_width,
  295. alloc.y+border_width
  296. );
  297. } else {
  298. Gdk.cairo_set_source_pixbuf(
  299. cr, icon_pix,
  300. alloc.x+border_width,
  301. alloc.y+border_width
  302. );
  303. }
  304. cr.paint();
  305. } catch (GLib.Error e) {
  306. warning("Could not load close-node-icon 'edit-delete'");
  307. } finally {
  308. cr.restore();
  309. }
  310. }
  311. y_offset += (int)this.get_title_line_height();
  312. foreach (DockRenderer dock_renderer in dock_renderers) {
  313. if (dock_renderer.get_dock() is GFlow.Sink) {
  314. dock_renderer.draw_dock(w, cr, sc, alloc.x + (int)border_width,
  315. alloc.y+y_offset + (int) border_width, alloc.width);
  316. } else if (dock_renderer.get_dock() is GFlow.Source) {
  317. dock_renderer.draw_dock(w, cr, sc, alloc.x-(int)border_width,
  318. alloc.y+y_offset + (int) border_width, alloc.width);
  319. }
  320. y_offset += dock_renderer.get_min_height();
  321. }
  322. Gtk.Widget child = children.nth_data(0);
  323. if (child != null) {
  324. Gtk.Allocation child_alloc = {0,0,0,0};
  325. child_alloc.x = (int)border_width;
  326. child_alloc.y = (int)border_width + y_offset;
  327. child_alloc.width = alloc.width - 2 * (int)border_width;
  328. child_alloc.height = alloc.height - 2 * (int)border_width - y_offset;
  329. child.size_allocate(child_alloc);
  330. this.child_redraw(child);
  331. }
  332. // Draw resize handle
  333. if (resizable) {
  334. sc.save();
  335. cr.save();
  336. cr.set_source_rgba(0.5,0.5,0.5,0.5);
  337. if ((this.get_style().get_state() & Gtk.StateFlags.DIR_LTR) > 0 ){
  338. cr.move_to(alloc.x + alloc.width,
  339. alloc.y + alloc.height);
  340. cr.line_to(alloc.x + alloc.width - resize_handle_size,
  341. alloc.y + alloc.height);
  342. cr.line_to(alloc.x + alloc.width,
  343. alloc.y + alloc.height - resize_handle_size);
  344. } else {
  345. cr.move_to(alloc.x,
  346. alloc.y + alloc.height);
  347. cr.line_to(alloc.x + resize_handle_size,
  348. alloc.y + alloc.height);
  349. cr.line_to(alloc.x,
  350. alloc.y + alloc.height - resize_handle_size);
  351. }
  352. cr.fill();
  353. cr.stroke();
  354. cr.restore();
  355. sc.restore();
  356. }
  357. if (selected) {
  358. draw_rubberband(w, cr, alloc.x, alloc.y, Gtk.StateFlags.NORMAL, &alloc.width, &alloc.height);
  359. }
  360. }
  361. }
  362. }