Class: MotionKit::TreeLayout
- Inherits:
-
BaseLayout
- Object
- BaseLayout
- MotionKit::TreeLayout
- Defined in:
- lib/motion-kit/helpers/tree_layout.rb
Overview
A sensible parent class for any Tree-like layout class. Platform agnostic. Any platform-specific tasks are offloaded to child elements (add_child, remove_child). You could use a TreeLayout subclass to construct a hierarchy representing a family tree, for instance. But that would be a silly use of MotionKit.
Direct Known Subclasses
Instance Attribute Summary
Attributes inherited from BaseLayout
Class Method Summary collapse
-
.view(*names) ⇒ Object
This is an ‘attr_reader`-like method that also calls `build_view` if the assigned to ivars in your `layout` method.
Instance Method Summary collapse
-
#add(element, element_id = nil, &block) ⇒ Object
Instantiates a view via ‘create` and adds the view to the current target.
-
#all(element_id) ⇒ Object
Returns all the elements with a given element_id.
-
#all_views(element_id) ⇒ Object
Just like ‘all`, but if `all` returns a Layout, this method returns the layout’s view.
- #always(&block) ⇒ Object
-
#build ⇒ Object
Builds the layout and then returns self for chaining.
-
#built? ⇒ Boolean
(also: #build?)
Checks if the layout has been built yet or not.
- #child_layouts ⇒ Object
-
#create(element, element_id = nil, &block) ⇒ Object
instantiates a view, possibly running a ‘layout block’ to add child views.
- #create_default_root_context ⇒ Object
- #first(element_id) ⇒ Object
-
#forget(element_id) ⇒ Object
Removes a view from the list of elements this layout is “tracking”, but leaves it in the view hierarchy.
-
#get(element_id) ⇒ Object
Retrieves a view by its element id.
-
#get_view(element_id) ⇒ Object
Just like ‘get`, but if `get` returns a Layout, this method returns the layout’s view.
- #initial(&block) ⇒ Object
- #initial? ⇒ Boolean
-
#initialize(args = {}) ⇒ TreeLayout
constructor
A new instance of TreeLayout.
-
#last(element_id) ⇒ Object
Retrieves a view by its element id.
-
#last_view(element_id) ⇒ Object
Just like ‘last`, but if `last` returns a Layout, this method returns the layout’s view.
- #name_element(element, element_id) ⇒ Object
-
#nearest(element_id, from: from_view) ⇒ Object
This searches for the “nearest” view with a given id.
-
#next(element_id, from: from_view) ⇒ Object
Search for a sibling: the next sibling that has the given id.
-
#nth(element_id, index) ⇒ Object
Returns the ‘N’th element with a given element_id, where “‘N’th” is passed in as ‘index`.
-
#nth_view(element_id, index) ⇒ Object
Just like ‘nth`, but if `nth` returns a Layout, this method returns the layout’s view.
-
#prev(element_id, from: from_view) ⇒ Object
Search for a sibling: the previous sibling that has the given id.
-
#reapply(&block) ⇒ Object
Blocks passed to ‘reapply` are only run when `reapply!` is called.
-
#reapply! ⇒ Object
Calls the style method of all objects in the view hierarchy that are part of this layout.
- #reapply? ⇒ Boolean
-
#reapply_blocks ⇒ Object
Only intended for private use.
-
#remove(element_id) ⇒ Object
Removes a view (or several with the same name) from the hierarchy and forgets it entirely.
-
#root(element, element_id = nil, &block) ⇒ Object
Assign a view to act as the ‘root’ view for this layout.
-
#run_reapply_blocks ⇒ Object
Only intended for private use.
-
#view ⇒ Object
The main view.
Methods inherited from BaseLayout
#add_deferred_block, #apply, #apply_with_context, #apply_with_target, #context, #deferred, #deferred_blocks, delegate_method_fix, #has_context?, #ipad?, #iphone35?, #iphone4?, #iphone?, #is_parent_layout?, #method_missing, #objc_version, #orientation?, #parent_layout, #retina?, #ruby_version, #run_deferred, #set_parent_layout, #target, #v
Methods included from BaseLayoutClassMethods
#layout_for, #memoize, #target_klasses, #targets
Constructor Details
#initialize(args = {}) ⇒ TreeLayout
Returns a new instance of TreeLayout.
60 61 62 63 64 65 |
# File 'lib/motion-kit/helpers/tree_layout.rb', line 60 def initialize(args={}) super @child_layouts = [] @reapply_blocks = [] @elements = {} end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method in the class MotionKit::BaseLayout
Class Method Details
.view(*names) ⇒ Object
This is an ‘attr_reader`-like method that also calls `build_view` if the assigned to ivars in your `layout` method.
You can also set multiple views in a single line.
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/motion-kit/helpers/tree_layout.rb', line 38 def view(*names) names.each do |name| ivar_name = "@#{name}" define_method(name) do unless instance_variable_get(ivar_name) view = self.get_view(name) unless view build_view unless @view view = instance_variable_get(ivar_name) || self.get_view(name) end self.send("#{name}=", view) return view end return instance_variable_get(ivar_name) end # KVO compliance attr_writer name end end |
Instance Method Details
#add(element, element_id = nil, &block) ⇒ Object
Instantiates a view via ‘create` and adds the view to the current target. If there is no context, a default root view can be created if that has been enabled (e.g. within the `layout` method). The block is run in the context of the new view.
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
# File 'lib/motion-kit/helpers/tree_layout.rb', line 218 def add(element, element_id=nil, &block) # make sure we have a target - raises NoContextError if none exists self.target unless @context create_default_root_context end # We want to be sure that the element has a supeview or superlayer before # the style method is called. element = initialize_element(element, element_id) self.apply(:add_child, element) style_and_context(element, element_id, &block) element end |
#all(element_id) ⇒ Object
Returns all the elements with a given element_id
281 282 283 284 285 286 |
# File 'lib/motion-kit/helpers/tree_layout.rb', line 281 def all(element_id) unless is_parent_layout? return parent_layout.all(element_id) end @elements[element_id] || [] end |
#all_views(element_id) ⇒ Object
Just like ‘all`, but if `all` returns a Layout, this method returns the layout’s view.
290 291 292 293 294 295 296 |
# File 'lib/motion-kit/helpers/tree_layout.rb', line 290 def all_views(element_id) element = all(element_id) if element.is_a?(Layout) element = element.view end element end |
#always(&block) ⇒ Object
187 188 189 190 191 192 193 194 195 196 |
# File 'lib/motion-kit/helpers/tree_layout.rb', line 187 def always(&block) raise ArgumentError.new('Block required') unless block if initial? yield end reapply(&block) return self end |
#build ⇒ Object
Builds the layout and then returns self for chaining.
76 77 78 79 |
# File 'lib/motion-kit/helpers/tree_layout.rb', line 76 def build view self end |
#built? ⇒ Boolean Also known as: build?
Checks if the layout has been built yet or not.
82 83 84 |
# File 'lib/motion-kit/helpers/tree_layout.rb', line 82 def built? !@view.nil? end |
#child_layouts ⇒ Object
235 236 237 |
# File 'lib/motion-kit/helpers/tree_layout.rb', line 235 def child_layouts @child_layouts end |
#create(element, element_id = nil, &block) ⇒ Object
instantiates a view, possibly running a ‘layout block’ to add child views.
129 130 131 132 133 134 |
# File 'lib/motion-kit/helpers/tree_layout.rb', line 129 def create(element, element_id=nil, &block) element = initialize_element(element, element_id) style_and_context(element, element_id, &block) element end |
#create_default_root_context ⇒ Object
449 450 451 452 453 454 455 456 457 458 459 |
# File 'lib/motion-kit/helpers/tree_layout.rb', line 449 def create_default_root_context if @assign_root # Originally I thought default_root should be `apply`ied like other # view-related methods, but actually this method *only* gets called # from within the `layout` method, and so should already be inside the # correct Layout subclass. @context = root(preset_root || default_root) else raise NoContextError.new("No top level view specified (missing outer 'create' method?)") end end |
#first(element_id) ⇒ Object
248 |
# File 'lib/motion-kit/helpers/tree_layout.rb', line 248 def first(element_id) ; get(element_id) ; end |
#forget(element_id) ⇒ Object
Removes a view from the list of elements this layout is “tracking”, but leaves it in the view hierarchy. Returns the views that were removed.
437 438 439 440 441 442 443 444 445 446 447 |
# File 'lib/motion-kit/helpers/tree_layout.rb', line 437 def forget(element_id) unless is_parent_layout? return parent_layout.remove(element_id) end removed = nil context(self.view) do removed = all(element_id) @elements[element_id] = nil end removed end |
#get(element_id) ⇒ Object
Retrieves a view by its element id. This will return the first view with this element_id in the tree, where first means the first object that was added with that name.
242 243 244 245 246 247 |
# File 'lib/motion-kit/helpers/tree_layout.rb', line 242 def get(element_id) unless is_parent_layout? return parent_layout.get(element_id) end @elements[element_id] && @elements[element_id].first end |
#get_view(element_id) ⇒ Object
Just like ‘get`, but if `get` returns a Layout, this method returns the layout’s view.
252 253 254 255 256 257 258 |
# File 'lib/motion-kit/helpers/tree_layout.rb', line 252 def get_view(element_id) element = get(element_id) if element.is_a?(Layout) element = element.view end element end |
#initial(&block) ⇒ Object
198 199 200 201 202 203 204 205 206 |
# File 'lib/motion-kit/helpers/tree_layout.rb', line 198 def initial(&block) raise ArgumentError.new('Block required') unless block puts('this method is no longer necessary! all code that *isn\'t in a `reapply` block is now only applied during initial setup.') if initial? yield end return self end |
#initial? ⇒ Boolean
183 184 185 |
# File 'lib/motion-kit/helpers/tree_layout.rb', line 183 def initial? @layout_state == :initial end |
#last(element_id) ⇒ Object
Retrieves a view by its element id. This will return the last view with this element_id in the tree, where last means the last object that was added with that name.
263 264 265 266 267 268 |
# File 'lib/motion-kit/helpers/tree_layout.rb', line 263 def last(element_id) unless is_parent_layout? return parent_layout.last(element_id) end @elements[element_id] && @elements[element_id].last end |
#last_view(element_id) ⇒ Object
Just like ‘last`, but if `last` returns a Layout, this method returns the layout’s view.
272 273 274 275 276 277 278 |
# File 'lib/motion-kit/helpers/tree_layout.rb', line 272 def last_view(element_id) element = last(element_id) if element.is_a?(Layout) element = element.view end element end |
#name_element(element, element_id) ⇒ Object
208 209 210 211 212 |
# File 'lib/motion-kit/helpers/tree_layout.rb', line 208 def name_element(element, element_id) element.motion_kit_id = element_id @elements[element_id] ||= [] @elements[element_id] << element end |
#nearest(element_id, from: from_view) ⇒ Object
This searches for the “nearest” view with a given id. First, all child views are checked. Then the search goes up to the parent view, and its child views are checked. This means any view that is in the parent view’s hierarchy is considered closer than a view in a grandparent’s hierarchy. This is a “depth-first” search, so any subview that contains a view with the element id
A–B–C–D* Starting at D, E is closer than F, because D&E are siblings.
\ \ \-E But F, G and H are closer than A or I, because they share a
\ \-F--G closer *parent* (B). The logic is, "B" is a container, and
\-I \-H all views in that container are in a closer family.
395 396 397 |
# File 'lib/motion-kit/helpers/tree_layout.rb', line 395 def nearest(element_id) nearest(element_id, from: target) end |
#next(element_id, from: from_view) ⇒ Object
Search for a sibling: the next sibling that has the given id
315 316 317 |
# File 'lib/motion-kit/helpers/tree_layout.rb', line 315 def next(element_id) self.next(element_id, from: target) end |
#nth(element_id, index) ⇒ Object
Returns the ‘N’th element with a given element_id, where “‘N’th” is passed in as ‘index`
300 301 302 |
# File 'lib/motion-kit/helpers/tree_layout.rb', line 300 def nth(element_id, index) self.all(element_id)[index] end |
#nth_view(element_id, index) ⇒ Object
Just like ‘nth`, but if `nth` returns a Layout, this method returns the layout’s view.
306 307 308 309 310 311 312 |
# File 'lib/motion-kit/helpers/tree_layout.rb', line 306 def nth_view(element_id, index) element = nth(element_id) if element.is_a?(Layout) element = element.view end element end |
#prev(element_id, from: from_view) ⇒ Object
Search for a sibling: the previous sibling that has the given id
350 351 352 |
# File 'lib/motion-kit/helpers/tree_layout.rb', line 350 def prev(element_id) prev(element_id, from: target) end |
#reapply(&block) ⇒ Object
Blocks passed to ‘reapply` are only run when `reapply!` is called.
163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/motion-kit/helpers/tree_layout.rb', line 163 def reapply(&block) raise ArgumentError.new('Block required') unless block raise InvalidDeferredError.new('reapply must be run inside of a context') unless @context if reapply? yield end block = block.weak! parent_layout.reapply_blocks << [@context, block] return self end |
#reapply! ⇒ Object
Calls the style method of all objects in the view hierarchy that are part of this layout. The views in a child layout are not styled, but those layouts will receive a ‘reapply!` message if no root is specified.
139 140 141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/motion-kit/helpers/tree_layout.rb', line 139 def reapply! root ||= self.view @layout_state = :reapply run_reapply_blocks @child_layouts.each do |child_layout| child_layout.reapply! end @layout_state = :initial return self end |
#reapply? ⇒ Boolean
153 154 155 |
# File 'lib/motion-kit/helpers/tree_layout.rb', line 153 def reapply? @layout_state == :reapply end |
#reapply_blocks ⇒ Object
Only intended for private use
158 159 160 |
# File 'lib/motion-kit/helpers/tree_layout.rb', line 158 def reapply_blocks @reapply_blocks ||= [] end |
#remove(element_id) ⇒ Object
Removes a view (or several with the same name) from the hierarchy and forgets it entirely. Returns the views that were removed.
422 423 424 425 426 427 428 429 430 431 432 433 |
# File 'lib/motion-kit/helpers/tree_layout.rb', line 422 def remove(element_id) unless is_parent_layout? return parent_layout.remove(element_id) end removed = forget(element_id) context(self.view) do removed.each do |element| self.apply(:remove_child, element) end end removed end |
#root(element, element_id = nil, &block) ⇒ Object
Assign a view to act as the ‘root’ view for this layout. This method can only be called once, and must be called before ‘add` is called for the first time (otherwise `add` will create a default root view). This method must be called from inside `layout`, otherwise you should just use `create`.
You can also call this method with just an element_id, and the default root view will be created.
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/motion-kit/helpers/tree_layout.rb', line 95 def root(element, element_id=nil, &block) if @view raise ContextConflictError.new("Already created the root view") end unless @assign_root raise InvalidRootError.new("You should only create a 'root' view from inside the 'layout' method (use 'create' elsewhere)") end @assign_root = false # this method can be called with just a symbol, to assign the root element_id if element.is_a?(Symbol) element_id = element # See note below about why we don't need to `apply(:default_root)` element = preset_root || default_root elsif preset_root && preset_root != element # You're trying to make two roots, one at initialization # and one in your layout itself. raise ContextConflictError.new("Already created the root view") end @view = initialize_element(element, element_id) if block if @context raise ContextConflictError.new("Already in a context") end end style_and_context(@view, element_id, &block) return @view end |
#run_reapply_blocks ⇒ Object
Only intended for private use
177 178 179 180 181 |
# File 'lib/motion-kit/helpers/tree_layout.rb', line 177 def run_reapply_blocks self.reapply_blocks.each do |target, block| context(target, &block) end end |
#view ⇒ Object
The main view. This method builds the layout and returns the root view.
68 69 70 71 72 73 |
# File 'lib/motion-kit/helpers/tree_layout.rb', line 68 def view unless is_parent_layout? return parent_layout.view end @view ||= build_view end |