cura 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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