class Shoes # This is the superclass for creating custom Shoes widgets. # # To use, inherit from {Shoes::Widget}. You get a few magical effects: # # * When you inherit from {Shoes::Widget}, you get a method in your Apps to # create your widgets. The method is lower- and snake-cased. It returns # an instance of your widget class. # * Your widgets delegate missing methods to their app object. This # allows you to use the Shoes DSL within your widgets. # # @example # class SayHello < Shoes::Widget # def initialize word # para "Hello #{word}", stroke: green, size: 80 # end # end # # Shoes.app do # say_hello 'Shoes' # end # class Widget include Common::Inspect Shoes::App.subscribe_to_dsl_methods self attr_accessor :parent attr_writer :app class << self attr_accessor :app end # lookup a bit more complicated as during initialize we do # not have @app yet... def app @app || self.class.app end def self.inherited(klass, &_blk) dsl_method = dsl_method_name(klass) Shoes::App.new_dsl_method(dsl_method) do |*args, &blk| # we set app 2 times because widgets execute most of their code # straight in initialize. I dunno if there is a good way of setting # an @app instance variable before initialize is executed. We could # hand it over in #initialize but that would break the interface # and people would have to set it themselves or make sure to call # super so for not it's like this. # Setting the ref on the instance is important as we might have # instances of the same widget in different Shoes::Apps so each one # needs to save the reference to the one it was started with klass.app = self widget_instance = klass.new(*args, &blk) widget_instance.app = self widget_instance.parent = @__app__.current_slot widget_instance end end def self.dsl_method_name(klass) klass.to_s[/(^|::)(\w+)$/, 2] .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2') .gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase end end end