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,180 @@
1
+ if Kernel.respond_to?(:require)
2
+ require "cura/attributes/has_initialize"
3
+ require "cura/attributes/has_focusability"
4
+ require "cura/attributes/has_colors"
5
+ require "cura/attributes/has_dimensions"
6
+ require "cura/attributes/has_events"
7
+ require "cura/attributes/has_offsets"
8
+ require "cura/attributes/has_relative_coordinates"
9
+ end
10
+
11
+ module Cura
12
+ module Component
13
+
14
+ # The base class for all components.
15
+ #
16
+ # All components use a box model similar to CSS.
17
+ # Margins, borders, paddings, then content.
18
+ class Base
19
+
20
+ include Attributes::HasInitialize
21
+ include Attributes::HasAttributes
22
+ include Attributes::HasDimensions
23
+ include Attributes::HasEvents
24
+ include Attributes::HasFocusability
25
+ include Attributes::HasColors
26
+ include Attributes::HasOffsets
27
+ include Attributes::HasRelativeCoordinates
28
+
29
+ # Get the cursor for this application.
30
+ # TODO: Delegate something like: def_delegate(:cursor) { application }
31
+ #
32
+ # @return [Cursor]
33
+ def cursor
34
+ application.cursor
35
+ end
36
+
37
+ # Get the pencil for this application.
38
+ # TODO: Delegate
39
+ #
40
+ # @return [Pencil]
41
+ def pencil
42
+ application.pencil
43
+ end
44
+
45
+ # Get the application of this object.
46
+ #
47
+ # @return [Application]
48
+ def application
49
+ return nil if parent.nil?
50
+
51
+ parent.application
52
+ end
53
+
54
+ # Focus on this component.
55
+ #
56
+ # @return [Component]
57
+ def focus
58
+ application.dispatcher.target = self
59
+ end
60
+
61
+ # Check whether this component is focused.
62
+ #
63
+ # @return [Boolean]
64
+ def focused?
65
+ application.dispatcher.target == self
66
+ end
67
+
68
+ # Determine if the given absolute coordinates are within the bounds of this component.
69
+ #
70
+ # @param [#to_h] options
71
+ # @option options [#to_i] :x
72
+ # @option options [#to_i] :y
73
+ # @return [Boolean]
74
+ def contains_coordinates?(options={})
75
+ options = options.to_h
76
+
77
+ (absolute_x..absolute_x + width).include?(options[:x].to_i) && (absolute_y..absolute_y + width).include?(options[:y].to_i)
78
+ end
79
+
80
+ # Get the foreground color of this object.
81
+ #
82
+ # @return [Color]
83
+ def foreground
84
+ get_or_inherit_color(:foreground, Color.black)
85
+ end
86
+
87
+ # Get the background color of this object.
88
+ #
89
+ # @return [Color]
90
+ def background
91
+ get_or_inherit_color(:background, Color.white)
92
+ end
93
+
94
+ # Instance inspection.
95
+ #
96
+ # @return [String]
97
+ def inspect
98
+ "#<#{self.class}:0x#{__id__.to_s(16)} x=#{x} y=#{y} absolute_x=#{absolute_x} absolute_y=#{absolute_y} w=#{width} h=#{height} parent=#{@parent.class}:0x#{@parent.__id__.to_s(16)}>"
99
+ end
100
+
101
+ # Update this component.
102
+ #
103
+ # @return [Component]
104
+ def update
105
+ self
106
+ end
107
+
108
+ # Draw this component.
109
+ #
110
+ # @return [Component]
111
+ def draw
112
+ draw_background
113
+ draw_border
114
+
115
+ self
116
+ end
117
+
118
+ protected
119
+
120
+ # Draw a point.
121
+ def draw_point(x, y, color=Cura::Color.black)
122
+ x = absolute_x + @offsets.left + x
123
+ y = absolute_y + @offsets.top + y
124
+
125
+ pencil.draw_point(x, y, color)
126
+ end
127
+
128
+ # Draw a rectangle.
129
+ # TODO: filled argument
130
+ def draw_rectangle(x, y, width, height, color=Cura::Color.black)
131
+ x = absolute_x + @offsets.left + x
132
+ y = absolute_y + @offsets.top + y
133
+
134
+ pencil.draw_rectangle(x, y, width, height, color)
135
+ end
136
+
137
+ # Draw a single character.
138
+ def draw_character(x, y, character, foreground=Cura::Color.black, background=Cura::Color.white, bold=false, underline=false)
139
+ x = absolute_x + @offsets.left + x
140
+ y = absolute_y + @offsets.top + y
141
+
142
+ pencil.draw_character(x, y, character, foreground, background, bold, underline)
143
+ end
144
+
145
+ # Draw text.
146
+ def draw_text(x, y, text, foreground=Cura::Color.black, background=Cura::Color.white, bold=false, underline=false)
147
+ x = absolute_x + @offsets.left + x
148
+ y = absolute_y + @offsets.top + y
149
+
150
+ pencil.draw_text(x, y, text, foreground, background, bold, underline)
151
+ end
152
+
153
+ # Draw the background of this component.
154
+ def draw_background
155
+ x = absolute_x + @margin.left + @border.left
156
+ y = absolute_y + @margin.top + @border.top
157
+ width = self.width + @padding.width
158
+ height = self.height + @padding.height
159
+ color = background
160
+
161
+ pencil.draw_rectangle(x, y, width, height, color)
162
+ end
163
+
164
+ # Draw the border of this component.
165
+ def draw_border # TODO
166
+ end
167
+
168
+ def get_or_inherit_color(name, default)
169
+ value = instance_variable_get("@#{name}")
170
+
171
+ return value unless value == :inherit
172
+ return default unless respond_to?(:parent) && parent.respond_to?(name)
173
+
174
+ parent.send(name)
175
+ end
176
+
177
+ end
178
+
179
+ end
180
+ end
@@ -0,0 +1,57 @@
1
+ if Kernel.respond_to?(:require)
2
+ require "cura/component/label"
3
+ end
4
+
5
+ module Cura
6
+ module Component
7
+
8
+ # A button component.
9
+ class Button < Label
10
+
11
+ on_event(:key_down) do |event|
12
+ click if event.target == self && event.name == :enter
13
+ end
14
+
15
+ on_event(:mouse_button) do |event|
16
+ click if event.target == self && event.up? && contains_coordinates?(x: event.x, y: event.y)
17
+ end
18
+
19
+ # @method focused_background
20
+ # Get the focused background color of this object.
21
+ #
22
+ # @return [Color]
23
+
24
+ # @method focused_background=(value)
25
+ # Set the focused background color of this object.
26
+ #
27
+ # @param [Color] value
28
+ # @return [Color]
29
+
30
+ attribute(:focused_background) { |value| validate_color_attribute(value) }
31
+
32
+ def initialize(attributes={})
33
+ @focusable = true
34
+ @foreground = Cura::Color.black
35
+ @background = Cura::Color.white
36
+ @focused_background = Color.new(78, 78, 78)
37
+
38
+ super
39
+ end
40
+
41
+ def background
42
+ focused? ? @focused_background : get_or_inherit_color(:background, Color.black)
43
+ end
44
+
45
+ # Click this button.
46
+ #
47
+ # @return [Button]
48
+ def click
49
+ application.dispatch_event(:click, target: self)
50
+
51
+ self
52
+ end
53
+
54
+ end
55
+
56
+ end
57
+ end
@@ -0,0 +1,77 @@
1
+ if Kernel.respond_to?(:require)
2
+ require "cura/attributes/has_children"
3
+ require "cura/component/base"
4
+ end
5
+
6
+ module Cura
7
+ module Component
8
+
9
+ # A component with children.
10
+ # When children are added, their parent will be set to this group.
11
+ class Group < Base
12
+
13
+ include Attributes::HasChildren
14
+
15
+ # Get the width of this group.
16
+ #
17
+ # @return [Integer]
18
+ def width
19
+ return @width unless @width == :auto
20
+ return 0 if children.empty?
21
+
22
+ children.collect { |child| child.x + child.width + child.offsets.width }.max
23
+ end
24
+
25
+ # Get the height of this group.
26
+ #
27
+ # @return [Integer]
28
+ def height
29
+ return @height unless @height == :auto
30
+ return 0 if children.empty?
31
+
32
+ children.collect { |child| child.y + child.height + child.offsets.height }.max
33
+ end
34
+
35
+ # Add a child to this group and set it's parent to this Group.
36
+ #
37
+ # @param [Component] component
38
+ # @return [Component]
39
+ def add_child(component)
40
+ component = super
41
+
42
+ component.parent = self
43
+
44
+ component
45
+ end
46
+
47
+ # Remove a child from this object's children at the given index and set it's parent to nil.
48
+ #
49
+ # @param [Integer] index
50
+ # @return [Component]
51
+ def delete_child_at(index)
52
+ component = super
53
+
54
+ component.parent = nil
55
+
56
+ component
57
+ end
58
+
59
+ # Update all children.
60
+ def update
61
+ super
62
+
63
+ update_children
64
+ end
65
+
66
+ # Draw all children relative to this location.
67
+ # TODO: If the dimensions of this group of this group are less than the computed dimensions, the drawing will be clipped.
68
+ def draw
69
+ super
70
+
71
+ draw_children
72
+ end
73
+
74
+ end
75
+
76
+ end
77
+ end
@@ -0,0 +1,224 @@
1
+ if Kernel.respond_to?(:require)
2
+ require "cura/component/base"
3
+ require "cura/attributes/has_attributes"
4
+ end
5
+
6
+ module Cura
7
+ module Component
8
+
9
+ # A component displaying text.
10
+ class Label < Base
11
+
12
+ include Attributes::HasAttributes
13
+
14
+ # Note that you can pass the following:
15
+ # alignment: { horizontal: true, vertical: true }
16
+ # instead of:
17
+ # horizontal_alignment: true, vertical_alignment: true
18
+ def initialize(attributes={})
19
+ @horizontal_alignment = :left
20
+ @vertical_alignment = :top
21
+ @bold = false
22
+ @underline = false
23
+ @text = ""
24
+
25
+ super
26
+ end
27
+
28
+ # TODO: #text_foreground, #text_background (? Maybe a separate Text component, like in )
29
+
30
+ # Get the width of this label.
31
+ #
32
+ # @return [Integer]
33
+ def width
34
+ return text_width if @width == :auto
35
+
36
+ @width
37
+ end
38
+
39
+ # Get the height of this label.
40
+ #
41
+ # @return [Integer]
42
+ def height
43
+ return text_height if @height == :auto
44
+
45
+ @height
46
+ end
47
+
48
+ # @method text
49
+ # Get the text of this label.
50
+ #
51
+ # @return [String]
52
+
53
+ # @method text=(value)
54
+ # Set the text of this label.
55
+ #
56
+ # @param [#to_s] value
57
+ # @return [String]
58
+ attribute(:text) { |value| value.to_s }
59
+
60
+ # Get the lines of this label.
61
+ #
62
+ # @return [<String>]
63
+ def lines
64
+ @text.split("\n") # NOTE: Would use String#lines but it's output doesn't think a trailing newline character constitutes a line unless it is followed by another character. #split also removes the newline characters.
65
+ end
66
+
67
+ # Get the width of the text of this label.
68
+ #
69
+ # @return [Integer]
70
+ def text_width
71
+ return 0 if @text.empty?
72
+
73
+ lines.collect(&:length).sort.last
74
+ end
75
+
76
+ # Get the height of the text of this label.
77
+ #
78
+ # @return [Integer]
79
+ def text_height
80
+ value = lines.length
81
+
82
+ value == 0 ? 1 : value
83
+ end
84
+
85
+ # @method bold?
86
+ # Get whether the text is bold.
87
+ #
88
+ # @return [Boolean]
89
+
90
+ # @method bold=(value)
91
+ # Set whether the text is bold.
92
+ #
93
+ # @return [Boolean]
94
+ attribute(:bold, query: true)
95
+
96
+ # @method underline?
97
+ # Get whether the text is underlined.
98
+ #
99
+ # @return [Boolean]
100
+
101
+ # @method underlined=(value)
102
+ # Set whether the text is underlined.
103
+ #
104
+ # @return [Boolean]
105
+ attribute(:underline, query: true)
106
+
107
+ # @method horizontal_alignment
108
+ # Get the horizontal alignment of this label.
109
+ #
110
+ # @return [Symbol]
111
+
112
+ # @method horizontal_alignment=(value)
113
+ # Set the horizontal alignment of this label.
114
+ # Must be :left, :center, or :right.
115
+ #
116
+ # @param [#to_sym] value
117
+ # @return [Symbol]
118
+ attribute(:horizontal_alignment) { |value| convert_horizontal_alignment_attribute(value) }
119
+
120
+ # @method vertical_alignment
121
+ # Get the vertical alignment of this label.
122
+ # Will be :left, :center, or :right.
123
+ #
124
+ # @return [Symbol]
125
+
126
+ # @method vertical_alignment=(value)
127
+ # Set the vertical alignment of this label.
128
+ # Must be :left, :center, or :right.
129
+ #
130
+ # @param [#to_sym] value
131
+ # @return [Symbol]
132
+ attribute(:vertical_alignment) { |value| convert_vertical_alignment_attribute(value) }
133
+
134
+ def draw
135
+ super
136
+
137
+ draw_text unless text.empty?
138
+ end
139
+
140
+ protected
141
+
142
+ # Helper method for subclasses
143
+ def text_to_draw
144
+ @text
145
+ end
146
+
147
+ # Helper method for subclasses
148
+ def character_to_draw(character)
149
+ character
150
+ end
151
+
152
+ # TODO: Should use instance vars
153
+ def draw_text
154
+ x_offset = x_offset_start = x_offset_from_alignment# + @offsets.left
155
+ y_offset = y_offset_from_alignment# + @offsets.top
156
+ absolute_x = self.absolute_x
157
+ absolute_y = self.absolute_y
158
+
159
+ text_to_draw.each_char do |character|
160
+ if character == "\n" # TODO: If multiline? Also check if outside the bounds of the drawing area
161
+ x_offset = x_offset_start
162
+
163
+ y_offset += 1
164
+ else
165
+ unless x_offset > width || y_offset > height
166
+ character = character_to_draw(character)
167
+ draw_character(x_offset, y_offset, character, foreground, background, @bold, @underline)
168
+ end
169
+
170
+ x_offset += 1
171
+ end
172
+ end
173
+ end
174
+
175
+ def x_offset_from_alignment
176
+ case horizontal_alignment
177
+ when :left then 0
178
+ when :center then ((text_width - width).abs / 2).to_i
179
+ when :right then (text_width - width).abs
180
+ end
181
+ end
182
+
183
+ def y_offset_from_alignment
184
+ case vertical_alignment
185
+ when :top then 0
186
+ when :center then ((text_height - height).abs / 2).to_i
187
+ when :bottom then (text_height - height).abs
188
+ end
189
+ end
190
+
191
+ protected
192
+
193
+ # TODO: Just use a #alignment attribute and have a Cura::Alignment object?
194
+ def convert_attributes(attributes={})
195
+ attributes = super
196
+
197
+ if attributes.key?(:alignment)
198
+ alignment_attributes = attributes.delete(:alignment).to_h
199
+
200
+ attributes[:horizontal_alignment] = alignment_attributes[:horizontal] if alignment_attributes.key?(:horizontal)
201
+ attributes[:vertical_alignment] = alignment_attributes[:vertical] if alignment_attributes.key?(:vertical)
202
+ end
203
+
204
+ attributes
205
+ end
206
+
207
+ def convert_horizontal_alignment_attribute(value)
208
+ value = value.to_sym
209
+ raise ArgumentError, "must be :left, :center, or :right" unless [:left, :center, :right].include?(value)
210
+
211
+ value
212
+ end
213
+
214
+ def convert_vertical_alignment_attribute(value)
215
+ value = value.to_sym
216
+ raise ArgumentError, "must be :top, :center, or :bottom" unless [:top, :center, :bottom].include?(value)
217
+
218
+ value
219
+ end
220
+
221
+ end
222
+
223
+ end
224
+ end