Count total rendered lines for all messages (used for scroll calculations). Uses cached value when possible to avoid O(N) full re-render on every scroll.
(&mut self, chat_state: &ChatState)
| 97 | /// Count total rendered lines for all messages (used for scroll calculations). |
| 98 | /// Uses cached value when possible to avoid O(N) full re-render on every scroll. |
| 99 | pub fn count_message_lines(&mut self, chat_state: &ChatState) -> usize { |
| 100 | let width = self.messages_area.map(|a| a.width).unwrap_or(80); |
| 101 | |
| 102 | // Return cached value if still valid (set by render_messages each frame) |
| 103 | if !self.lines_cache_dirty |
| 104 | && self.cached_msg_count == chat_state.messages.len() |
| 105 | && self.cached_width == width |
| 106 | && self.cached_total_lines > 0 |
| 107 | { |
| 108 | return self.cached_total_lines; |
| 109 | } |
| 110 | |
| 111 | // Try to compute from per-message render cache (avoids full re-render) |
| 112 | let mut total = 0; |
| 113 | let mut all_cached = true; |
| 114 | for msg in &chat_state.messages { |
| 115 | if let Some(entry) = self.render_cache.get(&msg.id) { |
| 116 | if entry.version == msg.version && entry.width == width { |
| 117 | total += entry.line_count; |
| 118 | continue; |
| 119 | } |
| 120 | } |
| 121 | all_cached = false; |
| 122 | break; |
| 123 | } |
| 124 | |
| 125 | if all_cached { |
| 126 | return total; |
| 127 | } |
| 128 | |
| 129 | // Full fallback: render all messages to count lines |
| 130 | let mut total = 0; |
| 131 | for msg in &chat_state.messages { |
| 132 | total += self.render_message(msg, width).items.len(); |
| 133 | } |
| 134 | total |
| 135 | } |
| 136 | |
| 137 | /// Mark the line count cache as dirty (call when streaming content changes) |
| 138 | pub fn invalidate_lines_cache(&mut self) { |
no test coverage detected