浏览代码

app/chatview: fix multiline msgs

jkds 7 月之前
父节点
当前提交
9af3c403a7
共有 3 个文件被更改,包括 185 次插入90 次删除
  1. 4 4
      bin/app/src/app/schema/mod.rs
  2. 19 10
      bin/app/src/ui/chatview/mod.rs
  3. 162 76
      bin/app/src/ui/chatview/page.rs

+ 4 - 4
bin/app/src/app/schema/mod.rs

@@ -540,8 +540,8 @@ pub async fn make(app: &App, window: SceneNodePtr, i18n_fish: &I18nBabelFish) {
     menu::make(app, content.clone(), i18n_fish).await;
 
     // @@@ Debug stuff @@@
-    //let chatview_node = app.sg_root.lookup_node("/window/content/dev_chat_layer").unwrap();
-    //chatview_node.set_property_bool(atom, Role::App, "is_visible", true).unwrap();
-    //let menu_node = app.sg_root.lookup_node("/window/content/menu_layer").unwrap();
-    //menu_node.set_property_bool(atom, Role::App, "is_visible", false).unwrap();
+    let chatview_node = app.sg_root.lookup_node("/window/content/dev_chat_layer").unwrap();
+    chatview_node.set_property_bool(atom, Role::App, "is_visible", true).unwrap();
+    let menu_node = app.sg_root.lookup_node("/window/content/menu_layer").unwrap();
+    menu_node.set_property_bool(atom, Role::App, "is_visible", false).unwrap();
 }

+ 19 - 10
bin/app/src/ui/chatview/mod.rs

@@ -451,7 +451,9 @@ impl ChatView {
             t!("Inserting new message");
 
             // Insert the privmsg since it doesn't already exist
-            let privmsg = msgbuf.insert_privmsg(timest, msg_id.clone(), nick.clone(), text.clone());
+            let privmsg = msgbuf
+                .insert_privmsg(timest, msg_id.clone(), nick.clone(), text.clone(), self.rect.get())
+                .await;
             if privmsg.is_none() {
                 // Not visible so no need to redraw
                 return
@@ -496,7 +498,11 @@ impl ChatView {
 
         // Add message to page
         let mut msgbuf = self.msgbuf.lock().await;
-        let Some(privmsg) = msgbuf.insert_privmsg(timest, msg_id, nick, text) else { return };
+        let Some(privmsg) =
+            msgbuf.insert_privmsg(timest, msg_id, nick, text, self.rect.get()).await
+        else {
+            return
+        };
         privmsg.confirmed = false;
         let atom = self.render_api.make_guard(gfxtag!("ChatView::handle_insert_unconf_line"));
         self.redraw_cached(atom.batch_id, &mut msgbuf).await;
@@ -558,7 +564,7 @@ impl ChatView {
 
         let mut msgbuf = self.msgbuf.lock().await;
 
-        let total_height = msgbuf.calc_total_height().await;
+        let total_height = msgbuf.calc_total_height(&rect).await;
         if total_height > top + preload_height {
             // Nothing to do here
             //t!("bgloader: buffer is sufficient [trace_id={trace_id}]");
@@ -598,12 +604,15 @@ impl ChatView {
             let chatmsg: ChatMsg = deserialize(&v).unwrap();
 
             //t!("{timest:?} {chatmsg:?} [trace_id={trace_id}]");
-            let msg_height = msgbuf.push_privmsg(
-                timest,
-                msg_id.clone(),
-                chatmsg.nick.clone(),
-                chatmsg.text.clone(),
-            );
+            let msg_height = msgbuf
+                .push_privmsg(
+                    timest,
+                    msg_id.clone(),
+                    chatmsg.nick.clone(),
+                    chatmsg.text.clone(),
+                    &rect,
+                )
+                .await;
 
             if let Some(url) = get_file_url(&chatmsg.text) {
                 msgbuf.insert_filemsg(
@@ -678,7 +687,7 @@ impl ChatView {
             return Some(0.)
         }
 
-        let total_height = msgbuf.calc_total_height().await;
+        let total_height = msgbuf.calc_total_height(&self.rect.get()).await;
         let max_allowed_scroll = if total_height > rect_h { total_height - rect_h } else { 0. };
 
         if scroll > max_allowed_scroll {

+ 162 - 76
bin/app/src/ui/chatview/page.rs

@@ -64,6 +64,7 @@ pub struct PrivMessage {
     is_selected: bool,
 
     mesh_cache: Option<Vec<DrawInstruction>>,
+    txt_layout: Option<parley::Layout<Color>>,
 }
 
 impl PrivMessage {
@@ -92,6 +93,7 @@ impl PrivMessage {
             confirmed: true,
             is_selected: false,
             mesh_cache: None,
+            txt_layout: None,
         })
     }
 
@@ -102,55 +104,22 @@ impl PrivMessage {
     }
 
     fn height(&self, line_height: f32) -> f32 {
-        // TODO: calculate based on actual layout height
-        // For now, assume a single line
-        line_height
+        self.txt_layout.as_ref().unwrap().len() as f32 * line_height
     }
 
-    async fn gen_mesh(
+    fn cache_txt_layout(
         &mut self,
+        txt_ctx: &mut text::TextContext,
         clip: &Rectangle,
         line_height: f32,
-        msg_spacing: f32,
         timestamp_width: f32,
         nick_colors: &[Color],
-        timestamp_color: Color,
         text_color: Color,
-        hi_bg_color: Color,
-        render_api: &RenderApi,
-    ) -> Vec<DrawInstruction> {
-        if let Some(instrs) = &self.mesh_cache {
-            return instrs.clone()
-        }
-
-        let mut all_instrs = vec![DrawInstruction::Move(Point::new(0., -line_height))];
-
-        // Draw selection background if selected
-        if self.is_selected {
-            let height = self.height(line_height) + msg_spacing;
-            let mut mesh = MeshBuilder::new(gfxtag!("chatview_privmsg_sel"));
-            mesh.draw_filled_box(
-                &Rectangle { x: 0., y: -height, w: clip.w, h: height },
-                hi_bg_color,
-            );
-            all_instrs
-                .push(DrawInstruction::Draw(mesh.alloc(render_api).draw_with_textures(vec![])));
+    ) {
+        if self.txt_layout.is_some() {
+            return
         }
 
-        let mut txt_ctx = text::TEXT_CTX.get().await;
-
-        // Timestamp layout
-        let timestr = Self::gen_timestr(self.timestamp);
-        let timestamp_layout = txt_ctx.make_layout(
-            &timestr,
-            timestamp_color,
-            self.timestamp_font_size,
-            line_height / self.timestamp_font_size,
-            self.window_scale,
-            None,
-            &[],
-        );
-
         // Message text layout
         let linetext = if self.nick == "NOTICE" {
             self.text.clone()
@@ -160,7 +129,7 @@ impl PrivMessage {
 
         let nick_color = select_nick_color(&self.nick, nick_colors);
 
-        let text_layout = if self.nick == "NOTICE" {
+        let txt_layout = if self.nick == "NOTICE" {
             txt_ctx.make_layout(
                 &linetext,
                 text_color,
@@ -184,6 +153,64 @@ impl PrivMessage {
                 &[(0..nick_end, nick_color)],
             )
         };
+        self.txt_layout = Some(txt_layout);
+    }
+
+    async fn gen_mesh(
+        &mut self,
+        clip: &Rectangle,
+        line_height: f32,
+        msg_spacing: f32,
+        timestamp_width: f32,
+        nick_colors: &[Color],
+        timestamp_color: Color,
+        text_color: Color,
+        hi_bg_color: Color,
+        render_api: &RenderApi,
+    ) -> Vec<DrawInstruction> {
+        if let Some(instrs) = &self.mesh_cache {
+            assert!(self.txt_layout.is_some());
+            return instrs.clone()
+        }
+
+        let mut txt_ctx = text::TEXT_CTX.get().await;
+
+        // Timestamp layout
+        let timestr = Self::gen_timestr(self.timestamp);
+        let timestamp_layout = txt_ctx.make_layout(
+            &timestr,
+            timestamp_color,
+            self.timestamp_font_size,
+            line_height / self.timestamp_font_size,
+            self.window_scale,
+            None,
+            &[],
+        );
+
+        self.cache_txt_layout(
+            &mut txt_ctx,
+            clip,
+            line_height,
+            timestamp_width,
+            nick_colors,
+            text_color,
+        );
+
+        drop(txt_ctx);
+
+        let mut all_instrs = vec![];
+
+        // Draw selection background if selected
+        if self.is_selected {
+            let height = self.height(line_height) + msg_spacing;
+            let mut mesh = MeshBuilder::new(gfxtag!("chatview_privmsg_sel"));
+            mesh.draw_filled_box(
+                &Rectangle { x: 0., y: -height, w: clip.w, h: height },
+                hi_bg_color,
+            );
+            all_instrs
+                .push(DrawInstruction::Draw(mesh.alloc(render_api).draw_with_textures(vec![])));
+        }
 
         // Render timestamp
         let timestamp_instrs =
@@ -192,8 +219,11 @@ impl PrivMessage {
 
         // Render message text offset by timestamp_width
         all_instrs.push(DrawInstruction::Move(Point::new(timestamp_width, 0.)));
-        let text_instrs =
-            text::render_layout(&text_layout, render_api, gfxtag!("chatview_privmsg_text"));
+        let text_instrs = text::render_layout(
+            self.txt_layout.as_ref().unwrap(),
+            render_api,
+            gfxtag!("chatview_privmsg_text"),
+        );
         all_instrs.extend(text_instrs);
 
         self.mesh_cache = Some(all_instrs.clone());
@@ -210,6 +240,7 @@ impl PrivMessage {
     fn clear_mesh(&mut self) {
         // Auto-deletes when refs are dropped
         self.mesh_cache = None;
+        self.txt_layout = None;
     }
 
     fn select(&mut self) {
@@ -288,11 +319,7 @@ impl DateMessage {
         );
         drop(txt_ctx);
 
-        let mut txt_instrs = text::render_layout(&layout, render_api, gfxtag!("chatview_datemsg"));
-        let mut instrs = Vec::with_capacity(1 + txt_instrs.len());
-        instrs.push(DrawInstruction::Move(Point::new(0., -line_height)));
-        instrs.append(&mut txt_instrs);
-
+        let mut instrs = text::render_layout(&layout, render_api, gfxtag!("chatview_datemsg"));
         // Cache the instructions
         self.mesh_cache = Some(instrs.clone());
         instrs
@@ -510,10 +537,9 @@ impl FileMessage {
         all_instrs
             .push(DrawInstruction::Move(Point::new(timestamp_width + Self::BOX_PADDING_X, 0.)));
         for layout in layouts {
-            all_instrs.push(DrawInstruction::Move(Point::new(0., -line_height)));
-
             let instrs = text::render_layout(&layout, render_api, gfxtag!("chatview_filemsg_text"));
             all_instrs.extend(instrs);
+            all_instrs.push(DrawInstruction::Move(Point::new(0., -line_height)));
         }
 
         self.mesh_cache = Some(all_instrs.clone());
@@ -626,6 +652,31 @@ impl Message {
         }
     }
 
+    fn cache_txt_layout(
+        &mut self,
+        txt_ctx: &mut text::TextContext,
+        clip: &Rectangle,
+        line_height: f32,
+        timestamp_width: f32,
+        nick_colors: &[Color],
+        text_color: Color,
+    ) {
+        match self {
+            Self::Priv(m) => {
+                m.cache_txt_layout(
+                    txt_ctx,
+                    clip,
+                    line_height,
+                    timestamp_width,
+                    nick_colors,
+                    text_color,
+                );
+            }
+            Self::Date(_) => {}
+            Self::File(_) => {}
+        }
+    }
+
     async fn gen_mesh(
         &mut self,
         clip: &Rectangle,
@@ -799,10 +850,13 @@ impl MessageBuffer {
         }
     }
 
-    pub async fn calc_total_height(&mut self) -> f32 {
+    pub async fn calc_total_height(&mut self, rect: &Rectangle) -> f32 {
         let line_height = self.line_height.get();
         let baseline = self.baseline.get();
+        let timestamp_width = self.timestamp_width.get();
         let msg_spacing = self.msg_spacing.get();
+        let text_color = self.text_color.get();
+        let nick_colors = self.read_nick_colors();
         let mut height = 0.;
 
         let msgs = self.msgs_with_date();
@@ -817,6 +871,17 @@ impl MessageBuffer {
                 height += msg_spacing;
             }
 
+            let mut txt_ctx = text::TEXT_CTX.get().await;
+            msg.cache_txt_layout(
+                &mut txt_ctx,
+                &rect,
+                line_height,
+                timestamp_width,
+                &nick_colors,
+                text_color,
+            );
+            drop(txt_ctx);
+
             height += msg.height(line_height);
         }
 
@@ -847,20 +912,24 @@ impl MessageBuffer {
         return true
     }
 
-    pub fn insert_privmsg(
+    pub async fn insert_privmsg(
         &mut self,
         timest: Timestamp,
         msg_id: MessageId,
         nick: String,
         text: String,
+        rect: Rectangle,
     ) -> Option<&mut PrivMessage> {
         t!("insert_privmsg({timest}, {msg_id}, {nick}, {text})");
+        let line_height = self.line_height.get();
         let font_size = self.font_size.get();
         let timestamp_font_size = self.timestamp_font_size.get();
         let timestamp_width = self.timestamp_width.get();
         let window_scale = self.window_scale.get();
+        let text_color = self.text_color.get();
+        let nick_colors = self.read_nick_colors();
 
-        let msg = PrivMessage::new(
+        let mut msg = PrivMessage::new(
             font_size,
             timestamp_font_size,
             window_scale,
@@ -870,6 +939,17 @@ impl MessageBuffer {
             text,
         );
 
+        let mut txt_ctx = text::TEXT_CTX.get().await;
+        msg.cache_txt_layout(
+            &mut txt_ctx,
+            &rect,
+            line_height,
+            timestamp_width,
+            &nick_colors,
+            text_color,
+        );
+        drop(txt_ctx);
+
         if self.msgs.is_empty() {
             self.msgs.push(msg);
             return self.msgs.last_mut().unwrap().get_privmsg_mut()
@@ -905,20 +985,24 @@ impl MessageBuffer {
         return self.msgs[idx].get_privmsg_mut()
     }
 
-    pub fn push_privmsg(
+    pub async fn push_privmsg(
         &mut self,
         timest: Timestamp,
         msg_id: MessageId,
         nick: String,
         text: String,
+        rect: &Rectangle,
     ) -> f32 {
         //t!("push_privmsg({timest}, {msg_id}, {nick}, {text})");
+        let line_height = self.line_height.get();
         let font_size = self.font_size.get();
         let timestamp_font_size = self.timestamp_font_size.get();
         let timestamp_width = self.timestamp_width.get();
         let window_scale = self.window_scale.get();
+        let text_color = self.text_color.get();
+        let nick_colors = self.read_nick_colors();
 
-        let msg = PrivMessage::new(
+        let mut msg = PrivMessage::new(
             font_size,
             timestamp_font_size,
             window_scale,
@@ -928,13 +1012,18 @@ impl MessageBuffer {
             text,
         );
 
-        let msg_height = msg.height(self.line_height.get());
-
-        if self.msgs.is_empty() {
-            self.msgs.push(msg);
-            return msg_height
-        }
+        let mut txt_ctx = text::TEXT_CTX.get().await;
+        msg.cache_txt_layout(
+            &mut txt_ctx,
+            rect,
+            line_height,
+            timestamp_width,
+            &nick_colors,
+            text_color,
+        );
+        drop(txt_ctx);
 
+        let msg_height = msg.height(self.line_height.get());
         self.msgs.push(msg);
         msg_height
     }
@@ -964,19 +1053,6 @@ impl MessageBuffer {
         let mut meshes = vec![];
         let mut current_pos = 0.;
         while let Some(msg) = msgs.next().await {
-            let mesh_height = msg.height(line_height);
-            let msg_bottom = current_pos;
-            let msg_top = current_pos + mesh_height;
-
-            if msg_bottom > scroll + rect.h {
-                break
-            }
-            if msg_top < scroll {
-                current_pos += msg_spacing;
-                current_pos += mesh_height;
-                continue
-            }
-
             let instrs = msg
                 .gen_mesh(
                     rect,
@@ -991,10 +1067,20 @@ impl MessageBuffer {
                 )
                 .await;
 
-            meshes.push((current_pos, instrs));
+            let mesh_height = msg.height(line_height);
+            current_pos += msg_spacing + mesh_height;
 
-            current_pos += msg_spacing;
-            current_pos += mesh_height;
+            let msg_top = current_pos;
+            let msg_bottom = current_pos - mesh_height;
+
+            if msg_bottom > scroll + rect.h {
+                break
+            }
+            if msg_top < scroll {
+                continue
+            }
+
+            meshes.push((current_pos, instrs));
         }
         meshes
     }