View difference between Paste ID: f63c6aaa9 and
SHOW:
|
|
- or go back to the newest paste.
1 | #!/usr/bin/env ruby | |
2 | ||
3 | require 'gtk2' | |
4 | require 'rational' | |
5 | ||
6 | =begin | |
7 | A drawing area, which can be panned and zoomed. | |
8 | (requires rational) | |
9 | Panning is done with middle mouse drag, zooming is with the scrollwheel. | |
10 | To zoom only into x or y axis, hold down the shift or ctrl keys respectively. | |
11 | You must create the following method: | |
12 | expose_event event, cairo, coordinates | |
13 | where | |
14 | event is the expose event | |
15 | cairo is the cairo context | |
16 | coordinates is an array with the [xstart, ystart, xend, yend] coordinates | |
17 | =end | |
18 | class DragZoomDrawingArea < Gtk::DrawingArea | |
19 | type_register | |
20 | ||
21 | def initialize | |
22 | super | |
23 | ||
24 | @xscale = @yscale = 1.to_r | |
25 | @xoffset = @yoffset = 0.to_r | |
26 | @xscalestep = @yscalestep = 2.to_r | |
27 | @drag_sig_handlers = [] | |
28 | ||
29 | signal_connect('expose-event') {|widget, event| | |
30 | cairo = window.create_cairo_context | |
31 | cairo.scale(@xscale, @yscale) | |
32 | cairo.translate(@xoffset, @yoffset) | |
33 | ||
34 | alloc = allocation | |
35 | width, height = alloc.width, alloc.height | |
36 | ||
37 | xstart = (0.to_r - @xoffset) / @xscale | |
38 | xend = (width.to_r - @xoffset) / @xscale | |
39 | ||
40 | ystart = (0.to_r - @yoffset) / @yscale | |
41 | yend = (height.to_r - @yoffset) / @yscale | |
42 | ||
43 | expose_event widget, event, cairo, [xstart, ystart, xend, yend] | |
44 | true | |
45 | } | |
46 | ||
47 | signal_connect('scroll-event') {|widget, event| | |
48 | do_xscale = do_yscale = true | |
49 | ||
50 | if event.state & Gdk::Window::ModifierType::SHIFT_MASK != 0 | |
51 | do_yscale = false | |
52 | end | |
53 | if event.state & Gdk::Window::ModifierType::CONTROL_MASK != 0 | |
54 | do_xscale = false | |
55 | end | |
56 | ||
57 | xscale_old, yscale_old = @xscale, @yscale | |
58 | case event.direction | |
59 | when Gdk::EventScroll::DOWN | |
60 | @xscale /= @xscalestep if do_xscale | |
61 | @yscale /= @yscalestep if do_yscale | |
62 | when Gdk::EventScroll::UP | |
63 | @xscale *= @xscalestep if do_xscale | |
64 | @yscale *= @yscalestep if do_yscale | |
65 | end | |
66 | ||
67 | @xoffset = event.x.to_i.to_r / @xscale - (event.x.to_i / xscale_old - @xoffset) | |
68 | @yoffset = event.y.to_i.to_r / @yscale - (event.y.to_i / yscale_old - @yoffset) | |
69 | queue_draw | |
70 | } | |
71 | ||
72 | @motion_cb = proc {|widget, event| | |
73 | deltax, deltay = event.x.to_i.to_r - @startx.to_i, event.y.to_i.to_r - @starty.to_i | |
74 | @xoffset += deltax / @xscale | |
75 | @yoffset += deltay / @yscale | |
76 | @startx, @starty = event.x, event.y | |
77 | queue_draw | |
78 | } | |
79 | ||
80 | signal_connect('button-press-event') {|widget, event| | |
81 | if(event.button == 2) | |
82 | @startx, @starty = event.x, event.y | |
83 | @drag_sig_handlers << signal_connect('motion-notify-event', &@motion_cb) | |
84 | end | |
85 | } | |
86 | ||
87 | signal_connect('button-release-event') {|widget, event| | |
88 | if(event.button == 2) | |
89 | @drag_sig_handlers.reject!{|s| signal_handler_disconnect(s); true} | |
90 | end | |
91 | } | |
92 | ||
93 | add_events(Gdk::Event::SCROLL_MASK | | |
94 | Gdk::Event::BUTTON_PRESS_MASK | | |
95 | Gdk::Event::BUTTON_RELEASE_MASK | | |
96 | Gdk::Event::POINTER_MOTION_MASK) | |
97 | end | |
98 | end | |
99 | ||
100 | class MyWindow < Gtk::Window | |
101 | def initialize | |
102 | super('interactive graph') | |
103 | set_size_request(500,500) | |
104 | signal_connect("delete-event") { Gtk.main_quit;true } | |
105 | ||
106 | ||
107 | drawing_area = DragZoomDrawingArea.new | |
108 | def drawing_area.expose_event widget, event, cairo, disp | |
109 | cairo.set_source_rgb(1,0,0) | |
110 | cairo.rectangle(100, 100, 50, 50) | |
111 | cairo.stroke | |
112 | end | |
113 | add(drawing_area) | |
114 | show_all | |
115 | end | |
116 | end | |
117 | ||
118 | app = MyWindow.new | |
119 | ||
120 | Gtk.main | |
121 | ||
122 |