require 'apotomo/event_handler' module Apotomo # Introduces event-processing functions into the StatefulWidget. module EventMethods attr_writer :page_updates # Replacement for the EventProcessor singleton queue. def page_updates @page_updates ||= [] end def self.included(base) base.extend(ClassMethods) base.after_initialize :add_class_event_handlers end def add_class_event_handlers(*) self.class.responds_to_event_options.each { |options| respond_to_event(*options) } end module ClassMethods def responds_to_event(*options) responds_to_event_options << options end alias_method :respond_to_event, :responds_to_event def responds_to_event_options @responds_to_event_options ||= [] end end # Instructs the widget to look out for type Events that are passing by while bubbling. # If an appropriate event is encountered the widget will send the targeted widget (or itself) to another # state, which implies an update of the invoked widget. # # You may configure the event handler with the following options: # :with => (required) the state to invoke on the target widget # :on => (optional) the targeted widget's id, defaults to self.name # :from => (optional) the source id of the widget that triggered the event, defaults to any widget # # Example: # # trap = cell(:input_field, :smell_like_cheese, 'mouse_trap') # trap.respond_to_event :mouseOver, :with => :catch_mouse # # This would instruct trap to catch a :mouseOver event from any widget (including itself) and # to invoke the state :catch_mouse on itself as trigger. # # # hunter = cell(:form, :hunt_for_mice, 'my_form') # hunter << cell(:input_field, :smell_like_cheese, 'mouse_trap') # hunter << cell(:text_area, :stick_like_honey, 'bear_trap') # hunter.respond_to_event :captured, :from => 'mouse_trap', :with => :refill_cheese, :on => 'mouse_trap' # # As both the bear- and the mouse trap can trigger a :captured event the later respond_to_event # would invoke :refill_cheese on the mouse_trap widget as soon as this and only this widget fired. # It is important to understand the :from parameter as it filters the event source - it wouldn't make # sense to refill the mouse trap if the bear trap snapped, would it? def respond_to_event(type, options) options[:once] = true if options[:once].nil? handler_opts = {} handler_opts[:widget_id] = options[:on] || self.name handler_opts[:state] = options[:with] handler = InvokeEventHandler.new(handler_opts) return if options[:once] and event_table.all_handlers_for(type, options[:from]).include?(handler) on(type, :do => handler, :from => options[:from]) end def trigger(*args) fire(*args) end # Invokes state on the widget and updates itself on the page. This should # never be called from outside but in setters when some internal value changed and must be # displayed instantly. # # Implements the following pattern (TODO: remove example as soon as invoke! proofed): # # def title=(str) # @title = str # peek(:update, self.name, :display, self.name) # trigger(:update) # end def invoke!(state) ### TODO: encapsulate in PageUpdateQueue: Apotomo::EventProcessor.instance.processed_handlers << [name, invoke(:state)] end protected # Get all handlers from self for the passed event (overriding Onfire#local_event_handlers). def local_event_handlers(event) event_table.all_handlers_for(event.type, event.source.name) # we key with widget_id. end end end