Class: RubyRich::Layout
- Inherits:
-
Object
- Object
- RubyRich::Layout
- Defined in:
- lib/ruby_rich/layout.rb
Instance Attribute Summary collapse
-
#children ⇒ Object
Returns the value of attribute children.
-
#content ⇒ Object
Returns the value of attribute content.
-
#dialog ⇒ Object
Returns the value of attribute dialog.
-
#height ⇒ Object
Returns the value of attribute height.
-
#live ⇒ Object
Returns the value of attribute live.
-
#name ⇒ Object
Returns the value of attribute name.
-
#parent ⇒ Object
Returns the value of attribute parent.
-
#ratio ⇒ Object
Returns the value of attribute ratio.
-
#show ⇒ Object
Returns the value of attribute show.
-
#size ⇒ Object
Returns the value of attribute size.
-
#split_direction ⇒ Object
readonly
Returns the value of attribute split_direction.
-
#width ⇒ Object
Returns the value of attribute width.
-
#x_offset ⇒ Object
Returns the value of attribute x_offset.
-
#y_offset ⇒ Object
Returns the value of attribute y_offset.
Instance Method Summary collapse
- #[](name) ⇒ Object
- #add_child(layout) ⇒ Object
- #calculate_dimensions(terminal_width, terminal_height) ⇒ Object
- #calculate_node_dimensions(available_width, available_height) ⇒ Object
- #draw ⇒ Object
- #find_by_name(name) ⇒ Object
- #hide_dialog ⇒ Object
-
#initialize(name: nil, ratio: 1, size: nil, width: nil, height: nil) ⇒ Layout
constructor
A new instance of Layout.
- #key(event_name, priority = 0, &block) ⇒ Object
- #notify_listeners(event_data) ⇒ Object
- #render ⇒ Object
- #render_dialog_into(buffer) ⇒ Object
- #render_into(buffer) ⇒ Object
- #render_to_buffer ⇒ Object
- #root ⇒ Object
- #show_dialog(dialog) ⇒ Object
- #split_column(*layouts) ⇒ Object
- #split_row(*layouts) ⇒ Object
- #update_content(content) ⇒ Object
Constructor Details
#initialize(name: nil, ratio: 1, size: nil, width: nil, height: nil) ⇒ Layout
Returns a new instance of Layout.
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# File 'lib/ruby_rich/layout.rb', line 7 def initialize(name: nil, ratio: 1, size: nil, width: nil, height: nil) @name = name @ratio = ratio @size = size @children = [] @content = nil @parent = nil @x_offset = 0 @y_offset = 0 @width = width if width @height = height if height @split_direction = nil @show = true @event_listeners = {} @event_intercepted = false end |
Instance Attribute Details
#children ⇒ Object
Returns the value of attribute children.
3 4 5 |
# File 'lib/ruby_rich/layout.rb', line 3 def children @children end |
#content ⇒ Object
Returns the value of attribute content.
3 4 5 |
# File 'lib/ruby_rich/layout.rb', line 3 def content @content end |
#dialog ⇒ Object
Returns the value of attribute dialog.
3 4 5 |
# File 'lib/ruby_rich/layout.rb', line 3 def dialog @dialog end |
#height ⇒ Object
Returns the value of attribute height.
4 5 6 |
# File 'lib/ruby_rich/layout.rb', line 4 def height @height end |
#live ⇒ Object
Returns the value of attribute live.
3 4 5 |
# File 'lib/ruby_rich/layout.rb', line 3 def live @live end |
#name ⇒ Object
Returns the value of attribute name.
3 4 5 |
# File 'lib/ruby_rich/layout.rb', line 3 def name @name end |
#parent ⇒ Object
Returns the value of attribute parent.
3 4 5 |
# File 'lib/ruby_rich/layout.rb', line 3 def parent @parent end |
#ratio ⇒ Object
Returns the value of attribute ratio.
3 4 5 |
# File 'lib/ruby_rich/layout.rb', line 3 def ratio @ratio end |
#show ⇒ Object
Returns the value of attribute show.
4 5 6 |
# File 'lib/ruby_rich/layout.rb', line 4 def show @show end |
#size ⇒ Object
Returns the value of attribute size.
3 4 5 |
# File 'lib/ruby_rich/layout.rb', line 3 def size @size end |
#split_direction ⇒ Object (readonly)
Returns the value of attribute split_direction.
5 6 7 |
# File 'lib/ruby_rich/layout.rb', line 5 def split_direction @split_direction end |
#width ⇒ Object
Returns the value of attribute width.
4 5 6 |
# File 'lib/ruby_rich/layout.rb', line 4 def width @width end |
#x_offset ⇒ Object
Returns the value of attribute x_offset.
4 5 6 |
# File 'lib/ruby_rich/layout.rb', line 4 def x_offset @x_offset end |
#y_offset ⇒ Object
Returns the value of attribute y_offset.
4 5 6 |
# File 'lib/ruby_rich/layout.rb', line 4 def y_offset @y_offset end |
Instance Method Details
#[](name) ⇒ Object
91 92 93 |
# File 'lib/ruby_rich/layout.rb', line 91 def [](name) find_by_name(name) end |
#add_child(layout) ⇒ Object
77 78 79 |
# File 'lib/ruby_rich/layout.rb', line 77 def add_child(layout) @children << layout end |
#calculate_dimensions(terminal_width, terminal_height) ⇒ Object
85 86 87 88 89 |
# File 'lib/ruby_rich/layout.rb', line 85 def calculate_dimensions(terminal_width, terminal_height) @x_offset = 0 @y_offset = 0 calculate_node_dimensions(terminal_width, terminal_height) end |
#calculate_node_dimensions(available_width, available_height) ⇒ Object
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 |
# File 'lib/ruby_rich/layout.rb', line 205 def calculate_node_dimensions(available_width, available_height) # 只在未设置宽度时计算宽度 @width ||= if @size [@size, available_width].min else available_width end # 只在未设置高度时计算高度 @height ||= if @size [@size, available_height].min else available_height end if @content.class == RubyRich::Panel @content.width = @width @content.height = @height end return if @children.empty? case @split_direction when :row remaining_width = @width fixed_children, flexible_children = @children.partition { |c| c.size } fixed_children.each do |child| child_width = [child.size, remaining_width].min child.width = child_width remaining_width -= child_width end total_ratio = flexible_children.sum(&:ratio) if total_ratio > 0 ratio_width = remaining_width.to_f / total_ratio flexible_children.each do |child| child_width = (child.ratio * ratio_width).floor child.width = child_width remaining_width -= child_width end flexible_children.last.width += remaining_width if remaining_width > 0 end @children.each { |child| child.height = @height } current_x = @x_offset @children.each do |child| child.x_offset = current_x child.y_offset = @y_offset current_x += child.width child.calculate_node_dimensions(child.width, child.height) end when :column remaining_height = @height fixed_children, flexible_children = @children.partition { |c| c.size } fixed_children.each do |child| child_height = [child.size, remaining_height].min child.height = child_height remaining_height -= child_height end total_ratio = flexible_children.sum(&:ratio) if total_ratio > 0 ratio_height = remaining_height.to_f / total_ratio flexible_children.each do |child| child_height = (child.ratio * ratio_height).floor child.height = child_height remaining_height -= child_height end flexible_children.last.height += remaining_height if remaining_height > 0 end @children.each { |child| child.width = @width } current_y = @y_offset @children.each do |child| child.x_offset = @x_offset child.y_offset = current_y current_y += child.height child.calculate_node_dimensions(child.width, child.height) end end end |
#draw ⇒ Object
128 129 130 |
# File 'lib/ruby_rich/layout.rb', line 128 def draw puts render end |
#find_by_name(name) ⇒ Object
95 96 97 98 99 100 101 102 |
# File 'lib/ruby_rich/layout.rb', line 95 def find_by_name(name) return self if @name == name @children.each do |child| result = child.find_by_name(name) return result if result end nil end |
#hide_dialog ⇒ Object
108 109 110 111 |
# File 'lib/ruby_rich/layout.rb', line 108 def hide_dialog @dialog.notify_listeners({:name=>:close}) @dialog = nil end |
#key(event_name, priority = 0, &block) ⇒ Object
24 25 26 27 28 29 30 |
# File 'lib/ruby_rich/layout.rb', line 24 def key(event_name, priority = 0, &block) unless @event_listeners[event_name] @event_listeners[event_name] = [] end @event_listeners[event_name] << { priority: priority, block: block } @event_listeners[event_name].sort_by! { |l| -l[:priority] } # Higher priority first end |
#notify_listeners(event_data) ⇒ Object
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/ruby_rich/layout.rb', line 37 def notify_listeners(event_data) return if @event_intercepted if @dialog @dialog.notify_listeners(event_data) else event_name = event_data[:name] if @event_listeners[event_name] @event_listeners[event_name].each do |listener| next if @event_intercepted result = listener[:block].call(event_data, self.root.live) if result == true @event_intercepted = true end end end unless @event_intercepted @children.each do |child| child.notify_listeners(event_data) end end end end |
#render ⇒ Object
113 114 115 116 117 |
# File 'lib/ruby_rich/layout.rb', line 113 def render # 将缓冲区转换为字符串(每行用换行符连接) buffer = render_to_buffer buffer.map { |line| line.compact.join("") }.join("\n") end |
#render_dialog_into(buffer) ⇒ Object
132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
# File 'lib/ruby_rich/layout.rb', line 132 def render_dialog_into(buffer) start_x = (@width - 2 - @dialog.width) / 2 + 1 start_y = (@height - 2 - @dialog.height) / 2 + 1 dialog_buffer = @dialog.render_to_buffer buffer.each_with_index do |arr, y| arr.each_with_index do |char, x| if x >= start_x && y >= start_y if y-start_y <= dialog_buffer.size-1 && x-start_x <= dialog_buffer[y-start_y].size-1 buffer[y][x] = dialog_buffer[y-start_y][x-start_x] end end end end end |
#render_into(buffer) ⇒ Object
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 |
# File 'lib/ruby_rich/layout.rb', line 147 def render_into(buffer) children.each { |child| child.render_into(buffer) } if children return unless content content_lines = if content.is_a?(String) content.split("\n")[0...height] else content.render[0...height] end content_lines.each_with_index do |line, line_index| y_pos = y_offset + line_index next if y_pos >= buffer.size in_escape = false escape_char = "" char_width = 0 # 初始宽度调整为0,方便位置计算 line.each_char do |char| # 处理ANSI转义码 if in_escape escape_char += char in_escape = false if char == 'm' if escape_char=="\e[0m" escape_char = "" end next elsif char.ord == 27 # 检测到转义开始符\e in_escape = true escape_char += char next end # 计算字符宽度 char_w = case char.ord when 0x0000..0x007F then 1 # 英文字符 when 0x4E00..0x9FFF then 2 # 中文字符 else Unicode::DisplayWidth.of(char) end # 计算字符的起始位置 x_start = x_offset + char_width # 超出右边界则跳过 next if x_start >= buffer[y_pos].size # 处理字符渲染(中文字符可能占用多个位置) char_w.times do |i| x_pos = x_start + i break if x_pos >= buffer[y_pos].size # 超出右边界停止 unless escape_char.empty? char = escape_char + char + "\e[0m" # 每次都记录字符的实际颜色 end buffer[y_pos][x_pos] = char unless i > 0 # 中文字符仅在第一个位置写入,避免覆盖 buffer[y_pos][x_pos+1] = nil if char_w == 2 end char_width += char_w # 更新累计宽度 end end end |
#render_to_buffer ⇒ Object
119 120 121 122 123 124 125 126 |
# File 'lib/ruby_rich/layout.rb', line 119 def render_to_buffer # 初始化缓冲区(二维数组,每个元素代表一个字符) buffer = Array.new(@height) { Array.new(@width, " ") } # 递归填充内容到缓冲区 render_into(buffer) render_dialog_into(buffer) if @dialog return buffer end |
#root ⇒ Object
61 62 63 |
# File 'lib/ruby_rich/layout.rb', line 61 def root @parent ? @parent.root : self end |
#show_dialog(dialog) ⇒ Object
104 105 106 |
# File 'lib/ruby_rich/layout.rb', line 104 def show_dialog(dialog) @dialog = dialog end |
#split_column(*layouts) ⇒ Object
71 72 73 74 75 |
# File 'lib/ruby_rich/layout.rb', line 71 def split_column(*layouts) @split_direction = :column layouts.each { |l| l.parent = self } @children.concat(layouts) end |
#split_row(*layouts) ⇒ Object
65 66 67 68 69 |
# File 'lib/ruby_rich/layout.rb', line 65 def split_row(*layouts) @split_direction = :row layouts.each { |l| l.parent = self } @children.concat(layouts) end |
#update_content(content) ⇒ Object
81 82 83 |
# File 'lib/ruby_rich/layout.rb', line 81 def update_content(content) @content = content end |