Class: Teacup::Stylesheet
- Inherits:
-
Object
- Object
- Teacup::Stylesheet
- Defined in:
- lib/teacup/stylesheet.rb,
lib/teacup/stylesheet_extensions/geometry.rb,
lib/teacup/stylesheet_extensions/rotation.rb,
lib/teacup/stylesheet_extensions/autoresize.rb,
lib/teacup/stylesheet_extensions/constraints.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.
Instance Attribute Summary collapse
-
#name ⇒ Object
readonly
Returns the value of attribute name.
Class Method Summary collapse
Instance Method Summary collapse
-
#app_size ⇒ Object
returns the application frame, which takes the status bar into account.
- #constrain(target, attribute = nil) ⇒ Object
- #constrain_above(relative_to, margin = 0) ⇒ Object
-
#constrain_below(relative_to, margin = 0) ⇒ Object
|.
- #constrain_bottom(y) ⇒ Object
- #constrain_center_x(x = 0) ⇒ Object
- #constrain_center_y(y = 0) ⇒ Object
- #constrain_height(height) ⇒ Object
- #constrain_left(x) ⇒ Object
- #constrain_right(x) ⇒ Object
- #constrain_size(width, height) ⇒ Object
- #constrain_to_left(relative_to, margin = 0) ⇒ Object
- #constrain_to_right(relative_to, margin = 0) ⇒ Object
- #constrain_top(y) ⇒ Object
- #constrain_width(width) ⇒ Object
-
#constrain_xy(x, y) ⇒ Object
|.
-
#device ⇒ Object
returns a bit-wise OR of the device masks.
- #device_is?(this_device) ⇒ Boolean
-
#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.
- #flexible_bottom ⇒ Object
- #flexible_height ⇒ Object
- #flexible_left ⇒ Object
- #flexible_right ⇒ Object
- #flexible_top ⇒ Object
- #flexible_width ⇒ Object
-
#flip(matrix, angle) ⇒ Object
rotates the “up & down” direction.
- #get_stylesheet_cache(stylename, target, orientation) ⇒ Object
- #identity ⇒ 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.
-
#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.
- #iPad ⇒ Object
- #iPadRetina ⇒ Object
- #iPhone ⇒ Object
- #iPhone35 ⇒ Object
- #iPhone4 ⇒ Object
- #iPhone5 ⇒ Object
- #iPhoneRetina ⇒ Object
- #none ⇒ Object
- #pi ⇒ Object
-
#query(stylename, target = nil, orientation = nil, seen = {}) ⇒ Object
Get the properties defined for the given stylename, in this Stylesheet and all those that have been imported.
-
#rotate(matrix, angle, x, y, z) ⇒ Object
rotates the layer arbitrarily.
-
#screen_size ⇒ Object
returns the device size in points, regardless of status bar.
- #set_stylesheet_cache(stylename, target, orientation, value) ⇒ Object
-
#spin(matrix, angle) ⇒ Object
spins, along the z axis.
-
#style(*queries) ⇒ Object
Add a set of properties for a given stylename or multiple stylenames.
-
#stylesheet_cache ⇒ Object
The stylesheet_cache stores “compiled” styles.
-
#twist(matrix, angle) ⇒ Object
rotates the “left & right” direction.
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.
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/teacup/stylesheet.rb', line 101 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 |
Instance Attribute Details
#name ⇒ Object (readonly)
Returns the value of attribute name.
68 69 70 |
# File 'lib/teacup/stylesheet.rb', line 68 def name @name end |
Class Method Details
.[](name) ⇒ Object
75 76 77 |
# File 'lib/teacup/stylesheet.rb', line 75 def [] name stylesheets[name] end |
.[]=(name, stylesheet) ⇒ Object
79 80 81 |
# File 'lib/teacup/stylesheet.rb', line 79 def []= name, stylesheet stylesheets[name] = stylesheet end |
.stylesheets ⇒ Object
71 72 73 |
# File 'lib/teacup/stylesheet.rb', line 71 def stylesheets @stylesheets ||= {} end |
Instance Method Details
#app_size ⇒ Object
returns the application frame, which takes the status bar into account
47 48 49 |
# File 'lib/teacup/stylesheet_extensions/geometry.rb', line 47 def app_size UIScreen.mainScreen.applicationFrame.size end |
#constrain(target, attribute = nil) ⇒ Object
4 5 6 7 8 9 10 |
# File 'lib/teacup/stylesheet_extensions/constraints.rb', line 4 def constrain(target, attribute=nil) if attribute.nil? attribute = target target = :self end Teacup::Constraint.new(target, attribute) end |
#constrain_above(relative_to, margin = 0) ⇒ Object
65 66 67 68 |
# File 'lib/teacup/stylesheet_extensions/constraints.rb', line 65 def constrain_above(relative_to, margin=0) margin = 8 if margin == :auto Teacup::Constraint.new(:self, :bottom).equals(relative_to, :top).minus(margin) end |
#constrain_below(relative_to, margin = 0) ⇒ Object
|
60 61 62 63 |
# File 'lib/teacup/stylesheet_extensions/constraints.rb', line 60 def constrain_below(relative_to, margin=0) margin = 8 if margin == :auto Teacup::Constraint.new(:self, :top).equals(relative_to, :bottom).plus(margin) end |
#constrain_bottom(y) ⇒ Object
36 37 38 |
# File 'lib/teacup/stylesheet_extensions/constraints.rb', line 36 def constrain_bottom(y) Teacup::Constraint.new(:self, :bottom).equals(:superview, :bottom).plus(y) end |
#constrain_center_x(x = 0) ⇒ Object
28 29 30 |
# File 'lib/teacup/stylesheet_extensions/constraints.rb', line 28 def constrain_center_x(x=0) Teacup::Constraint.new(:self, :center_x).equals(:superview, :center_x).plus(x) end |
#constrain_center_y(y = 0) ⇒ Object
40 41 42 |
# File 'lib/teacup/stylesheet_extensions/constraints.rb', line 40 def constrain_center_y(y=0) Teacup::Constraint.new(:self, :center_y).equals(:superview, :center_y).plus(y) end |
#constrain_height(height) ⇒ Object
48 49 50 |
# File 'lib/teacup/stylesheet_extensions/constraints.rb', line 48 def constrain_height(height) Teacup::Constraint.new(:self, :height).equals(height) end |
#constrain_left(x) ⇒ Object
20 21 22 |
# File 'lib/teacup/stylesheet_extensions/constraints.rb', line 20 def constrain_left(x) Teacup::Constraint.new(:self, :left).equals(:superview, :left).plus(x) end |
#constrain_right(x) ⇒ Object
24 25 26 |
# File 'lib/teacup/stylesheet_extensions/constraints.rb', line 24 def constrain_right(x) Teacup::Constraint.new(:self, :right).equals(:superview, :right).plus(x) end |
#constrain_size(width, height) ⇒ Object
52 53 54 55 56 57 |
# File 'lib/teacup/stylesheet_extensions/constraints.rb', line 52 def constrain_size(width, height) [ Teacup::Constraint.new(:self, :width).equals(width), Teacup::Constraint.new(:self, :height).equals(height), ] end |
#constrain_to_left(relative_to, margin = 0) ⇒ Object
75 76 77 78 |
# File 'lib/teacup/stylesheet_extensions/constraints.rb', line 75 def constrain_to_left(relative_to, margin=0) margin = 20 if margin == :auto Teacup::Constraint.new(:self, :right).equals(relative_to, :left).minus(margin) end |
#constrain_to_right(relative_to, margin = 0) ⇒ Object
70 71 72 73 |
# File 'lib/teacup/stylesheet_extensions/constraints.rb', line 70 def constrain_to_right(relative_to, margin=0) margin = 20 if margin == :auto Teacup::Constraint.new(:self, :left).equals(relative_to, :right).plus(margin) end |
#constrain_top(y) ⇒ Object
32 33 34 |
# File 'lib/teacup/stylesheet_extensions/constraints.rb', line 32 def constrain_top(y) Teacup::Constraint.new(:self, :top).equals(:superview, :top).plus(y) end |
#constrain_width(width) ⇒ Object
44 45 46 |
# File 'lib/teacup/stylesheet_extensions/constraints.rb', line 44 def constrain_width(width) Teacup::Constraint.new(:self, :width).equals(width) end |
#constrain_xy(x, y) ⇒ Object
|
13 14 15 16 17 18 |
# File 'lib/teacup/stylesheet_extensions/constraints.rb', line 13 def constrain_xy(x, y) [ Teacup::Constraint.new(:self, :left).equals(:superview, :left).plus(x), Teacup::Constraint.new(:self, :top).equals(:superview, :top).plus(y), ] end |
#device ⇒ Object
returns a bit-wise OR of the device masks
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/teacup/stylesheet_extensions/geometry.rb', line 52 def device @@this_device ||= nil return @@this_device if @@this_device @@this_device = 0 if UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPhone @@this_device |= iPhone if UIScreen.mainScreen.respond_to?(:scale) && UIScreen.mainScreen.scale == 2 @@this_device |= iPhoneRetina end if UIScreen.mainScreen.bounds.size.height == 568 @@this_device |= iPhone4 else @@this_device |= iPhone35 end else @@this_device |= iPad if UIScreen.mainScreen.respond_to? :scale @@this_device |= iPadRetina end end return @@this_device end |
#device_is?(this_device) ⇒ Boolean
79 80 81 82 |
# File 'lib/teacup/stylesheet_extensions/geometry.rb', line 79 def device_is?(this_device) this_device = self.send(this_device) if this_device.is_a? Symbol return device & this_device > 0 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.
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 |
# File 'lib/teacup/stylesheet.rb', line 263 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 |
#flexible_bottom ⇒ Object
34 35 36 |
# File 'lib/teacup/stylesheet_extensions/autoresize.rb', line 34 def flexible_bottom UIViewAutoresizingFlexibleBottomMargin end |
#flexible_height ⇒ Object
30 31 32 |
# File 'lib/teacup/stylesheet_extensions/autoresize.rb', line 30 def flexible_height UIViewAutoresizingFlexibleHeight end |
#flexible_left ⇒ Object
14 15 16 |
# File 'lib/teacup/stylesheet_extensions/autoresize.rb', line 14 def flexible_left UIViewAutoresizingFlexibleLeftMargin end |
#flexible_right ⇒ Object
22 23 24 |
# File 'lib/teacup/stylesheet_extensions/autoresize.rb', line 22 def flexible_right UIViewAutoresizingFlexibleRightMargin end |
#flexible_top ⇒ Object
26 27 28 |
# File 'lib/teacup/stylesheet_extensions/autoresize.rb', line 26 def flexible_top UIViewAutoresizingFlexibleTopMargin end |
#flexible_width ⇒ Object
18 19 20 |
# File 'lib/teacup/stylesheet_extensions/autoresize.rb', line 18 def flexible_width UIViewAutoresizingFlexibleWidth end |
#flip(matrix, angle) ⇒ Object
rotates the “up & down” direction. The bottom of the view will rotate towards the user as angle increases.
15 16 17 |
# File 'lib/teacup/stylesheet_extensions/rotation.rb', line 15 def flip matrix, angle CATransform3DRotate(matrix, angle, 1, 0, 0) end |
#get_stylesheet_cache(stylename, target, orientation) ⇒ Object
133 134 135 136 |
# File 'lib/teacup/stylesheet.rb', line 133 def get_stylesheet_cache(stylename, target, orientation) orientation ||= UIApplication.sharedApplication.statusBarOrientation stylesheet_cache[stylename][target][orientation] end |
#identity ⇒ Object
5 6 7 |
# File 'lib/teacup/stylesheet_extensions/rotation.rb', line 5 def identity [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1] 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.
166 167 168 169 |
# File 'lib/teacup/stylesheet.rb', line 166 def import(name_or_stylesheet) @stylesheet_cache = nil imported << name_or_stylesheet end |
#imported_stylesheets ⇒ Object
The array of Stylesheets that have been imported into this one.
248 249 250 251 252 253 254 255 256 257 258 |
# File 'lib/teacup/stylesheet.rb', line 248 def imported_stylesheets imported.map do |name_or_stylesheet| if name_or_stylesheet.is_a? Teacup::Stylesheet name_or_stylesheet elsif Teacup::Stylesheet.stylesheets.has_key? name_or_stylesheet 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 end end |
#inspect ⇒ Object
A unique and hopefully meaningful description of this Object.
241 242 243 |
# File 'lib/teacup/stylesheet.rb', line 241 def inspect "Teacup::Stylesheet[#{name.inspect}] = #{styles.inspect}" end |
#iPad ⇒ Object
33 |
# File 'lib/teacup/stylesheet_extensions/geometry.rb', line 33 def iPad ; 1 << 5 ; end |
#iPadRetina ⇒ Object
34 |
# File 'lib/teacup/stylesheet_extensions/geometry.rb', line 34 def iPadRetina ; 1 << 6 ; end |
#iPhone ⇒ Object
29 |
# File 'lib/teacup/stylesheet_extensions/geometry.rb', line 29 def iPhone ; 1 << 1 ; end |
#iPhone35 ⇒ Object
32 |
# File 'lib/teacup/stylesheet_extensions/geometry.rb', line 32 def iPhone35 ; 1 << 4 ; end |
#iPhone4 ⇒ Object
31 |
# File 'lib/teacup/stylesheet_extensions/geometry.rb', line 31 def iPhone4 ; 1 << 3 ; end |
#iPhone5 ⇒ Object
36 37 38 39 |
# File 'lib/teacup/stylesheet_extensions/geometry.rb', line 36 def iPhone5 NSLog('TEACUP WARNING: iPhone5 method is deprecated in lieu of size-based method names (iPhone4, iPhone35)') 1 << 3 end |
#iPhoneRetina ⇒ Object
30 |
# File 'lib/teacup/stylesheet_extensions/geometry.rb', line 30 def iPhoneRetina ; 1 << 2 ; end |
#none ⇒ Object
10 11 12 |
# File 'lib/teacup/stylesheet_extensions/autoresize.rb', line 10 def none UIViewAutoresizingNone end |
#pi ⇒ Object
9 10 11 |
# File 'lib/teacup/stylesheet_extensions/rotation.rb', line 9 def pi 3.1415926 end |
#query(stylename, target = 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.
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
# File 'lib/teacup/stylesheet.rb', line 183 def query(stylename, target=nil, orientation=nil, seen={}) return {} if seen[self] return {} unless stylename unless get_stylesheet_cache(stylename, target, orientation) # 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. if @block instance_eval &@block @block = nil end seen[self] = true set_stylesheet_cache(stylename, target, orientation, styles[stylename].build(target, orientation, seen)) end # mutable hashes could mess with our cache, so return a duplicate get_stylesheet_cache(stylename, target, orientation).dup end |
#rotate(matrix, angle, x, y, z) ⇒ Object
rotates the layer arbitrarily
32 33 34 |
# File 'lib/teacup/stylesheet_extensions/rotation.rb', line 32 def rotate matrix, angle, x, y, z CATransform3DRotate(matrix, angle, x, y, z) end |
#screen_size ⇒ Object
returns the device size in points, regardless of status bar
42 43 44 |
# File 'lib/teacup/stylesheet_extensions/geometry.rb', line 42 def screen_size UIScreen.mainScreen.bounds.size end |
#set_stylesheet_cache(stylename, target, orientation, value) ⇒ Object
138 139 140 141 |
# File 'lib/teacup/stylesheet.rb', line 138 def set_stylesheet_cache(stylename, target, orientation, value) orientation ||= UIApplication.sharedApplication.statusBarOrientation self.stylesheet_cache[stylename][target][orientation] = value end |
#spin(matrix, angle) ⇒ Object
spins, along the z axis. This is probably the one you want, for “spinning” a view like you might a drink coaster or paper napkin.
27 28 29 |
# File 'lib/teacup/stylesheet_extensions/rotation.rb', line 27 def spin matrix, angle CATransform3DRotate(matrix, angle, 0, 0, 1) end |
#style(*queries) ⇒ Object
Add a set of properties for a given stylename or multiple stylenames.
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 |
# File 'lib/teacup/stylesheet.rb', line 220 def style(*queries) if queries[-1].is_a? Hash properties = queries.pop else # empty style declarations are allowed 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)
125 126 127 128 129 130 131 |
# File 'lib/teacup/stylesheet.rb', line 125 def stylesheet_cache @stylesheet_cache ||= Hash.new { |cache,_stylename| cache[_stylename] = Hash.new { |_target,_orientation| _target[_orientation] = {} } } end |
#twist(matrix, angle) ⇒ Object
rotates the “left & right” direction. The right side of the view will rotate towards the user as angle increases.
21 22 23 |
# File 'lib/teacup/stylesheet_extensions/rotation.rb', line 21 def twist matrix, angle CATransform3DRotate(matrix, angle, 0, 1, 0) end |