teacup 1.3.4 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -1
- data/Gemfile.lock +3 -3
- data/README.md +1172 -319
- data/Rakefile +8 -1
- data/app/app_delegate.rb +1 -1
- data/app/controllers/appearance_controller.rb +13 -0
- data/app/controllers/landscape_only_controller.rb +1 -1
- data/app/controllers/{first_controller.rb → main_controller.rb} +4 -3
- data/app/controllers/motion_layout_controller.rb +22 -0
- data/app/styles/appearance.rb +24 -0
- data/app/styles/main_styles.rb +8 -6
- data/app/views/custom_view.rb +1 -0
- data/lib/teacup/calculations.rb +2 -2
- data/lib/teacup/{z_core_extensions → core_extensions}/ca_layer.rb +0 -0
- data/lib/teacup/core_extensions/view_getters.rb +61 -0
- data/lib/teacup/handler.rb +14 -14
- data/lib/teacup/layout.rb +94 -17
- data/lib/teacup/stylesheet.rb +61 -26
- data/lib/teacup/stylesheet_extensions/transform.rb +88 -0
- data/lib/teacup/teacup_controller.rb +122 -0
- data/lib/teacup/teacup_util.rb +12 -7
- data/lib/teacup/teacup_view.rb +329 -0
- data/lib/teacup/version.rb +1 -1
- data/lib/teacup-ios/appearance.rb +96 -0
- data/lib/teacup-ios/core_extensions/teacup_handlers.rb +183 -0
- data/lib/teacup-ios/core_extensions/ui_view.rb +30 -0
- data/lib/teacup-ios/core_extensions/ui_view_controller.rb +110 -0
- data/lib/{dummy.rb → teacup-ios/dummy.rb} +2 -6
- data/lib/teacup-ios/handler.rb +23 -0
- data/lib/{teacup → teacup-ios}/style.rb +9 -10
- data/lib/teacup-ios/stylesheet_extensions/autoresize.rb +169 -0
- data/lib/{teacup/stylesheet_extensions/geometry.rb → teacup-ios/stylesheet_extensions/device.rb} +0 -0
- data/lib/teacup-osx/core_extensions/ns_view.rb +39 -0
- data/lib/teacup-osx/core_extensions/ns_view_controller.rb +21 -0
- data/lib/teacup-osx/core_extensions/ns_window.rb +39 -0
- data/lib/teacup-osx/core_extensions/ns_window_controller.rb +29 -0
- data/lib/{teacup/z_core_extensions/z_handlers.rb → teacup-osx/core_extensions/teacup_handlers.rb} +30 -47
- data/lib/teacup-osx/dummy.rb +80 -0
- data/lib/teacup-osx/handler.rb +16 -0
- data/lib/teacup-osx/style.rb +83 -0
- data/lib/teacup-osx/style_extensions/autoresize.rb +169 -0
- data/lib/teacup.rb +12 -11
- data/samples/Tweets/Gemfile +4 -0
- data/samples/Tweets/Gemfile.lock +16 -0
- data/samples/Tweets/README +7 -0
- data/samples/Tweets/Rakefile +9 -0
- data/samples/Tweets/app/app_delegate.rb +18 -0
- data/samples/Tweets/app/data_parser.rb +10 -0
- data/samples/Tweets/app/json_parser.rb +12 -0
- data/samples/Tweets/app/main_window.rb +99 -0
- data/samples/Tweets/app/menu.rb +108 -0
- data/samples/Tweets/app/stylesheet.rb +21 -0
- data/samples/Tweets/app/tweet.rb +11 -0
- data/samples/Tweets/resources/Credits.rtf +29 -0
- data/samples/Tweets/spec/main_spec.rb +9 -0
- data/samples/teacup-osx/.gitignore +1 -0
- data/samples/teacup-osx/Gemfile +4 -0
- data/samples/teacup-osx/Gemfile.lock +16 -0
- data/samples/teacup-osx/Rakefile +9 -0
- data/samples/teacup-osx/app/app_delegate.rb +23 -0
- data/samples/teacup-osx/app/controller.rb +11 -0
- data/samples/teacup-osx/app/menu.rb +108 -0
- data/samples/teacup-osx/app/window.rb +12 -0
- data/samples/teacup-osx/resources/Credits.rtf +29 -0
- data/samples/teacup-osx/resources/teacup.png +0 -0
- data/samples/teacup-osx/spec/main_spec.rb +9 -0
- data/spec/ios/appearance_spec.rb +18 -0
- data/spec/{calculations_spec.rb → ios/calculations_spec.rb} +0 -0
- data/spec/{constraints_spec.rb → ios/constraints_spec.rb} +0 -0
- data/spec/{custom_class_spec.rb → ios/custom_class_spec.rb} +0 -0
- data/spec/{gradient_spec.rb → ios/gradient_spec.rb} +1 -1
- data/spec/ios/layout_module_spec.rb +54 -0
- data/spec/ios/layout_spec.rb +50 -0
- data/spec/{main_spec.rb → ios/main_spec.rb} +52 -13
- data/spec/ios/motion_layout_spec.rb +44 -0
- data/spec/{present_modal_spec.rb → ios/present_modal_spec.rb} +0 -0
- data/spec/{style_spec.rb → ios/style_spec.rb} +1 -1
- data/spec/ios/stylesheet_extensions/autoresize_spec.rb +50 -0
- data/spec/{stylesheet_spec.rb → ios/stylesheet_spec.rb} +12 -0
- data/spec/{ui_view_getters_spec.rb → ios/ui_view_getters_spec.rb} +0 -0
- data/spec/{uiswitch_spec.rb → ios/uiswitch_spec.rb} +0 -0
- data/spec/{view_spec.rb → ios/view_spec.rb} +23 -2
- metadata +85 -35
- data/lib/teacup/stylesheet_extensions/autoresize.rb +0 -39
- data/lib/teacup/stylesheet_extensions/rotation.rb +0 -37
- data/lib/teacup/z_core_extensions/ui_view.rb +0 -262
- data/lib/teacup/z_core_extensions/ui_view_controller.rb +0 -263
- data/lib/teacup/z_core_extensions/ui_view_getters.rb +0 -58
@@ -0,0 +1,88 @@
|
|
1
|
+
|
2
|
+
module Teacup
|
3
|
+
class Stylesheet
|
4
|
+
|
5
|
+
def pi
|
6
|
+
Math::PI
|
7
|
+
end
|
8
|
+
|
9
|
+
def transform_view
|
10
|
+
@@transform_layer ||= TransformView.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def transform_layer
|
14
|
+
@@transform_layer ||= TransformLayer.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def identity
|
18
|
+
NSLog("The Stylesheet method `identity` is deprecated, use `transform_layer.identity` instead")
|
19
|
+
[1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
class TransformLayer
|
25
|
+
|
26
|
+
def identity
|
27
|
+
[1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]
|
28
|
+
end
|
29
|
+
|
30
|
+
# rotates the "up & down" direction. The bottom of the view will rotate
|
31
|
+
# towards the user as angle increases.
|
32
|
+
def flip(angle)
|
33
|
+
CATransform3DRotate(identity, angle, 1, 0, 0)
|
34
|
+
end
|
35
|
+
|
36
|
+
# rotates the "left & right" direction. The right side of the view will
|
37
|
+
# rotate towards the user as angle increases.
|
38
|
+
def twist(angle)
|
39
|
+
CATransform3DRotate(identity, angle, 0, 1, 0)
|
40
|
+
end
|
41
|
+
|
42
|
+
# spins, along the z axis. This is probably the one you want, for
|
43
|
+
# "spinning" a view like you might a drink coaster or paper napkin.
|
44
|
+
def spin(angle)
|
45
|
+
CATransform3DRotate(identity, angle, 0, 0, 1)
|
46
|
+
end
|
47
|
+
|
48
|
+
# rotates the layer arbitrarily
|
49
|
+
def rotate(angle, x, y, z)
|
50
|
+
CATransform3DRotate(identity, angle, x, y, z)
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
class TransformView
|
56
|
+
|
57
|
+
def identity
|
58
|
+
[1, 0, 0, 1, 0, 0]
|
59
|
+
end
|
60
|
+
|
61
|
+
# Rotates the view counterclockwise
|
62
|
+
def rotate angle
|
63
|
+
CGAffineTransformMakeRotation(angle)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Scales the view
|
67
|
+
def scale scale_x, scale_y=nil
|
68
|
+
scale_y ||= scale_x
|
69
|
+
CGAffineTransformMakeScale(scale_x, scale_y)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Translates the view
|
73
|
+
def translate point, y=nil
|
74
|
+
if point.respond_to?(:x) &&point.respond_to?(:y)
|
75
|
+
x = point.x
|
76
|
+
y = point.y
|
77
|
+
elsif point.is_a? Array
|
78
|
+
x = point[0]
|
79
|
+
y = point[1]
|
80
|
+
else
|
81
|
+
x = point
|
82
|
+
end
|
83
|
+
CGAffineTransformMakeTranslation(x, y)
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# Adds methods to the UIViewController/NSViewController/NSWindowController
|
2
|
+
# classes to make defining a layout and stylesheet very easy. Also provides
|
3
|
+
# rotation methods that analyze
|
4
|
+
module Teacup
|
5
|
+
module ControllerClass
|
6
|
+
|
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
|
+
# class 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
|
+
end
|
47
|
+
|
48
|
+
module Controller
|
49
|
+
def self.included(base)
|
50
|
+
base.extend ControllerClass
|
51
|
+
end
|
52
|
+
|
53
|
+
# Assigning a new stylesheet triggers {restyle!}.
|
54
|
+
#
|
55
|
+
# Assigning a stylesheet is an *alternative* to returning a Stylesheet in
|
56
|
+
# the {stylesheet} method. Note that {restyle!} calls {stylesheet}, so while
|
57
|
+
# assigning a stylesheet will trigger {restyle!}, your stylesheet will not
|
58
|
+
# be picked up if you don't return it in a custom stylesheet method.
|
59
|
+
#
|
60
|
+
# @return Teacup::Stylesheet
|
61
|
+
#
|
62
|
+
# @example
|
63
|
+
#
|
64
|
+
# stylesheet = Teacup::Stylesheet[:ipadhorizontal]
|
65
|
+
# stylesheet = :ipadhorizontal
|
66
|
+
def stylesheet=(new_stylesheet)
|
67
|
+
super
|
68
|
+
if self.viewLoaded?
|
69
|
+
self.view.restyle!
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Instantiate the layout from the class, and then call layoutDidLoad.
|
74
|
+
#
|
75
|
+
# If you want to use Teacup in your controller, please hook into layoutDidLoad,
|
76
|
+
# not viewDidLoad or windowDidLoad (they call this method).
|
77
|
+
def teacupDidLoad
|
78
|
+
# look for a layout_definition in the list of ancestors
|
79
|
+
layout_definition = nil
|
80
|
+
my_stylesheet = self.stylesheet
|
81
|
+
parent_class = self.class
|
82
|
+
while parent_class != NSObject and not (layout_definition && my_stylesheet)
|
83
|
+
if not my_stylesheet and parent_class.respond_to?(:stylesheet)
|
84
|
+
my_stylesheet = parent_class.stylesheet
|
85
|
+
end
|
86
|
+
|
87
|
+
if not layout_definition and parent_class.respond_to?(:layout_definition)
|
88
|
+
layout_definition = parent_class.layout_definition
|
89
|
+
end
|
90
|
+
parent_class = parent_class.superclass
|
91
|
+
end
|
92
|
+
|
93
|
+
should_restyle = Teacup.should_restyle_and_block
|
94
|
+
|
95
|
+
if my_stylesheet and not self.stylesheet
|
96
|
+
self.stylesheet = my_stylesheet
|
97
|
+
end
|
98
|
+
|
99
|
+
if layout_definition
|
100
|
+
stylename, properties, block = layout_definition
|
101
|
+
layout(top_level_view, stylename, properties, &block)
|
102
|
+
end
|
103
|
+
|
104
|
+
if should_restyle
|
105
|
+
Teacup.should_restyle!
|
106
|
+
self.top_level_view.restyle!
|
107
|
+
end
|
108
|
+
|
109
|
+
if defined? NSLayoutConstraint
|
110
|
+
self.top_level_view.apply_constraints
|
111
|
+
end
|
112
|
+
|
113
|
+
layoutDidLoad
|
114
|
+
end
|
115
|
+
|
116
|
+
def layoutDidLoad
|
117
|
+
true
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
data/lib/teacup/teacup_util.rb
CHANGED
@@ -1,17 +1,22 @@
|
|
1
1
|
module Teacup
|
2
2
|
module_function
|
3
3
|
|
4
|
+
# Returns all the subviews of `target` that have a stylename. `target` is not
|
5
|
+
# included in the list. Used by the motion-layout integration in layout.rb.
|
6
|
+
def get_styled_subviews(target)
|
7
|
+
retval = target.subviews.select { |v| v.stylename }
|
8
|
+
retval.concat(target.subviews.map do |subview|
|
9
|
+
get_styled_subviews(subview)
|
10
|
+
end)
|
11
|
+
retval.flatten
|
12
|
+
end
|
13
|
+
|
4
14
|
def to_instance(class_or_instance)
|
5
15
|
if class_or_instance.is_a? Class
|
6
|
-
unless class_or_instance <= UIView
|
7
|
-
raise "Expected subclass of UIView, got: #{class_or_instance.inspect}"
|
8
|
-
end
|
9
16
|
return class_or_instance.new
|
10
|
-
elsif class_or_instance.is_a?(UIView)
|
11
|
-
return class_or_instance
|
12
17
|
else
|
13
|
-
|
18
|
+
return class_or_instance
|
14
19
|
end
|
15
20
|
end
|
16
21
|
|
17
|
-
end
|
22
|
+
end
|
@@ -0,0 +1,329 @@
|
|
1
|
+
# Teacup's View extensions defines some utility functions for View that enable a
|
2
|
+
# 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
|
+
module Teacup
|
7
|
+
module View
|
8
|
+
|
9
|
+
# The current stylename that is used to look up properties in the stylesheet.
|
10
|
+
attr_reader :stylename
|
11
|
+
|
12
|
+
# A list of style classes that will be merged in (lower priority than stylename)
|
13
|
+
attr_reader :style_classes
|
14
|
+
|
15
|
+
# Any class that includes Teacup::Layout gets a `layout` method, which assigns
|
16
|
+
# itself as the 'teacup_next_responder'.
|
17
|
+
attr_accessor :teacup_next_responder
|
18
|
+
|
19
|
+
# Enable debug messages for this object
|
20
|
+
attr_accessor :debug
|
21
|
+
|
22
|
+
# Alter the stylename of this view.
|
23
|
+
#
|
24
|
+
# This will cause new styles to be applied from the stylesheet.
|
25
|
+
#
|
26
|
+
# If you are using Pixate, it will also set the pixate `styleId` property.
|
27
|
+
#
|
28
|
+
# @param Symbol stylename
|
29
|
+
def stylename=(new_stylename)
|
30
|
+
@stylename = new_stylename
|
31
|
+
if respond_to?(:'setStyleId:')
|
32
|
+
setStyleId(new_stylename)
|
33
|
+
end
|
34
|
+
if respond_to?(:'setNuiClass:')
|
35
|
+
setNuiClass(new_stylename)
|
36
|
+
end
|
37
|
+
restyle!
|
38
|
+
end
|
39
|
+
|
40
|
+
# Why stop at just one stylename!? Assign a bunch of them using
|
41
|
+
# `style_classes`. These are distinct from `stylename`, and `stylename` styles
|
42
|
+
# are given priority of `style_classes`.
|
43
|
+
#
|
44
|
+
# If you are using Pixate, it will also set the pixate `styleClass` property.
|
45
|
+
#
|
46
|
+
# @param Array [Symbol] style_classes
|
47
|
+
def style_classes=(new_style_classes)
|
48
|
+
@style_classes = new_style_classes
|
49
|
+
if respond_to?(:setStyleClass)
|
50
|
+
setStyleClass(new_style_classes.join(' '))
|
51
|
+
end
|
52
|
+
restyle!
|
53
|
+
end
|
54
|
+
|
55
|
+
def style_classes
|
56
|
+
@style_classes ||= []
|
57
|
+
end
|
58
|
+
|
59
|
+
def add_style_class(stylename)
|
60
|
+
unless style_classes.include?
|
61
|
+
style_classes << stylename
|
62
|
+
restyle!
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def remove_style_class(stylename)
|
67
|
+
if style_classes.delete(stylename)
|
68
|
+
restyle!
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Alter the stylesheet of this view.
|
73
|
+
#
|
74
|
+
# This will cause new styles to be applied using the current stylename,
|
75
|
+
# and will recurse into subviews.
|
76
|
+
#
|
77
|
+
# If you would prefer that a given UIView object does not inherit the
|
78
|
+
# stylesheet from its parents, override the 'stylesheet' method to
|
79
|
+
# return the correct value at all times.
|
80
|
+
#
|
81
|
+
# @param Teacup::Stylesheet stylesheet.
|
82
|
+
def stylesheet=(new_stylesheet)
|
83
|
+
should_restyle = Teacup.should_restyle_and_block
|
84
|
+
|
85
|
+
@stylesheet = new_stylesheet
|
86
|
+
|
87
|
+
if should_restyle
|
88
|
+
Teacup.should_restyle!
|
89
|
+
restyle!
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def stylesheet
|
94
|
+
if @stylesheet.is_a? Symbol
|
95
|
+
@stylesheet = Teacup::Stylesheet[@stylesheet]
|
96
|
+
end
|
97
|
+
# is a stylesheet assigned explicitly?
|
98
|
+
retval = @stylesheet
|
99
|
+
return retval if retval
|
100
|
+
|
101
|
+
# the 'teacup_next_responder' is assigned in the `layout` method, and links
|
102
|
+
# any views created there to the custom class (could be a controller, could
|
103
|
+
# be any class that includes Teacup::Layout). That responder is checked
|
104
|
+
# next, but only if it wouldn't result in a circular loop.
|
105
|
+
if ! retval && @teacup_next_responder && teacup_next_responder != self
|
106
|
+
retval = @teacup_next_responder.stylesheet
|
107
|
+
end
|
108
|
+
|
109
|
+
# lastly, go up the chain; either a controller or superview
|
110
|
+
if ! retval && nextResponder && nextResponder.respond_to?(:stylesheet)
|
111
|
+
retval = nextResponder.stylesheet
|
112
|
+
end
|
113
|
+
|
114
|
+
return retval
|
115
|
+
end
|
116
|
+
|
117
|
+
def restyle!(orientation=nil)
|
118
|
+
if Teacup.should_restyle?
|
119
|
+
if stylesheet && stylesheet.is_a?(Teacup::Stylesheet)
|
120
|
+
style_classes.each do |stylename|
|
121
|
+
style(stylesheet.query(stylename, self, orientation))
|
122
|
+
end
|
123
|
+
style(stylesheet.query(self.stylename, self, orientation))
|
124
|
+
end
|
125
|
+
subviews.each { |subview| subview.restyle!(orientation) }
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def get_ns_constraints
|
130
|
+
# gets the array of Teacup::Constraint objects
|
131
|
+
my_constraints = (@teacup_constraints || []).map { |constraint, relative_to|
|
132
|
+
if constraint.is_a?(Teacup::Constraint)
|
133
|
+
constraint
|
134
|
+
else
|
135
|
+
if relative_to == true
|
136
|
+
Teacup::Constraint.from_sym(constraint)
|
137
|
+
else
|
138
|
+
Teacup::Constraint.from_sym(constraint, relative_to)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
}.flatten.map do |original_constraint|
|
142
|
+
constraint = original_constraint.copy
|
143
|
+
|
144
|
+
view_class = self.class
|
145
|
+
|
146
|
+
case original_constraint.target
|
147
|
+
when view_class
|
148
|
+
constraint.target = original_constraint.target
|
149
|
+
when :self
|
150
|
+
constraint.target = self
|
151
|
+
when :superview
|
152
|
+
constraint.target = self.superview
|
153
|
+
when Symbol, String
|
154
|
+
container = self
|
155
|
+
constraint.target = nil
|
156
|
+
while container && constraint.target.nil?
|
157
|
+
constraint.target = container.viewWithStylename(original_constraint.target)
|
158
|
+
container = container.superview
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
case original_constraint.relative_to
|
163
|
+
when nil
|
164
|
+
constraint.relative_to = nil
|
165
|
+
when view_class
|
166
|
+
constraint.relative_to = original_constraint.relative_to
|
167
|
+
when :self
|
168
|
+
constraint.relative_to = self
|
169
|
+
when :superview
|
170
|
+
constraint.relative_to = self.superview
|
171
|
+
when Symbol, String
|
172
|
+
# TODO: this re-checks lots of views - everytime it goes up to the
|
173
|
+
# superview, it checks all the leaves again.
|
174
|
+
container = self
|
175
|
+
constraint.relative_to = nil
|
176
|
+
while container && constraint.relative_to.nil?
|
177
|
+
constraint.relative_to = container.viewWithStylename(original_constraint.relative_to)
|
178
|
+
container = container.superview
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
if original_constraint.relative_to && ! constraint.relative_to
|
183
|
+
puts "Could not find #{original_constraint.relative_to.inspect}"
|
184
|
+
container = self
|
185
|
+
tab = ' '
|
186
|
+
while container && constraint.relative_to.nil?
|
187
|
+
tab << '->'
|
188
|
+
puts "#{tab} #{container.stylename.inspect}"
|
189
|
+
container = container.superview
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
# the return value, for the map
|
194
|
+
constraint.nslayoutconstraint
|
195
|
+
end
|
196
|
+
|
197
|
+
unless my_constraints.empty?
|
198
|
+
self.setTranslatesAutoresizingMaskIntoConstraints(false)
|
199
|
+
end
|
200
|
+
|
201
|
+
# now add all che child constraints
|
202
|
+
subviews.each do |subview|
|
203
|
+
my_constraints.concat(subview.get_ns_constraints)
|
204
|
+
end
|
205
|
+
|
206
|
+
my_constraints
|
207
|
+
end
|
208
|
+
|
209
|
+
def apply_constraints
|
210
|
+
if @teacup_added_constraints
|
211
|
+
@teacup_added_constraints.each do |constraint|
|
212
|
+
self.removeConstraint(constraint)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
@teacup_added_constraints = nil
|
216
|
+
all_constraints = get_ns_constraints
|
217
|
+
|
218
|
+
return if all_constraints.empty?
|
219
|
+
|
220
|
+
@teacup_added_constraints = []
|
221
|
+
all_constraints.each do |ns_constraint|
|
222
|
+
@teacup_added_constraints << ns_constraint
|
223
|
+
self.addConstraint(ns_constraint)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
# Applies styles pulled from a stylesheet, but does not assign those styles to
|
228
|
+
# any property. This is a one-shot use method, meant to be used as
|
229
|
+
# initialization or to apply styles that should not be reapplied during a
|
230
|
+
# rotation.
|
231
|
+
def apply_stylename(stylename)
|
232
|
+
if stylesheet && stylesheet.is_a?(Teacup::Stylesheet)
|
233
|
+
style(stylesheet.query(stylename, self))
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
# Animate a change to a new stylename.
|
238
|
+
#
|
239
|
+
# This is equivalent to wrapping a call to .stylename= inside
|
240
|
+
# UIView.beginAnimations.
|
241
|
+
#
|
242
|
+
# @param Symbol the new stylename
|
243
|
+
# @param Options the options for the animation (may include the
|
244
|
+
# duration and the curve)
|
245
|
+
#
|
246
|
+
def animate_to_stylename(stylename, options={})
|
247
|
+
return if self.stylename == stylename
|
248
|
+
|
249
|
+
teacup_animation(options) do
|
250
|
+
self.stylename = stylename
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
# Animate a change to a new list of style_classes.
|
255
|
+
#
|
256
|
+
# This is equivalent to wrapping a call to .style_classes= inside
|
257
|
+
# UIView.beginAnimations.
|
258
|
+
#
|
259
|
+
# @param Symbol the new stylename
|
260
|
+
# @param Options the options for the animation (may include the
|
261
|
+
# duration and the curve)
|
262
|
+
#
|
263
|
+
def animate_to_styles(style_classes, options={})
|
264
|
+
return if self.style_classes == style_classes
|
265
|
+
|
266
|
+
teacup_animation(options) do
|
267
|
+
self.style_classes = style_classes
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
# Animate a change to new styles
|
272
|
+
#
|
273
|
+
# This is equivalent to wrapping a call to .style() inside
|
274
|
+
# UIView.beginAnimations.
|
275
|
+
#
|
276
|
+
# @param Hash the new styles and options for the animation
|
277
|
+
#
|
278
|
+
def animate_to_style(style)
|
279
|
+
teacup_animation(options) do
|
280
|
+
self.style(style)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
# Apply style properties to this element.
|
285
|
+
#
|
286
|
+
# Takes a hash of properties such as may have been read from a stylesheet
|
287
|
+
# or passed as parameters to {Teacup::Layout#layout}, and applies them to
|
288
|
+
# the element.
|
289
|
+
#
|
290
|
+
# Does a little bit of magic (that may be split out as 'sugarcube') to
|
291
|
+
# make properties work as you'd expect.
|
292
|
+
#
|
293
|
+
# If you try and assign something in properties that is not supported,
|
294
|
+
# a warning message will be emitted.
|
295
|
+
#
|
296
|
+
# @param Hash the properties to set.
|
297
|
+
def style(properties)
|
298
|
+
if properties.key?(:constraints)
|
299
|
+
add_uniq_constraints(properties.delete(:constraints))
|
300
|
+
end
|
301
|
+
|
302
|
+
apply_style_properties(properties)
|
303
|
+
end
|
304
|
+
|
305
|
+
def apply_style_properties(properties)
|
306
|
+
Teacup.apply_hash self, properties
|
307
|
+
end
|
308
|
+
|
309
|
+
def add_uniq_constraints(constraint)
|
310
|
+
@teacup_constraints ||= {}
|
311
|
+
|
312
|
+
if constraint.is_a? Array
|
313
|
+
constraint.each do |constraint|
|
314
|
+
add_uniq_constraints(constraint)
|
315
|
+
end
|
316
|
+
elsif constraint.is_a? Hash
|
317
|
+
constraint.each do |sym, relative_to|
|
318
|
+
@teacup_constraints[sym] = relative_to
|
319
|
+
end
|
320
|
+
elsif constraint.is_a?(Teacup::Constraint) || constraint.is_a?(Symbol)
|
321
|
+
@teacup_constraints[constraint] = true
|
322
|
+
else
|
323
|
+
raise "Unsupported constraint: #{constraint.inspect}"
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
|
328
|
+
end
|
329
|
+
end
|
data/lib/teacup/version.rb
CHANGED
@@ -0,0 +1,96 @@
|
|
1
|
+
module Teacup
|
2
|
+
# An interface to style views using the UIAppearance protocol.
|
3
|
+
#
|
4
|
+
# Work similarly as the Stylesheet class.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
#
|
8
|
+
# Teacup::Appearance.new do
|
9
|
+
#
|
10
|
+
# style UINavigationBar,
|
11
|
+
# tintColor: UIColor.colorWithRed(0.886, green:0.635, blue:0, alpha: 1)
|
12
|
+
#
|
13
|
+
# end
|
14
|
+
class Appearance < Stylesheet
|
15
|
+
TeacupAppearanceApplyNotification = 'TeacupAppearanceApplyNotification'
|
16
|
+
|
17
|
+
def self.apply
|
18
|
+
NSNotificationCenter.defaultCenter.postNotificationName(TeacupAppearanceApplyNotification, object:nil)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Contains a list of styles associated with "containers". These do not get
|
22
|
+
# merged like the usual `style` declarations.
|
23
|
+
def when_contained_in
|
24
|
+
@when_contained_in ||= []
|
25
|
+
end
|
26
|
+
|
27
|
+
# disable the stylesheet 'name' parameter, and assign the "super secret"
|
28
|
+
# stylesheet name
|
29
|
+
def initialize(&block)
|
30
|
+
NSNotificationCenter.defaultCenter.addObserver(self,
|
31
|
+
selector: :'apply_appearance:',
|
32
|
+
name: UIApplicationDidFinishLaunchingNotification,
|
33
|
+
object: nil)
|
34
|
+
NSNotificationCenter.defaultCenter.addObserver(self,
|
35
|
+
selector: :'apply_appearance:',
|
36
|
+
name: TeacupAppearanceApplyNotification,
|
37
|
+
object: nil)
|
38
|
+
|
39
|
+
super(&block)
|
40
|
+
end
|
41
|
+
|
42
|
+
def exclude_instance_vars
|
43
|
+
@exclude_instance_vars ||= super + [:@when_contained_in]
|
44
|
+
end
|
45
|
+
|
46
|
+
# Styles that have the `when_contained_in` setting need to be kept separate
|
47
|
+
def style(*queries)
|
48
|
+
# do not modify queries, it gets passed to `super`
|
49
|
+
if queries[-1].is_a? Hash
|
50
|
+
properties = queries[-1]
|
51
|
+
else
|
52
|
+
# empty style declarations are allowed, but accomplish nothing.
|
53
|
+
return
|
54
|
+
end
|
55
|
+
|
56
|
+
if properties.include?(:when_contained_in)
|
57
|
+
# okay NOW modify queries
|
58
|
+
queries.pop
|
59
|
+
queries.each do |stylename|
|
60
|
+
style = Style.new
|
61
|
+
style.stylename = stylename
|
62
|
+
style.stylesheet = self
|
63
|
+
style.merge!(properties)
|
64
|
+
|
65
|
+
when_contained_in << [stylename, style]
|
66
|
+
end
|
67
|
+
else
|
68
|
+
super
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# This block is only run once, and the properties object from
|
73
|
+
# when_contained_in is a copy (via Teacup::Style.new), so we remove the
|
74
|
+
# when_contained_in property using `delete`
|
75
|
+
def apply_appearance(notification=nil)
|
76
|
+
return unless run_block
|
77
|
+
NSNotificationCenter.defaultCenter.removeObserver(self, name:UIApplicationDidFinishLaunchingNotification, object:nil)
|
78
|
+
NSNotificationCenter.defaultCenter.removeObserver(self, name:TeacupAppearanceApplyNotification, object:nil)
|
79
|
+
|
80
|
+
when_contained_in.each do |klass, properties|
|
81
|
+
contained_in = properties.delete(:when_contained_in)
|
82
|
+
contained_in = [contained_in] unless contained_in.is_a?(NSArray)
|
83
|
+
# make a copy and add nil to the end
|
84
|
+
contained_in += [nil]
|
85
|
+
appearance = klass.send(:'appearanceWhenContainedIn:', *contained_in)
|
86
|
+
Teacup.apply_hash appearance, properties.build, klass
|
87
|
+
end
|
88
|
+
styles.each do |klass, properties|
|
89
|
+
appearance = klass.appearance
|
90
|
+
Teacup.apply_hash appearance, properties.build, klass
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|