Class: Tuile::Component::Window
- Inherits:
-
Component
- Object
- Component
- Tuile::Component::Window
- Includes:
- 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
-
#caption ⇒ String
@return — the current caption, empty by default.
-
#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.
- #children ⇒ Array<Component>
- #focusable? ⇒ Boolean
-
#frame_caption ⇒ String
The caption text as it appears in the rendered border, including the shortcut prefix when #key_shortcut is set.
-
#handle_mouse(event) ⇒ void
@param
event. -
#initialize(caption = "") ⇒ Window
constructor
@param
caption. -
#key_shortcut=(key) ⇒ void
@param
key. -
#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 into the Screen#buffer.
-
#scrollbar=(value) ⇒ void
@param
value.
Constructor Details
#initialize(caption = "") ⇒ Window
@param caption
18 19 20 21 22 23 24 25 26 27 28 29 |
# File 'lib/tuile/component/window.rb', line 18 def initialize(caption = "") super() @border_right = 1 @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
#caption ⇒ String
@return — the current caption, empty by default.
126 127 128 |
# File 'lib/tuile/component/window.rb', line 126 def caption @caption end |
#footer ⇒ Component?
@return — optional focusable component occupying the bottom border row, always spanning the full inner width.
35 36 37 |
# File 'lib/tuile/component/window.rb', line 35 def @footer end |
#footer_text ⇒ StyledString, ...
40 41 42 |
# File 'lib/tuile/component/window.rb', line 40 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.
204 205 206 207 208 209 210 211 212 213 |
# File 'lib/tuile/component/window.rb', line 204 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 |
#children ⇒ Array<Component>
90 91 92 |
# File 'lib/tuile/component/window.rb', line 90 def children @footer.nil? ? super : super + [@footer] end |
#focusable? ⇒ Boolean
31 |
# File 'lib/tuile/component/window.rb', line 31 def focusable? = true |
#frame_caption ⇒ String
The caption text as it appears in the rendered border, including the shortcut prefix when Tuile::Component#key_shortcut is set.
218 219 220 221 |
# File 'lib/tuile/component/window.rb', line 218 def frame_caption c = @caption || "" key_shortcut.nil? ? c : "[#{key_shortcut}]-#{c}" end |
#handle_mouse(event) ⇒ void
This method returns an undefined value.
@param event
96 97 98 99 100 101 102 |
# File 'lib/tuile/component/window.rb', line 96 def handle_mouse(event) if @footer&.rect&.contains?(event.point) @footer.handle_mouse(event) else super end end |
#key_shortcut=(key) ⇒ void
This method returns an undefined value.
@param key
155 156 157 158 159 |
# File 'lib/tuile/component/window.rb', line 155 def key_shortcut=(key) super # The shortcut key is shown in the caption — repaint. invalidate end |
#layout(content) ⇒ void
This method returns an undefined value.
@param content
165 166 167 |
# File 'lib/tuile/component/window.rb', line 165 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).
229 230 231 232 233 234 |
# File 'lib/tuile/component/window.rb', line 229 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.
2179 |
# File 'sig/tuile.rbs', line 2179
def on_focus: () -> void
|
#rect=(new_rect) ⇒ void
This method returns an undefined value.
@param new_rect
106 107 108 109 |
# File 'lib/tuile/component/window.rb', line 106 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.
146 147 148 149 150 151 |
# File 'lib/tuile/component/window.rb', line 146 def repaint return if rect.empty? super repaint_border end |
#repaint_border ⇒ void
This method returns an undefined value.
Paints the window border into the Screen#buffer. Title is clipped to the inner width so the box never overflows Tuile::Component#rect; when the window is active the whole border is drawn in Theme#active_border_color.
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
# File 'lib/tuile/component/window.rb', line 173 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 title = frame_caption.to_s title = title[0, inner_w] if title.length > inner_w dashes = "─" * (inner_w - title.length) fg = active? ? screen.theme.active_border_color : nil = StyledString::Style.new(fg: fg) buf = screen.buffer buf.set_line(left, top, StyledString.styled("┌#{title}#{dashes}┐", fg: fg)) (1..(h - 2)).each do |dy| buf.set_char(left, top + dy, "│", ) buf.set_char(left + w - 1, top + dy, "│", ) end buf.set_line(left, top + h - 1, bottom_border(inner_w, fg)) if h >= 2 end |
#scrollbar=(value) ⇒ void
This method returns an undefined value.
@param value
113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/tuile/component/window.rb', line 113 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 |