#!/usr/bin/env ruby # wxRuby2 Sample Code. Copyright (c) 2004-2008 wxRuby development team # Adapted for wxRuby3 # Copyright (c) M.J.N. Corino, The Netherlands ### require 'wx' # This sample demonstrates how to dynamically connect and disconnect # event handlers, and how to create custom event types and listeners # associated with user-defined control classes. # A custom type of event associated with a target control. Note that for # user-defined controls, the associated event should inherit from # Wx::CommandEvent rather than Wx::Event. class TargetHitEvent < Wx::CommandEvent # Create a new unique constant identifier, associate this class # with events of that identifier, and create a shortcut 'evt_target' # method for setting up this handler. EVT_HIT_TARGET = Wx::EvtHandler.register_class(self, nil, 'evt_target', 1) def initialize(target, score, distance) # The constant id is the arg to super super(EVT_HIT_TARGET) # client_data should be used to store any information associated # with the event. self.client_data = { :score => score, :distance => distance } self.id = target.get_id end # Returns the points score associated with this event def score client_data[:score] end # Returns the distance (in pixels) from the centre of the target def distance client_data[:distance] end end class CountEvent < Wx::CommandEvent EVT_COUNTER = Wx::EvtHandler.register_class(self, nil, 'evt_counter', 1) def initialize(id) super(EVT_COUNTER, id) end end # An example of a simple user-written control, which displays a # "bulls-eye" like target, and sends events with a score and distance class TargetControl < Wx::Window TargetCircle = Struct.new(:radius, :score, :brush) def initialize(parent, *args) super(parent, *args) # Set up the scores and sizes of the rings @radii = [ TargetCircle[ 0.1, 20, Wx::RED_BRUSH ], TargetCircle[ 0.25, 10, Wx::BLUE_BRUSH ], TargetCircle[ 0.4, 5, Wx::GREEN_BRUSH ] ] evt_paint { | e | on_paint(e) } evt_left_down { | e | on_left_down(e) } end # What point is at the centre (assuming this control is always square) def centre_point size.width / 2 end # Called whenever the target is repainted, draws a series of # concentric circles def on_paint(evt) paint do | dc | dc.clear @radii.reverse_each do | circ | dc.brush = circ.brush dc.draw_circle(centre_point, centre_point, ( size.width * circ.radius).to_i ) end end end # Test if the target was hit, and generate a TargetHitEvent if so def on_left_down(evt) # quick bit of pythagoras... distance = Math.sqrt( ( evt.x - centre_point ) ** 2 + ( evt.y - centre_point ) ** 2 ) # See which target ring, if any, was hit by the event @radii.each do | circ | if distance < ( size.width * circ.radius) # Create an instance of the event evt = TargetHitEvent.new(self, circ.score, distance) # This sends the event for processing by listeners event_handler.process_event(evt) break end end end def try_before(event) event_handler.queue_event(CountEvent.new(self.id)) if TargetHitEvent === event super end end # Container frame for the target control class TargetFrame < Wx::Frame def initialize(title) super(nil, :title => title, :size => [300, 300]) @tgt = TargetControl.new(self) # This user-defined event handling method was set up by # EvtHandler.register_class, above evt_target(@tgt.get_id) { | e | on_target(e) } @counter = 0 evt_counter(@tgt.get_id) { | e | on_counter(e) } @listening = true evt_size { | e | on_size(e) } setup_menus create_status_bar(2) end # What's done when the target is hit def on_target(evt) msg = "Target hit for score %i, %.2f pixels from centre" % [ evt.score, evt.distance ] set_status_text(msg, 0) end def on_counter(evt) @counter += 1 set_status_text(@counter.to_s, 1) end # Keep the target centred and square def on_size(evt) smaller = [ evt.size.width, evt.size.height ].min @tgt.centre @tgt.size = Wx::Size.new(smaller, smaller) @tgt.refresh end # Toggle whether or not we are listening for events from the bulls-eye # target def on_toggle_connect if @listening # Remove :evt_target event handler for the @tgt disconnect(@tgt.get_id, Wx::ID_ANY, :evt_target) menu_bar.check(TOGGLE_LISTEN, false) self.status_text = "Ignoring target events" @listening = false else # Restore evt_target event handler for the @tgt evt_target(@tgt.get_id) { | e | on_target(e) } menu_bar.check(TOGGLE_LISTEN, true) self.status_text = '' @listening = true end end def on_about msg = sprintf("This is the About dialog of the event handling sample.\n" \ "Welcome to wxRuby, version %s", Wx::WXRUBY_VERSION) Wx::MessageDialog(self, msg, 'About Event Handling', Wx::OK | Wx::ICON_INFORMATION) do |about_dlg| about_dlg.ok_label = Wx::ButtonLabel.new('Close') about_dlg.show_modal end end TOGGLE_LISTEN = 1001 def setup_menus menu_file = Wx::Menu.new menu_help = Wx::Menu.new menu_help.append(Wx::ID_ABOUT, "&About...\tF1", "Show about dialog") evt_menu(Wx::ID_ABOUT) { on_about } menu_file.append(Wx::ID_EXIT, "E&xit\tAlt-X", "Quit this program") evt_menu(Wx::ID_EXIT) { self.close } menu_file.append_check_item(TOGGLE_LISTEN, "L&isten for events", "Toggle listening for target events") evt_menu(TOGGLE_LISTEN) { on_toggle_connect } menu_bar = Wx::MenuBar.new menu_bar.append(menu_file, "&File") menu_bar.append(menu_help, "&Help") self.menu_bar = menu_bar menu_bar.check(TOGGLE_LISTEN, true) end end module EventSample include WxRuby::Sample if defined? WxRuby::Sample def self.describe { file: __FILE__, summary: 'wxRuby event handling example.', description: <<~__TXT wxRuby example demonstrating event handling. This sample demonstrates how to dynamically connect and disconnect event handlers, and how to create custom event types and listeners associated with user-defined control classes. __TXT } end def self.activate frame = TargetFrame.new("Event Handling Sample") frame.show frame end if $0 == __FILE__ Wx::App.run { EventSample.activate } end end