charming 0.1.2 → 0.1.3

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 (68) hide show
  1. checksums.yaml +4 -4
  2. data/lib/charming/application.rb +3 -3
  3. data/lib/charming/controller/class_methods.rb +2 -2
  4. data/lib/charming/controller/command_palette.rb +2 -2
  5. data/lib/charming/controller/rendering.rb +2 -2
  6. data/lib/charming/controller/session_state.rb +1 -1
  7. data/lib/charming/generators/component_generator.rb +1 -1
  8. data/lib/charming/generators/templates/app/application.template +1 -1
  9. data/lib/charming/generators/templates/app/layout.template +3 -6
  10. data/lib/charming/generators/templates/app/view.template +1 -1
  11. data/lib/charming/generators/templates/component/component.rb.template +1 -1
  12. data/lib/charming/generators/templates/screen/view.rb.template +1 -1
  13. data/lib/charming/generators/templates/view/view.rb.template +1 -1
  14. data/lib/charming/internal/renderer/differential.rb +13 -5
  15. data/lib/charming/internal/terminal/tty_backend.rb +22 -2
  16. data/lib/charming/presentation/component.rb +3 -5
  17. data/lib/charming/presentation/components/activity_indicator.rb +173 -134
  18. data/lib/charming/presentation/components/command_palette.rb +94 -96
  19. data/lib/charming/presentation/components/command_palette_modal.rb +33 -0
  20. data/lib/charming/presentation/components/empty_state.rb +47 -49
  21. data/lib/charming/presentation/components/form/builder.rb +52 -54
  22. data/lib/charming/presentation/components/form/confirm.rb +49 -51
  23. data/lib/charming/presentation/components/form/field.rb +94 -96
  24. data/lib/charming/presentation/components/form/input.rb +53 -55
  25. data/lib/charming/presentation/components/form/note.rb +27 -29
  26. data/lib/charming/presentation/components/form/select.rb +84 -86
  27. data/lib/charming/presentation/components/form/textarea.rb +67 -69
  28. data/lib/charming/presentation/components/form.rb +120 -122
  29. data/lib/charming/presentation/components/keyboard_handler.rb +41 -43
  30. data/lib/charming/presentation/components/list.rb +123 -125
  31. data/lib/charming/presentation/components/markdown.rb +21 -23
  32. data/lib/charming/presentation/components/modal.rb +46 -48
  33. data/lib/charming/presentation/components/progressbar.rb +51 -53
  34. data/lib/charming/presentation/components/spinner.rb +40 -42
  35. data/lib/charming/presentation/components/table.rb +109 -111
  36. data/lib/charming/presentation/components/text_area.rb +219 -221
  37. data/lib/charming/presentation/components/text_input.rb +120 -122
  38. data/lib/charming/presentation/components/viewport.rb +218 -220
  39. data/lib/charming/presentation/layout/builder.rb +64 -66
  40. data/lib/charming/presentation/layout/overlay.rb +48 -50
  41. data/lib/charming/presentation/layout/pane.rb +122 -118
  42. data/lib/charming/presentation/layout/rect.rb +14 -16
  43. data/lib/charming/presentation/layout/screen_layout.rb +40 -42
  44. data/lib/charming/presentation/layout/split.rb +101 -103
  45. data/lib/charming/presentation/layout.rb +28 -30
  46. data/lib/charming/presentation/markdown/block_renderers.rb +94 -96
  47. data/lib/charming/presentation/markdown/inline_renderers.rb +52 -54
  48. data/lib/charming/presentation/markdown/render_context.rb +12 -14
  49. data/lib/charming/presentation/markdown/renderer.rb +84 -86
  50. data/lib/charming/presentation/markdown/syntax_highlighter.rb +57 -59
  51. data/lib/charming/presentation/markdown.rb +4 -6
  52. data/lib/charming/presentation/template_view.rb +22 -24
  53. data/lib/charming/presentation/templates/erb_handler.rb +4 -6
  54. data/lib/charming/presentation/templates.rb +47 -49
  55. data/lib/charming/presentation/ui/ansi_codes.rb +66 -68
  56. data/lib/charming/presentation/ui/ansi_slicer.rb +67 -69
  57. data/lib/charming/presentation/ui/border.rb +24 -26
  58. data/lib/charming/presentation/ui/border_painter.rb +37 -39
  59. data/lib/charming/presentation/ui/canvas.rb +59 -61
  60. data/lib/charming/presentation/ui/style.rb +173 -175
  61. data/lib/charming/presentation/ui/theme.rb +133 -135
  62. data/lib/charming/presentation/ui/width.rb +12 -14
  63. data/lib/charming/presentation/ui.rb +69 -71
  64. data/lib/charming/presentation/view.rb +103 -105
  65. data/lib/charming/runtime.rb +23 -10
  66. data/lib/charming/version.rb +1 -1
  67. data/lib/charming.rb +3 -2
  68. metadata +2 -1
@@ -1,56 +1,54 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Charming
4
- module Presentation
5
- module Layout
6
- # Overlay is a compositing node used by ScreenLayout for floating elements (modals,
7
- # dialogs, command palettes). It positions its content at *top*/*left* (each may be
8
- # `:center` or an absolute cell offset) and optionally sizes it via *width*/*height*
9
- # with an outer *style*.
10
- class Overlay
11
- # The vertical and horizontal offset (cell count or `:center`) of the overlay
12
- # within the parent canvas.
13
- attr_reader :top, :left
14
-
15
- # *content* (or a *block*) provides the body. *top*/*left* default to :center.
16
- # *width*/*height* fix the overlay's dimensions; when unset, the content's natural
17
- # size is used. *style* wraps the rendered content in a UI::Style.
18
- def initialize(content: nil, block: nil, view: nil, top: :center, left: :center, width: nil, height: nil, style: nil)
19
- @content = content
20
- @block = block
21
- @view = view
22
- @top = top
23
- @left = left
24
- @width = width
25
- @height = height
26
- @style = style
27
- end
28
-
29
- # Renders the overlay's content; when *width* or *height* is set, places the rendered
30
- # content into a sized canvas before returning.
31
- def render
32
- return styled_content unless width || height
33
-
34
- UI.place(styled_content, width: width || UI.block_width(styled_content.lines(chomp: true)), height: height || styled_content.lines.count)
35
- end
36
-
37
- private
38
-
39
- # The raw content, body block, view, and sizing/style options.
40
- attr_reader :content, :block, :view, :width, :height, :style
41
-
42
- # Returns the rendered content wrapped in the configured *style* (when present).
43
- def styled_content
44
- return rendered_content unless style
45
-
46
- style.render(rendered_content)
47
- end
48
-
49
- # Evaluates the content (block or constant) and returns its rendered string.
50
- def rendered_content
51
- value = block ? view.instance_exec(&block) : content
52
- value.respond_to?(:render) ? value.render.to_s : value.to_s
53
- end
4
+ module Layout
5
+ # Overlay is a compositing node used by ScreenLayout for floating elements (modals,
6
+ # dialogs, command palettes). It positions its content at *top*/*left* (each may be
7
+ # `:center` or an absolute cell offset) and optionally sizes it via *width*/*height*
8
+ # with an outer *style*.
9
+ class Overlay
10
+ # The vertical and horizontal offset (cell count or `:center`) of the overlay
11
+ # within the parent canvas.
12
+ attr_reader :top, :left
13
+
14
+ # *content* (or a *block*) provides the body. *top*/*left* default to :center.
15
+ # *width*/*height* fix the overlay's dimensions; when unset, the content's natural
16
+ # size is used. *style* wraps the rendered content in a UI::Style.
17
+ def initialize(content: nil, block: nil, view: nil, top: :center, left: :center, width: nil, height: nil, style: nil)
18
+ @content = content
19
+ @block = block
20
+ @view = view
21
+ @top = top
22
+ @left = left
23
+ @width = width
24
+ @height = height
25
+ @style = style
26
+ end
27
+
28
+ # Renders the overlay's content; when *width* or *height* is set, places the rendered
29
+ # content into a sized canvas before returning.
30
+ def render
31
+ return styled_content unless width || height
32
+
33
+ UI.place(styled_content, width: width || UI.block_width(styled_content.lines(chomp: true)), height: height || styled_content.lines.count)
34
+ end
35
+
36
+ private
37
+
38
+ # The raw content, body block, view, and sizing/style options.
39
+ attr_reader :content, :block, :view, :width, :height, :style
40
+
41
+ # Returns the rendered content wrapped in the configured *style* (when present).
42
+ def styled_content
43
+ return rendered_content unless style
44
+
45
+ style.render(rendered_content)
46
+ end
47
+
48
+ # Evaluates the content (block or constant) and returns its rendered string.
49
+ def rendered_content
50
+ value = block ? view.instance_exec(&block) : content
51
+ value.respond_to?(:render) ? value.render.to_s : value.to_s
54
52
  end
55
53
  end
56
54
  end
@@ -1,143 +1,147 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Charming
4
- module Presentation
5
- module Layout
6
- # Pane is a leaf layout node: a single rectangle with optional border, padding, and
7
- # styling, containing a piece of content (a string, a View, or a block evaluated in the
8
- # view's context). Panes with a `name` and `focus: true` are registered as focusable
9
- # slots in the controller's focus ring.
10
- class Pane
11
- # The pane's focus slot name, fixed width, fixed height, and grow weight.
12
- attr_reader :name, :width, :height, :grow
13
-
14
- # *name* is the focus slot identifier (optional). *content* or *block* provides the body.
15
- # *width*/*height*/*grow* control sizing. *border* may be `true` (normal border) or a
16
- # border name symbol. *padding* may be 1, 2, or 4 values (CSS-style shorthand).
17
- # *style* sets the base style; *focused_style* overrides it when the pane is focused.
18
- # *focus: true* marks the pane as focusable. *scroll*/*clip*/*wrap* control how
19
- # overflow content is rendered (via the embedded Viewport).
20
- def initialize(name: nil, content: nil, block: nil, view: nil, width: nil, height: nil, grow: nil, border: nil, padding: nil, style: nil, focused_style: nil, focus: false, scroll: false, clip: true, wrap: false)
21
- @name = name
22
- @content = content
23
- @block = block
24
- @view = view
25
- @width = width
26
- @height = height
27
- @grow = grow
28
- @border = border
29
- @padding = padding
30
- @style = style
31
- @focused_style = focused_style
32
- @focus = focus
33
- @scroll = scroll
34
- @clip = clip
35
- @wrap = wrap
36
- end
4
+ module Layout
5
+ # Pane is a leaf layout node: a single rectangle with optional border, padding, and
6
+ # styling, containing a piece of content (a string, a View, or a block evaluated in the
7
+ # view's context). Panes with a `name` and `focus: true` are registered as focusable
8
+ # slots in the controller's focus ring.
9
+ class Pane
10
+ # The pane's focus slot name, fixed width, fixed height, and grow weight.
11
+ attr_reader :name, :width, :height, :grow
12
+
13
+ # *name* is the focus slot identifier (optional). *content* or *block* provides the body.
14
+ # *width*/*height*/*grow* control sizing. *border* may be `true` (normal border) or a
15
+ # border name symbol. *padding* may be 1, 2, or 4 values (CSS-style shorthand).
16
+ # *style* sets the base style; *focused_style* overrides it when the pane is focused.
17
+ # *focus: true* marks the pane as focusable. *scroll*/*clip*/*wrap* control how
18
+ # overflow content is rendered (via the embedded Viewport).
19
+ def initialize(name: nil, content: nil, block: nil, view: nil, width: nil, height: nil, grow: nil, border: nil, padding: nil, style: nil, focused_style: nil, focus: false, scroll: false, clip: true, wrap: false)
20
+ @name = name
21
+ @content = content
22
+ @block = block
23
+ @view = view
24
+ @width = width
25
+ @height = height
26
+ @grow = grow
27
+ @border = border
28
+ @padding = padding
29
+ @style = style
30
+ @focused_style = focused_style
31
+ @focus = focus
32
+ @scroll = scroll
33
+ @clip = clip
34
+ @wrap = wrap
35
+ end
37
36
 
38
- # Raises ArgumentError — panes are leaves and cannot contain layout children.
39
- def add_child(_node)
40
- raise ArgumentError, "pane cannot contain layout children"
41
- end
37
+ # Raises ArgumentError — panes are leaves and cannot contain layout children.
38
+ def add_child(_node)
39
+ raise ArgumentError, "pane cannot contain layout children"
40
+ end
42
41
 
43
- # Returns [name] when the pane is marked focusable and has a name, otherwise [].
44
- def focusable_names
45
- (focus && name) ? [name] : []
46
- end
42
+ # Returns [name] when the pane is marked focusable and has a name, otherwise [].
43
+ def focusable_names
44
+ (focus && name) ? [name] : []
45
+ end
47
46
 
48
- # Renders the pane into *rect*, applying the configured style, border, and padding
49
- # around the evaluated content.
50
- def render(rect)
51
- outer_style(rect).render(rendered_content(rect))
52
- end
47
+ # Renders the pane into *rect*, applying the configured style, border, and padding
48
+ # around the evaluated content.
49
+ def render(rect)
50
+ outer_style(rect).render(rendered_content(rect))
51
+ end
53
52
 
54
- private
53
+ private
55
54
 
56
- # The raw content, the body block, the view used for instance_exec, and styling options.
57
- attr_reader :content, :block, :view, :border, :padding, :style, :focused_style, :focus, :scroll, :clip, :wrap
55
+ # The raw content, the body block, the view used for instance_exec, and styling options.
56
+ attr_reader :content, :block, :view, :border, :padding, :style, :focused_style, :focus, :scroll, :clip, :wrap
58
57
 
59
- # Returns the content string for *rect*, optionally clipped/scrolled by an embedded Viewport.
60
- def rendered_content(rect)
61
- value = evaluate_content
62
- return value unless clip || scroll
58
+ # Returns the content string for *rect*, optionally clipped/scrolled by an embedded Viewport.
59
+ def rendered_content(rect)
60
+ content_rect = inner_rect(rect)
61
+ value = evaluate_content(content_rect)
62
+ return value unless clip || scroll
63
63
 
64
- Components::Viewport.new(content: value, width: inner_rect(rect).width, height: inner_rect(rect).height, wrap: wrap).render
65
- end
64
+ Components::Viewport.new(content: value, width: content_rect.width, height: content_rect.height, wrap: wrap).render
65
+ end
66
66
 
67
- # Evaluates the configured content (block or constant) and renders it to a string.
68
- def evaluate_content
69
- value = block ? view.instance_exec(&block) : content
70
- value.respond_to?(:render) ? value.render.to_s : value.to_s
67
+ # Evaluates the configured content (block or constant) and renders it to a string.
68
+ # Pane blocks may accept the inner content Rect when they need exact available dimensions.
69
+ def evaluate_content(content_rect)
70
+ value = if block
71
+ block.arity.zero? ? view.instance_exec(&block) : view.instance_exec(content_rect, &block)
72
+ else
73
+ content
71
74
  end
75
+ value.respond_to?(:render) ? value.render.to_s : value.to_s
76
+ end
72
77
 
73
- # Builds the outer style object with optional border and padding, sized to the
74
- # inner rect of the pane.
75
- def outer_style(rect)
76
- styled = current_style
77
- styled = styled.border(border_style) if border
78
- styled = styled.padding(*padding_values) if padding
79
- styled.width(inner_rect(rect).width).height(inner_rect(rect).height)
80
- end
78
+ # Builds the outer style object with optional border and padding, sized to the
79
+ # inner rect of the pane.
80
+ def outer_style(rect)
81
+ styled = current_style
82
+ styled = styled.border(border_style) if border
83
+ styled = styled.padding(*padding_values) if padding
84
+ styled.width(inner_rect(rect).width).height(inner_rect(rect).height)
85
+ end
81
86
 
82
- # Returns the active style: the focused variant when the pane is focused, otherwise
83
- # the configured style or a default UI::Style.
84
- def current_style
85
- return focused_pane_style if focused?
87
+ # Returns the active style: the focused variant when the pane is focused, otherwise
88
+ # the configured style or a default UI::Style.
89
+ def current_style
90
+ return focused_pane_style if focused?
86
91
 
87
- style || UI.style
88
- end
92
+ style || UI.style
93
+ end
89
94
 
90
- # Returns the focused-pane style: the focused_style override, or the theme's title style.
91
- def focused_pane_style
92
- focused_style || view.__send__(:theme).title
93
- end
95
+ # Returns the focused-pane style: the focused_style override, or the theme's title style.
96
+ def focused_pane_style
97
+ focused_style || view.__send__(:theme).title
98
+ end
94
99
 
95
- # True when the pane is configured for focus and the view reports it as currently focused.
96
- def focused?
97
- focus && name && view.focused?(name)
98
- end
100
+ # True when the pane is configured for focus and the view reports it as currently focused.
101
+ def focused?
102
+ focus && name && view.focused?(name)
103
+ end
99
104
 
100
- # Returns the inner Rect after border and padding insets are applied.
101
- def inner_rect(rect)
102
- rect.inset(
103
- top: border_top + padding_top,
104
- right: border_right + padding_right,
105
- bottom: border_bottom + padding_bottom,
106
- left: border_left + padding_left
107
- )
108
- end
105
+ # Returns the inner Rect after border and padding insets are applied.
106
+ def inner_rect(rect)
107
+ rect.inset(
108
+ top: border_top + padding_top,
109
+ right: border_right + padding_right,
110
+ bottom: border_bottom + padding_bottom,
111
+ left: border_left + padding_left
112
+ )
113
+ end
109
114
 
110
- # Resolves the border style symbol: :normal when border is `true`, otherwise the configured value.
111
- def border_style
112
- (border == true) ? :normal : border
113
- end
115
+ # Resolves the border style symbol: :normal when border is `true`, otherwise the configured value.
116
+ def border_style
117
+ (border == true) ? :normal : border
118
+ end
114
119
 
115
- # Border thickness on each side (1 when a border is configured, 0 otherwise).
116
- def border_top = border ? 1 : 0
117
- def border_right = border ? 1 : 0
118
- def border_bottom = border ? 1 : 0
119
- def border_left = border ? 1 : 0
120
+ # Border thickness on each side (1 when a border is configured, 0 otherwise).
121
+ def border_top = border ? 1 : 0
122
+ def border_right = border ? 1 : 0
123
+ def border_bottom = border ? 1 : 0
124
+ def border_left = border ? 1 : 0
120
125
 
121
- # The padding values normalized to [top, right, bottom, left] form.
122
- def padding_values
123
- @padding_values ||= expand_padding(Array(padding))
124
- end
126
+ # The padding values normalized to [top, right, bottom, left] form.
127
+ def padding_values
128
+ @padding_values ||= expand_padding(Array(padding))
129
+ end
125
130
 
126
- # Per-side padding values (0 when no padding is configured).
127
- def padding_top = padding ? padding_values[0] : 0
128
- def padding_right = padding ? padding_values[1] : 0
129
- def padding_bottom = padding ? padding_values[2] : 0
130
- def padding_left = padding ? padding_values[3] : 0
131
-
132
- # Normalizes 1/2/4 padding arguments to [top, right, bottom, left].
133
- def expand_padding(values)
134
- case values.length
135
- when 1 then [values[0], values[0], values[0], values[0]]
136
- when 2 then [values[0], values[1], values[0], values[1]]
137
- when 4 then values
138
- else
139
- raise ArgumentError, "padding expects 1, 2, or 4 values"
140
- end
131
+ # Per-side padding values (0 when no padding is configured).
132
+ def padding_top = padding ? padding_values[0] : 0
133
+ def padding_right = padding ? padding_values[1] : 0
134
+ def padding_bottom = padding ? padding_values[2] : 0
135
+ def padding_left = padding ? padding_values[3] : 0
136
+
137
+ # Normalizes 1/2/4 padding arguments to [top, right, bottom, left].
138
+ def expand_padding(values)
139
+ case values.length
140
+ when 1 then [values[0], values[0], values[0], values[0]]
141
+ when 2 then [values[0], values[1], values[0], values[1]]
142
+ when 4 then values
143
+ else
144
+ raise ArgumentError, "padding expects 1, 2, or 4 values"
141
145
  end
142
146
  end
143
147
  end
@@ -1,22 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Charming
4
- module Presentation
5
- module Layout
6
- # Rect is an immutable rectangle with a top-left position (x, y) and dimensions
7
- # (width, height). Layout operations produce new Rect instances rather than mutating
8
- # existing ones.
9
- Rect = Data.define(:x, :y, :width, :height) do
10
- # Returns a new Rect inset by *top*/*right*/*bottom*/*left* cells. The result is
11
- # clamped to a minimum width/height of 0.
12
- def inset(top: 0, right: 0, bottom: 0, left: 0)
13
- Rect.new(
14
- x: x + left,
15
- y: y + top,
16
- width: [width - left - right, 0].max,
17
- height: [height - top - bottom, 0].max
18
- )
19
- end
4
+ module Layout
5
+ # Rect is an immutable rectangle with a top-left position (x, y) and dimensions
6
+ # (width, height). Layout operations produce new Rect instances rather than mutating
7
+ # existing ones.
8
+ Rect = Data.define(:x, :y, :width, :height) do
9
+ # Returns a new Rect inset by *top*/*right*/*bottom*/*left* cells. The result is
10
+ # clamped to a minimum width/height of 0.
11
+ def inset(top: 0, right: 0, bottom: 0, left: 0)
12
+ Rect.new(
13
+ x: x + left,
14
+ y: y + top,
15
+ width: [width - left - right, 0].max,
16
+ height: [height - top - bottom, 0].max
17
+ )
20
18
  end
21
19
  end
22
20
  end
@@ -1,59 +1,57 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Charming
4
- module Presentation
5
- module Layout
6
- # ScreenLayout is the root of a layout tree. It owns a single child (typically a Split
7
- # or Pane) rendered into the full terminal screen, and an ordered list of Overlays
8
- # composited on top of the rendered body.
9
- class ScreenLayout
10
- # *screen* is the Charming::Screen whose dimensions define the layout area.
11
- # *background* (optional) is a UI::Style applied to the empty canvas behind the body.
12
- def initialize(screen:, background: nil)
13
- @screen = screen
14
- @background = background
15
- @child = nil
16
- @overlays = []
17
- end
4
+ module Layout
5
+ # ScreenLayout is the root of a layout tree. It owns a single child (typically a Split
6
+ # or Pane) rendered into the full terminal screen, and an ordered list of Overlays
7
+ # composited on top of the rendered body.
8
+ class ScreenLayout
9
+ # *screen* is the Charming::Screen whose dimensions define the layout area.
10
+ # *background* (optional) is a UI::Style applied to the empty canvas behind the body.
11
+ def initialize(screen:, background: nil)
12
+ @screen = screen
13
+ @background = background
14
+ @child = nil
15
+ @overlays = []
16
+ end
18
17
 
19
- # Sets the single root child. Raises ArgumentError when a child is already present.
20
- def add_child(node)
21
- raise ArgumentError, "screen_layout accepts one root layout node" if child
18
+ # Sets the single root child. Raises ArgumentError when a child is already present.
19
+ def add_child(node)
20
+ raise ArgumentError, "screen_layout accepts one root layout node" if child
22
21
 
23
- @child = node
24
- end
22
+ @child = node
23
+ end
25
24
 
26
- # Appends an overlay to be composited on top of the body, in registration order.
27
- def add_overlay(node)
28
- overlays << node
29
- end
25
+ # Appends an overlay to be composited on top of the body, in registration order.
26
+ def add_overlay(node)
27
+ overlays << node
28
+ end
30
29
 
31
- # Returns the focusable names from the child, or [] when no child has been added.
32
- def focusable_names
33
- child ? child.focusable_names : []
34
- end
30
+ # Returns the focusable names from the child, or [] when no child has been added.
31
+ def focusable_names
32
+ child ? child.focusable_names : []
33
+ end
35
34
 
36
- # Renders the child into the full-screen rect, then overlays each registered overlay
37
- # on top in order.
38
- def render
39
- body = UI.place(render_child, width: screen.width, height: screen.height, background: background)
35
+ # Renders the child into the full-screen rect, then overlays each registered overlay
36
+ # on top in order.
37
+ def render
38
+ body = UI.place(render_child, width: screen.width, height: screen.height, background: background)
40
39
 
41
- overlays.reduce(body) do |current, overlay|
42
- UI.overlay(current, overlay.render, top: overlay.top, left: overlay.left)
43
- end
40
+ overlays.reduce(body) do |current, overlay|
41
+ UI.overlay(current, overlay.render, top: overlay.top, left: overlay.left)
44
42
  end
43
+ end
45
44
 
46
- private
45
+ private
47
46
 
48
- # The screen, background style, the single child, and the list of overlays.
49
- attr_reader :screen, :background, :child, :overlays
47
+ # The screen, background style, the single child, and the list of overlays.
48
+ attr_reader :screen, :background, :child, :overlays
50
49
 
51
- # Renders the child into a full-screen Rect, or returns an empty string when no child.
52
- def render_child
53
- return "" unless child
50
+ # Renders the child into a full-screen Rect, or returns an empty string when no child.
51
+ def render_child
52
+ return "" unless child
54
53
 
55
- child.render(Rect.new(x: 0, y: 0, width: screen.width, height: screen.height))
56
- end
54
+ child.render(Rect.new(x: 0, y: 0, width: screen.width, height: screen.height))
57
55
  end
58
56
  end
59
57
  end