Explorar el Código

app/chatview: migrate DateMessage to text2 API

jkds hace 7 meses
padre
commit
3564bd1f84

+ 2 - 2
bin/app/Makefile

@@ -22,7 +22,7 @@ DEBUG_FEATURES = --features=enable-filelog,enable-plugins
 
 #DEV_FEATURES = --features=enable-filelog,enable-netdebug,emulate-android
 #DEV_FEATURES = --features=enable-filelog,enable-netdebug,enable-plugins
-DEV_FEATURES = --features=enable-plugins
+DEV_FEATURES = --features=schema-test
 
 default: build-release
 	./darkfi-app
@@ -82,7 +82,7 @@ assets/forest_720x1280.mp4:
 # Developer targets
 
 dev: $(SRC) fonts assets/forest_1920x1080.ivf
-	$(CARGO) lbuild $(DEV_FEATURES)
+	$(CARGO) lbuild --no-default-features $(DEV_FEATURES)
 	-mv target/debug/darkfi-app .
 	./darkfi-app
 

+ 0 - 34
bin/app/src/app/mod.rs

@@ -260,37 +260,3 @@ impl App {
         });
     }
 }
-
-// Just for testing
-#[allow(dead_code)]
-fn populate_tree(tree: &sled::Tree) {
-    let chat_txt = include_str!("../../data/chat.txt");
-    for line in chat_txt.lines() {
-        let parts: Vec<&str> = line.splitn(3, ' ').collect();
-        assert_eq!(parts.len(), 3);
-        let time_parts: Vec<&str> = parts[0].splitn(2, ':').collect();
-        let (hour, min) = (time_parts[0], time_parts[1]);
-        let hour = hour.parse::<u32>().unwrap();
-        let min = min.parse::<u32>().unwrap();
-        let dt: NaiveDateTime =
-            NaiveDate::from_ymd_opt(2024, 8, 6).unwrap().and_hms_opt(hour, min, 0).unwrap();
-        let timest = dt.and_utc().timestamp_millis() as u64;
-
-        let nick = parts[1].to_string();
-        let text = parts[2].to_string();
-
-        // serial order is important here
-        let timest = timest.to_be_bytes();
-        assert_eq!(timest.len(), 8);
-        let mut key = [0u8; 8 + 32];
-        key[..8].clone_from_slice(&timest);
-
-        let msg = chatview::ChatMsg { nick, text };
-        let mut val = vec![];
-        msg.encode(&mut val).unwrap();
-
-        tree.insert(&key, val).unwrap();
-    }
-    // O(n)
-    d!("populated db with {} lines", tree.len());
-}

+ 35 - 0
bin/app/src/app/schema/chat.rs

@@ -1604,3 +1604,38 @@ pub async fn make(
     });
     app.tasks.lock().unwrap().push(editz_text_task);
 }
+
+// Just for testing
+#[allow(dead_code)]
+pub(super) fn populate_tree(tree: &sled::Tree) {
+    use chrono::{NaiveDate, NaiveDateTime};
+    let chat_txt = include_str!("../../../data/chat.txt");
+    for line in chat_txt.lines() {
+        let parts: Vec<&str> = line.splitn(3, ' ').collect();
+        assert_eq!(parts.len(), 3);
+        let time_parts: Vec<&str> = parts[0].splitn(2, ':').collect();
+        let (hour, min) = (time_parts[0], time_parts[1]);
+        let hour = hour.parse::<u32>().unwrap();
+        let min = min.parse::<u32>().unwrap();
+        let dt: NaiveDateTime =
+            NaiveDate::from_ymd_opt(2024, 8, 6).unwrap().and_hms_opt(hour, min, 0).unwrap();
+        let timest = dt.and_utc().timestamp_millis() as u64;
+
+        let nick = parts[1].to_string();
+        let text = parts[2].to_string();
+
+        // serial order is important here
+        let timest = timest.to_be_bytes();
+        assert_eq!(timest.len(), 8);
+        let mut key = [0u8; 8 + 32];
+        key[..8].clone_from_slice(&timest);
+
+        let msg = chatview::ChatMsg { nick, text };
+        let mut val = vec![];
+        msg.encode(&mut val).unwrap();
+
+        tree.insert(&key, val).unwrap();
+    }
+    // O(n)
+    debug!(target: "app::schema", "populated db with {} lines", tree.len());
+}

+ 18 - 9
bin/app/src/app/schema/test.rs

@@ -16,33 +16,37 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
+use sled_overlay::sled;
+
 use crate::{
     app::{
         node::{
-            create_layer, create_singleline_edit, create_text, create_vector_art, create_video,
+            create_chatview, create_layer, create_singleline_edit, create_text, create_vector_art,
+            create_video,
         },
         App,
     },
-    expr,
+    expr::{self, Compiler},
     mesh::COLOR_PURPLE,
     prop::{PropertyAtomicGuard, PropertyFloat32, Role},
     scene::SceneNodePtr,
-    ui::{BaseEdit, BaseEditType, Layer, Text, VectorArt, VectorShape, Video},
+    ui::{BaseEdit, BaseEditType, ChatView, Layer, Text, VectorArt, VectorShape, Video},
     util::i18n::I18nBabelFish,
 };
+use super::chat::populate_tree;
 
 const LIGHTMODE: bool = false;
 
 #[cfg(target_os = "android")]
 mod ui_consts {
-    //pub const CHATDB_PATH: &str = "/data/data/darkfi.app/chatdb/";
+    pub const CHATDB_PATH: &str = "/data/data/darkfi.app/chatdb/";
     //pub const KING_PATH: &str = "king.png";
     pub const VID_PATH: &str = "forest_720x1280.mp4";
 }
 
 #[cfg(not(target_os = "android"))]
 mod ui_consts {
-    //pub const CHATDB_PATH: &str = "chatdb";
+    pub const CHATDB_PATH: &str = "chatdb";
     //pub const KING_PATH: &str = "assets/king.png";
     pub const VID_PATH: &str = "assets/forest_1920x1080.ivf";
 }
@@ -61,6 +65,8 @@ pub async fn make(app: &App, window: SceneNodePtr, i18n_fish: &I18nBabelFish) {
     )
     .unwrap();
 
+    let mut cc = Compiler::new();
+
     // Create a layer called view
     let layer_node = create_layer("view");
     let prop = layer_node.get_property("rect").unwrap();
@@ -226,6 +232,7 @@ pub async fn make(app: &App, window: SceneNodePtr, i18n_fish: &I18nBabelFish) {
     */
 
     // Create KING GNU!
+    /*
     let node = create_video("king");
     let prop = node.get_property("rect").unwrap();
     prop.set_f32(atom, Role::App, 0, 80.).unwrap();
@@ -236,6 +243,7 @@ pub async fn make(app: &App, window: SceneNodePtr, i18n_fish: &I18nBabelFish) {
     node.set_property_u32(atom, Role::App, "z_index", 1).unwrap();
     let node = node.setup(|me| Video::new(me, app.render_api.clone(), app.ex.clone())).await;
     layer_node.link(node);
+    */
 
     // Create some text
     let node = create_text("label");
@@ -261,13 +269,13 @@ pub async fn make(app: &App, window: SceneNodePtr, i18n_fish: &I18nBabelFish) {
     prop.set_f32(atom, Role::App, 2, 0.).unwrap();
     prop.set_f32(atom, Role::App, 3, 1.).unwrap();
     node.set_property_u32(atom, Role::App, "z_index", 1).unwrap();
+    node.set_property_bool(atom, Role::App, "debug", true).unwrap();
 
     let node = node
         .setup(|me| Text::new(me, window_scale.clone(), app.render_api.clone(), i18n_fish.clone()))
         .await;
     layer_node.link(node);
 
-    /*
     // ChatView
     let node = create_chatview("chatty");
     let prop = node.get_property("rect").unwrap();
@@ -318,7 +326,7 @@ pub async fn make(app: &App, window: SceneNodePtr, i18n_fish: &I18nBabelFish) {
         1.00, 0.30, 0.00, 1.
     ];
     for c in nick_colors {
-        prop.push_f32(Role::App, c).unwrap();
+        prop.push_f32(atom, Role::App, c).unwrap();
     }
 
     let prop = node.get_property("hi_bg_color").unwrap();
@@ -348,13 +356,13 @@ pub async fn make(app: &App, window: SceneNodePtr, i18n_fish: &I18nBabelFish) {
                 window_scale.clone(),
                 app.render_api.clone(),
                 app.text_shaper.clone(),
-                app.ex.clone(),
+                app.sg_root.clone(),
             )
         })
         .await;
     layer_node.link(node);
-    */
 
+    /*
     // Text edit
     let node = create_singleline_edit("editz");
     //let node = create_multiline_edit("editz");
@@ -449,4 +457,5 @@ pub async fn make(app: &App, window: SceneNodePtr, i18n_fish: &I18nBabelFish) {
         node.call_method("focus", vec![]).await.unwrap();
     });
     app.tasks.lock().unwrap().push(focus_task);
+    */
 }

+ 1 - 1
bin/app/src/logger.rs

@@ -115,7 +115,7 @@ pub fn setup_logging() -> Option<WorkerGuard> {
     #[cfg(not(target_os = "android"))]
     {
         let terminal_layer = tracing_subscriber::fmt::Layer::new()
-            .with_span_events(FmtSpan::ENTER | FmtSpan::CLOSE)
+            //.with_span_events(FmtSpan::ENTER | FmtSpan::CLOSE)
             .event_format(EventFormatter::new(true, true))
             .fmt_fields(tracing_subscriber::fmt::format::debug_fn(
                 darkfi::util::logger::terminal_field_formatter,

+ 1 - 0
bin/app/src/text2/render.rs

@@ -78,6 +78,7 @@ pub fn render_layout_with_opts(
             }
         }
     }
+
     // Render the atlas
     let atlas = atlas.make();
 

+ 22 - 23
bin/app/src/ui/chatview/mod.rs

@@ -460,22 +460,24 @@ impl ChatView {
                 return
             }
 
+            #[cfg(feature = "enable-plugins")]
             if let Some(url) = get_file_url(&text) {
-                if let Some(fud) = self.sg_root.lookup_node("/plugin/fud") {
-                    msgbuf.insert_filemsg(
-                        timest,
-                        msg_id,
-                        FileMessageStatus::Initializing,
-                        nick,
-                        url.clone(),
-                    );
-
-                    let mut data = vec![];
-                    url.encode(&mut data).unwrap();
-                    fud.call_method("get", data).await.unwrap();
-                }
-            } else {
-                error!(target: "ui::chatview", "Fud plugin has not been loaded");
+                // This is incorrect. Scenegraph paths should not be hardcoded in widgets
+                // nor should there be dependencies on other widgets.
+                // Instead use signals and slots through app layer. See how focus is done with
+                // the edit widgets.
+                let fud = self.sg_root.lookup_node("/plugin/fud").unwrap();
+                msgbuf.insert_filemsg(
+                    timest,
+                    msg_id,
+                    FileMessageStatus::Initializing,
+                    nick,
+                    url.clone(),
+                );
+
+                let mut data = vec![];
+                url.encode(&mut data).unwrap();
+                fud.call_method("get", data).await.unwrap();
             }
         }
 
@@ -587,11 +589,6 @@ impl ChatView {
             }
         };
 
-        let Some(fud) = self.sg_root.lookup_node("/plugin/fud") else {
-            error!(target: "ui::chatview", "Fud plugin has not been loaded");
-            return
-        };
-
         let mut do_redraw = false;
         for entry in iter {
             let Ok((k, v)) = entry else { break };
@@ -609,7 +606,10 @@ impl ChatView {
                 chatmsg.text.clone(),
             );
 
+            // See comment in handle_insert_line()
+            #[cfg(feature = "enable-plugins")]
             if let Some(url) = get_file_url(&chatmsg.text) {
+                let fud = self.sg_root.lookup_node("/plugin/fud").unwrap();
                 msgbuf.insert_filemsg(
                     timest,
                     msg_id,
@@ -634,7 +634,6 @@ impl ChatView {
             }
             remaining_visible -= msg_height;
         }
-        //t!("do_redraw = {do_redraw} [trace_id={trace_id}]");
         if do_redraw {
             let atom = self.render_api.make_guard(gfxtag!("ChatView::handle_bgload"));
             self.redraw_cached(atom.batch_id, &mut msgbuf).await;
@@ -710,7 +709,7 @@ impl ChatView {
 
         let meshes = msgbuf.gen_meshes(rect, scroll).await;
 
-        for (y_pos, mesh) in meshes {
+        for (y_pos, mut minstrs) in meshes {
             // Apply scroll and scissor
             // We use the scissor for scrolling
             // Because we use the scissor, our actual rect is now rect instead of parent_rect
@@ -720,7 +719,7 @@ impl ChatView {
             let pos = Point::from([off_x, off_y]);
 
             instrs.push(DrawInstruction::SetPos(pos));
-            instrs.push(DrawInstruction::Draw(mesh));
+            instrs.append(&mut minstrs);
         }
 
         instrs

+ 105 - 120
bin/app/src/ui/chatview/page.rs

@@ -35,12 +35,13 @@ use url::Url;
 
 use super::{max, MessageId, Timestamp};
 use crate::{
-    gfx::{gfxtag, DrawMesh, ManagedTexturePtr, Rectangle, RenderApi},
+    gfx::{gfxtag, DrawInstruction, DrawMesh, ManagedTexturePtr, Point, Rectangle, RenderApi},
     mesh::{
         Color, MeshBuilder, COLOR_BLUE, COLOR_CYAN, COLOR_GREEN, COLOR_PINK, COLOR_RED, COLOR_WHITE,
     },
     prop::{PropertyBool, PropertyColor, PropertyFloat32, PropertyPtr},
     text::{self, Glyph, GlyphPositionIter, TextShaper, TextShaperPtr},
+    text2,
     util::enumerate_mut,
 };
 
@@ -379,34 +380,19 @@ impl std::fmt::Debug for PrivMessage {
 pub struct DateMessage {
     font_size: f32,
     window_scale: f32,
-
     timestamp: Timestamp,
-    glyphs: Vec<Glyph>,
-
-    atlas: text::RenderedAtlas,
-    mesh_cache: Option<DrawMesh>,
+    mesh_cache: Option<Vec<DrawInstruction>>,
 }
 
 impl DateMessage {
     pub fn new(
         font_size: f32,
         window_scale: f32,
-
         timestamp: Timestamp,
-
-        text_shaper: &TextShaper,
-        render_api: &RenderApi,
+        _render_api: &RenderApi,
     ) -> Message {
-        let datestr = Self::datestr(timestamp);
         let timestamp = Self::timest_to_midnight(timestamp);
-
-        let glyphs = text_shaper.shape(datestr, font_size, window_scale);
-
-        let mut atlas = text::Atlas::new(render_api, gfxtag!("chatview_datemsg"));
-        atlas.push(&glyphs);
-        let atlas = atlas.make();
-
-        Message::Date(Self { font_size, window_scale, timestamp, glyphs, atlas, mesh_cache: None })
+        Message::Date(Self { font_size, window_scale, timestamp, mesh_cache: None })
     }
 
     fn datestr(timestamp: Timestamp) -> String {
@@ -423,66 +409,60 @@ impl DateMessage {
         timestamp
     }
 
-    /// clear_mesh() must be called after this.
     fn adjust_params(
         &mut self,
         font_size: f32,
         window_scale: f32,
-        text_shaper: &TextShaper,
-        render_api: &RenderApi,
+        _text_shaper: &TextShaper,
+        _render_api: &RenderApi,
     ) {
         self.font_size = font_size;
         self.window_scale = window_scale;
-
-        let datestr = Self::datestr(self.timestamp);
-        self.glyphs = text_shaper.shape(datestr, font_size, window_scale);
-
-        let mut atlas = text::Atlas::new(render_api, gfxtag!("chatview_datemsg"));
-        atlas.push(&self.glyphs);
-        self.atlas = atlas.make();
+        self.mesh_cache = None;
     }
 
-    //fn adjust_width(&mut self, line_width: f32) { }
-
     fn clear_mesh(&mut self) {
-        // Auto-deletes when refs are dropped
         self.mesh_cache = None;
     }
 
-    fn gen_mesh(
+    async fn gen_mesh(
         &mut self,
-        clip: &Rectangle,
+        _clip: &Rectangle,
         line_height: f32,
-        baseline: f32,
+        _baseline: f32,
         _nick_colors: &[Color],
         timestamp_color: Color,
         _text_color: Color,
-        debug_render: bool,
+        _debug_render: bool,
         render_api: &RenderApi,
-    ) -> DrawMesh {
-        let mut mesh = MeshBuilder::new(gfxtag!("chatview_datemsg"));
-
-        let glyph_pos_iter =
-            GlyphPositionIter::new(self.font_size, self.window_scale, &self.glyphs, baseline);
-        for (mut glyph_rect, glyph) in glyph_pos_iter.zip(self.glyphs.iter()) {
-            let uv_rect = self.atlas.fetch_uv(glyph.glyph_id).expect("missing glyph UV rect");
-            glyph_rect.y -= line_height;
-            mesh.draw_box(&glyph_rect, timestamp_color, uv_rect);
+    ) -> Vec<DrawInstruction> {
+        // Return cached mesh if available
+        if let Some(cache) = &self.mesh_cache {
+            return cache.clone()
         }
 
-        if debug_render {
-            mesh.draw_outline(
-                &Rectangle { x: 0., y: -line_height, w: clip.w, h: line_height },
-                COLOR_PINK,
-                1.,
-            );
-        }
+        let datestr = Self::datestr(self.timestamp);
 
-        let mesh = mesh.alloc(render_api);
-        let mesh = mesh.draw_with_textures(vec![self.atlas.texture.clone()]);
-        self.mesh_cache = Some(mesh.clone());
+        let mut txt_ctx = text2::TEXT_CTX.get().await;
+        let layout = txt_ctx.make_layout(
+            &datestr,
+            timestamp_color,
+            self.font_size,
+            line_height / self.font_size,
+            self.window_scale,
+            None,
+            &[],
+        );
+        drop(txt_ctx);
 
-        mesh
+        let mut txt_instrs = text2::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);
+
+        // Cache the instructions
+        self.mesh_cache = Some(instrs.clone());
+        instrs
     }
 }
 
@@ -854,7 +834,7 @@ impl Message {
         }
     }
 
-    fn gen_mesh(
+    async fn gen_mesh(
         &mut self,
         clip: &Rectangle,
         line_height: f32,
@@ -867,44 +847,51 @@ impl Message {
         hi_bg_color: Color,
         debug_render: bool,
         render_api: &RenderApi,
-    ) -> Vec<DrawMesh> {
+    ) -> Vec<DrawInstruction> {
         match self {
-            Self::Priv(m) => vec![m.gen_mesh(
-                clip,
-                line_height,
-                msg_spacing,
-                baseline,
-                timestamp_width,
-                nick_colors,
-                timestamp_color,
-                text_color,
-                hi_bg_color,
-                debug_render,
-                render_api,
-            )],
-            Self::Date(m) => vec![m.gen_mesh(
-                clip,
-                line_height,
-                baseline,
-                // No timestamp_width
-                nick_colors,
-                timestamp_color,
-                text_color,
-                // No hi_bg_color since dates can't be highlighted
-                debug_render,
-                render_api,
-            )],
-            Self::File(m) => m.gen_mesh(
-                clip,
-                line_height,
-                baseline,
-                timestamp_width,
-                nick_colors,
-                timestamp_color,
-                text_color,
-                debug_render,
-                render_api,
-            ),
+            Self::Priv(m) => {
+                let mesh = m.gen_mesh(
+                    clip,
+                    line_height,
+                    msg_spacing,
+                    baseline,
+                    timestamp_width,
+                    nick_colors,
+                    timestamp_color,
+                    text_color,
+                    hi_bg_color,
+                    debug_render,
+                    render_api,
+                );
+                vec![DrawInstruction::Draw(mesh)]
+            }
+            Self::Date(m) => {
+                m.gen_mesh(
+                    clip,
+                    line_height,
+                    baseline,
+                    nick_colors,
+                    timestamp_color,
+                    text_color,
+                    debug_render,
+                    render_api,
+                )
+                .await
+            }
+            Self::File(m) => {
+                let meshes = m.gen_mesh(
+                    clip,
+                    line_height,
+                    baseline,
+                    timestamp_width,
+                    nick_colors,
+                    timestamp_color,
+                    text_color,
+                    debug_render,
+                    render_api,
+                );
+                meshes.into_iter().map(DrawInstruction::Draw).collect()
+            }
         }
     }
 
@@ -1221,8 +1208,12 @@ impl MessageBuffer {
         msg_height
     }
 
-    /// Generate caches and return meshes
-    pub async fn gen_meshes(&mut self, rect: &Rectangle, scroll: f32) -> Vec<(f32, DrawMesh)> {
+    /// Generate caches and return draw instructions
+    pub async fn gen_meshes(
+        &mut self,
+        rect: &Rectangle,
+        scroll: f32,
+    ) -> Vec<(f32, Vec<DrawInstruction>)> {
         let line_height = self.line_height.get();
         let msg_spacing = self.msg_spacing.get();
         let baseline = self.baseline.get();
@@ -1255,27 +1246,27 @@ impl MessageBuffer {
                 continue
             }
 
-            for mesh in msg.gen_mesh(
-                rect,
-                line_height,
-                msg_spacing,
-                baseline,
-                timestamp_width,
-                &nick_colors,
-                timest_color,
-                text_color,
-                hi_bg_color,
-                debug_render,
-                &render_api,
-            ) {
-                meshes.push((current_pos, mesh));
-            }
+            let instrs = msg
+                .gen_mesh(
+                    rect,
+                    line_height,
+                    msg_spacing,
+                    baseline,
+                    timestamp_width,
+                    &nick_colors,
+                    timest_color,
+                    text_color,
+                    hi_bg_color,
+                    debug_render,
+                    &render_api,
+                )
+                .await;
+
+            meshes.push((current_pos, instrs));
 
             current_pos += msg_spacing;
             current_pos += mesh_height;
         }
-
-        //t!("gen_meshes() returning {} meshes", meshes.len());
         meshes
     }
 
@@ -1364,13 +1355,7 @@ impl MessageBuffer {
         let timest = Local.from_local_datetime(&dt).unwrap().timestamp_millis() as u64;
 
         if !self.date_msgs.contains_key(&date) {
-            let datemsg = DateMessage::new(
-                font_size,
-                window_scale,
-                timest,
-                &self.text_shaper,
-                &self.render_api,
-            );
+            let datemsg = DateMessage::new(font_size, window_scale, timest, &self.render_api);
             self.date_msgs.insert(date, datemsg);
         }
 

+ 9 - 0
bin/app/src/ui/text.rs

@@ -24,6 +24,7 @@ use tracing::instrument;
 
 use crate::{
     gfx::{gfxtag, DrawCall, DrawInstruction, Rectangle, RenderApi},
+    mesh::MeshBuilder,
     prop::{
         BatchGuardPtr, PropertyAtomicGuard, PropertyBool, PropertyColor, PropertyFloat32,
         PropertyRect, PropertyStr, PropertyUint32, Role,
@@ -158,6 +159,14 @@ impl Text {
         let mut instrs = vec![DrawInstruction::Move(rect.pos())];
         instrs.append(&mut self.regen_mesh().await);
 
+        if self.debug.get() {
+            let rect = self.rect.get().with_zero_pos();
+            let mut mesh = MeshBuilder::new(gfxtag!("text_debug-rect"));
+            mesh.draw_outline(&rect, [0., 1., 0., 0.7], 1.);
+            let mesh = mesh.alloc(&self.render_api).draw_untextured();
+            instrs.push(DrawInstruction::Draw(mesh));
+        }
+
         Some(DrawUpdate {
             key: self.dc_key,
             draw_calls: vec![(