teacup 0.0.1.pre → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -3
- data/CHANGES.md +26 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +1 -1
- data/LICENSE +26 -0
- data/README.md +383 -95
- data/Rakefile +8 -36
- data/app/app_delegate.rb +14 -0
- data/app/controllers/first_controller.rb +71 -0
- data/app/controllers/landscape_only_controller.rb +16 -0
- data/app/controllers/tableview_controller.rb +0 -0
- data/app/styles/main_styles.rb +111 -0
- data/app/views/custom_view.rb +4 -0
- data/lib/dummy.rb +56 -0
- data/lib/teacup/handler.rb +99 -0
- data/lib/teacup/layout.rb +22 -74
- data/lib/teacup/merge_defaults.rb +45 -0
- data/lib/teacup/style.rb +93 -0
- data/lib/teacup/stylesheet.rb +242 -0
- data/lib/teacup/stylesheet_extensions/rotation.rb +38 -0
- data/lib/teacup/version.rb +1 -1
- data/lib/teacup/z_core_extensions/ca_layer.rb +6 -0
- data/lib/teacup/z_core_extensions/ui_view.rb +119 -0
- data/lib/teacup/z_core_extensions/ui_view_controller.rb +179 -0
- data/lib/teacup/z_core_extensions/ui_view_getters.rb +34 -0
- data/lib/teacup/z_core_extensions/z_handlers.rb +57 -0
- data/lib/teacup.rb +16 -33
- data/samples/Hai/Rakefile +7 -2
- data/samples/Hai/app/app_delegate.rb +2 -2
- data/samples/Hai/app/hai_controller.rb +8 -4
- data/samples/Hai/styles/iphone.rb +40 -0
- data/spec/main_spec.rb +226 -0
- data/spec/style_spec.rb +171 -0
- data/spec/stylesheet_spec.rb +348 -0
- data/spec/view_spec.rb +103 -0
- data/teacup.gemspec +13 -13
- metadata +47 -46
- data/.rspec +0 -2
- data/lib/teacup/contributors.rb +0 -7
- data/lib/teacup/core_extensions/ui_view.rb +0 -4
- data/lib/teacup/core_extensions/ui_view_controller.rb +0 -62
- data/lib/teacup/style_sheet.rb +0 -195
- data/lib/teacup/view.rb +0 -123
- data/pkg/teacup-0.0.0.gem +0 -0
- data/pkg/teacup-0.0.1.gem +0 -0
- data/proposals/other/README.md +0 -45
- data/proposals/other/app/config/application.rb +0 -1
- data/proposals/other/app/config/boot.rb +0 -1
- data/proposals/other/app/config/initializers/twitter.rb +0 -7
- data/proposals/other/app/controllers/events_controller.rb +0 -28
- data/proposals/other/app/controllers/venues_controller.rb +0 -4
- data/proposals/other/app/db/README.md +0 -16
- data/proposals/other/app/db/migrations/20120514201043_create_events.rb +0 -9
- data/proposals/other/app/db/migrations/20120514201044_add_price_to_events.rb +0 -5
- data/proposals/other/app/db/migrations/20120514201045_create_venues.rb +0 -8
- data/proposals/other/app/db/schema.rb +0 -19
- data/proposals/other/app/models/event.rb +0 -14
- data/proposals/other/app/models/venue.rb +0 -3
- data/proposals/other/app/views/events/edit.ipad.rb +0 -8
- data/proposals/other/app/views/events/edit.iphone.rb +0 -7
- data/proposals/other/app/views/events/show.ipad.rb +0 -2
- data/proposals/other/app/views/events/show.iphone.rb +0 -3
- data/samples/Hai/styles/ipad.rb +0 -11
- data/samples/Hai/styles/ipad_vertical.rb +0 -3
- data/spec/spec_helper.rb +0 -5
- data/spec/teacup/contributions_spec.rb +0 -13
- data/spec/teacup/version_spec.rb +0 -9
@@ -0,0 +1,242 @@
|
|
1
|
+
module Teacup
|
2
|
+
# Stylesheets in Teacup act as a central configuration mechanism,
|
3
|
+
# they have two aims:
|
4
|
+
#
|
5
|
+
# 1. Allow you to store "Details" away from the main body of your code.
|
6
|
+
# (controllers shouldn't have to be filled with style rules)
|
7
|
+
# 2. Allow you to easily re-use configuration in many places.
|
8
|
+
#
|
9
|
+
# The API really provides only two methods, {Stylesheet#style} to store properties
|
10
|
+
# on the Stylesheet; and {Stylesheet#query} to get them out again:
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# stylesheet = Teacup::Stylesheet.new
|
14
|
+
# stylesheet.style :buttons, :corners => :rounded
|
15
|
+
# # => nil
|
16
|
+
# stylesheet.query :buttons
|
17
|
+
# # => {:corners => :rounded}
|
18
|
+
#
|
19
|
+
# In addition to this, two separate mechanisms are provided for sharing
|
20
|
+
# configuration within stylesheets.
|
21
|
+
#
|
22
|
+
# Firstly, if you set the ':extends' property for a given stylename, then on lookup
|
23
|
+
# the Stylesheet will merge the properties for the ':extends' stylename into the return
|
24
|
+
# value. Conflicts are resolved so that properties with the original stylename
|
25
|
+
# are resolved in its favour.
|
26
|
+
#
|
27
|
+
# @example
|
28
|
+
# Teacup::Stylesheet.new(:ipad) do
|
29
|
+
# style :button,
|
30
|
+
# backgroundImage: UIImage.imageNamed("big_red_shiny_button"),
|
31
|
+
# top: 100
|
32
|
+
#
|
33
|
+
# style :ok_button, extends: :button,
|
34
|
+
# title: "OK!",
|
35
|
+
# top: 200
|
36
|
+
#
|
37
|
+
# end
|
38
|
+
# Teacup::Stylesheet[:ipad].query(:ok_button)
|
39
|
+
# # => {backgroundImage: UIImage.imageNamed("big_red_shiny_button"), top: 200, title: "OK!"}
|
40
|
+
#
|
41
|
+
# Secondly, you can import Stylesheets into each other, in exactly the same way as you
|
42
|
+
# can include Modules into each other in Ruby. This allows you to share rules between
|
43
|
+
# Stylesheets.
|
44
|
+
#
|
45
|
+
# As you'd expect, conflicts are resolved so that the Stylesheet on which you
|
46
|
+
# call query has the highest precedence.
|
47
|
+
#
|
48
|
+
# @example
|
49
|
+
# Teacup::Stylesheet.new(:ipad) do
|
50
|
+
# style :ok_button,
|
51
|
+
# title: "OK!"
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# Teacup::Stylesheet.new(:ipadvertical) do
|
55
|
+
# import :ipad
|
56
|
+
# style :ok_button,
|
57
|
+
# width: 80
|
58
|
+
# end
|
59
|
+
# Teacup::Stylesheet[:ipadvertical].query(:ok_button)
|
60
|
+
# # => {title: "OK!", width: 80}
|
61
|
+
#
|
62
|
+
# The two merging mechanisms are considered independently, so you can override
|
63
|
+
# a property both in a ':extends' rule, and also in an imported Stylesheet. In such a
|
64
|
+
# a case the Stylesheet inclusion conflicts are resolved independently; and then in
|
65
|
+
# a second phase, the ':extends' chain is flattened.
|
66
|
+
#
|
67
|
+
class Stylesheet
|
68
|
+
attr_reader :name
|
69
|
+
|
70
|
+
class << self
|
71
|
+
def stylesheets
|
72
|
+
@stylesheets ||= {}
|
73
|
+
end
|
74
|
+
|
75
|
+
def [] name
|
76
|
+
stylesheets[name]
|
77
|
+
end
|
78
|
+
|
79
|
+
def []= name, stylesheet
|
80
|
+
stylesheets[name] = stylesheet
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Create a new Stylesheet.
|
85
|
+
#
|
86
|
+
# If a name is provided then a new constant will be created using that name.
|
87
|
+
#
|
88
|
+
# @param name, The (optional) name to give.
|
89
|
+
# @param &block, The body of the Stylesheet instance_eval'd.
|
90
|
+
#
|
91
|
+
# @example
|
92
|
+
# Teacup::Stylesheet.new(:ipadvertical) do
|
93
|
+
# import :ipadbase
|
94
|
+
# style :continue_button,
|
95
|
+
# top: 50
|
96
|
+
# end
|
97
|
+
#
|
98
|
+
# Teacup::Stylesheet[:ipadvertical].query(:continue_button)
|
99
|
+
# # => {top: 50}
|
100
|
+
#
|
101
|
+
def initialize(name=nil, &block)
|
102
|
+
if name
|
103
|
+
@name = name.to_sym
|
104
|
+
Teacup::Stylesheet[@name] = self
|
105
|
+
end
|
106
|
+
|
107
|
+
# we just store the block for now, because some classes are not "ready"
|
108
|
+
# for instance, calling `UIFont.systemFontOfSize()` will cause the
|
109
|
+
# application to crash. We will lazily-load this block in `query`, and
|
110
|
+
# then set it to nil.
|
111
|
+
@block = block
|
112
|
+
end
|
113
|
+
|
114
|
+
# Include another Stylesheet into this one, the rules defined
|
115
|
+
# within it will have lower precedence than those defined here
|
116
|
+
# in the case that they share the same keys.
|
117
|
+
#
|
118
|
+
# @param Symbol|Teacup::Stylesheet the name of the stylesheet,
|
119
|
+
# or the stylesheet to include.
|
120
|
+
#
|
121
|
+
# When defining a stylesheet declaratively, it is better to use the symbol
|
122
|
+
# that represents a stylesheet, as the constant may not be defined yet:
|
123
|
+
#
|
124
|
+
# @example
|
125
|
+
# Teacup::Stylesheet.new(:ipadvertical) do
|
126
|
+
# import :ipadbase
|
127
|
+
# import :verticaltweaks
|
128
|
+
# end
|
129
|
+
#
|
130
|
+
#
|
131
|
+
# If you are using anonymous stylesheets however, then it will be necessary
|
132
|
+
# to pass an actual stylesheet object.
|
133
|
+
#
|
134
|
+
# @example
|
135
|
+
# @stylesheet.import(base_stylesheet)
|
136
|
+
#
|
137
|
+
def import(name_or_stylesheet)
|
138
|
+
imported << name_or_stylesheet
|
139
|
+
end
|
140
|
+
|
141
|
+
# Get the properties defined for the given stylename, in this Stylesheet and
|
142
|
+
# all those that have been imported.
|
143
|
+
#
|
144
|
+
# The Style class handles precedence rules, and extending via `:extends` and
|
145
|
+
# `import`. If needs the orientation in order to merge and remove the
|
146
|
+
# appropriate orientation styles.
|
147
|
+
#
|
148
|
+
# @param Symbol stylename, the stylename to look up.
|
149
|
+
# @return Hash[Symbol, *] the resulting properties.
|
150
|
+
# @example
|
151
|
+
# Teacup::Stylesheet[:ipadbase].query(:continue_button)
|
152
|
+
# # => {backgroundImage: UIImage.imageNamed("big_red_shiny_button"), title: "Continue!", top: 50}
|
153
|
+
def query(stylename, target=nil, orientation=nil, seen={})
|
154
|
+
return {} if seen[self]
|
155
|
+
|
156
|
+
# the block handed to Stylesheet#new is not run immediately - it is run
|
157
|
+
# the first time the stylesheet is queried. This fixes bugs related to
|
158
|
+
# some resources (fonts) not available when the application is first
|
159
|
+
# started. The downside is that @instance variables and variables that
|
160
|
+
# should be closed over are not.
|
161
|
+
if @block
|
162
|
+
instance_eval &@block
|
163
|
+
@block = nil
|
164
|
+
end
|
165
|
+
seen[self] = true
|
166
|
+
|
167
|
+
styles[stylename].build(target, orientation, seen)
|
168
|
+
end
|
169
|
+
|
170
|
+
# Add a set of properties for a given stylename or multiple stylenames.
|
171
|
+
#
|
172
|
+
# @param Symbol, *stylename
|
173
|
+
# @param Hash[Symbol, Object], properties
|
174
|
+
#
|
175
|
+
# @example
|
176
|
+
# Teacup::Stylesheet.new(:ipadbase) do
|
177
|
+
# style :pretty_button,
|
178
|
+
# backgroundImage: UIImage.imageNamed("big_red_shiny_button")
|
179
|
+
#
|
180
|
+
# style :continue_button, extends: :pretty_button,
|
181
|
+
# title: "Continue!",
|
182
|
+
# top: 50
|
183
|
+
# end
|
184
|
+
def style(*queries)
|
185
|
+
if queries[-1].is_a? Hash
|
186
|
+
properties = queries.pop
|
187
|
+
else
|
188
|
+
# empty style declarations are allowed
|
189
|
+
return
|
190
|
+
end
|
191
|
+
|
192
|
+
queries.each do |stylename|
|
193
|
+
# merge into styles[stylename], new properties "win"
|
194
|
+
Teacup::merge_defaults(properties, styles[stylename], styles[stylename])
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# A unique and hopefully meaningful description of this Object.
|
199
|
+
#
|
200
|
+
# @return String
|
201
|
+
def inspect
|
202
|
+
"Teacup::Stylesheet[#{name.inspect}] = #{styles.inspect}"
|
203
|
+
end
|
204
|
+
|
205
|
+
# The array of Stylesheets that have been imported into this one.
|
206
|
+
#
|
207
|
+
# @return Array[Stylesheet]
|
208
|
+
def imported_stylesheets
|
209
|
+
imported.map do |name_or_stylesheet|
|
210
|
+
if name_or_stylesheet.is_a? Teacup::Stylesheet
|
211
|
+
name_or_stylesheet
|
212
|
+
elsif Teacup::Stylesheet.stylesheets.has_key? name_or_stylesheet
|
213
|
+
Teacup::Stylesheet.stylesheets[name_or_stylesheet]
|
214
|
+
else
|
215
|
+
raise "Teacup tried to import Stylesheet #{name_or_stylesheet.inspect} into Stylesheet[#{self.name.inspect}], but it didn't exist"
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
protected
|
221
|
+
|
222
|
+
# The list of Stylesheets or names that have been imported into this one.
|
223
|
+
#
|
224
|
+
# @return Array[Symbol|Stylesheet]
|
225
|
+
def imported
|
226
|
+
@imported ||= []
|
227
|
+
end
|
228
|
+
|
229
|
+
# The actual contents of this stylesheet as a Hash from stylename to properties.
|
230
|
+
#
|
231
|
+
# @return Hash[Symbol, Hash]
|
232
|
+
def styles
|
233
|
+
@styles ||= Hash.new{ |_styles, stylename|
|
234
|
+
@styles[stylename] = Style.new
|
235
|
+
@styles[stylename].stylename = stylename
|
236
|
+
@styles[stylename].stylesheet = self
|
237
|
+
@styles[stylename]
|
238
|
+
}
|
239
|
+
end
|
240
|
+
|
241
|
+
end
|
242
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
|
2
|
+
module Teacup
|
3
|
+
|
4
|
+
class Stylesheet
|
5
|
+
def identity
|
6
|
+
[1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]
|
7
|
+
end
|
8
|
+
|
9
|
+
def pi
|
10
|
+
3.1415926
|
11
|
+
end
|
12
|
+
|
13
|
+
# rotates the "up & down" direction. The bottom of the view will rotate
|
14
|
+
# towards the user as angle increases.
|
15
|
+
def flip matrix, angle
|
16
|
+
CATransform3DRotate(matrix, angle, 1, 0, 0)
|
17
|
+
end
|
18
|
+
|
19
|
+
# rotates the "left & right" direction. The right side of the view will
|
20
|
+
# rotate towards the user as angle increases.
|
21
|
+
def twist matrix, angle
|
22
|
+
CATransform3DRotate(matrix, angle, 0, 1, 0)
|
23
|
+
end
|
24
|
+
|
25
|
+
# spins, along the z axis. This is probably the one you want, for
|
26
|
+
# "spinning" a view like you might a drink coaster or paper napkin.
|
27
|
+
def spin matrix, angle
|
28
|
+
CATransform3DRotate(matrix, angle, 0, 0, 1)
|
29
|
+
end
|
30
|
+
|
31
|
+
# rotates the layer arbitrarily
|
32
|
+
def rotate matrix, angle, x, y, z
|
33
|
+
CATransform3DRotate(matrix, angle, x, y, z)
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
data/lib/teacup/version.rb
CHANGED
@@ -0,0 +1,119 @@
|
|
1
|
+
# Teacup's UIView extensions defines some utility functions for UIView that
|
2
|
+
# enable a lot of the magic for Teacup::Layout.
|
3
|
+
|
4
|
+
# Users of teacup should be able to ignore the contents of this file for
|
5
|
+
# the most part.
|
6
|
+
class UIView
|
7
|
+
include Teacup::Layout
|
8
|
+
|
9
|
+
# The current stylename that is used to look up properties in the stylesheet.
|
10
|
+
attr_reader :stylename
|
11
|
+
|
12
|
+
# Enable debug messages for this object
|
13
|
+
attr_accessor :debug
|
14
|
+
|
15
|
+
# The current stylesheet will be looked at when properties are needed. It
|
16
|
+
# is loaded lazily, so that assignment can occur before the Stylesheet has
|
17
|
+
# been created.
|
18
|
+
def stylesheet
|
19
|
+
if Symbol === @stylesheet
|
20
|
+
@stylesheet = Teacup::Stylesheet[@stylesheet]
|
21
|
+
end
|
22
|
+
|
23
|
+
@stylesheet
|
24
|
+
end
|
25
|
+
|
26
|
+
# Alter the stylename of this view.
|
27
|
+
#
|
28
|
+
# This will cause new styles to be applied from the stylesheet.
|
29
|
+
#
|
30
|
+
# @param Symbol stylename
|
31
|
+
def stylename=(new_stylename)
|
32
|
+
@stylename = new_stylename
|
33
|
+
restyle!
|
34
|
+
end
|
35
|
+
|
36
|
+
# Alter the stylesheet of this view.
|
37
|
+
#
|
38
|
+
# This will cause new styles to be applied using the current stylename,
|
39
|
+
# and will recurse into subviews.
|
40
|
+
#
|
41
|
+
# If you would prefer that a given UIView object does not inherit the
|
42
|
+
# stylesheet from its parents, override the 'stylesheet' method to
|
43
|
+
# return the correct value at all times.
|
44
|
+
#
|
45
|
+
# @param Teacup::Stylesheet stylesheet.
|
46
|
+
def stylesheet=(new_stylesheet)
|
47
|
+
@stylesheet = new_stylesheet
|
48
|
+
restyle!
|
49
|
+
subviews.each{ |subview| subview.stylesheet = new_stylesheet }
|
50
|
+
end
|
51
|
+
|
52
|
+
def restyle!(orientation=nil)
|
53
|
+
style(stylesheet.query(stylename, self, orientation)) if stylesheet
|
54
|
+
subviews.each{ |subview| subview.restyle!(orientation) }
|
55
|
+
end
|
56
|
+
|
57
|
+
# Animate a change to a new stylename.
|
58
|
+
#
|
59
|
+
# This is equivalent to wrapping a call to .stylename= inside
|
60
|
+
# UIView.beginAnimations.
|
61
|
+
#
|
62
|
+
# @param Symbol the new stylename
|
63
|
+
# @param Options the options for the animation (may include the
|
64
|
+
# duration and the curve)
|
65
|
+
#
|
66
|
+
def animate_to_stylename(stylename, options={})
|
67
|
+
return if self.stylename == stylename
|
68
|
+
|
69
|
+
UIView.beginAnimations(nil, context: nil)
|
70
|
+
# TODO: This should be in a style-sheet!
|
71
|
+
UIView.setAnimationDuration(options[:duration]) if options[:duration]
|
72
|
+
UIView.setAnimationCurve(options[:curve]) if options[:curve]
|
73
|
+
self.stylename = stylename
|
74
|
+
UIView.commitAnimations
|
75
|
+
end
|
76
|
+
|
77
|
+
# Animate a change to new styles
|
78
|
+
#
|
79
|
+
# This is equivalent to wrapping a call to .style() inside
|
80
|
+
# UIView.beginAnimations.
|
81
|
+
#
|
82
|
+
# @param Hash the new styles and options for the animation
|
83
|
+
#
|
84
|
+
def animate_to_style(style)
|
85
|
+
UIView.beginAnimations(nil, context: nil)
|
86
|
+
UIView.setAnimationDuration(style[:duration]) if style[:duration]
|
87
|
+
UIView.setAnimationCurve(style[:curve]) if style[:curve]
|
88
|
+
style(style)
|
89
|
+
UIView.commitAnimations
|
90
|
+
end
|
91
|
+
|
92
|
+
# Apply style properties to this element.
|
93
|
+
#
|
94
|
+
# Takes a hash of properties such as may have been read from a stylesheet
|
95
|
+
# or passed as parameters to {Teacup::Layout#layout}, and applies them to
|
96
|
+
# the element.
|
97
|
+
#
|
98
|
+
# Does a little bit of magic (that may be split out as 'sugarcube') to
|
99
|
+
# make properties work as you'd expect.
|
100
|
+
#
|
101
|
+
# If you try and assign something in properties that is not supported,
|
102
|
+
# a warning message will be emitted.
|
103
|
+
#
|
104
|
+
# @param Hash the properties to set.
|
105
|
+
def style(properties, orientation=nil)
|
106
|
+
Teacup.apply_hash self, properties
|
107
|
+
properties.each do |key, value|
|
108
|
+
Teacup.apply self, key, value
|
109
|
+
end
|
110
|
+
|
111
|
+
self.setNeedsDisplay
|
112
|
+
self.setNeedsLayout
|
113
|
+
end
|
114
|
+
|
115
|
+
def top_level_view
|
116
|
+
return self
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
# Adds methods to the UIViewController class to make defining a layout and
|
2
|
+
# stylesheet very easy. Also provides rotation methods that analyze
|
3
|
+
class UIViewController
|
4
|
+
include Teacup::Layout
|
5
|
+
|
6
|
+
class << self
|
7
|
+
attr_reader :layout_definition
|
8
|
+
|
9
|
+
# Define the layout of a controller's view.
|
10
|
+
#
|
11
|
+
# This function is analogous to Teacup::Layout#layout, though it is
|
12
|
+
# designed so you can create an entire layout in a declarative manner in
|
13
|
+
# your controller.
|
14
|
+
#
|
15
|
+
# The hope is that this declarativeness will allow us to automatically
|
16
|
+
# deal with common iOS programming tasks (like releasing views when
|
17
|
+
# low-memory conditions occur) for you. This is still not implemented
|
18
|
+
# though.
|
19
|
+
#
|
20
|
+
# @param name The stylename for your controller's view.
|
21
|
+
#
|
22
|
+
# @param properties Any extra styles that you want to apply.
|
23
|
+
#
|
24
|
+
# @param &block The block in which you should define your layout.
|
25
|
+
# It will be instance_exec'd in the context of a
|
26
|
+
# controller instance.
|
27
|
+
#
|
28
|
+
# @example
|
29
|
+
# MyViewController < UIViewController
|
30
|
+
# layout :my_view do
|
31
|
+
# subview UILabel, title: "Test"
|
32
|
+
# subview UITextField, {
|
33
|
+
# frame: [[200, 200], [100, 100]]
|
34
|
+
# delegate: self
|
35
|
+
# }
|
36
|
+
# subview UIView, :shiny_thing) {
|
37
|
+
# subview UIView, :centre_of_shiny_thing
|
38
|
+
# }
|
39
|
+
# end
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
def layout(stylename=nil, properties={}, &block)
|
43
|
+
@layout_definition = [stylename, properties, block]
|
44
|
+
end
|
45
|
+
|
46
|
+
def stylesheet(new_stylesheet=nil)
|
47
|
+
if new_stylesheet.nil?
|
48
|
+
return @stylesheet
|
49
|
+
end
|
50
|
+
|
51
|
+
@stylesheet = new_stylesheet
|
52
|
+
end
|
53
|
+
|
54
|
+
end # class << self
|
55
|
+
|
56
|
+
# Returns a stylesheet to use to style the contents of this controller's
|
57
|
+
# view. You can also assign a stylesheet to {stylesheet=}, which will in
|
58
|
+
# turn call {restyle!}.
|
59
|
+
#
|
60
|
+
# This method will be queried each time {restyle!} is called, and also
|
61
|
+
# implicitly whenever Teacup needs to draw your layout (currently only at
|
62
|
+
# view load time).
|
63
|
+
#
|
64
|
+
# @return Teacup::Stylesheet
|
65
|
+
#
|
66
|
+
# @example
|
67
|
+
#
|
68
|
+
# def stylesheet
|
69
|
+
# if [UIDeviceOrientationLandscapeLeft,
|
70
|
+
# UIDeviceOrientationLandscapeRight].include?(UIDevice.currentDevice.orientation)
|
71
|
+
# Teacup::Stylesheet[:ipad]
|
72
|
+
# else
|
73
|
+
# Teacup::Stylesheet[:ipadvertical]
|
74
|
+
# end
|
75
|
+
# end
|
76
|
+
def stylesheet
|
77
|
+
@stylesheet
|
78
|
+
end
|
79
|
+
|
80
|
+
# Assigning a new stylesheet triggers {restyle!}, so do this during a
|
81
|
+
# rotation to get your different layouts applied.
|
82
|
+
#
|
83
|
+
# Assigning a stylesheet is an *alternative* to returning a Stylesheet in
|
84
|
+
# the {stylesheet} method. Note that {restyle!} calls {stylesheet}, so while
|
85
|
+
# assigning a stylesheet will trigger {restyle!}, your stylesheet will not
|
86
|
+
# be picked up if you don't return it in a custom stylesheet method.
|
87
|
+
#
|
88
|
+
# @return Teacup::Stylesheet
|
89
|
+
#
|
90
|
+
# @example
|
91
|
+
#
|
92
|
+
# stylesheet = Teacup::Stylesheet[:ipadhorizontal]
|
93
|
+
# stylesheet = :ipadhorizontal
|
94
|
+
def stylesheet=(new_stylesheet)
|
95
|
+
@stylesheet = new_stylesheet
|
96
|
+
if view
|
97
|
+
view.stylesheet = new_stylesheet
|
98
|
+
view.restyle!
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def top_level_view
|
103
|
+
return self.view
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
# Instantiate the layout from the class, and then call layoutDidLoad.
|
108
|
+
#
|
109
|
+
# If you want to use Teacup in your controller, please hook into layoutDidLoad,
|
110
|
+
# not viewDidLoad.
|
111
|
+
def viewDidLoad
|
112
|
+
|
113
|
+
if not self.stylesheet
|
114
|
+
self.stylesheet = self.class.stylesheet
|
115
|
+
end
|
116
|
+
|
117
|
+
if self.class.layout_definition
|
118
|
+
stylename, properties, block = self.class.layout_definition
|
119
|
+
layout(view, stylename, properties, &block)
|
120
|
+
end
|
121
|
+
|
122
|
+
layoutDidLoad
|
123
|
+
end
|
124
|
+
|
125
|
+
def viewWillAppear(animated)
|
126
|
+
self.view.restyle!
|
127
|
+
end
|
128
|
+
|
129
|
+
def layoutDidLoad
|
130
|
+
true
|
131
|
+
end
|
132
|
+
|
133
|
+
# The compiling mechanisms combined with how UIKit works of rubymotion do
|
134
|
+
# not allow the `shouldAutorotateToInterfaceOrientation` method to be
|
135
|
+
# overridden in modules/extensions. So instead, HERE is the code for what
|
136
|
+
# `shouldAutorotateToInterfaceOrientation` should look like if you want
|
137
|
+
# to use the teacup rotation stuff. Call this method from your own
|
138
|
+
# `shouldAutorotateToInterfaceOrientation` method.
|
139
|
+
#
|
140
|
+
# the teacup developers apologize for any inconvenience. :-)
|
141
|
+
def autorotateToOrientation(orientation)
|
142
|
+
if view.stylesheet and view.stylename
|
143
|
+
properties = view.stylesheet.query(view.stylename, self, orientation)
|
144
|
+
|
145
|
+
# check for orientation-specific properties
|
146
|
+
case orientation
|
147
|
+
when UIInterfaceOrientationPortrait
|
148
|
+
# portrait is "on" by default, must be turned off explicitly
|
149
|
+
if properties.supports?(:portrait) == nil and properties.supports?(:upside_up) == nil
|
150
|
+
return true
|
151
|
+
end
|
152
|
+
|
153
|
+
return (properties.supports?(:portrait) or properties.supports?(:upside_up))
|
154
|
+
when UIInterfaceOrientationPortraitUpsideDown
|
155
|
+
if UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPhone
|
156
|
+
# iphone must have an explicit upside-down style, otherwise this returns
|
157
|
+
# false
|
158
|
+
return properties.supports?(:upside_down)
|
159
|
+
else
|
160
|
+
# ipad can just have a portrait style
|
161
|
+
return (properties.supports?(:portrait) or properties.supports?(:upside_down))
|
162
|
+
end
|
163
|
+
when UIInterfaceOrientationLandscapeLeft
|
164
|
+
return (properties.supports?(:landscape) or properties.supports?(:landscape_left))
|
165
|
+
when UIInterfaceOrientationLandscapeRight
|
166
|
+
return (properties.supports?(:landscape) or properties.supports?(:landscape_right))
|
167
|
+
end
|
168
|
+
|
169
|
+
return false
|
170
|
+
end
|
171
|
+
|
172
|
+
return orientation == UIInterfaceOrientationPortrait
|
173
|
+
end
|
174
|
+
|
175
|
+
def willAnimateRotationToInterfaceOrientation(orientation, duration:duration)
|
176
|
+
view.restyle!(orientation)
|
177
|
+
end
|
178
|
+
|
179
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# Methods to retrieve a subview using the stylename as a key
|
2
|
+
# Kinda similar to jQuery-style $().find('stylename')
|
3
|
+
class UIView
|
4
|
+
|
5
|
+
# get one stylesheet by stylename
|
6
|
+
# my_view[:button] :button => #<UIButton..>
|
7
|
+
def viewWithStylename name
|
8
|
+
subviews.each do |view|
|
9
|
+
if view.stylename == name
|
10
|
+
return view
|
11
|
+
end
|
12
|
+
end
|
13
|
+
subviews.each do |view|
|
14
|
+
if v = view.viewWithStylename(name)
|
15
|
+
return v
|
16
|
+
end
|
17
|
+
end
|
18
|
+
nil # couldn't find it
|
19
|
+
end
|
20
|
+
|
21
|
+
# get stylesheets by stylename
|
22
|
+
# my_view.all :button => [#<UIButton..>, #<UIButton...>]
|
23
|
+
def viewsWithStylename name
|
24
|
+
r = []
|
25
|
+
subviews.each do |view|
|
26
|
+
if view.stylename == name
|
27
|
+
r.push name
|
28
|
+
end
|
29
|
+
r += view.viewsWithStylename name
|
30
|
+
end
|
31
|
+
r
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
##|
|
2
|
+
##| UIView.frame
|
3
|
+
##|
|
4
|
+
Teacup.handler UIView, :left, :x { |view, x|
|
5
|
+
f = view.frame
|
6
|
+
f.origin.x = x
|
7
|
+
view.frame = f
|
8
|
+
}
|
9
|
+
|
10
|
+
Teacup.handler UIView, :right { |view, r|
|
11
|
+
f = view.frame
|
12
|
+
f.origin.x = r - f.size.width
|
13
|
+
view.frame = f
|
14
|
+
}
|
15
|
+
|
16
|
+
Teacup.handler UIView, :top, :y { |view, y|
|
17
|
+
f = view.frame
|
18
|
+
f.origin.y = y
|
19
|
+
view.frame = f
|
20
|
+
}
|
21
|
+
|
22
|
+
Teacup.handler UIView, :bottom { |view, b|
|
23
|
+
f = view.frame
|
24
|
+
f.origin.y = b - f.size.height
|
25
|
+
view.frame = f
|
26
|
+
}
|
27
|
+
|
28
|
+
Teacup.handler UIView, :width { |view, w|
|
29
|
+
f = view.frame
|
30
|
+
f.size.width = w
|
31
|
+
view.frame = f
|
32
|
+
}
|
33
|
+
|
34
|
+
Teacup.handler UIView, :height { |view, h|
|
35
|
+
f = view.frame
|
36
|
+
f.size.height = h
|
37
|
+
view.frame = f
|
38
|
+
}
|
39
|
+
|
40
|
+
Teacup.handler UIView, :origin { |view, origin|
|
41
|
+
f = view.frame
|
42
|
+
f.origin = origin
|
43
|
+
view.frame = f
|
44
|
+
}
|
45
|
+
|
46
|
+
Teacup.handler UIView, :size { |view, size|
|
47
|
+
f = view.frame
|
48
|
+
f.size = size
|
49
|
+
view.frame = f
|
50
|
+
}
|
51
|
+
|
52
|
+
##|
|
53
|
+
##| UIButton
|
54
|
+
##|
|
55
|
+
Teacup.handler UIButton, :title { |view, title|
|
56
|
+
view.setTitle(title, forState: UIControlStateNormal)
|
57
|
+
}
|