# File gtk2/sample/gtk-demo/drawingarea.rb, line 25 def initialize # Pixmap for scribble area, to store current scribbles @pixmap = nil super('Drawing Area') self.border_width = 8 vbox = Gtk::VBox.new(false, 8) vbox.border_width = 8 add(vbox) ## Create the checkerboard area label = Gtk::Label.new label.set_markup('<u>Checkerboard pattern</u>') vbox.pack_start(label, false, false, 0) frame = Gtk::Frame.new frame.shadow_type = Gtk::SHADOW_IN vbox.pack_start(frame, true, true, 0) da = Gtk::DrawingArea.new # set a minimum size da.set_size_request(100, 100) frame.add(da) da.signal_connect('expose_event') do |widget, event| checkerboard_expose(widget) end ## Create the scribble area label = Gtk::Label.new label.set_markup('<u>Scribble area</u>') vbox.pack_start(label, false, false, 0) frame = Gtk::Frame.new frame.shadow_type = Gtk::SHADOW_IN vbox.pack_start(frame, true, true, 0) da = Gtk::DrawingArea.new # set a minimum size da.set_size_request(100, 100) frame.add(da) # Signals used to handle backing pixmap da.signal_connect('expose_event') do |*args| scribble_expose_event(*args) end da.signal_connect('configure_event') do |widget, event| scribble_configure_event(widget) end # Event signals da.signal_connect('motion_notify_event') do |*args| scribble_motion_notify_event(*args) end da.signal_connect('button_press_event') do |*args| scribble_button_press_event(*args) end # Ask to receive events the drawing area doesn't normally # subscribe to da.events |= (Gdk::Event::LEAVE_NOTIFY_MASK | Gdk::Event::BUTTON_PRESS_MASK | Gdk::Event::POINTER_MOTION_MASK | Gdk::Event::POINTER_MOTION_HINT_MASK) end
# File gtk3/sample/gtk-demo/drawingarea.rb, line 110 def checkerboard_draw(da, cairo_context) # At the start of a draw handler, a clip region of event.area # is set on the window, and event.area has been cleared to the # widget's background color. The docs for # gdk_window_begin_paint_region give more details on how this # works. xcount = 0 width = da.allocated_width height = da.allocated_height SPACING.step(da.allocation.width, CHECK_SIZE + SPACING) do |i| ycount = xcount % 2; # start with even/odd depending on row SPACING.step(da.allocation.height, CHECK_SIZE + SPACING) do |j| if ycount % 2 == 1 cairo_context.set_source_rgb(0.45777, 0, 0.45777) else cairo_context.set_source_rgb(1, 1, 1) end # If we're outside the clip, this will do nothing. cairo_context.fill do cairo_context.rectangle(i, j, CHECK_SIZE, CHECK_SIZE) end ycount += 1 end xcount += 1 end # return true because we've handled this event, so no # further processing is required. # return true end
# File gtk2/sample/gtk-demo/drawingarea.rb, line 115 def checkerboard_expose(da) # At the start of an expose handler, a clip region of event.area # is set on the window, and event.area has been cleared to the # widget's background color. The docs for # gdk_window_begin_paint_region give more details on how this # works. # It would be a bit more efficient to keep these # GC's around instead of recreating on each expose, but # this is the lazy/slow way. gc1 = Gdk::GC.new(da.window) gc1.rgb_fg_color = Gdk::Color.new(30000, 0, 30000) gc2 = Gdk::GC.new(da.window) gc2.rgb_fg_color = Gdk::Color.new(65535, 65535, 65535) xcount = 0 SPACING.step(da.allocation.width, CHECK_SIZE + SPACING) do |i| ycount = xcount % 2; # start with even/odd depending on row SPACING.step(da.allocation.height, CHECK_SIZE + SPACING) do |j| gc = if ycount % 2 == 1 gc1 else gc2 end # If we're outside event.area, this will do nothing. # It might be mildly more efficient if we handled # the clipping ourselves, but again we're feeling lazy. da.window.draw_rectangle(gc, true, i, j, CHECK_SIZE, CHECK_SIZE) ycount += 1 end xcount += 1 end # return true because we've handled this event, so no # further processing is required. # return true end
# File gtk2/sample/gtk-demo/drawingarea.rb, line 173 def draw_brush(widget, x, y) update_rect = Gdk::Rectangle.new(x - 3, y - 3, 6, 6) # Paint to the pixmap, where we store our state @pixmap.draw_rectangle(widget.style.black_gc, true, update_rect.x, update_rect.y, update_rect.width, update_rect.height) # Now invalidate the affected region of the drawing area. widget.window.invalidate(update_rect, false) end
Create a new pixmap of the appropriate size to store our scribbles
# File gtk2/sample/gtk-demo/drawingarea.rb, line 96 def scribble_configure_event(widget) @pixmap = Gdk::Pixmap.new(self.window, widget.allocation.width, widget.allocation.height, -1) # Initialize the pixmap to white @pixmap.draw_rectangle(widget.style.white_gc, true, 0, 0, widget.allocation.width, widget.allocation.height) # We've handled the configure event, no need for further processing. return true end
Redraw the screen from the surface
# File gtk3/sample/gtk-demo/drawingarea.rb, line 144 def scribble_draw(widget, event) event.set_source(@cairo_context.target) event.paint return false end
Redraw the screen from the pixmap
# File gtk2/sample/gtk-demo/drawingarea.rb, line 158 def scribble_expose_event(widget, event) # We use the 'foreground GC' for the widget since it already exists, # but honestly any GC would work. The only thing to worry about # is whether the GC has an inappropriate clip region set. widget.window.draw_drawable(widget.style.fg_gc(widget.state), @pixmap, # Only copy the area that was exposed. event.area.x, event.area.y, event.area.x, event.area.y, event.area.width, event.area.height) return false end
# File gtk2/sample/gtk-demo/drawingarea.rb, line 200 def scribble_motion_notify_event(widget, event) unless @pixmap # paranoia check, in case we haven't gotten a configure event return false end # This call is very important; it requests the next motion event. # If you don't call Gdk::Window#pointer you'll only get # a single motion event. The reason is that we specified # Gdk::POINTER_MOTION_HINT_MASK to Gtk::Widget#set_events. # If we hadn't specified that, we could just use event.x, event.y # as the pointer location. But we'd also get deluged in events. # By requesting the next event as we handle the current one, # we avoid getting a huge number of events faster than we # can cope. win, x, y, state = event.window.pointer if (state & Gdk::Window::BUTTON1_MASK) != 0 draw_brush(widget, x, y) end # We've handled it, stop processing return true end