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 ⇒ Component?
@return — optional focusable component occupying the bottom border row, always spanning the full inner width.
-
#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 line.
-
#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
-
#handle_mouse(event) ⇒ void
@param
event. -
#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 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_line/#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 line: 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 31 |
# File 'lib/tuile/component/window.rb', line 20 def initialize(caption = nil) super() @border_right = 1 self.caption = caption @content = nil # Optional bottom-row widget slot (e.g. a search field), spanning the # full inner width; and optional bottom-border chrome text embedded in # the border line (mutually exclusive — the component, when present, # occupies the row and hides the text). @footer = nil @footer_text = StyledString::EMPTY end |
Instance Attribute Details
#footer ⇒ Component?
@return — optional focusable component occupying the bottom border row, always spanning the full inner width.
37 38 39 |
# File 'lib/tuile/component/window.rb', line 37 def @footer end |
#footer_text ⇒ StyledString, ...
42 43 44 |
# File 'lib/tuile/component/window.rb', line 42 def @footer_text end |
Instance Method Details
#bottom_border(inner_w, fg) ⇒ StyledString
Builds the bottom border line. 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.
197 198 199 200 201 202 203 204 205 206 |
# File 'lib/tuile/component/window.rb', line 197 def bottom_border(inner_w, fg) interior = if @footer || @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.
2348 |
# File 'sig/tuile.rbs', line 2348
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
2355 |
# File 'sig/tuile.rbs', line 2355
def caption=: ((String | StyledString)? new_caption) -> void
|
#focusable? ⇒ Boolean
33 |
# File 'lib/tuile/component/window.rb', line 33 def focusable? = true |
#handle_mouse(event) ⇒ void
This method returns an undefined value.
@param event
95 96 97 98 99 100 101 |
# File 'lib/tuile/component/window.rb', line 95 def handle_mouse(event) if @footer&.rect&.contains?(event.point) @footer.handle_mouse(event) else super end end |
#layout(content) ⇒ void
This method returns an undefined value.
@param content
146 147 148 |
# File 'lib/tuile/component/window.rb', line 146 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 over the bottom border row, spanning the full inner width (the only dimension a bottom-row widget needs — the window already knows it).
214 215 216 217 218 219 |
# File 'lib/tuile/component/window.rb', line 214 def return if @footer.nil? || rect.empty? width = [rect.width - 2, 0].max @footer.rect = Rect.new(rect.left + 1, rect.top + rect.height - 1, width, 1) end |
#on_focus ⇒ void
This method returns an undefined value.
2357 |
# File 'sig/tuile.rbs', line 2357
def on_focus: () -> void
|
#rect=(new_rect) ⇒ void
This method returns an undefined value.
@param new_rect
105 106 107 108 |
# File 'lib/tuile/component/window.rb', line 105 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.
135 136 137 138 139 140 |
# File 'lib/tuile/component/window.rb', line 135 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_line/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 lines 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.
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/tuile/component/window.rb', line 157 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_line(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_line(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
112 113 114 115 116 117 118 119 120 121 122 |
# File 'lib/tuile/component/window.rb', line 112 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 line: 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.
182 183 184 185 186 187 |
# File 'lib/tuile/component/window.rb', line 182 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 |