lib/ctioga2/graphics/styles/base.rb in ctioga2-0.10.1 vs lib/ctioga2/graphics/styles/base.rb in ctioga2-0.11

- old
+ new

@@ -21,17 +21,57 @@ # All the styles module Styles # This style is the base class of a series of style objects that # share one common feature: all their attributes can be set - # using the set_from_hash function. + # using the #set_from_hash function. class BasicStyle OldAttrAccessor = method(:attr_accessor) AllStyles = [] + # Returns the list of valid aliases, including those defined + # in parent classes. + def self.aliases + ret = if superclass.respond_to? :aliases + superclass.aliases + else + {} + end + if @aliases + ret.merge!(@aliases) + end + return ret + end + + def self.normalize_in(name, fmt = "%s") + name = name.to_s.downcase.gsub('-', '_') + als = self.aliases + if als + for k, v in als + if fmt % k == name + name = fmt % v + break + end + end + end + return name + end + + def self.normalize_out(name) + return name.gsub('_', '-') + end + + def self.normalize_hash(hsh, fmt = "%s") + ret = {} + for k,v in hsh + ret[normalize_in(k, fmt)] = v + end + return ret + end + def self.inherited(cls) AllStyles << cls end # This redefinition of attr_accessor allows to track for the @@ -55,10 +95,14 @@ else [] end end + def self.defined_aliases + return @aliases || {} + end + # Returns the type of all attributes (chaining to the parent # when applicable) def self.attribute_types return ( @attribute_types || {} ). merge( @@ -82,33 +126,35 @@ # for instance to have halign and valign as aliases for the # less intuitive alignment and justification. def self.typed_attribute(symbol, type) sym = symbol.to_sym self.attr_accessor(sym) - type = CmdArg.new(type) unless type.respond_to? :string_to_type + # The unless type.respond_to? :string_to_type seems + type = CmdArg.new(type) # unless type.respond_to? :string_to_type @attribute_types ||= {} @attribute_types[sym] = type return type end # Define an attribute to be the alias for something else. - def self.alias_for(symbol, target) - target = target.to_sym - if ! @attribute_types[target] - raise "Declaring alias #{symbol} for unexisting target #{target}" + # + # @todo Maybe make multiple aliases ? + def self.alias_for(what, target, define_methods = false) + target = self.normalize_in(target) + what = self.normalize_in(what) + @aliases ||= {} + @aliases[what] = target + if define_methods + alias_method what.to_sym, target.to_sym + alias_method "#{what}=".to_sym, "#{target}=".to_sym end - symbol = symbol.to_sym - @attribute_types[symbol] = @attribute_types[target] - @attributes << symbol - alias_method symbol, target - alias_method "#{symbol}=".to_sym, "#{target}=".to_sym end # Returns the type of an attribute, or _nil_ if there is no # attribute of that name. Handles sub-styles correctly. def self.attribute_type(symbol, fmt = "%s") - name = symbol.to_s + name = self.normalize_in(symbol.to_s, fmt) for k,v in attribute_types if (fmt % k.to_s) == name if v.respond_to? :type return v.type @@ -120,11 +166,11 @@ if @sub_styles # Not always present too for sub in @sub_styles sym, cls, fmt2, fc = *sub f = fmt % fmt2 - ret = cls.attribute_type(symbol, f) + ret = cls.attribute_type(name, f) return ret if ret end end return nil end @@ -177,10 +223,20 @@ fmt = key % fmt ret.merge!(cls.options_hash(fmt)) end end + # And now we expand options + if @aliases + for k, v in @aliases + v = key % v + if ret.key?(v) + ret[key % k] = ret[v] + end + end + end + return ret end def self.sub_styles return @sub_styles @@ -199,10 +255,11 @@ # @todo Maybe there should be a way to detect extra attributes ? # # This function returns the number of properties that were # effectively set (including those set in sub-styles) def set_from_hash(hash, name = "%s") + hash = self.class.normalize_hash(hash, name) nb_set = 0 for key_name in self.class.attributes hash_key = name % key_name if hash.key? hash_key self.send("#{key_name}=", hash[hash_key]) @@ -254,24 +311,48 @@ end end end # Converts to a hash. Does the reverse of #set_from_hash. + # + # _nil_ values get stripped off (but not false values, of course). def to_hash(name = "%s") retval = {} for attr in self.class.attributes if instance_variable_defined?("@#{attr}") - retval[name % attr] = instance_variable_get("@#{attr}") + val = instance_variable_get("@#{attr}") + if ! val.nil? + retval[name % attr] = val + end end end return retval end # Updates information from another object. def update_from_other(other_object) set_from_hash(other_object.to_hash) end + # Converts a hash in text format into a format suitable for + # feeding to #set_from_hash. Only relevant keys are + # converted. Keys that exist in the options hash but are not + # Strings are left untouched + def self.convert_string_hash(opts, key = "%s") + cnv = self.options_hash(key) + + ret = {} + for k,v in opts + if cnv.key? k + if v.is_a? String + ret[k] = cnv[k].type.string_to_type(v) + else + ret[k] = v + end + end + end + return ret + end end end end end