Class: Teacup::Stylesheet
- Inherits:
-
Object
- Object
- Teacup::Stylesheet
- Includes:
- StylesheetExtension
- Defined in:
- lib/teacup/stylesheet.rb
Overview
Stylesheets in Teacup act as a central configuration mechanism, they have two aims:
-
Allow you to store “Details” away from the main body of your code. (controllers shouldn’t have to be filled with style rules)
-
Allow you to easily re-use configuration in many places.
The API really provides only two methods, #style to store properties on the Stylesheet; and #query to get them out again:
In addition to this, two separate mechanisms are provided for sharing configuration within stylesheets.
Firstly, if you set the ‘:extends’ property for a given stylename, then on lookup the Stylesheet will merge the properties for the ‘:extends’ stylename into the return value. Conflicts are resolved so that properties with the original stylename are resolved in its favour.
Secondly, you can import Stylesheets into each other, in exactly the same way as you can include Modules into each other in Ruby. This allows you to share rules between Stylesheets.
As you’d expect, conflicts are resolved so that the Stylesheet on which you call query has the highest precedence.
The two merging mechanisms are considered independently, so you can override a property both in a ‘:extends’ rule, and also in an imported Stylesheet. In such a a case the Stylesheet inclusion conflicts are resolved independently; and then in a second phase, the ‘:extends’ chain is flattened.
Direct Known Subclasses
Instance Attribute Summary collapse
-
#name ⇒ Object
readonly
Returns the value of attribute name.
Class Method Summary collapse
Instance Method Summary collapse
- #exclude_instance_vars ⇒ Object
-
#extends_style?(stylename, extended_name, checked = []) ⇒ Boolean
In the [rare] case you need to know whether the style extends another style, this method will find out quickly.
- #get_stylesheet_cache(stylename, target_class, orientation) ⇒ Object
-
#import(name_or_stylesheet) ⇒ Object
Include another Stylesheet into this one, the rules defined within it will have lower precedence than those defined here in the case that they share the same keys.
- #import_instance_vars(from_stylesheet) ⇒ Object
-
#imported_stylesheets ⇒ Object
The array of Stylesheets that have been imported into this one.
-
#initialize(name = nil, &block) ⇒ Stylesheet
constructor
Create a new Stylesheet.
-
#inspect ⇒ Object
A unique and hopefully meaningful description of this Object.
-
#method_missing(stylename, &styles) ⇒ Object
supports the ‘new-style’ stylesheet syntax.
-
#query(stylename, target_class = nil, orientation = nil, seen = {}) ⇒ Object
Get the properties defined for the given stylename, in this Stylesheet and all those that have been imported.
-
#run_block ⇒ Object
the block handed to Stylesheet#new is not run immediately - it is run the first time the stylesheet is queried.
- #set_stylesheet_cache(stylename, target_class, orientation, value) ⇒ Object
-
#style(*queries) ⇒ Object
Add a set of properties for a given stylename or multiple stylenames.
-
#stylesheet_cache ⇒ Object
The stylesheet_cache stores “compiled” styles.
Methods included from StylesheetExtension
#app_size, #autoresize, #constrain, #constrain_above, #constrain_below, #constrain_bottom, #constrain_center_x, #constrain_center_y, #constrain_height, #constrain_left, #constrain_right, #constrain_size, #constrain_to_left, #constrain_to_right, #constrain_top, #constrain_width, #constrain_xy, #device, #device_is?, #flexible_bottom, #flexible_height, #flexible_left, #flexible_right, #flexible_top, #flexible_width, #flip, #iPad, #iPadRetina, #iPhone, #iPhone35, #iPhone4, #iPhone5, #iPhoneRetina, #identity, #pi, #rotate, #screen_size, #spin, #transform_layer, #transform_view, #twist
Constructor Details
#initialize(name = nil, &block) ⇒ Stylesheet
Create a new Stylesheet.
If a name is provided then a new constant will be created using that name.
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/teacup/stylesheet.rb', line 105 def initialize(name=nil, &block) if name @name = name.to_sym if Teacup::Stylesheet[@name] NSLog("TEACUP WARNING: A stylesheet with the name #{@name.inspect} has already been created.") end Teacup::Stylesheet[@name] = self end # we just store the block for now, because some classes are not "ready" # for instance, calling `UIFont.systemFontOfSize()` will cause the # application to crash. We will lazily-load this block in `query`, and # then set it to nil. @block = block end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
Instance Attribute Details
#name ⇒ Object (readonly)
Returns the value of attribute name.
70 71 72 |
# File 'lib/teacup/stylesheet.rb', line 70 def name @name end |
Class Method Details
.[](name) ⇒ Object
78 79 80 |
# File 'lib/teacup/stylesheet.rb', line 78 def [] name stylesheets[name] end |
.[]=(name, stylesheet) ⇒ Object
82 83 84 |
# File 'lib/teacup/stylesheet.rb', line 82 def []= name, stylesheet stylesheets[name] = stylesheet end |
.stylesheets ⇒ Object
74 75 76 |
# File 'lib/teacup/stylesheet.rb', line 74 def stylesheets @stylesheets ||= {} end |
Instance Method Details
#exclude_instance_vars ⇒ Object
315 316 317 |
# File 'lib/teacup/stylesheet.rb', line 315 def exclude_instance_vars @exclude_instance_vars ||= [:@name, :@block, :@imported, :@styles, :@stylesheet_cache] end |
#extends_style?(stylename, extended_name, checked = []) ⇒ Boolean
In the [rare] case you need to know whether the style extends another style, this method will find out quickly. This is mostly useful in the ‘UIView#viewWithStylename` method.
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 |
# File 'lib/teacup/stylesheet.rb', line 294 def extends_style?(stylename, extended_name, checked=[]) return true if stylename == extended_name extended_style_names = styles[stylename][:extends] return false unless extended_style_names extended_style_names = [extended_style_names] unless extended_style_names.is_a? Array return true if extended_style_names.any? { |name| name == extended_name } retval = false extended_style_names.each do |recusive_check| next if checked.include?(recusive_check) checked << recusive_check if extends_style?(recusive_check, extended_name, checked) retval = true break end end return retval end |
#get_stylesheet_cache(stylename, target_class, orientation) ⇒ Object
141 142 143 |
# File 'lib/teacup/stylesheet.rb', line 141 def get_stylesheet_cache(stylename, target_class, orientation) self.stylesheet_cache[stylename][target_class][orientation] end |
#import(name_or_stylesheet) ⇒ Object
Include another Stylesheet into this one, the rules defined within it will have lower precedence than those defined here in the case that they share the same keys.
When defining a stylesheet declaratively, it is better to use the symbol that represents a stylesheet, as the constant may not be defined yet:
If you are using anonymous stylesheets however, then it will be necessary to pass an actual stylesheet object.
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/teacup/stylesheet.rb', line 172 def import(name_or_stylesheet) @stylesheet_cache = nil imported << name_or_stylesheet sheet = nil if name_or_stylesheet.is_a? Teacup::Stylesheet sheet = name_or_stylesheet elsif Teacup::Stylesheet.stylesheets.has_key? name_or_stylesheet sheet = Teacup::Stylesheet.stylesheets[name_or_stylesheet] end if sheet import_instance_vars(sheet) end end |
#import_instance_vars(from_stylesheet) ⇒ Object
319 320 321 322 323 324 325 326 |
# File 'lib/teacup/stylesheet.rb', line 319 def import_instance_vars(from_stylesheet) from_stylesheet.run_block from_stylesheet.instance_variables.each do |var| next if exclude_instance_vars.include? var self.instance_variable_set(var, from_stylesheet.instance_variable_get(var)) end end |
#imported_stylesheets ⇒ Object
The array of Stylesheets that have been imported into this one.
277 278 279 280 281 282 283 284 285 286 287 288 289 |
# File 'lib/teacup/stylesheet.rb', line 277 def imported_stylesheets imported.map do |name_or_stylesheet| if name_or_stylesheet.is_a? Teacup::Stylesheet sheet = name_or_stylesheet elsif Teacup::Stylesheet.stylesheets.has_key? name_or_stylesheet sheet = Teacup::Stylesheet.stylesheets[name_or_stylesheet] else raise "Teacup tried to import Stylesheet #{name_or_stylesheet.inspect} into Stylesheet[#{self.name.inspect}], but it didn't exist" end sheet end end |
#inspect ⇒ Object
A unique and hopefully meaningful description of this Object.
270 271 272 |
# File 'lib/teacup/stylesheet.rb', line 270 def inspect "#{self.class.name}[#{name.inspect}] = #{styles.inspect}" end |
#query(stylename, target_class = nil, orientation = nil, seen = {}) ⇒ Object
Get the properties defined for the given stylename, in this Stylesheet and all those that have been imported.
The Style class handles precedence rules, and extending via ‘:extends` and `import`. If needs the orientation in order to merge and remove the appropriate orientation styles.
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 |
# File 'lib/teacup/stylesheet.rb', line 216 def query(stylename, target_class=nil, orientation=nil, seen={}) return {} if seen[self] return {} unless stylename cached = get_stylesheet_cache(stylename, target_class, orientation) if cached # mutable hashes could mess with our cache, so return a duplicate return cached.dup else run_block seen[self] = true built = styles[stylename].build(target_class, orientation, seen) set_stylesheet_cache(stylename, target_class, orientation, built) return built.dup end end |
#run_block ⇒ Object
the block handed to Stylesheet#new is not run immediately - it is run the first time the stylesheet is queried. This fixes bugs related to some resources (fonts) not available when the application is first started. The downside is that @instance variables and variables that should be closed over are not.
195 196 197 198 199 200 201 202 |
# File 'lib/teacup/stylesheet.rb', line 195 def run_block if @block _block = @block @block = nil instance_eval &_block true end end |
#set_stylesheet_cache(stylename, target_class, orientation, value) ⇒ Object
145 146 147 |
# File 'lib/teacup/stylesheet.rb', line 145 def set_stylesheet_cache(stylename, target_class, orientation, value) self.stylesheet_cache[stylename][target_class][orientation] = value end |
#style(*queries) ⇒ Object
Add a set of properties for a given stylename or multiple stylenames.
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 |
# File 'lib/teacup/stylesheet.rb', line 248 def style(*queries) if queries[-1].is_a? Hash properties = queries.pop else # empty style declarations are allowed, but accomplish nothing. return end queries.each do |stylename| # reset the stylesheet_cache for this stylename @stylesheet_cache.delete(stylename) if @stylesheet_cache # merge into styles[stylename] (an instance of Teacup::Style). new # properties "win" over existing properties. Teacup::merge_defaults(properties, styles[stylename], styles[stylename]) end end |
#stylesheet_cache ⇒ Object
The stylesheet_cache stores “compiled” styles. It is reset whenever the stylesheet imports a new Stylesheet, and when a style entry is added or changed (then only that entry is removed)
This method builds the gnarly hash that stores this stuff - the get/set methods use this method to ensure the object exists, in other places the or the entire cache)
129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/teacup/stylesheet.rb', line 129 def stylesheet_cache @stylesheet_cache ||= Hash.new(&lambda do |by_stylename,_stylename| by_stylename[_stylename] = Hash.new(&lambda do |by_target_class,_target_class| if _target_class && ! _target_class.is_a?(Class) _target_class = _target_class.class end by_target_class[_target_class] = {} end.weak!) end.weak!) end |