minimap.vala 9.3 KB

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