vedeu 0.2.3 → 0.2.4

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 (90) hide show
  1. checksums.yaml +4 -4
  2. data/docs/api.md +2 -0
  3. data/docs/events.md +0 -4
  4. data/examples/cursor_app/cursor_app.rb +56 -44
  5. data/lib/vedeu/api/api.rb +4 -6
  6. data/lib/vedeu/api/composition.rb +3 -7
  7. data/lib/vedeu/api/interface.rb +2 -3
  8. data/lib/vedeu/api/menu.rb +5 -3
  9. data/lib/vedeu/api/stream.rb +34 -0
  10. data/lib/vedeu/application.rb +4 -7
  11. data/lib/vedeu/configuration/api.rb +0 -5
  12. data/lib/vedeu/configuration/cli.rb +0 -1
  13. data/lib/vedeu/configuration/configuration.rb +0 -12
  14. data/lib/vedeu/input/input.rb +0 -3
  15. data/lib/vedeu/launcher.rb +0 -1
  16. data/lib/vedeu/models/{attributes/background.rb → background.rb} +0 -4
  17. data/lib/vedeu/models/char.rb +43 -0
  18. data/lib/vedeu/models/colour.rb +0 -1
  19. data/lib/vedeu/models/composition.rb +4 -3
  20. data/lib/vedeu/models/cursor.rb +70 -146
  21. data/lib/vedeu/models/{attributes/foreground.rb → foreground.rb} +0 -4
  22. data/lib/vedeu/models/geometry.rb +19 -20
  23. data/lib/vedeu/models/interface.rb +36 -5
  24. data/lib/vedeu/models/keymap.rb +7 -4
  25. data/lib/vedeu/models/line.rb +31 -11
  26. data/lib/vedeu/models/offset.rb +84 -0
  27. data/lib/vedeu/models/stream.rb +37 -13
  28. data/lib/vedeu/models/style.rb +0 -1
  29. data/lib/vedeu/output/area.rb +284 -0
  30. data/lib/vedeu/output/clear.rb +2 -4
  31. data/lib/vedeu/output/compositor.rb +0 -4
  32. data/lib/vedeu/output/refresh.rb +1 -2
  33. data/lib/vedeu/output/render.rb +12 -163
  34. data/lib/vedeu/output/view.rb +0 -2
  35. data/lib/vedeu/output/viewport.rb +132 -0
  36. data/lib/vedeu/repositories/buffers.rb +11 -22
  37. data/lib/vedeu/repositories/cursors.rb +20 -47
  38. data/lib/vedeu/repositories/events.rb +2 -1
  39. data/lib/vedeu/repositories/focus.rb +16 -16
  40. data/lib/vedeu/repositories/groups.rb +1 -4
  41. data/lib/vedeu/repositories/interfaces.rb +10 -3
  42. data/lib/vedeu/repositories/keymaps.rb +0 -3
  43. data/lib/vedeu/repositories/menus.rb +0 -2
  44. data/lib/vedeu/repositories/offsets.rb +47 -0
  45. data/lib/vedeu/{models/attributes → support}/coercions.rb +0 -0
  46. data/lib/vedeu/{models/attributes → support}/colour_translator.rb +0 -16
  47. data/lib/vedeu/support/esc.rb +0 -22
  48. data/lib/vedeu/support/event.rb +1 -17
  49. data/lib/vedeu/support/exceptions.rb +0 -3
  50. data/lib/vedeu/support/grid.rb +0 -3
  51. data/lib/vedeu/support/log.rb +13 -14
  52. data/lib/vedeu/support/move.rb +50 -0
  53. data/lib/vedeu/support/position.rb +17 -9
  54. data/lib/vedeu/{models/attributes → support}/presentation.rb +1 -4
  55. data/lib/vedeu/support/registrar.rb +2 -2
  56. data/lib/vedeu/support/repository.rb +23 -2
  57. data/lib/vedeu/support/terminal.rb +0 -2
  58. data/lib/vedeu/support/trace.rb +0 -11
  59. data/lib/vedeu.rb +11 -5
  60. data/test/lib/vedeu/api/api_test.rb +14 -5
  61. data/test/lib/vedeu/api/composition_test.rb +9 -26
  62. data/test/lib/vedeu/api/keymap_test.rb +1 -1
  63. data/test/lib/vedeu/api/menu_test.rb +6 -0
  64. data/test/lib/vedeu/api/stream_test.rb +32 -0
  65. data/test/lib/vedeu/models/{attributes/background_test.rb → background_test.rb} +0 -0
  66. data/test/lib/vedeu/models/char_test.rb +46 -0
  67. data/test/lib/vedeu/models/composition_test.rb +7 -0
  68. data/test/lib/vedeu/models/cursor_test.rb +63 -80
  69. data/test/lib/vedeu/models/{attributes/foreground_test.rb → foreground_test.rb} +0 -0
  70. data/test/lib/vedeu/models/geometry_test.rb +8 -10
  71. data/test/lib/vedeu/models/interface_test.rb +26 -21
  72. data/test/lib/vedeu/models/keymap_test.rb +6 -0
  73. data/test/lib/vedeu/models/line_test.rb +82 -3
  74. data/test/lib/vedeu/models/offset_test.rb +121 -0
  75. data/test/lib/vedeu/models/stream_test.rb +107 -8
  76. data/test/lib/vedeu/output/area_test.rb +242 -0
  77. data/test/lib/vedeu/output/compositor_test.rb +12 -3
  78. data/test/lib/vedeu/output/render_test.rb +34 -39
  79. data/test/lib/vedeu/output/viewport_test.rb +36 -0
  80. data/test/lib/vedeu/repositories/cursors_test.rb +32 -9
  81. data/test/lib/vedeu/repositories/focus_test.rb +8 -0
  82. data/test/lib/vedeu/repositories/offsets_test.rb +32 -0
  83. data/test/lib/vedeu/{models/attributes → support}/coercions_test.rb +0 -0
  84. data/test/lib/vedeu/{models/attributes → support}/colour_translator_test.rb +0 -0
  85. data/test/lib/vedeu/support/move_test.rb +35 -0
  86. data/test/lib/vedeu/{models/attributes → support}/presentation_test.rb +0 -0
  87. data/test/lib/vedeu/support/registrar_test.rb +6 -0
  88. data/test/lib/vedeu_test.rb +5 -0
  89. data/vedeu.gemspec +1 -1
  90. metadata +37 -17
@@ -0,0 +1,84 @@
1
+ module Vedeu
2
+
3
+ # Offset represents the scroll position of the content for an interface. The
4
+ # values herein are relative to the geometry of the interface.
5
+ #
6
+ # @note (to self) An offset is a position. A cursor resides at a position in
7
+ # an interface, ergo we can calculate cursors based from offsets. Also, we
8
+ # could rename Offset to Position, then kill Cursor, Cursors, and the old
9
+ # Position class.
10
+ #
11
+ # @api private
12
+ class Offset
13
+
14
+ attr_reader :name
15
+
16
+ # Returns a new instance of Offset.
17
+ #
18
+ # @param attributes [Hash]
19
+ # @return [Offset]
20
+ def initialize(attributes = {})
21
+ @attributes = defaults.merge!(attributes)
22
+ @name = @attributes[:name]
23
+ @y = @attributes[:y]
24
+ @x = @attributes[:x]
25
+ end
26
+
27
+ # Return a convenient hash of the current values of this instance.
28
+ #
29
+ # @return [Hash]
30
+ def attributes
31
+ {
32
+ name: name,
33
+ y: y,
34
+ x: x,
35
+ }
36
+ end
37
+
38
+ # Adjusts the offset using the relative values provided, and updates the
39
+ # stored attributes. The values passed are -1, 0 or 1.
40
+ #
41
+ # @param relative_y [Fixnum] Move up (-1), or down (1), or no action (0).
42
+ # @param relative_x [Fixnum] Move left (-1), or right (1), or no action (0).
43
+ # @return [Offset]
44
+ def move(relative_y, relative_x)
45
+ @y += relative_y
46
+ @x += relative_x
47
+
48
+ Offsets.update(attributes)
49
+ end
50
+
51
+ # Returns the current x offset, correcting to 0 if less than 0.
52
+ #
53
+ # @return [Fixnum]
54
+ def x
55
+ return @x = 0 if @x <= 0
56
+
57
+ @x
58
+ end
59
+
60
+ # Returns the current y offset, correcting to 0 if less than 0.
61
+ #
62
+ # @return [Fixnum]
63
+ def y
64
+ return @y = 0 if @y <= 0
65
+
66
+ @y
67
+ end
68
+
69
+ private
70
+
71
+ # Return the default values for an instance of Offset.
72
+ #
73
+ # @return [Hash]
74
+ def defaults
75
+ {
76
+ name: '',
77
+ y: 0,
78
+ x: 0,
79
+ }
80
+ end
81
+
82
+ end # Offset
83
+
84
+ end # Vedeu
@@ -40,27 +40,51 @@ module Vedeu
40
40
  end
41
41
  end
42
42
 
43
- # Returns the content of this stream.
43
+ # Returns an array of characters, each element is the escape sequences of
44
+ # colours and styles for this stream, the character itself, and the escape
45
+ # sequences of colours and styles for the parent of the stream
46
+ # ({Vedeu::Line}).
47
+ #
48
+ # @return [Array]
49
+ def chars
50
+ return [] if empty?
51
+
52
+ char_attrs = view_attributes.merge!({ parent: parent })
53
+
54
+ @_chars ||= content.chars.map do |c|
55
+ Char.new(char_attrs.merge!({ value: c })).to_s
56
+ end
57
+ end
58
+
59
+ # Returns the text aligned if a width was set, otherwise just the text. This
60
+ # method also has the alias_method :data, a convenience method to provide
61
+ # Presentation with a consistent interface.
44
62
  #
45
63
  # @return [String]
46
64
  def content
47
- data
65
+ width? ? aligned : text
48
66
  end
67
+ alias_method :data, :content
49
68
 
50
- private
69
+ # Returns a boolean indicating whether the stream has content.
70
+ #
71
+ # @return [Boolean]
72
+ def empty?
73
+ size == 0
74
+ end
51
75
 
52
- # Returns the text aligned if a width was set, otherwise just the text.
76
+ # Returns the size of the content in characters without formatting.
53
77
  #
54
- # @api private
55
- # @return [String]
56
- def data
57
- width? ? aligned : text
78
+ # @return [Fixnum]
79
+ def size
80
+ content.size
58
81
  end
59
82
 
83
+ private
84
+
60
85
  # Returns an aligned string if the string is shorter than the specified
61
86
  # width; the excess area being padded by spaces.
62
87
  #
63
- # @api private
64
88
  # @return [String]
65
89
  def aligned
66
90
  case align
@@ -72,7 +96,6 @@ module Vedeu
72
96
 
73
97
  # Returns a boolean to indicate whether this stream has a width set.
74
98
  #
75
- # @api private
76
99
  # @return [Boolean]
77
100
  def width?
78
101
  !!width
@@ -80,7 +103,6 @@ module Vedeu
80
103
 
81
104
  # The default values for a new instance of Stream.
82
105
  #
83
- # @api private
84
106
  # @return [Hash]
85
107
  def defaults
86
108
  {
@@ -93,12 +115,14 @@ module Vedeu
93
115
  }
94
116
  end
95
117
 
96
- # @api private
118
+ # @param method [Symbol] The name of the method sought.
119
+ # @param args [Array] The arguments which the method was to be invoked with.
120
+ # @param block [Proc] The optional block provided to the method.
97
121
  # @return []
98
122
  def method_missing(method, *args, &block)
99
123
  Vedeu.log("Stream#method_missing '#{method.to_s}' (args: #{args.inspect})")
100
124
 
101
- @self_before_instance_eval.send(method, *args, &block)
125
+ @self_before_instance_eval.send(method, *args, &block) if @self_before_instance_eval
102
126
  end
103
127
 
104
128
  end # Stream
@@ -38,7 +38,6 @@ module Vedeu
38
38
 
39
39
  # Converts the style or styles into terminal escape sequences.
40
40
  #
41
- # @api private
42
41
  # @return [String]
43
42
  def escape_sequences
44
43
  return '' unless defined_value?(values)
@@ -0,0 +1,284 @@
1
+ module Vedeu
2
+
3
+ # Provides a collection of methods useful in calculating geometries for
4
+ # interfaces, cursors and viewports.
5
+ #
6
+ class Area
7
+
8
+ attr_reader :attributes, :height, :x_min, :y_min, :width, :x, :y
9
+
10
+ # Returns an instance of Area.
11
+ #
12
+ # @param interface [Interface]
13
+ # @return [Area]
14
+ def self.from_interface(interface)
15
+ new({
16
+ y_min: interface.top,
17
+ height: interface.height,
18
+ x_min: interface.left,
19
+ width: interface.width
20
+ })
21
+ end
22
+
23
+ # Returns an instance of Area.
24
+ #
25
+ # @param attributes [Hash] The attributes to initialize this class with.
26
+ # @option attributes :y_min [Fixnum] The starting y coordinate for the area,
27
+ # equivalent to +:top+ in {Geometry} parlance.
28
+ # @option attributes :height [Fixnum] The number of rows or lines to use.
29
+ # @option attributes :x_min [Fixnum] The starting x coordinate for the area,
30
+ # equivalent to +:left+ in {Geometry} parlance.
31
+ # @option attributes :width [Fixnum] The number of characters or columns to
32
+ # use.
33
+ # @option attributes :y [Fixnum] A coordinate in the area. This value should
34
+ # be an actual terminal coordinate.
35
+ # @option attributes :x [Fixnum] A coordinate in the area. This value should
36
+ # be an actual terminal coordinate.
37
+ #
38
+ # @return [Area]
39
+ def initialize(attributes = {})
40
+ @attributes = defaults.merge!(attributes)
41
+
42
+ @y_min = @attributes.fetch(:y_min)
43
+ @height = @attributes.fetch(:height)
44
+
45
+ @x_min = @attributes.fetch(:x_min)
46
+ @width = @attributes.fetch(:width)
47
+
48
+ @y = @attributes.fetch(:y)
49
+ @x = @attributes.fetch(:x)
50
+ end
51
+
52
+ # Returns the same as #y_range, except as indices of an array.
53
+ #
54
+ # @example
55
+ # # height = 4
56
+ # y_indices # => [0, 1, 2, 3]
57
+ #
58
+ # @return [Array]
59
+ def y_indices
60
+ (0...height).to_a
61
+ end
62
+
63
+ # Returns the same as #x_range, except as indices of an array.
64
+ #
65
+ # @example
66
+ # # width = 10
67
+ # x_indices # => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
68
+ #
69
+ # @return [Array]
70
+ def x_indices
71
+ (0...width).to_a
72
+ end
73
+
74
+ # Returns the maximum y coordinate for an area.
75
+ #
76
+ # @example
77
+ # # y_min = 2
78
+ # # height = 4
79
+ # y_max # => 6
80
+ #
81
+ # @return [Fixnum]
82
+ def y_max
83
+ if height <= 0
84
+ 0
85
+
86
+ else
87
+ y_min + height
88
+
89
+ end
90
+ end
91
+
92
+ # Returns the maximum x coordinate for an area.
93
+ #
94
+ # @example
95
+ # # x_min = 5
96
+ # # width = 20
97
+ # x_max # => 25
98
+ #
99
+ # @return [Fixnum]
100
+ def x_max
101
+ if width <= 0
102
+ 0
103
+
104
+ else
105
+ x_min + width
106
+
107
+ end
108
+ end
109
+
110
+ # Returns the maximum y index for an area.
111
+ #
112
+ # @example
113
+ # # height = 3
114
+ # y_max_index # => 2
115
+ #
116
+ # @return [Fixnum]
117
+ def y_max_index
118
+ return 0 if y_indices.empty?
119
+
120
+ y_indices.last
121
+ end
122
+
123
+ # Returns the maximum x index for an area.
124
+ #
125
+ # @example
126
+ # # width = 6
127
+ # x_max_index # => 5
128
+ #
129
+ # @return [Fixnum]
130
+ def x_max_index
131
+ return 0 if x_indices.empty?
132
+
133
+ x_indices.last
134
+ end
135
+
136
+ # Returns the y coordinate as an offset index in the area's y range. When a
137
+ # value is provided, the y coordinate is overridden. Crudely corrects out of
138
+ # range values.
139
+ #
140
+ # @example
141
+ # # y_range = [7, 8, 9, 10]
142
+ # # y = 8
143
+ # y_index # => 1
144
+ # y_index(10) # => 3
145
+ # y_index(5) # => 0
146
+ # y_index(15) # => 3
147
+ #
148
+ # @param value [Fixnum]
149
+ # @return [Fixnum]
150
+ def y_index(value = y)
151
+ if height <= 0 || value <= y_min
152
+ 0
153
+
154
+ elsif value >= y_max
155
+ y_max_index
156
+
157
+ else
158
+ y_range.index(value)
159
+
160
+ end
161
+ end
162
+
163
+ # Returns the x coordinate as an offset index in the area's x range. When a
164
+ # value is provided, the x coordinate is overridden. Crudely corrects out of
165
+ # range values.
166
+ #
167
+ # @example
168
+ # # x_range = [4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
169
+ # # x = 8
170
+ # x_index # => 4
171
+ # x_index(11) # => 7
172
+ # x_index(2) # => 0
173
+ # x_index(15) # => 9
174
+ #
175
+ # @param value [Fixnum]
176
+ # @return [Fixnum]
177
+ def x_index(value = x)
178
+ if width <= 0 || value <= x_min
179
+ 0
180
+
181
+ elsif value >= x_max
182
+ x_max_index
183
+
184
+ else
185
+ x_range.index(value)
186
+
187
+ end
188
+ end
189
+
190
+ # Returns the actual position of y for a given index. Crudely corrects out
191
+ # of range values.
192
+ #
193
+ # @example
194
+ # # y_range = [7, 8, 9, 10, 11]
195
+ # y_position # => 7
196
+ # y_position(-2) # => 7
197
+ # y_position(2) # => 9
198
+ # y_position(7) # => 11
199
+ #
200
+ # @param index [Fixnum]
201
+ # @return [Fixnum]
202
+ def y_position(index = 0)
203
+ if index <= 0
204
+ y_min
205
+
206
+ elsif index >= y_max_index
207
+ y_max
208
+
209
+ else
210
+ y_range[index]
211
+
212
+ end
213
+ end
214
+
215
+ # Returns the actual position of x for a given index. Crudely corrects out
216
+ # of range values.
217
+ #
218
+ # @example
219
+ # # x_range = [4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
220
+ # x_position # => 4
221
+ # x_position(-2) # => 4
222
+ # x_position(2) # => 6
223
+ # x_position(15) # => 13
224
+ #
225
+ # @param index [Fixnum]
226
+ # @return [Fixnum]
227
+ def x_position(index = 0)
228
+ if index <= 0
229
+ x_min
230
+
231
+ elsif index >= x_max_index
232
+ x_max
233
+
234
+ else
235
+ x_range[index]
236
+
237
+ end
238
+ end
239
+
240
+ # Returns an array with all coordinates from x to x_max.
241
+ #
242
+ # @example
243
+ # # width = 10
244
+ # # x_min = 4
245
+ # # x_max = 14
246
+ # x_range # => [4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
247
+ #
248
+ # @return [Array]
249
+ def x_range
250
+ (x_min...x_max).to_a
251
+ end
252
+
253
+ # Returns an array with all coordinates from y to y_max.
254
+ #
255
+ # @example
256
+ # # height = 4
257
+ # # y_min = 7
258
+ # # y_max = 11
259
+ # y_range # => [7, 8, 9, 10]
260
+ #
261
+ # @return [Array]
262
+ def y_range
263
+ (y_min...y_max).to_a
264
+ end
265
+
266
+ private
267
+
268
+ # Returns the default attributes for an Area instance.
269
+ #
270
+ # @return [Hash]
271
+ def defaults
272
+ {
273
+ height: 0,
274
+ x_min: 0,
275
+ y_min: 0,
276
+ width: 0,
277
+ x: 0,
278
+ y: 0,
279
+ }
280
+ end
281
+
282
+ end # Area
283
+
284
+ end # Vedeu
@@ -32,7 +32,7 @@ module Vedeu
32
32
  Vedeu.log("Clearing view: '#{interface.name}'")
33
33
 
34
34
  rows.inject([colours]) do |line, index|
35
- line << interface.origin(index) { ' ' * interface.viewport_width }
35
+ line << interface.origin(index) { ' ' * interface.width }
36
36
  end.join
37
37
  end
38
38
 
@@ -40,16 +40,14 @@ module Vedeu
40
40
 
41
41
  attr_reader :interface
42
42
 
43
- # @api private
44
43
  # @return [String]
45
44
  def colours
46
45
  interface.colour.to_s
47
46
  end
48
47
 
49
- # @api private
50
48
  # @return [Enumerator]
51
49
  def rows
52
- interface.viewport_height.times
50
+ interface.height.times
53
51
  end
54
52
 
55
53
  end # Clear
@@ -36,7 +36,6 @@ module Vedeu
36
36
  # Renders the buffer unless empty, otherwise clears the area which the
37
37
  # interface occupies.
38
38
  #
39
- # @api private
40
39
  # @return [String]
41
40
  def view
42
41
  if buffer
@@ -51,7 +50,6 @@ module Vedeu
51
50
  # Combine the buffer attributes with the interface attributes. Buffer
52
51
  # presentation attributes will override interface defaults.
53
52
  #
54
- # @api private
55
53
  # @return [Hash]
56
54
  def new_interface
57
55
  combined = interface
@@ -63,7 +61,6 @@ module Vedeu
63
61
 
64
62
  # Returns the attributes of the named interface (layout).
65
63
  #
66
- # @api private
67
64
  # @return [Hash]
68
65
  def interface
69
66
  @_interface ||= Vedeu::Interfaces.find(name)
@@ -71,7 +68,6 @@ module Vedeu
71
68
 
72
69
  # Returns the attributes of the latest buffer (view).
73
70
  #
74
- # @api private
75
71
  # @return [Hash]
76
72
  def buffer
77
73
  @_buffer ||= Vedeu::Buffers.latest(name)
@@ -37,7 +37,7 @@ module Vedeu
37
37
  # Refresh an interface by name.
38
38
  #
39
39
  # @param name [String] The name of the interface to be refreshed.
40
- # @return [|BufferNotFound]
40
+ # @return [Array|BufferNotFound]
41
41
  def by_name(name)
42
42
  Vedeu::Compositor.render(name)
43
43
  end
@@ -46,7 +46,6 @@ module Vedeu
46
46
  # When the event is called, the interface, or all interfaces belonging to
47
47
  # the group with this name will be refreshed.
48
48
  #
49
- # @api private
50
49
  # @param type [Symbol]
51
50
  # @param name [String]
52
51
  # @param delay [Float]