teacup 1.3.4 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. data/Gemfile +1 -1
  2. data/Gemfile.lock +3 -3
  3. data/README.md +1172 -319
  4. data/Rakefile +8 -1
  5. data/app/app_delegate.rb +1 -1
  6. data/app/controllers/appearance_controller.rb +13 -0
  7. data/app/controllers/landscape_only_controller.rb +1 -1
  8. data/app/controllers/{first_controller.rb → main_controller.rb} +4 -3
  9. data/app/controllers/motion_layout_controller.rb +22 -0
  10. data/app/styles/appearance.rb +24 -0
  11. data/app/styles/main_styles.rb +8 -6
  12. data/app/views/custom_view.rb +1 -0
  13. data/lib/teacup/calculations.rb +2 -2
  14. data/lib/teacup/{z_core_extensions → core_extensions}/ca_layer.rb +0 -0
  15. data/lib/teacup/core_extensions/view_getters.rb +61 -0
  16. data/lib/teacup/handler.rb +14 -14
  17. data/lib/teacup/layout.rb +94 -17
  18. data/lib/teacup/stylesheet.rb +61 -26
  19. data/lib/teacup/stylesheet_extensions/transform.rb +88 -0
  20. data/lib/teacup/teacup_controller.rb +122 -0
  21. data/lib/teacup/teacup_util.rb +12 -7
  22. data/lib/teacup/teacup_view.rb +329 -0
  23. data/lib/teacup/version.rb +1 -1
  24. data/lib/teacup-ios/appearance.rb +96 -0
  25. data/lib/teacup-ios/core_extensions/teacup_handlers.rb +183 -0
  26. data/lib/teacup-ios/core_extensions/ui_view.rb +30 -0
  27. data/lib/teacup-ios/core_extensions/ui_view_controller.rb +110 -0
  28. data/lib/{dummy.rb → teacup-ios/dummy.rb} +2 -6
  29. data/lib/teacup-ios/handler.rb +23 -0
  30. data/lib/{teacup → teacup-ios}/style.rb +9 -10
  31. data/lib/teacup-ios/stylesheet_extensions/autoresize.rb +169 -0
  32. data/lib/{teacup/stylesheet_extensions/geometry.rb → teacup-ios/stylesheet_extensions/device.rb} +0 -0
  33. data/lib/teacup-osx/core_extensions/ns_view.rb +39 -0
  34. data/lib/teacup-osx/core_extensions/ns_view_controller.rb +21 -0
  35. data/lib/teacup-osx/core_extensions/ns_window.rb +39 -0
  36. data/lib/teacup-osx/core_extensions/ns_window_controller.rb +29 -0
  37. data/lib/{teacup/z_core_extensions/z_handlers.rb → teacup-osx/core_extensions/teacup_handlers.rb} +30 -47
  38. data/lib/teacup-osx/dummy.rb +80 -0
  39. data/lib/teacup-osx/handler.rb +16 -0
  40. data/lib/teacup-osx/style.rb +83 -0
  41. data/lib/teacup-osx/style_extensions/autoresize.rb +169 -0
  42. data/lib/teacup.rb +12 -11
  43. data/samples/Tweets/Gemfile +4 -0
  44. data/samples/Tweets/Gemfile.lock +16 -0
  45. data/samples/Tweets/README +7 -0
  46. data/samples/Tweets/Rakefile +9 -0
  47. data/samples/Tweets/app/app_delegate.rb +18 -0
  48. data/samples/Tweets/app/data_parser.rb +10 -0
  49. data/samples/Tweets/app/json_parser.rb +12 -0
  50. data/samples/Tweets/app/main_window.rb +99 -0
  51. data/samples/Tweets/app/menu.rb +108 -0
  52. data/samples/Tweets/app/stylesheet.rb +21 -0
  53. data/samples/Tweets/app/tweet.rb +11 -0
  54. data/samples/Tweets/resources/Credits.rtf +29 -0
  55. data/samples/Tweets/spec/main_spec.rb +9 -0
  56. data/samples/teacup-osx/.gitignore +1 -0
  57. data/samples/teacup-osx/Gemfile +4 -0
  58. data/samples/teacup-osx/Gemfile.lock +16 -0
  59. data/samples/teacup-osx/Rakefile +9 -0
  60. data/samples/teacup-osx/app/app_delegate.rb +23 -0
  61. data/samples/teacup-osx/app/controller.rb +11 -0
  62. data/samples/teacup-osx/app/menu.rb +108 -0
  63. data/samples/teacup-osx/app/window.rb +12 -0
  64. data/samples/teacup-osx/resources/Credits.rtf +29 -0
  65. data/samples/teacup-osx/resources/teacup.png +0 -0
  66. data/samples/teacup-osx/spec/main_spec.rb +9 -0
  67. data/spec/ios/appearance_spec.rb +18 -0
  68. data/spec/{calculations_spec.rb → ios/calculations_spec.rb} +0 -0
  69. data/spec/{constraints_spec.rb → ios/constraints_spec.rb} +0 -0
  70. data/spec/{custom_class_spec.rb → ios/custom_class_spec.rb} +0 -0
  71. data/spec/{gradient_spec.rb → ios/gradient_spec.rb} +1 -1
  72. data/spec/ios/layout_module_spec.rb +54 -0
  73. data/spec/ios/layout_spec.rb +50 -0
  74. data/spec/{main_spec.rb → ios/main_spec.rb} +52 -13
  75. data/spec/ios/motion_layout_spec.rb +44 -0
  76. data/spec/{present_modal_spec.rb → ios/present_modal_spec.rb} +0 -0
  77. data/spec/{style_spec.rb → ios/style_spec.rb} +1 -1
  78. data/spec/ios/stylesheet_extensions/autoresize_spec.rb +50 -0
  79. data/spec/{stylesheet_spec.rb → ios/stylesheet_spec.rb} +12 -0
  80. data/spec/{ui_view_getters_spec.rb → ios/ui_view_getters_spec.rb} +0 -0
  81. data/spec/{uiswitch_spec.rb → ios/uiswitch_spec.rb} +0 -0
  82. data/spec/{view_spec.rb → ios/view_spec.rb} +23 -2
  83. metadata +85 -35
  84. data/lib/teacup/stylesheet_extensions/autoresize.rb +0 -39
  85. data/lib/teacup/stylesheet_extensions/rotation.rb +0 -37
  86. data/lib/teacup/z_core_extensions/ui_view.rb +0 -262
  87. data/lib/teacup/z_core_extensions/ui_view_controller.rb +0 -263
  88. data/lib/teacup/z_core_extensions/ui_view_getters.rb +0 -58
data/Rakefile CHANGED
@@ -1,5 +1,11 @@
1
1
  $:.unshift('/Library/RubyMotion/lib')
2
- require 'motion/project'
2
+ if ENV.fetch('platform', 'ios') == 'ios'
3
+ require 'motion/project/template/ios'
4
+ elsif ENV['platform'] == 'osx'
5
+ require 'motion/project/template/osx'
6
+ else
7
+ raise "Unsupported platform #{ENV['platform']}"
8
+ end
3
9
  require 'bundler'
4
10
  Bundler.require
5
11
 
@@ -8,4 +14,5 @@ Motion::Project::App.setup do |app|
8
14
  # Use `rake config' to see complete project settings.
9
15
  app.name = 'teacup'
10
16
  app.identifier = 'com.rubymotion.teacup'
17
+ app.specs_dir = "spec/#{app.template}/"
11
18
  end
data/app/app_delegate.rb CHANGED
@@ -6,7 +6,7 @@ class AppDelegate
6
6
  application.setStatusBarHidden(true, withAnimation:UIStatusBarAnimationSlide)
7
7
 
8
8
  @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
9
- ctlr = ConstraintsController.new
9
+ ctlr = MotionLayoutController.new
10
10
  @window.rootViewController = ctlr
11
11
  @window.rootViewController.wantsFullScreenLayout = true
12
12
  @window.makeKeyAndVisible
@@ -0,0 +1,13 @@
1
+ class CustomAppearanceController < UIViewController
2
+ attr :label_1
3
+ attr :container
4
+ attr :label_2
5
+
6
+ layout do
7
+ @label_1 = subview(CustomAppearanceLabel)
8
+ @container = subview(CustomAppearanceContainer) do
9
+ @label_2 = subview(CustomAppearanceLabel)
10
+ end
11
+ end
12
+
13
+ end
@@ -2,7 +2,7 @@
2
2
  class LandscapeOnlyController < UIViewController
3
3
 
4
4
  def viewDidLoad
5
- UIApplication.sharedApplication.windows[0].rootViewController = FirstController.alloc.init
5
+ UIApplication.sharedApplication.windows[0].rootViewController = MainController.alloc.init
6
6
  end
7
7
 
8
8
  def shouldAutorotateToInterfaceOrientation(orientation)
@@ -1,7 +1,8 @@
1
+ class MainController < UIViewController
2
+ attr :welcome
3
+ attr :button
1
4
 
2
- class FirstController < UIViewController
3
-
4
- stylesheet :first
5
+ stylesheet :main
5
6
 
6
7
  layout :root do
7
8
  subview(CustomView, :background) do
@@ -0,0 +1,22 @@
1
+ class MotionLayoutController < UIViewController
2
+ attr :label1
3
+ attr :label2
4
+ attr :label3
5
+
6
+ layout :root do
7
+ @label1 = subview(UILabel, :label1, text: 'label1')
8
+ @label2 = subview(UILabel, :label2, text: 'label2')
9
+ @label3 = subview(UILabel, :label3, text: 'label3')
10
+ end
11
+
12
+ def layoutDidLoad
13
+ auto do
14
+ metrics "margin" => 20, "top" => 100
15
+ horizontal '|-margin-[label1]-margin-[label2(==label1)]-margin-|'
16
+ horizontal '|-margin-[label3]-margin-|'
17
+ vertical '|-top-[label1]'
18
+ vertical '|-220-[label3(==label1)]'
19
+ end
20
+ end
21
+
22
+ end
@@ -0,0 +1,24 @@
1
+ class CustomAppearanceLabel < UILabel
2
+ end
3
+
4
+ class CustomAppearanceContainer < UIView
5
+ end
6
+
7
+ Teacup.handler UIView, :redHerring do |view, alpha|
8
+ view.alpha = alpha
9
+ end
10
+
11
+ Teacup::Appearance.new do
12
+
13
+ style CustomAppearanceLabel,
14
+ redHerring: 0.75,
15
+ numberOfLines: 3
16
+
17
+ style CustomAppearanceContainer,
18
+ backgroundColor: UIColor.whiteColor
19
+
20
+ style CustomAppearanceLabel, when_contained_in: CustomAppearanceContainer,
21
+ redHerring: 0.5,
22
+ numberOfLines: 2
23
+
24
+ end
@@ -1,5 +1,5 @@
1
1
 
2
- Teacup::Stylesheet.new(:first) do
2
+ Teacup::Stylesheet.new(:main) do
3
3
 
4
4
  # enable orientations on the root view
5
5
  style :root,
@@ -8,8 +8,8 @@ Teacup::Stylesheet.new(:first) do
8
8
  width: 320,
9
9
  height: 480,
10
10
  backgroundColor: UIColor.yellowColor,
11
- portrait: true,
12
- upside_down: false,
11
+ portrait: true,
12
+ upside_down: false,
13
13
 
14
14
  layer: {
15
15
  cornerRadius: 10.0,
@@ -21,13 +21,13 @@ Teacup::Stylesheet.new(:first) do
21
21
 
22
22
  landscape_left: {
23
23
  layer: {
24
- transform: spin(identity, -pi / 2),
24
+ transform: transform_layer.spin(-pi / 2),
25
25
  },
26
26
  },
27
27
 
28
28
  landscape_right: {
29
29
  layer: {
30
- transform: spin(identity, pi / 2),
30
+ transform: transform_layer.spin(pi / 2),
31
31
  },
32
32
  }
33
33
 
@@ -50,6 +50,7 @@ Teacup::Stylesheet.new(:first) do
50
50
  left: 10,
51
51
  top: 30,
52
52
  backgroundColor: UIColor.blackColor,
53
+ custom_attr: :custom_value,
53
54
 
54
55
  portrait: {
55
56
  width: 300,
@@ -90,7 +91,6 @@ Teacup::Stylesheet.new(:first) do
90
91
 
91
92
 
92
93
  style :next_message,
93
- width: 130,
94
94
  height: 20,
95
95
  portrait: nil,
96
96
  title: "Next Message..."
@@ -101,11 +101,13 @@ Teacup::Stylesheet.new(:first) do
101
101
  portrait: {
102
102
  left: 150,
103
103
  top: 370,
104
+ width: 140, # this should get overridden
104
105
  },
105
106
 
106
107
  landscape: {
107
108
  left: 20,
108
109
  top: 200,
110
+ width: 140, # this should get overridden
109
111
  }
110
112
 
111
113
  end
@@ -1,4 +1,5 @@
1
1
 
2
2
  class CustomView < UIView
3
3
  # not very custom, is it!?
4
+ attr_accessor :custom_attr
4
5
  end
@@ -10,9 +10,9 @@ module Teacup
10
10
 
11
11
  case dimension
12
12
  when :width
13
- CGRectGetWidth(view.superview.bounds) * percent + offset
13
+ view.superview.bounds.size.width * percent + offset
14
14
  when :height
15
- CGRectGetHeight(view.superview.bounds) * percent + offset
15
+ view.superview.bounds.size.height * percent + offset
16
16
  else
17
17
  raise "Unknown dimension #{dimension}"
18
18
  end
@@ -0,0 +1,61 @@
1
+ # Methods to retrieve a subview using the stylename as a key
2
+ # Kinda similar to jQuery-style $(el).find('stylename')
3
+ module Teacup
4
+ module View
5
+
6
+ # get one subview by stylename or class. If the receiver matches, it will be
7
+ # returned
8
+ # my_view.viewWithStylename :button => #<UIButton..>
9
+ # my_view.viewWithStylename UIButton => #<UIButton..>
10
+ def viewWithStylename name_or_class
11
+ return self if self._teacup_check_stylename(name_or_class)
12
+
13
+ view = subviews.find { |view| view._teacup_check_stylename(name_or_class) }
14
+ return view if view
15
+
16
+ # found_subview will get assigned to the view we want, but the subview is
17
+ # what is returned.
18
+ found_subview = nil
19
+ view = subviews.find { |subview| found_subview = subview.viewWithStylename(name_or_class) }
20
+ return found_subview if view
21
+
22
+ return nil # couldn't find it
23
+ end
24
+
25
+ # get all subviews by stylename or class
26
+ # my_view.viewsWithStylename :button => [#<UIButton..>, #<UIButton...>]
27
+ # my_view.viewsWithStylename UIButton => [#<UIButton..>, #<UIButton...>]
28
+ def viewsWithStylename name_or_class
29
+ retval = []
30
+ retval << self if self._teacup_check_stylename(name_or_class)
31
+
32
+ search_views = [].concat(self.subviews)
33
+ # ewww, a traditional for loop! the search_views array is modified in place,
34
+ # and `each` and other methods don't like that.
35
+ index = 0
36
+ while index < search_views.length
37
+ view = search_views[index]
38
+ if view._teacup_check_stylename(name_or_class)
39
+ retval << view
40
+ end
41
+ search_views.concat(view.subviews)
42
+ index += 1
43
+ end
44
+
45
+ return retval
46
+ end
47
+
48
+ def _teacup_check_stylename(name_or_class)
49
+ if name_or_class.is_a? Class
50
+ return self.is_a?(name_or_class)
51
+ elsif stylename == name_or_class
52
+ return true
53
+ elsif stylesheet.is_a?(Teacup::Stylesheet)
54
+ return stylesheet.extends_style?(self.stylename, name_or_class)
55
+ end
56
+ return false
57
+ end
58
+
59
+ end
60
+
61
+ end
@@ -27,23 +27,31 @@ module Teacup
27
27
 
28
28
  # applies a Hash of styles, and converts the frame styles (origin, size, top,
29
29
  # left, width, height) into one frame property.
30
- def apply_hash(target, properties)
30
+ #
31
+ # For UIAppearance support, the class of the UIView class that is being
32
+ # modified can be passed in
33
+ def apply_hash(target, properties, klass=nil)
31
34
  properties.each do |key, value|
32
- Teacup.apply target, key, value
35
+ Teacup.apply target, key, value, klass
33
36
  end
34
37
  end
35
38
 
36
39
  # Applies a single style to a target. Delegates to a teacup handler if one is
37
40
  # found.
38
- def apply(target, key, value)
41
+ def apply(target, key, value, klass=nil)
39
42
  # note about `debug`: not all objects in this method are a UIView instance,
40
43
  # so don't assume that the object *has* a debug method.
41
44
  if value.is_a? Proc
42
- value = target.instance_exec(&value)
45
+ if value.arity == 1
46
+ value = value.call(target)
47
+ else
48
+ value = target.instance_exec(&value)
49
+ end
43
50
  end
44
51
 
52
+ klass ||= target.class
45
53
  handled = false
46
- target.class.ancestors.each do |ancestor|
54
+ klass.ancestors.each do |ancestor|
47
55
  if Teacup.handlers[ancestor].has_key? key
48
56
  NSLog "#{ancestor.name} is handling #{key} = #{value.inspect}" if target.respond_to? :debug and target.debug
49
57
  if Teacup.handlers[ancestor][key].arity == 1
@@ -71,15 +79,7 @@ module Teacup
71
79
  setter = 'set' + key.to_s.sub(/^./) {|c| c.capitalize} + ':'
72
80
  end
73
81
 
74
- if assign and target.respond_to?(assign)
75
- NSLog "Setting #{key} = #{value.inspect}" if target.respond_to? :debug and target.debug
76
- target.send(assign, value)
77
- elsif target.respondsToSelector(setter)
78
- NSLog "Calling target(#{key}, #{value.inspect})" if target.respond_to? :debug and target.debug
79
- target.send(setter, value)
80
- else
81
- NSLog "TEACUP WARNING: Can't apply #{setter.inspect}#{assign and " or " + assign.inspect or ""} to #{target.inspect}"
82
- end
82
+ Teacup.apply_method(target, assign, setter, value)
83
83
  end
84
84
 
85
85
  def handlers
data/lib/teacup/layout.rb CHANGED
@@ -1,4 +1,23 @@
1
1
  module Teacup
2
+ # Adds the class `stylesheet` method to
3
+ module LayoutClass
4
+
5
+ # Calling this method in the class body will assign a default stylesheet for
6
+ # all instances of your class
7
+ def stylesheet(new_stylesheet=nil)
8
+ if new_stylesheet.nil?
9
+ if @stylesheet.is_a? Symbol
10
+ @stylesheet = Teacup::Stylesheet[@stylesheet]
11
+ end
12
+
13
+ return @stylesheet
14
+ end
15
+
16
+ @stylesheet = new_stylesheet
17
+ end
18
+
19
+ end
20
+
2
21
  # Teacup::Layout defines a layout and subview function that can be used to
3
22
  # declare and configure the layout of views and the view hierarchy in your
4
23
  # application.
@@ -18,6 +37,10 @@ module Teacup
18
37
  # end
19
38
  # end
20
39
  module Layout
40
+ def self.included(base)
41
+ base.extend LayoutClass
42
+ end
43
+
21
44
  # Assign a Stylesheet or Stylesheet name (Symbol)
22
45
  #
23
46
  # @return val
@@ -60,7 +83,7 @@ module Teacup
60
83
  @stylesheet = Teacup::Stylesheet[@stylesheet]
61
84
  end
62
85
 
63
- @stylesheet
86
+ @stylesheet || self.class.stylesheet
64
87
  end
65
88
 
66
89
  # Alter the layout of a view
@@ -74,7 +97,10 @@ module Teacup
74
97
  # current stylesheet (see {stylesheet}) for this
75
98
  # element will be immediately applied.
76
99
  #
77
- # @param properties The third parameter is optional, and is a Hash
100
+ # @param classes The third parameter is optional, and is a list
101
+ # of style classes.
102
+ #
103
+ # @param properties The fourth parameter is optional, and is a Hash
78
104
  # of properties to apply to the view directly.
79
105
  #
80
106
  # @param &block If a block is passed, it is evaluated such that
@@ -100,34 +126,49 @@ module Teacup
100
126
  # subview(UIImage, backgroundColor: UIColor.colorWithImagePattern(image)
101
127
  # end
102
128
  #
103
- def layout(view_or_class, name_or_properties=nil, properties_or_nil=nil, &block)
129
+ def layout(view_or_class, *teacup_settings, &block)
104
130
  view = Teacup.to_instance(view_or_class)
105
131
 
106
- name = nil
107
- properties = properties_or_nil
132
+ # prevents the calling of restyle! until we return to this method
133
+ should_restyle = Teacup.should_restyle_and_block
108
134
 
109
- if name_or_properties.is_a? Hash
110
- name = nil
111
- properties = name_or_properties
112
- elsif name_or_properties
113
- name = name_or_properties.to_sym
135
+ teacup_style = Style.new
136
+
137
+ teacup_settings.each do |setting|
138
+ case setting
139
+ when Symbol, String
140
+ view.stylename = setting
141
+ when Hash
142
+ # override settings, but apply them to teacup_style so that it remains
143
+ # a Teacup::Style object
144
+ Teacup::merge_defaults(setting, teacup_style, teacup_style)
145
+ when Enumerable
146
+ view.style_classes = setting
147
+ when nil
148
+ # skip. this is so that helper methods that accept arguments like
149
+ # stylename can pass those on to this method without having to
150
+ # introspect the values (just set the default value to `nil`)
151
+ #
152
+ # long story short: tests will fail `nil` is not ignore here
153
+ else
154
+ raise "The argument #{setting.inspect} is not supported in Teacup::Layout::layout()"
155
+ end
114
156
  end
115
157
 
116
- # prevents the calling of restyle! until we return to this method
117
- should_restyle = Teacup.should_restyle_and_block
158
+ Teacup.apply_hash view, teacup_style.build(view)
118
159
 
119
160
  # assign the 'teacup_next_responder', which is queried for a stylesheet if
120
161
  # one is not explicitly assigned to the view
121
- view.teacup_next_responder = self
122
- view.stylename = name
123
- if properties
124
- view.style(properties) if properties
162
+ if view.is_a? Layout
163
+ view.teacup_next_responder = self
125
164
  end
126
165
 
127
166
  if block_given?
128
167
  superview_chain << view
129
168
  begin
130
- instance_exec(view, &block) if block_given?
169
+ # yield will not work if this is defined in the context of the
170
+ # UIViewController `layout` class method.
171
+ instance_exec(view, &block)
131
172
  rescue NoMethodError => e
132
173
  NSLog("Exception executing layout(#{view.inspect}) in #{self.inspect} (stylesheet=#{stylesheet})")
133
174
  raise e
@@ -140,6 +181,7 @@ module Teacup
140
181
  Teacup.should_restyle!
141
182
  view.restyle!
142
183
  end
184
+
143
185
  view
144
186
  end
145
187
 
@@ -191,6 +233,41 @@ module Teacup
191
233
  instance
192
234
  end
193
235
 
236
+ ##|
237
+ ##| Motion-Layout support
238
+ ##|
239
+
240
+ # Calling this method uses Nick Quaranto's motion-layout gem to provide ASCII
241
+ # art style access to autolayout. It assigns all the subviews by stylename,
242
+ # and assigns `self.view` as the target view. Beyond that, it's up to you to
243
+ # implement the layout methods:
244
+ #
245
+ # auto do
246
+ # metrics 'margin' => 20
247
+ # vertical "|-[top]-margin-[bottom]-|"
248
+ # horizontal "|-margin-[top]-margin-|"
249
+ # horizontal "|-margin-[bottom]-margin-|"
250
+ # end
251
+ def auto(layout_view=top_level_view, layout_subviews={}, &layout_block)
252
+ raise "gem install 'motion-layout'" unless defined? Motion::Layout
253
+
254
+ Teacup.get_styled_subviews(top_level_view).each do |view|
255
+ if ! layout_subviews[view.stylename.to_s]
256
+ layout_subviews[view.stylename.to_s] = view
257
+ end
258
+ end
259
+
260
+ Motion::Layout.new do |layout|
261
+ layout.view layout_view
262
+ layout.subviews layout_subviews
263
+ layout.instance_eval(&layout_block)
264
+ end
265
+ end
266
+
267
+ def top_level_view
268
+ raise "No default view has been defined for #{self.class}. Implement `top_level_view`."
269
+ end
270
+
194
271
  protected
195
272
 
196
273
  # Get's the current stack of views in nested calls to layout.
@@ -68,6 +68,7 @@ module Teacup
68
68
  attr_reader :name
69
69
 
70
70
  class << self
71
+
71
72
  def stylesheets
72
73
  @stylesheets ||= {}
73
74
  end
@@ -79,6 +80,7 @@ module Teacup
79
80
  def []= name, stylesheet
80
81
  stylesheets[name] = stylesheet
81
82
  end
83
+
82
84
  end
83
85
 
84
86
  # Create a new Stylesheet.
@@ -123,20 +125,18 @@ module Teacup
123
125
  # @stylesheet_cache object is manipulated directly (to invalidate entries,
124
126
  # or the entire cache)
125
127
  def stylesheet_cache
126
- @stylesheet_cache ||= Hash.new { |cache,_stylename|
127
- cache[_stylename] = Hash.new { |_target,_orientation|
128
+ @stylesheet_cache ||= Hash.new do |cache,_stylename|
129
+ cache[_stylename] = Hash.new do |_target,_orientation|
128
130
  _target[_orientation] = {}
129
- }
130
- }
131
+ end
132
+ end
131
133
  end
132
134
 
133
135
  def get_stylesheet_cache(stylename, target, orientation)
134
- orientation ||= UIApplication.sharedApplication.statusBarOrientation
135
136
  stylesheet_cache[stylename][target][orientation]
136
137
  end
137
138
 
138
139
  def set_stylesheet_cache(stylename, target, orientation, value)
139
- orientation ||= UIApplication.sharedApplication.statusBarOrientation
140
140
  self.stylesheet_cache[stylename][target][orientation] = value
141
141
  end
142
142
 
@@ -166,6 +166,33 @@ module Teacup
166
166
  def import(name_or_stylesheet)
167
167
  @stylesheet_cache = nil
168
168
  imported << name_or_stylesheet
169
+
170
+ sheet = nil
171
+ if name_or_stylesheet.is_a? Teacup::Stylesheet
172
+ sheet = name_or_stylesheet
173
+ elsif Teacup::Stylesheet.stylesheets.has_key? name_or_stylesheet
174
+ sheet = Teacup::Stylesheet.stylesheets[name_or_stylesheet]
175
+ end
176
+
177
+ if sheet
178
+ import_instance_vars(sheet)
179
+ end
180
+ end
181
+
182
+ # the block handed to Stylesheet#new is not run immediately - it is run
183
+ # the first time the stylesheet is queried. This fixes bugs related to
184
+ # some resources (fonts) not available when the application is first
185
+ # started. The downside is that @instance variables and variables that
186
+ # should be closed over are not.
187
+ #
188
+ # @return true if the block was run. false otherwise
189
+ def run_block
190
+ if @block
191
+ _block = @block
192
+ @block = nil
193
+ instance_eval &_block
194
+ true
195
+ end
169
196
  end
170
197
 
171
198
  # Get the properties defined for the given stylename, in this Stylesheet and
@@ -185,15 +212,7 @@ module Teacup
185
212
  return {} unless stylename
186
213
 
187
214
  unless get_stylesheet_cache(stylename, target, orientation)
188
- # the block handed to Stylesheet#new is not run immediately - it is run
189
- # the first time the stylesheet is queried. This fixes bugs related to
190
- # some resources (fonts) not available when the application is first
191
- # started. The downside is that @instance variables and variables that
192
- # should be closed over are not.
193
- if @block
194
- instance_eval &@block
195
- @block = nil
196
- end
215
+ run_block
197
216
  seen[self] = true
198
217
 
199
218
  set_stylesheet_cache(stylename, target, orientation, styles[stylename].build(target, orientation, seen))
@@ -221,7 +240,7 @@ module Teacup
221
240
  if queries[-1].is_a? Hash
222
241
  properties = queries.pop
223
242
  else
224
- # empty style declarations are allowed
243
+ # empty style declarations are allowed, but accomplish nothing.
225
244
  return
226
245
  end
227
246
 
@@ -239,7 +258,7 @@ module Teacup
239
258
  #
240
259
  # @return String
241
260
  def inspect
242
- "Teacup::Stylesheet[#{name.inspect}] = #{styles.inspect}"
261
+ "#{self.class.name}[#{name.inspect}] = #{styles.inspect}"
243
262
  end
244
263
 
245
264
  # The array of Stylesheets that have been imported into this one.
@@ -248,12 +267,14 @@ module Teacup
248
267
  def imported_stylesheets
249
268
  imported.map do |name_or_stylesheet|
250
269
  if name_or_stylesheet.is_a? Teacup::Stylesheet
251
- name_or_stylesheet
270
+ sheet = name_or_stylesheet
252
271
  elsif Teacup::Stylesheet.stylesheets.has_key? name_or_stylesheet
253
- Teacup::Stylesheet.stylesheets[name_or_stylesheet]
272
+ sheet = Teacup::Stylesheet.stylesheets[name_or_stylesheet]
254
273
  else
255
274
  raise "Teacup tried to import Stylesheet #{name_or_stylesheet.inspect} into Stylesheet[#{self.name.inspect}], but it didn't exist"
256
275
  end
276
+
277
+ sheet
257
278
  end
258
279
  end
259
280
 
@@ -281,6 +302,19 @@ module Teacup
281
302
  return retval
282
303
  end
283
304
 
305
+ def exclude_instance_vars
306
+ @exclude_instance_vars ||= [:@name, :@block, :@imported, :@styles, :@stylesheet_cache]
307
+ end
308
+
309
+ def import_instance_vars(from_stylesheet)
310
+ from_stylesheet.run_block
311
+
312
+ from_stylesheet.instance_variables.each do |var|
313
+ next if exclude_instance_vars.include? var
314
+ self.instance_variable_set(var, from_stylesheet.instance_variable_get(var))
315
+ end
316
+ end
317
+
284
318
  protected
285
319
 
286
320
  # The list of Stylesheets or names that have been imported into this one.
@@ -290,16 +324,17 @@ protected
290
324
  @imported ||= []
291
325
  end
292
326
 
293
- # The actual contents of this stylesheet as a Hash from stylename to properties.
327
+ # The styles hash contains Teacup::Style objects, which get linked to the
328
+ # current stylesheet.
294
329
  #
295
330
  # @return Hash[Symbol, Hash]
296
331
  def styles
297
- @styles ||= Hash.new{ |_styles, stylename|
298
- @styles[stylename] = Style.new
299
- @styles[stylename].stylename = stylename
300
- @styles[stylename].stylesheet = self
301
- @styles[stylename]
302
- }
332
+ @styles ||= Hash.new do |_styles, stylename|
333
+ _styles[stylename] = Style.new
334
+ _styles[stylename].stylename = stylename
335
+ _styles[stylename].stylesheet = self
336
+ _styles[stylename]
337
+ end
303
338
  end
304
339
 
305
340
  end