cura 0.0.1

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.
Files changed (115) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +15 -0
  3. data/Gemfile.lock +122 -0
  4. data/LICENSE +20 -0
  5. data/README.md +159 -0
  6. data/Rakefile +42 -0
  7. data/cura.gemspec +23 -0
  8. data/examples/box_model/bin/box_model +11 -0
  9. data/examples/box_model/debug.log +0 -0
  10. data/examples/box_model/lib/box_model.rb +21 -0
  11. data/examples/box_model/lib/box_model/application.rb +24 -0
  12. data/examples/hello_world/bin/hello_world +10 -0
  13. data/examples/hello_world/lib/hello_world.rb +201 -0
  14. data/examples/mruby-examples/README.md +10 -0
  15. data/examples/mruby-examples/include/cura_examples.h +6 -0
  16. data/examples/mruby-examples/mrbgem.rake +14 -0
  17. data/examples/mruby-examples/src/gem_init.c +34 -0
  18. data/examples/mruby-examples/tools/hello_world/hello_world.c +6 -0
  19. data/examples/todo_list/app.log +9 -0
  20. data/examples/todo_list/bin/todo_list +26 -0
  21. data/examples/todo_list/data.db +0 -0
  22. data/examples/todo_list/debug.log +0 -0
  23. data/examples/todo_list/lib/todo_list.rb +28 -0
  24. data/examples/todo_list/lib/todo_list/application.rb +54 -0
  25. data/examples/todo_list/lib/todo_list/component/header.rb +27 -0
  26. data/examples/todo_list/lib/todo_list/component/list.rb +50 -0
  27. data/examples/todo_list/lib/todo_list/component/list_item.rb +28 -0
  28. data/examples/todo_list/lib/todo_list/component/list_items.rb +97 -0
  29. data/examples/todo_list/lib/todo_list/component/lists.rb +89 -0
  30. data/examples/todo_list/lib/todo_list/database.rb +50 -0
  31. data/examples/todo_list/lib/todo_list/model/list.rb +9 -0
  32. data/examples/todo_list/lib/todo_list/model/list_item.rb +9 -0
  33. data/examples/todo_list/profile.html +11354 -0
  34. data/lib/cura.rb +13 -0
  35. data/lib/cura/adapter.rb +67 -0
  36. data/lib/cura/application.rb +245 -0
  37. data/lib/cura/attributes/has_ancestry.rb +40 -0
  38. data/lib/cura/attributes/has_application.rb +31 -0
  39. data/lib/cura/attributes/has_attributes.rb +89 -0
  40. data/lib/cura/attributes/has_children.rb +113 -0
  41. data/lib/cura/attributes/has_colors.rb +66 -0
  42. data/lib/cura/attributes/has_coordinates.rb +49 -0
  43. data/lib/cura/attributes/has_dimensions.rb +63 -0
  44. data/lib/cura/attributes/has_events.rb +89 -0
  45. data/lib/cura/attributes/has_focusability.rb +37 -0
  46. data/lib/cura/attributes/has_initialize.rb +15 -0
  47. data/lib/cura/attributes/has_offsets.rb +82 -0
  48. data/lib/cura/attributes/has_orientation.rb +53 -0
  49. data/lib/cura/attributes/has_relative_coordinates.rb +39 -0
  50. data/lib/cura/attributes/has_root.rb +104 -0
  51. data/lib/cura/attributes/has_side_attributes.rb +95 -0
  52. data/lib/cura/attributes/has_windows.rb +70 -0
  53. data/lib/cura/borders.rb +16 -0
  54. data/lib/cura/color.rb +330 -0
  55. data/lib/cura/component/base.rb +180 -0
  56. data/lib/cura/component/button.rb +57 -0
  57. data/lib/cura/component/group.rb +77 -0
  58. data/lib/cura/component/label.rb +224 -0
  59. data/lib/cura/component/listbox.rb +152 -0
  60. data/lib/cura/component/pack.rb +144 -0
  61. data/lib/cura/component/scrollbar.rb +184 -0
  62. data/lib/cura/component/textbox.rb +118 -0
  63. data/lib/cura/cursor.rb +77 -0
  64. data/lib/cura/error/base.rb +10 -0
  65. data/lib/cura/error/invalid_adapter.rb +18 -0
  66. data/lib/cura/error/invalid_application.rb +18 -0
  67. data/lib/cura/error/invalid_color.rb +18 -0
  68. data/lib/cura/error/invalid_component.rb +18 -0
  69. data/lib/cura/error/invalid_middleware.rb +18 -0
  70. data/lib/cura/event.rb +38 -0
  71. data/lib/cura/event/base.rb +108 -0
  72. data/lib/cura/event/click.rb +14 -0
  73. data/lib/cura/event/dispatcher.rb +122 -0
  74. data/lib/cura/event/focus.rb +14 -0
  75. data/lib/cura/event/handler.rb +74 -0
  76. data/lib/cura/event/key_down.rb +68 -0
  77. data/lib/cura/event/middleware/aimer/base.rb +38 -0
  78. data/lib/cura/event/middleware/aimer/dispatcher_target.rb +24 -0
  79. data/lib/cura/event/middleware/aimer/mouse_focus.rb +48 -0
  80. data/lib/cura/event/middleware/aimer/target_option.rb +38 -0
  81. data/lib/cura/event/middleware/base.rb +21 -0
  82. data/lib/cura/event/middleware/dispatch.rb +20 -0
  83. data/lib/cura/event/middleware/translator/base.rb +37 -0
  84. data/lib/cura/event/middleware/translator/mouse_click.rb +44 -0
  85. data/lib/cura/event/mouse.rb +34 -0
  86. data/lib/cura/event/mouse_button.rb +103 -0
  87. data/lib/cura/event/mouse_wheel_down.rb +14 -0
  88. data/lib/cura/event/mouse_wheel_up.rb +14 -0
  89. data/lib/cura/event/resize.rb +17 -0
  90. data/lib/cura/event/selected.rb +14 -0
  91. data/lib/cura/event/unfocus.rb +14 -0
  92. data/lib/cura/focus_controller.rb +89 -0
  93. data/lib/cura/key.rb +313 -0
  94. data/lib/cura/margins.rb +16 -0
  95. data/lib/cura/offsets.rb +91 -0
  96. data/lib/cura/padding.rb +16 -0
  97. data/lib/cura/pencil.rb +29 -0
  98. data/lib/cura/version.rb +6 -0
  99. data/lib/cura/window.rb +85 -0
  100. data/spec/cura/attributes/has_ancestry_spec.rb +108 -0
  101. data/spec/cura/attributes/has_application_spec.rb +59 -0
  102. data/spec/cura/attributes/has_attributes_spec.rb +75 -0
  103. data/spec/cura/attributes/has_children_spec.rb +169 -0
  104. data/spec/cura/attributes/has_colors_spec.rb +20 -0
  105. data/spec/cura/attributes/has_coordinates_spec.rb +19 -0
  106. data/spec/cura/attributes/has_dimensions_spec.rb +19 -0
  107. data/spec/cura/attributes/has_events_spec.rb +18 -0
  108. data/spec/cura/attributes/has_focusability_spec.rb +58 -0
  109. data/spec/cura/attributes/has_offsets_spec.rb +18 -0
  110. data/spec/cura/attributes/has_orientation_spec.rb +102 -0
  111. data/spec/cura/attributes/has_relative_coordinates_spec.rb +18 -0
  112. data/spec/cura/attributes/has_side_attributes_spec.rb +19 -0
  113. data/spec/spec_helper.rb +12 -0
  114. data/spec/support/shared_examples_for_attributes.rb +122 -0
  115. metadata +211 -0
@@ -0,0 +1,14 @@
1
+ if Kernel.respond_to?(:require)
2
+ require "cura/event/base"
3
+ end
4
+
5
+ module Cura
6
+ module Event
7
+
8
+ # Dispatched when a component is clicked.
9
+ class Click < Base
10
+
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,122 @@
1
+ if Kernel.respond_to?(:require)
2
+ require "cura/attributes/has_initialize"
3
+ require "cura/attributes/has_attributes"
4
+ require "cura/attributes/has_application"
5
+ require "cura/attributes/has_events"
6
+
7
+ require "cura/event/base"
8
+
9
+ require "cura/error/invalid_middleware"
10
+ end
11
+
12
+ module Cura
13
+ module Event
14
+
15
+ # Polls or peeks for events since the last execution and dispatches them to the appropriate component.
16
+ class Dispatcher
17
+
18
+ include Attributes::HasInitialize
19
+ include Attributes::HasAttributes
20
+ include Attributes::HasApplication
21
+
22
+ def initialize(attributes={})
23
+ super
24
+
25
+ raise ArgumentError, "application must be set" if @application.nil?
26
+
27
+ @wait_time = 100
28
+ @target = @application if @target.nil?
29
+ @middleware = []
30
+ end
31
+
32
+ # @method wait_time
33
+ # Get the time to wait for events in milliseconds in the run loop.
34
+ #
35
+ # @return [Integer]
36
+
37
+ # @method wait_time=(value)
38
+ # Set the time to wait for events in milliseconds in the run loop.
39
+ # Set to 0 to wait forever (poll instead of peek).
40
+ #
41
+ # @param [#to_i] value
42
+ # @return [Integer]
43
+
44
+ attribute(:wait_time) do |value|
45
+ value = value.to_i
46
+ value = 0 if value < 0
47
+
48
+ value
49
+ end
50
+
51
+ # @method target
52
+ # Get the object with an event handler to dispatch events to.
53
+ #
54
+ # @return [Cura::Attributes::HasEvents]
55
+ attr_reader :target # TODO: use .attribute() { |value| ... }
56
+
57
+ # @method target=(value)
58
+ # Set the object with an event handler to dispatch events to.
59
+ # Setting to nil will automatially set the target to the application.
60
+ #
61
+ # @param [nil, Cura::Attributes::HasEvents] value
62
+ # @return [Cura::Attributes::HasEvents]
63
+ def target=(value)
64
+ raise TypeError, "target must be a Cura::Attributes::HasEvents or nil" unless value.nil? || value.is_a?(Attributes::HasEvents)
65
+
66
+ dispatch_event(:unfocus)
67
+ @target = value.nil? ? @application : value
68
+ dispatch_event(:focus)
69
+
70
+ value
71
+ end
72
+
73
+ # The middleware stack which an event will pass through before being dispatched.
74
+ # Middleware must be an object responding to #call.
75
+ #
76
+ # @return [Array]
77
+ attr_reader :middleware
78
+
79
+ # Poll or peek for events and dispatch it if one was found.
80
+ #
81
+ # @return [Event::Dispatcher]
82
+ def run
83
+ event = @wait_time == 0 ? poll : peek(@wait_time)
84
+
85
+ dispatch_event(event) unless event.nil?
86
+ end
87
+
88
+ # Wait forever for an event.
89
+ #
90
+ # @return [Event::Base]
91
+ def poll
92
+ @application.adapter.poll_event
93
+ end
94
+
95
+ # Wait a set amount of time for an event.
96
+ #
97
+ # @param [#to_i] milliseconds The amount of time to wait in milliseconds.
98
+ # @return [nil, Event::Base] The event, if handled.
99
+ def peek(milliseconds=100)
100
+ @application.adapter.peek_event(milliseconds.to_i)
101
+ end
102
+
103
+ # Dispatch an event to the target or application, if the target is nil.
104
+ #
105
+ # @param [#to_sym] event The name of the event class to create an instance of or an event instance.
106
+ # @param [#to_hash, #to_h] options The options to pass through the middleware.
107
+ # @return [Event::Base] The dispatched event.
108
+ def dispatch_event(event, options={})
109
+ event = Event.new_from_name(event, options) if event.respond_to?(:to_sym)
110
+ raise TypeError, "event must be an Event::Base" unless event.is_a?(Event::Base)
111
+
112
+ options = { dispatcher: self, event: event, dispatch_queue: [] }.merge(options.to_h)
113
+
114
+ @middleware.each { |middleware| middleware.call(options) }
115
+
116
+ options[:dispatch_queue].each(&:dispatch)
117
+ end
118
+
119
+ end
120
+
121
+ end
122
+ end
@@ -0,0 +1,14 @@
1
+ if Kernel.respond_to?(:require)
2
+ require "cura/event/base"
3
+ end
4
+
5
+ module Cura
6
+ module Event
7
+
8
+ # Dispatched when a component is focused.
9
+ class Focus < Base
10
+
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,74 @@
1
+ if Kernel.respond_to?(:require)
2
+ require "cura/attributes/has_events"
3
+ end
4
+
5
+ module Cura
6
+ module Event
7
+
8
+ # The event handler.
9
+ # Each {Cura::Component} as well as several other class's instances have an instance of this handler.
10
+ # The handler can have multiple callbacks defined for an arbitrary event name.
11
+ class Handler
12
+
13
+ # @param [Cura::Attributes::HasEvents] host The object this handler is attached to.
14
+ def initialize(host)
15
+ raise TypeError, "host must be a Cura::Attributes::HasEvents" unless host.is_a?(Cura::Attributes::HasEvents)
16
+
17
+ @host = host
18
+ @callbacks = { default: [] }
19
+ end
20
+
21
+ # Get the object this handler is attached to.
22
+ #
23
+ # @return [Object]
24
+ attr_reader :host
25
+
26
+ # The callbacks defined on this handler.
27
+ # Key is the event name and the value is an Array of Proc instances.
28
+ #
29
+ # @return [Hash<Symbol,Array>]
30
+ attr_reader :callbacks
31
+
32
+ # Add a callback to the event chain.
33
+ # The first registered callback will be the first one called (FIFO).
34
+ # If no event_name is given, the callback is registered to the `:default` name, which are called before all others.
35
+ def register(event_name=:default, *arguments, &block)
36
+ (@callbacks[event_name] ||= []) << { block: block, arguments: arguments }
37
+ end
38
+
39
+ # Run all callbacks registered on this instance for the given event.
40
+ # The event object is given as a block argument to the callbacks.
41
+ # TODO: These should be able to break the callback chain by returning false in the callback (which would also break the delegation chain).
42
+ # TODO: The event should be delegated to the host's #parent if there are no callbacks registered for it, if it responds to #parent, and it's not nil.
43
+ def handle(event)
44
+ callbacks = @callbacks[:default] + @callbacks[event.class.name].to_a
45
+
46
+ chain_broken = false
47
+ callbacks.each do |callback|
48
+
49
+ result = host.instance_exec(event, *callback[:arguments], &callback[:block])
50
+
51
+ # TODO: Optional event consumption
52
+ if result == false
53
+ # chain_broken = true # TODO TODO TODO TODO TODO
54
+
55
+ break
56
+ end
57
+
58
+ end
59
+
60
+ delegate_event(event) unless chain_broken
61
+ end
62
+
63
+ protected
64
+
65
+ # Propagate the event to the host's applicable #parent or #application.
66
+ # TODO: Why is the handler responsible for propagation? Should the component or a Event::Propagator be responsible?
67
+ def delegate_event(event)
68
+ host.parent.event_handler.handle(event) if host.respond_to?(:parent) && host.parent.respond_to?(:event_handler)
69
+ end
70
+
71
+ end
72
+
73
+ end
74
+ end
@@ -0,0 +1,68 @@
1
+ if Kernel.respond_to?(:require)
2
+ require "cura/event/base"
3
+ end
4
+
5
+ module Cura
6
+ module Event
7
+
8
+ # Dispatched when a key's state changes from up to down.
9
+ class KeyDown < Base
10
+
11
+ def initialize(attributes={})
12
+ @control = false
13
+
14
+ super
15
+
16
+ raise ArgumentError, "name must be set" if @name.nil?
17
+ end
18
+
19
+ # Get whether the key was pressed while holding the control key.
20
+ #
21
+ # @return [Boolean]
22
+ def control?
23
+ @control
24
+ end
25
+
26
+ # Get the key name.
27
+ #
28
+ # @return [Integer]
29
+ attr_reader :name
30
+
31
+ # Get whether the key is printable.
32
+ #
33
+ # @return [Boolean]
34
+ def printable?
35
+ return false if @control
36
+
37
+ Key.name_is_printable?(@name)
38
+ end
39
+
40
+ # Get the printable character for the key.
41
+ #
42
+ # @return [nil, String]
43
+ def character
44
+ Key.character_from_name(@name)
45
+ end
46
+
47
+ protected
48
+
49
+ # Set if the key was pressed while holding the control key.
50
+ #
51
+ # @param [Boolean] value
52
+ # @return [Boolean]
53
+ def control=(value)
54
+ @control = !!value
55
+ end
56
+
57
+ # Set the key name.
58
+ #
59
+ # @param [#to_sym] value
60
+ # @return [String]
61
+ def name=(value)
62
+ @name = value.to_sym
63
+ end
64
+
65
+ end
66
+
67
+ end
68
+ end
@@ -0,0 +1,38 @@
1
+ if Kernel.respond_to?(:require)
2
+ require "cura/event/middleware/base"
3
+ end
4
+
5
+ module Cura
6
+ module Event
7
+ module Middleware
8
+ module Aimer
9
+
10
+ # The base class for event middleware which set's a target, if needed.
11
+ class Base
12
+
13
+ # Call this middleware.
14
+ #
15
+ # @param [#to_h] options
16
+ # @option options [Event::Dispatcher] :dispatcher
17
+ # @option options [Event::Base] :event
18
+ # @option options [Attributes::HasEvents] :target The optional target of the event.
19
+ def call(options={})
20
+ set_target(options) if should_aim?(options)
21
+ end
22
+
23
+ protected
24
+
25
+ def should_aim?(options={})
26
+ options[:event].target.nil?
27
+ end
28
+
29
+ def set_target(_options={})
30
+ # Does nothing on purpose
31
+ end
32
+
33
+ end
34
+
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,24 @@
1
+ if Kernel.respond_to?(:require)
2
+ require "cura/event/middleware/aimer/base"
3
+ end
4
+
5
+ module Cura
6
+ module Event
7
+ module Middleware
8
+ module Aimer
9
+
10
+ # Sets the event's target to the dispatcher's target, if it isn't already set.
11
+ class DispatcherTarget < Base
12
+
13
+ protected
14
+
15
+ def set_target(options={})
16
+ options[:event].target ||= options[:dispatcher].target
17
+ end
18
+
19
+ end
20
+
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,48 @@
1
+ if Kernel.respond_to?(:require)
2
+ require "cura/event/mouse_button"
3
+
4
+ require "cura/event/middleware/aimer/base"
5
+ end
6
+
7
+ module Cura
8
+ module Event
9
+ module Middleware
10
+ module Aimer
11
+
12
+ # Sets the dispatcher's target to the component underneath the cursor on mouse button events.
13
+ class MouseFocus < Base
14
+
15
+ protected
16
+
17
+ def should_aim?(options={})
18
+ options[:event].is_a?(Event::MouseButton) && options[:event].down?
19
+ end
20
+
21
+ def set_target(options={})
22
+ component = nearest_focusable_ancestor(top_most_component_at(options))
23
+
24
+ options[:dispatcher].target = component unless options[:dispatcher].target == component
25
+ end
26
+
27
+ # TODO: Some kind of hit tester class that initializes with a Window to get it's root element and determines
28
+ # if a set of coordinates are within the bounds of which component in the view tree.
29
+ def top_most_component_at(options={})
30
+ # TODO: Focused window? Or some way of determining which window so use top_most_component_at with
31
+ window = options[:dispatcher].application.windows.first # TODO: Should be getting the screen coordinates from the event, not relevent to the window
32
+ window.children(true).reverse.find { |child| child.contains_coordinates?(x: options[:event].x, y: options[:event].y) }
33
+ end
34
+
35
+ def nearest_focusable_ancestor(component)
36
+ return nil unless component.respond_to?(:focusable?)
37
+ return component if component.focusable?
38
+ return nil unless component.respond_to?(:parent)
39
+
40
+ nearest_focusable_ancestor(component.parent)
41
+ end
42
+
43
+ end
44
+
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,38 @@
1
+ if Kernel.respond_to?(:require)
2
+ require "cura/event/middleware/aimer/base"
3
+ end
4
+
5
+ module Cura
6
+ module Event
7
+ module Middleware
8
+ module Aimer
9
+
10
+ # Sets the event's target to the component passed by an optional :target option.
11
+ class TargetOption < Base
12
+
13
+ # Call this middleware.
14
+ #
15
+ # @param [#to_h] options
16
+ # @option options [Event::Dispatcher] :dispatcher
17
+ # @option options [Event::Base] :event
18
+ # @option options [Attributes::HasEvents] :target The optional target of the event.
19
+ def call(options={})
20
+ super # Only here for documentation
21
+ end
22
+
23
+ protected
24
+
25
+ def should_aim?(options={})
26
+ options.key?(:target)
27
+ end
28
+
29
+ def set_target(options={})
30
+ options[:event].target = options[:target]
31
+ end
32
+
33
+ end
34
+
35
+ end
36
+ end
37
+ end
38
+ end