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.
- 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
|