minimap.vala 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. /********************************************************************
  2. # Copyright 2014-2020 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. /**
  23. * A Widget that draws a minmap of a {@link GtkFlow.NodeView}
  24. *
  25. * Please set the nodeview property after integrating the referenced
  26. * {@link GtkFlow.NodeView} into its respective container
  27. */
  28. public class Minimap : Gtk.DrawingArea {
  29. private GtkFlow.NodeView? _nodeview = null;
  30. private Gtk.ScrolledWindow? _scrolledwindow = null;
  31. private ulong draw_signal = 0;
  32. private ulong hadjustment_signal = 0;
  33. private ulong vadjustment_signal = 0;
  34. private int offset_x = 0;
  35. private int offset_y = 0;
  36. private double ratio = 0.0;
  37. private int rubber_width = 0;
  38. private int rubber_height = 0;
  39. private bool move_rubber = false;
  40. private Gtk.EventControllerMotion motioncontroller;
  41. private Gtk.GestureClick clickcontroller;
  42. /**
  43. * The nodeview that this Minimap should depict
  44. *
  45. * You may either add a {@link GtkFlow.NodeView} directly or a
  46. * {@link Gtk.ScrolledWindow} that contains a {@link GtkFlow.NodeView}
  47. * as its child.
  48. */
  49. public NodeView nodeview {
  50. get {
  51. return this._nodeview;
  52. }
  53. set {
  54. if (this._nodeview != null) {
  55. GLib.SignalHandler.disconnect(this._nodeview, this.draw_signal);
  56. }
  57. if (this._scrolledwindow != null) {
  58. GLib.SignalHandler.disconnect(this._nodeview, this.hadjustment_signal);
  59. GLib.SignalHandler.disconnect(this._nodeview, this.vadjustment_signal);
  60. }
  61. if (value == null) {
  62. this._nodeview = null;
  63. this._scrolledwindow = null;
  64. } else {
  65. this._nodeview = value;
  66. this._scrolledwindow = null;
  67. if (value.get_parent() is Gtk.ScrolledWindow) {
  68. this._scrolledwindow = value.get_parent() as Gtk.ScrolledWindow;
  69. } else {
  70. if (value.get_parent() is Gtk.Viewport) {
  71. if (value.get_parent().get_parent() is Gtk.ScrolledWindow) {
  72. this._scrolledwindow = value.get_parent().get_parent() as Gtk.ScrolledWindow;
  73. this.hadjustment_signal = this._scrolledwindow.hadjustment.notify["value"].connect(
  74. ()=>{this.queue_draw();}
  75. );
  76. this.vadjustment_signal = this._scrolledwindow.vadjustment.notify["value"].connect(
  77. ()=>{this.queue_draw();}
  78. );
  79. }
  80. }
  81. }
  82. //this.draw_signal = this._nodeview.draw.connect((w, cr)=>{this.queue_draw(); return true;});
  83. }
  84. this.queue_draw();
  85. }
  86. }
  87. /**
  88. * Create a new Minimap
  89. */
  90. public Minimap() {
  91. this.set_size_request(50,50);
  92. this.motioncontroller = new Gtk.EventControllerMotion();
  93. this.clickcontroller = new Gtk.GestureClick();
  94. this.motioncontroller.motion.connect((x,y)=> { this.do_motion_notify_event(x,y);});
  95. this.clickcontroller.pressed.connect((n,x,y)=>{ this.do_button_release_event(n,x,y); });
  96. this.clickcontroller.released.connect((n,x,y)=>{ this.do_button_press_event(n,x,y); });
  97. }
  98. private bool do_button_press_event(int n, double x, double y) {
  99. uint but = this.clickcontroller.get_current_button();
  100. message("%u",but);
  101. /*if ( e.type == Gdk.EventType.@2BUTTON_PRESS
  102. || e.type == Gdk.EventType.@3BUTTON_PRESS)
  103. return false;*/
  104. this.move_rubber = true;
  105. double halloc = double.max(0, x - offset_x - rubber_width / 2) * ratio;
  106. double valloc = double.max(0, y - offset_y - rubber_height / 2) * ratio;
  107. this._scrolledwindow.hadjustment.value = halloc;
  108. this._scrolledwindow.vadjustment.value = valloc;
  109. return true;
  110. }
  111. private bool do_motion_notify_event(double x, double y) {
  112. if (!this.move_rubber || this._scrolledwindow == null) {
  113. return false;
  114. }
  115. double halloc = double.max(0, x - offset_x - rubber_width / 2) * ratio;
  116. double valloc = double.max(0, y - offset_y - rubber_height / 2) * ratio;
  117. this._scrolledwindow.hadjustment.value = halloc;
  118. this._scrolledwindow.vadjustment.value = valloc;
  119. return true;
  120. }
  121. private bool do_button_release_event(int n, double x, double y) {
  122. uint but = this.clickcontroller.get_current_button();
  123. message("%u",but);
  124. /*if ( e.type == Gdk.EventType.@2BUTTON_PRESS
  125. || e.type == Gdk.EventType.@3BUTTON_PRESS)
  126. return false;*/
  127. this.move_rubber = false;
  128. return true;
  129. }
  130. /**
  131. * Draws the minimap. This method is called internally
  132. */
  133. private new void snapshot(Gtk.Snapshot sn) {
  134. Gtk.Allocation own_alloc;
  135. this.get_allocation(out own_alloc);
  136. var alloc_rect = Graphene.Rect.alloc().init(own_alloc.x, own_alloc.y, own_alloc.width, own_alloc.height);
  137. Cairo.Context cr = sn.append_cairo(alloc_rect);
  138. Gtk.StyleContext sc = this.get_style_context();
  139. sc.render_background(cr, 0, 0, own_alloc.width, own_alloc.height);
  140. if (this._nodeview != null) {
  141. Gtk.Allocation nv_alloc;
  142. this._nodeview.get_allocation(out nv_alloc);
  143. this.offset_x = 0;
  144. this.offset_y = 0;
  145. int height = 0;
  146. int width = 0;
  147. if (own_alloc.width> own_alloc.height) {
  148. width = (int)((double) nv_alloc.width / nv_alloc.height * own_alloc.height);
  149. height = own_alloc.height;
  150. offset_x = (own_alloc.width - width ) / 2;
  151. } else {
  152. height = (int)((double) nv_alloc.height / nv_alloc.width * own_alloc.width);
  153. width = own_alloc.width;
  154. offset_y = (own_alloc.height - height ) / 2;
  155. }
  156. this.ratio = (double) nv_alloc.width / width;
  157. foreach(Node n in this._nodeview.get_nodes()) {
  158. Gtk.Allocation alloc;
  159. n.get_allocation(out alloc);
  160. cr.save();
  161. if (n.highlight_color != null) {
  162. cr.set_source_rgba(n.highlight_color.red,n.highlight_color.green,n.highlight_color.blue,0.5);
  163. } else {
  164. cr.set_source_rgba(0.4,0.4,0.4,0.5);
  165. }
  166. cr.rectangle(offset_x + alloc.x/ratio, offset_y + alloc.y/ratio, alloc.width/ratio, alloc.height/ratio);
  167. cr.fill();
  168. cr.restore();
  169. }
  170. if (this._scrolledwindow != null) {
  171. Gtk.Allocation sw_alloc;
  172. this._scrolledwindow.get_allocation(out sw_alloc);
  173. if (sw_alloc.width < nv_alloc.width || sw_alloc.height < nv_alloc.height) {
  174. this.rubber_width = (int)(sw_alloc.width / ratio);
  175. this.rubber_height = (int)(sw_alloc.height / ratio);
  176. draw_rubberband(this, cr,
  177. (int)(offset_x + this._scrolledwindow.hadjustment.value / ratio),
  178. (int)(offset_y + this._scrolledwindow.vadjustment.value / ratio),
  179. Gtk.StateFlags.NORMAL,
  180. &this.rubber_width, &this.rubber_height);
  181. }
  182. }
  183. }
  184. }
  185. /**
  186. * Internal method to initialize this NodeView as a {@link Gtk.Widget}
  187. */
  188. public override void realize() {
  189. /*Gtk.Allocation alloc;
  190. this.get_allocation(out alloc);
  191. var attr = Gdk.WindowAttr();
  192. attr.window_type = Gdk.WindowType.CHILD;
  193. attr.x = alloc.x;
  194. attr.y = alloc.y;
  195. attr.width = alloc.width;
  196. attr.height = alloc.height;
  197. attr.visual = this.get_visual();
  198. attr.event_mask = this.get_events()
  199. | Gdk.EventMask.POINTER_MOTION_MASK
  200. | Gdk.EventMask.BUTTON_PRESS_MASK
  201. | Gdk.EventMask.BUTTON_RELEASE_MASK;
  202. Gdk.WindowAttributesType mask = Gdk.WindowAttributesType.X
  203. | Gdk.WindowAttributesType.X
  204. | Gdk.WindowAttributesType.VISUAL;
  205. var window = new Gdk.Window(this.get_parent_window(), attr, mask);
  206. this.set_window(window);
  207. this.register_window(window);
  208. this.set_realized(true);*/
  209. }
  210. }
  211. }