Class: RMX
- Inherits:
-
Object
- Object
- RMX
- Defined in:
- lib/motion/ui.rb,
lib/motion/env.rb,
lib/motion/base.rb,
lib/motion/util.rb,
lib/rmx/version.rb,
lib/motion/events.rb,
lib/motion/layout.rb,
lib/motion/accessors.rb
Defined Under Namespace
Classes: Layout
Constant Summary collapse
- DEBUG_LONGTASK =
Env['rmx_debug_longtask'] == '1'
- DEBUG_DEALLOC =
Env['rmx_debug_dealloc'] == '1'
- DEBUG_EVENTS =
Env['rmx_debug_events'] == '1'
- DEBUG_QUEUES =
Env['rmx_debug_queues'] == '1'
- DEBUG =
Env['rmx_debug'] == '1'
- VERSION =
"0.6.2"
- CREATE_EVENT_PROXY =
proc do RMXEventsFromProxy.new end
- CREATE_WEAK_HOLDER =
proc do RMXWeakHolder.new end
Class Method Summary collapse
- .app ⇒ Object
-
.assert_main_thread! ⇒ Object
Raises an exception when called from a thread other than the main thread.
-
.block_on_main_q(block, *args) ⇒ Object
call the block immediately if called on the main thread with the given args, otherwise call it async on the main queue.
- .currentKeyboardHeight ⇒ Object
-
.inline_or_on_main_q(&block) ⇒ Object
call the block immediately if called on the main thread, otherwise call it async on the main queue.
- .ios_version ⇒ Object
- .keyboardWillChangeFrame(notification) ⇒ Object
- .processKeyboardWillChange ⇒ Object
- .safe_block(block) ⇒ Object
- .screen_pixel ⇒ Object
Instance Method Summary collapse
- #_events_from_proxy ⇒ Object
- #debounce(unique_id, opts = {}, &block) ⇒ Object
- #debounce_runloop(unique_id, run_immediately = false, &block) ⇒ Object
- #debounce_seconds(seconds, unique_id, run_immediately = false, &block) ⇒ Object
- #events_from_proxy ⇒ Object
-
#initialize(_object) ⇒ RMX
constructor
A new instance of RMX.
-
#ivar(*args, &block) ⇒ Object
Shortcut to instance_variable_get and instance_variable_get: 1 arg for instance_variable_get 1 arg and block for instance_variable_get || instance_variable_set 2 args for instance_variable_set.
- #nil_instance_variables! ⇒ Object
- #now_and_on(event, opts = {}, &block) ⇒ Object
-
#off(event = nil, execution_block = nil) ⇒ Object
RMX(@model).off(:fire, &block) # remove :fire for specific handler RMX(@model).off(:fire) # remove all :fire in all knowns contexts RMX(@model).off # remove all events in all known contexts.
-
#on(event, opts = {}, &block) ⇒ Object
register a callback when an event is triggered on this object.
-
#once(event, opts = {}, &block) ⇒ Object
register a callback when an event is triggered on this object and remove it after it fires once.
- #own_methods ⇒ Object
- #require_queue!(queue, file, line) ⇒ Object
-
#trigger(event, *values) ⇒ Object
trigger an event with value on this object.
- #unsafe_unretained_object ⇒ Object
-
#weak_attr_accessor(*attrs) ⇒ Object
creates an
attr_accessor
like behavior, but the object is stored within an NSHashTable.weakObjectsHashTable and retrieved from the NSHashTable on demand.
Constructor Details
#initialize(_object) ⇒ RMX
Returns a new instance of RMX.
3 4 5 6 |
# File 'lib/motion/base.rb', line 3 def initialize(_object) @unsafe_unretained_object_holder = RMXUnsafeUnretainedHolder.new(_object) self end |
Class Method Details
.app ⇒ Object
3 4 5 |
# File 'lib/motion/ui.rb', line 3 def self.app UIApplication.sharedApplication end |
.assert_main_thread! ⇒ Object
Raises an exception when called from a thread other than the main thread. Good for development and experimenting.
15 16 17 |
# File 'lib/motion/util.rb', line 15 def self.assert_main_thread! raise "Expected main thread. #{Dispatch::Queue.current.description}" unless NSThread.currentThread.isMainThread end |
.block_on_main_q(block, *args) ⇒ Object
call the block immediately if called on the main thread with the given args, otherwise call it async on the main queue. silently ignores nil blocks to avoid if !block.nil? checks, useful for async callbacks that optionally take a callback
35 36 37 38 39 40 41 |
# File 'lib/motion/util.rb', line 35 def self.block_on_main_q(block, *args) unless block.nil? inline_or_on_main_q do block.call(*args) end end end |
.currentKeyboardHeight ⇒ Object
46 47 48 |
# File 'lib/motion/ui.rb', line 46 def self.currentKeyboardHeight @currentKeyboardHeight || 0 end |
.inline_or_on_main_q(&block) ⇒ Object
call the block immediately if called on the main thread, otherwise call it async on the main queue
21 22 23 24 25 26 27 28 29 |
# File 'lib/motion/util.rb', line 21 def self.inline_or_on_main_q(&block) if NSThread.currentThread.isMainThread block.call else Dispatch::Queue.main do block.call end end end |
.ios_version ⇒ Object
7 8 9 |
# File 'lib/motion/ui.rb', line 7 def self.ios_version @ios_version ||= UIDevice.currentDevice.systemVersion.split(".").take(2).join(".").to_f end |
.keyboardWillChangeFrame(notification) ⇒ Object
15 16 17 18 |
# File 'lib/motion/ui.rb', line 15 def self.keyboardWillChangeFrame(notification) @keyboardWillChangeFrameNotification = notification processKeyboardWillChange end |
.processKeyboardWillChange ⇒ Object
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/motion/ui.rb', line 20 def self.processKeyboardWillChange return unless notification = @keyboardWillChangeFrameNotification info = notification.userInfo keyboardFrame = info.objectForKey(UIKeyboardFrameEndUserInfoKey).CGRectValue bounds = UIScreen.mainScreen.bounds animationDuration = info.objectForKey(UIKeyboardAnimationDurationUserInfoKey).doubleValue # below the screen # above the screen # left of the screen # right of the screen currentKeyboardHeight = if keyboardFrame.origin.y >= bounds.size.height || keyboardFrame.origin.y <= bounds.origin.y - keyboardFrame.size.height || keyboardFrame.origin.x <= bounds.origin.x - keyboardFrame.size.width || keyboardFrame.origin.x >= bounds.size.width 0 else keyboardFrame.size.height end # p "================>" if currentKeyboardHeight != @currentKeyboardHeight @currentKeyboardHeight = currentKeyboardHeight # p "currentKeyboardHeight", currentKeyboardHeight # p "keyboardFrame", keyboardFrame # p "UIScreen.mainScreen.bounds", UIScreen.mainScreen.bounds NSNotificationCenter.defaultCenter.postNotificationName("rmxKeyboardChanged", object:nil, userInfo:{ :height => currentKeyboardHeight, :animationDuration => animationDuration }) end @keyboardWillChangeFrameNotification = nil end |
.safe_block(block) ⇒ Object
3 4 5 6 7 8 9 10 11 |
# File 'lib/motion/util.rb', line 3 def self.safe_block(block) weak_block_owner_holder = RMXWeakHolder.new(block.owner) block.weak! proc do |*args| if wbo = weak_block_owner_holder.value block.call(*args) end end end |
.screen_pixel ⇒ Object
11 12 13 |
# File 'lib/motion/ui.rb', line 11 def self.screen_pixel 1.0 / UIScreen.mainScreen.scale end |
Instance Method Details
#_events_from_proxy ⇒ Object
15 16 17 18 19 20 21 |
# File 'lib/motion/events.rb', line 15 def _events_from_proxy res = nil RMXEventsFromProxy::QUEUE.sync do res = ivar(:_rmx_events_from_proxy) end res end |
#debounce(unique_id, opts = {}, &block) ⇒ Object
94 95 96 97 98 99 100 |
# File 'lib/motion/util.rb', line 94 def debounce(unique_id, opts={}, &block) if (seconds = opts[:seconds]) && seconds > 0 debounce_seconds(seconds, unique_id, opts[:now], &block) else debounce_runloop(unique_id, opts[:now], &block) end end |
#debounce_runloop(unique_id, run_immediately = false, &block) ⇒ Object
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/motion/util.rb', line 102 def debounce_runloop(unique_id, run_immediately=false, &block) if object = unsafe_unretained_object lookup = Thread.current["rmx_debounce_runloop"] ||= {} key = [ object, unique_id ] lookup[key] ||= begin block.call if run_immediately CFRunLoopPerformBlock( CFRunLoopGetCurrent(), KCFRunLoopDefaultMode, lambda do lookup.delete(key) block.call end ) true end nil end end |
#debounce_seconds(seconds, unique_id, run_immediately = false, &block) ⇒ Object
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/motion/util.rb', line 122 def debounce_seconds(seconds, unique_id, run_immediately=false, &block) if object = unsafe_unretained_object lookup = Thread.current["rmx_debounce_seconds"] ||= {} key = [ object, unique_id ] lookup[key] ||= begin block.call if run_immediately units = CFGregorianUnits.new units.seconds = seconds CFRunLoopAddTimer( CFRunLoopGetCurrent(), CFRunLoopTimerCreateWithHandler( KCFAllocatorDefault, CFAbsoluteTimeAddGregorianUnits( CFAbsoluteTimeGetCurrent(), nil, units ), 0, 0, 0, lambda do |timer| lookup.delete(key) block.call end ), KCFRunLoopDefaultMode ) true end nil end end |
#events_from_proxy ⇒ Object
7 8 9 10 11 12 13 |
# File 'lib/motion/events.rb', line 7 def events_from_proxy res = nil RMXEventsFromProxy::QUEUE.sync do res = ivar(:_rmx_events_from_proxy, &CREATE_EVENT_PROXY) end res end |
#ivar(*args, &block) ⇒ Object
Shortcut to instance_variable_get and instance_variable_get: 1 arg for instance_variable_get 1 arg and block for instance_variable_get || instance_variable_set 2 args for instance_variable_set
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/motion/util.rb', line 59 def ivar(*args, &block) if object = unsafe_unretained_object key = args[0] val = nil if args.size == 1 if block val = object.instance_variable_get("@#{key}") if val.nil? val = block.call object.instance_variable_set("@#{key}", val) val end else val = object.instance_variable_get("@#{key}") end elsif args.size == 2 val = args[1] object.instance_variable_set("@#{key}", val) else raise "RMX#ivar called with invalid arguments: #{args.inspect}" end val end end |
#nil_instance_variables! ⇒ Object
84 85 86 87 88 89 90 91 92 |
# File 'lib/motion/util.rb', line 84 def nil_instance_variables! if object = unsafe_unretained_object ivars = [] + object.instance_variables while ivar = ivars.pop object.instance_variable_set(ivar, nil) end true end end |
#now_and_on(event, opts = {}, &block) ⇒ Object
34 35 36 |
# File 'lib/motion/events.rb', line 34 def now_and_on(event, opts={}, &block) events_from_proxy.now_and_on(event, opts.dup, &block) end |
#off(event = nil, execution_block = nil) ⇒ Object
RMX(@model).off(:fire, &block) # remove :fire for specific handler RMX(@model).off(:fire) # remove all :fire in all knowns contexts RMX(@model).off # remove all events in all known contexts
48 49 50 51 52 |
# File 'lib/motion/events.rb', line 48 def off(event=nil, execution_block=nil) if proxy = _events_from_proxy proxy.off(event, execution_block) end end |
#on(event, opts = {}, &block) ⇒ Object
register a callback when an event is triggered on this object.
24 25 26 27 28 29 30 31 32 |
# File 'lib/motion/events.rb', line 24 def on(event, opts={}, &block) if object = unsafe_unretained_object _opts = opts.dup if _opts[:strong] _opts[:strong] = [ object, block.owner ] end events_from_proxy.on(event, _opts, &block) end end |
#once(event, opts = {}, &block) ⇒ Object
register a callback when an event is triggered on this object and remove it after it fires once
39 40 41 42 43 |
# File 'lib/motion/events.rb', line 39 def once(event, opts={}, &block) _opts = opts.dup _opts[:limit] = 1 on(event, _opts, &block) end |
#own_methods ⇒ Object
49 50 51 52 53 |
# File 'lib/motion/util.rb', line 49 def own_methods if object = unsafe_unretained_object (object.methods - (object.superclass.methods)).sort end end |
#require_queue!(queue, file, line) ⇒ Object
43 44 45 46 47 |
# File 'lib/motion/util.rb', line 43 def require_queue!(queue, file, line) unless Dispatch::Queue.current.description == queue.description raise "WRONG QUEUE: was: #{Dispatch::Queue.current.description}, expected: #{queue.description}. #{@object.value.inspect} #{file}:#{line}, #{caller.inspect}" end end |
#trigger(event, *values) ⇒ Object
trigger an event with value on this object
55 56 57 58 59 |
# File 'lib/motion/events.rb', line 55 def trigger(event, *values) if proxy = _events_from_proxy proxy.trigger(event, *values) end end |
#unsafe_unretained_object ⇒ Object
8 9 10 |
# File 'lib/motion/base.rb', line 8 def unsafe_unretained_object @unsafe_unretained_object_holder.value end |
#weak_attr_accessor(*attrs) ⇒ Object
creates an attr_accessor
like behavior, but the object is stored within an NSHashTable.weakObjectsHashTable and retrieved from the NSHashTable on demand. does not conform to KVO like a normal attr_accessor.
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# File 'lib/motion/accessors.rb', line 11 def weak_attr_accessor(*attrs) if object = unsafe_unretained_object attrs.each do |attr| attr_holder = "#{attr}_holder" object.send(:define_method, attr) do if holder = RMX.new(self).ivar(attr_holder) holder.value end end object.send(:define_method, "#{attr}=") do |val| holder = RMX.new(self).ivar(attr_holder, &CREATE_WEAK_HOLDER) holder.value = val val end end end end |