Class: Tuile::Component::Window
- Inherits:
-
Component
- Object
- Component
- Tuile::Component::Window
- Includes:
- HasCaption, HasContent
- Defined in:
- lib/tuile/component/window.rb,
sig/tuile.rbs
Overview
A window with a frame, a #caption and a content Tuile::Component. Doesn't support overlapping with other windows: it paints its entire contents and doesn't clip if there are other overlapping windows.
The window's content is unset by default; assign one via HasContent#content=.
Window is considered invisible if #rect is empty. The window won't draw when invisible. (Repaint of detached windows is short-circuited by #invalidate; subclasses don't need to re-check.)
Direct Known Subclasses
Instance Attribute Summary collapse
-
#footer_text ⇒ StyledString, ...
@return — optional chrome embedded into the bottom border line, mirroring #caption on the top line.
Attributes included from HasContent
Instance Method Summary collapse
-
#bottom_border(inner_w, fg) ⇒ StyledString
Builds the bottom border row.
-
#caption ⇒ StyledString
Read through this method, never
@caption— the ivar stays nil until the first non-empty set (#caption= short-circuits when unchanged). -
#caption= ⇒ void
Sets the caption and invalidates the component.
- #focusable? ⇒ Boolean
-
#footer ⇒ Component?
@return — optional focusable component occupying the bottom border row, always spanning the full inner width.
-
#footer=(new_footer) ⇒ void
Mounts a component in the bottom border row, spanning the full inner width and positioned automatically;
nilremoves it. -
#initialize(caption = nil) ⇒ Window
constructor
@param
caption— the border title, coerced the same way HasCaption#caption= coerces it. -
#layout(content) ⇒ void
@param
content. -
#layout_footer ⇒ void
Positions the footer slot over the bottom border row, spanning the full inner width (the only dimension a bottom-row widget needs — the window already knows it).
- #on_focus ⇒ void
-
#rect=(new_rect) ⇒ void
@param
new_rect. -
#repaint ⇒ void
Fully repaints the window: both frame and contents.
-
#repaint_border ⇒ void
Paints the window border via #draw_text/#draw_char, so the border cells inherit #effective_bg_color — a #bg_color on the window tints border and content alike.
-
#scrollbar=(value) ⇒ void
@param
value. -
#top_border(inner_w, fg) ⇒ StyledString
Builds the top border row: corners, #caption embedded at its own width, dashes filling the remainder.
Constructor Details
#initialize(caption = nil) ⇒ Window
@param caption — the border title, coerced the same way HasCaption#caption= coerces it.
20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/tuile/component/window.rb', line 20 def initialize(caption = nil) super() @border_right = 1 self.caption = caption @content = nil # The bottom row holds either a widget or chrome text, never both; see # the precedence note on #footer=. @footer_slot = Slot.new add_child(@footer_slot) # appended: the footer paints over the border row @footer_text = StyledString::EMPTY end |
Instance Attribute Details
#footer_text ⇒ StyledString, ...
41 42 43 |
# File 'lib/tuile/component/window.rb', line 41 def @footer_text end |
Instance Method Details
#bottom_border(inner_w, fg) ⇒ StyledString
Builds the bottom border row. The corners take the border color; the interior is plain dashes when a #footer component occupies the row (it overpaints them) or when there's no chrome, otherwise it carries #footer_text embedded at its own width — keeping the text's own styling — with dashes filling the remainder up to the inner width.
@param inner_w — the border's interior width.
@param fg — the active-border color, or nil when inactive.
167 168 169 170 171 172 173 174 175 176 |
# File 'lib/tuile/component/window.rb', line 167 def bottom_border(inner_w, fg) interior = if || @footer_text.empty? StyledString.styled("─" * inner_w, fg: fg) else = @footer_text.slice(0, inner_w) + StyledString.styled("─" * (inner_w - .display_width), fg: fg) end StyledString.styled("└", fg: fg) + interior + StyledString.styled("┘", fg: fg) end |
#caption ⇒ StyledString
Read through this method, never @caption — the ivar stays nil until
the first non-empty set (#caption= short-circuits when unchanged).
@return — the caption; empty when never set.
3625 |
# File 'sig/tuile.rbs', line 3625
def caption: () -> StyledString
|
#caption= ⇒ void
This method returns an undefined value.
Sets the caption and invalidates the component. No-op when unchanged. A
String is parsed via StyledString.parse (embedded ANSI is honored);
a StyledString is used as-is; nil clears it.
@param new_caption
3632 |
# File 'sig/tuile.rbs', line 3632
def caption=: ((String | StyledString)? new_caption) -> void
|
#focusable? ⇒ Boolean
32 |
# File 'lib/tuile/component/window.rb', line 32 def focusable? = true |
#footer ⇒ Component?
@return — optional focusable component occupying the bottom border row, always spanning the full inner width.
36 |
# File 'lib/tuile/component/window.rb', line 36 def = @footer_slot.content |
#footer=(new_footer) ⇒ void
This method returns an undefined value.
Mounts a component in the bottom border row, spanning the full inner
width and positioned automatically; nil removes it.
Precedence: a footer component present hides #footer_text; absent, the text embeds into the bottom border. No window needs both at once.
@param new_footer
67 68 69 70 71 |
# File 'lib/tuile/component/window.rb', line 67 def () @footer_slot.content = invalidate # repaint border row that the footer covers/uncovers end |
#layout(content) ⇒ void
This method returns an undefined value.
@param content
116 117 118 |
# File 'lib/tuile/component/window.rb', line 116 def layout(content) content.rect = Rect.new(rect.left + 1, rect.top + 1, rect.width - 1 - @border_right, rect.height - 2) end |
#layout_footer ⇒ void
This method returns an undefined value.
Positions the footer slot over the bottom border row, spanning the full inner width (the only dimension a bottom-row widget needs — the window already knows it).
An unoccupied slot gets an empty rect, not the row — a Slot clears whatever it is given, which would blank the border underneath.
187 188 189 190 191 192 193 194 195 |
# File 'lib/tuile/component/window.rb', line 187 def if .nil? || rect.empty? @footer_slot.rect = Rect.new(0, 0, 0, 0) return end width = [rect.width - 2, 0].max @footer_slot.rect = Rect.new(rect.left + 1, rect.top + rect.height - 1, width, 1) end |
#on_focus ⇒ void
This method returns an undefined value.
3634 |
# File 'sig/tuile.rbs', line 3634
def on_focus: () -> void
|
#rect=(new_rect) ⇒ void
This method returns an undefined value.
@param new_rect
75 76 77 78 |
# File 'lib/tuile/component/window.rb', line 75 def rect=(new_rect) super end |
#repaint ⇒ void
This method returns an undefined value.
Fully repaints the window: both frame and contents.
Window deliberately paints over its entire rect (border around the edge, content/footer over the interior), so we don't need the Tuile::Component#repaint default's auto-clear — but we do still want its "re-invalidate children" effect, since the border overpaints whatever the content/footer drew on the perimeter. Calling super handles both: the auto-clear is harmless (we re-paint over it), and the invalidation queues content + footer for repaint in the same cycle.
105 106 107 108 109 110 |
# File 'lib/tuile/component/window.rb', line 105 def repaint return if rect.empty? super repaint_border end |
#repaint_border ⇒ void
This method returns an undefined value.
Paints the window border via Tuile::Component#draw_text/Tuile::Component#draw_char, so the border cells inherit Tuile::Component#effective_bg_color — a Tuile::Component#bg_color on the window tints border and content alike. Both border rows are clipped by display width, so no caption overflows the box; when the window is active the whole border — the caption's own colors included — is drawn in Theme#active_border_color.
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/tuile/component/window.rb', line 127 def repaint_border return if rect.empty? w = rect.width h = rect.height top = rect.top left = rect.left inner_w = [w - 2, 0].max fg = active? ? screen.theme.active_border_color : nil = StyledString::Style.new(fg: fg) draw_text(left, top, top_border(inner_w, fg).slice(0, w)) (1..(h - 2)).each do |dy| draw_char(left, top + dy, "│", ) draw_char(left + w - 1, top + dy, "│", ) end draw_text(left, top + h - 1, bottom_border(inner_w, fg).slice(0, w)) if h >= 2 end |
#scrollbar=(value) ⇒ void
This method returns an undefined value.
@param value
82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/tuile/component/window.rb', line 82 def (value) unless content.respond_to?(:scrollbar_visibility=) raise Tuile::Error, "scrollbar= requires a content component that supports scrollbar_visibility=, got #{content.inspect}" end content. = value ? :visible : :gone @border_right = value ? 0 : 1 invalidate layout(content) end |
#top_border(inner_w, fg) ⇒ StyledString
Builds the top border row: corners, #caption embedded at its own
width, dashes filling the remainder. The caption keeps its own styling
unless fg is set — an active window's border claims it.
@param inner_w — the border's interior width.
@param fg — the active-border color, or nil when inactive.
152 153 154 155 156 157 |
# File 'lib/tuile/component/window.rb', line 152 def top_border(inner_w, fg) title = caption.slice(0, inner_w) title = title.with_fg(fg) if fg dashes = StyledString.styled("─" * (inner_w - title.display_width), fg: fg) StyledString.styled("┌", fg: fg) + title + dashes + StyledString.styled("┐", fg: fg) end |