mirror of
https://github.com/zhigang1992/rmq.git
synced 2026-05-22 19:09:21 +08:00
327 lines
8.8 KiB
Ruby
327 lines
8.8 KiB
Ruby
module RubyMotionQuery
|
|
class RMQ
|
|
|
|
# Most everything uses filter to do its work. This is mostly used internally
|
|
# but you can use it too. Just pass a block that returns views, it will be
|
|
# called for every view that is *selected*
|
|
#
|
|
# @param return_array returns array not rmq: return_array: true
|
|
# @param uniq removes duplicate views: uniq: true
|
|
# @param limit limits the number of views *per selected view*: limit: true
|
|
#
|
|
# @return [RMQ]
|
|
def filter(opts = {}, &block)
|
|
out = []
|
|
limit = opts[:limit]
|
|
|
|
selected.each do |view|
|
|
results = yield(view)
|
|
unless RMQ.is_blank?(results)
|
|
out << results
|
|
break if limit && (out.length >= limit)
|
|
end
|
|
end
|
|
out.flatten!
|
|
out.uniq! if opts[:uniq]
|
|
|
|
if opts[:return_array]
|
|
out
|
|
else
|
|
RMQ.create_with_array_and_selectors(out, selectors, @context, self)
|
|
end
|
|
end
|
|
|
|
# @return [RMQ] All selected views
|
|
#
|
|
# @example
|
|
# rmq.all.log
|
|
def all
|
|
self.view_controller.rmq.find
|
|
end
|
|
|
|
# @return [RMQ] A new rmq instance reducing selected views to those that match selectors provided
|
|
#
|
|
# @param selectors your selector
|
|
#
|
|
# @example
|
|
# rmq(UIImage).and(:some_tag).attr(image: nil)
|
|
def and(*working_selectors)
|
|
return self unless working_selectors
|
|
normalize_selectors(working_selectors)
|
|
|
|
self.select do |view|
|
|
match(view, working_selectors)
|
|
end
|
|
end
|
|
|
|
# @return [RMQ] A new rmq instance removing selected views that match selectors provided
|
|
#
|
|
# @param selectors
|
|
#
|
|
# @example
|
|
# # Entire family of labels from siblings on down
|
|
# rmq(my_label).parent.find(UILabel).not(my_label).move(left: 10)
|
|
def not(*working_selectors)
|
|
return self unless working_selectors
|
|
normalize_selectors(working_selectors)
|
|
|
|
self.reject do |view|
|
|
match(view, working_selectors)
|
|
end
|
|
end
|
|
|
|
# @return [RMQ] A new rmq instance adding the context to the selected views
|
|
#
|
|
# @example
|
|
# rmq(my_view).children.and_self
|
|
def and_self
|
|
if self.parent_rmq
|
|
out = self.parent_rmq.selected.dup
|
|
out << selected
|
|
out.flatten!
|
|
RMQ.create_with_array_and_selectors(out, selectors, @context, self)
|
|
else
|
|
self
|
|
end
|
|
end
|
|
alias :add_self :and_self
|
|
|
|
# @return [RMQ] The parent rmq instance
|
|
#
|
|
# @example
|
|
# rmq(test_view).find(UIImageView).tag(:foo).end.find(UILabel).tag(:bar)
|
|
def end
|
|
self.parent_rmq || self
|
|
end
|
|
|
|
# @return [RMQ] rmq instance selecting the parent of the selected view(s)
|
|
#
|
|
# @example
|
|
# rmq(my_view).parent.find(:delete_button).toggle_enabled
|
|
def parent
|
|
closest(UIView)
|
|
end
|
|
alias :superview :parent
|
|
|
|
# @return [RMQ] Instance selecting the parents, grandparents, etc, all the way up the tree
|
|
# of the selected view(s)
|
|
#
|
|
# @param selectors
|
|
#
|
|
# @example
|
|
# rmq(my_view).parents.log
|
|
def parents(*working_selectors)
|
|
normalize_selectors(working_selectors)
|
|
|
|
filter(uniq: true) do |view|
|
|
superviews = all_superviews_for(view)
|
|
|
|
if RMQ.is_blank?(working_selectors)
|
|
superviews
|
|
else
|
|
superviews.inject([]) do |subview, out|
|
|
out << subview if match(subview, working_selectors)
|
|
out
|
|
end
|
|
end
|
|
end
|
|
end
|
|
alias :superviews :parents
|
|
|
|
# Get the descendants of each view in the current set of selected views, filtered by a selector(s)
|
|
#
|
|
# @return [RMQ] Instance selecting the children, grandchildren, etc of the selected view(s)
|
|
#
|
|
# @param selectors
|
|
#
|
|
# @example
|
|
# rmq(my_view).find(UIButton).show
|
|
def find(*working_selectors)
|
|
normalize_selectors(working_selectors)
|
|
|
|
filter(uniq: true) do |view|
|
|
subviews = all_subviews_for(view)
|
|
|
|
if RMQ.is_blank?(working_selectors)
|
|
subviews
|
|
else
|
|
subviews.inject([]) do |out, subview|
|
|
out << subview if match(subview, working_selectors)
|
|
out
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
# @return [RMQ] Instance selecting the children of the selected view(s)
|
|
#
|
|
# @param selectors
|
|
#
|
|
# @example
|
|
# rmq(my_view).children.show
|
|
def children(*working_selectors)
|
|
normalize_selectors(working_selectors)
|
|
|
|
filter do |view|
|
|
subviews = view.subviews
|
|
|
|
if RMQ.is_blank?(working_selectors)
|
|
subviews
|
|
else
|
|
subviews.inject([]) do |out, subview|
|
|
out << subview if match(subview, working_selectors)
|
|
out
|
|
end
|
|
end
|
|
end
|
|
end
|
|
alias :subviews :children
|
|
|
|
|
|
# @return [RMQ] Siblings of the selected view(s)
|
|
#
|
|
# @param selectors
|
|
#
|
|
# @example
|
|
# rmq(my_view).siblings.send(:poke)
|
|
def siblings(*working_selectors)
|
|
normalize_selectors(working_selectors)
|
|
|
|
self.parent.children.not(selected)
|
|
end
|
|
|
|
|
|
# @return [RMQ] Sibling above to the selected view(s)
|
|
#
|
|
# @param selectors
|
|
#
|
|
# @example
|
|
# rmq(my_view).next(UITextField).focus
|
|
def next(*working_selectors)
|
|
normalize_selectors(working_selectors)
|
|
|
|
filter do |view|
|
|
subs = view.superview.subviews
|
|
location = subs.index(view)
|
|
if location < subs.length - 1
|
|
subs[location + 1]
|
|
end
|
|
end
|
|
end
|
|
|
|
# @return [RMQ] Sibling below to the selected view(s)
|
|
#
|
|
# @param selectors
|
|
#
|
|
# @example
|
|
# rmq(my_view).prev(UITextField).focus
|
|
def prev(*working_selectors)
|
|
normalize_selectors(working_selectors)
|
|
|
|
filter do |view|
|
|
subs = view.superview.subviews
|
|
location = subs.index(view)
|
|
if location > 0
|
|
subs[location - 1]
|
|
end
|
|
end
|
|
end
|
|
|
|
# For each selected view, get the first view that matches the selector(s) by testing the view's parent and
|
|
# traversing up through its ancestors in the tree
|
|
#
|
|
# @return [RMQ] Instance selecting the first parent or grandparent or ancestor up the tree of the selected view(s)
|
|
#
|
|
# @param selectors
|
|
#
|
|
# @example
|
|
# rmq.closest(UIScrollView).get.setContentOffset([0,0])
|
|
def closest(*working_selectors)
|
|
normalize_selectors(working_selectors)
|
|
|
|
filter do |view|
|
|
closest_view(view, working_selectors)
|
|
end
|
|
end
|
|
|
|
# When you call rmq within a controller or view, a new instance of RMQ is created with the selectors you may or may
|
|
# not have supplied. That instance will determine what view_controller it should use for stuff like traversing,
|
|
# stylesheet, etc.
|
|
#
|
|
# * rmq method called in a controller: that controller is used
|
|
#
|
|
# * rmq method called in a view and that view is within the subview tree of a controller: that controller is used
|
|
#
|
|
#
|
|
# * rmq method called in a view and that view is NOT within any subview tree of any controller (common in a UITableViewCell
|
|
# for example). In this case it will use the view's controller or the "current controller". Generally
|
|
# that is what is desired, however there are some situations where another controller should be used. In that
|
|
# situation create your own rmq instance and assign the controller you'd rather use
|
|
#
|
|
# * if an rmq instance was created from another rmq instance, it will attempt to the parent_rmq's controller, if it exists
|
|
#
|
|
# @return [UIViewController] Controller of this rmq instance
|
|
#
|
|
# @example
|
|
# rmq(my_view).view_controller
|
|
def view_controller
|
|
@_view_controller ||= begin
|
|
if @context.is_a?(UIViewController)
|
|
@context
|
|
else # in view
|
|
if vc = RMQ.controller_for_view(@context)
|
|
vc
|
|
else
|
|
if self.parent_rmq && vc = self.parent_rmq.view_controller
|
|
vc
|
|
else
|
|
vc = RMQ.app.current_view_controller
|
|
@context.rmq_data.view_controller = vc if @context
|
|
vc
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
def view_controller=(value)
|
|
@_view_controller = WeakRef.new(value)
|
|
end
|
|
|
|
# @return [UIView] Root view of this rmq instance's controller
|
|
#
|
|
# @example
|
|
# rmq.root_view
|
|
def root_view
|
|
vc = self.view_controller
|
|
if RMQ.is_blank?(vc)
|
|
self.context_or_context_view
|
|
else
|
|
self.view_controller.view
|
|
end
|
|
end
|
|
|
|
# @return [UIWindow] Window of the root_view
|
|
#
|
|
# @example
|
|
# rmq.window
|
|
def window
|
|
self.root_view.window
|
|
end
|
|
|
|
protected
|
|
|
|
def closest_view(view, working_selectors)
|
|
if nr = view.nextResponder
|
|
if match(nr, working_selectors)
|
|
nr
|
|
else
|
|
closest_view(nr,working_selectors)
|
|
end
|
|
else
|
|
nil
|
|
end
|
|
end
|
|
|
|
end
|
|
end
|