diff --git a/bin/darkwallet/data/close.obj b/bin/darkwallet/data/close.obj new file mode 100644 index 000000000000..dd8d7d88d0fe --- /dev/null +++ b/bin/darkwallet/data/close.obj @@ -0,0 +1,38 @@ +# Blender 4.3.1 +# www.blender.org +mtllib close.mtl +o close +v 0.000000 0.000000 0.000000 +v 0.000000 0.000000 -0.194555 +v 0.194555 0.000000 0.000000 +v 0.550000 0.000000 -0.744555 +v 0.744555 0.000000 -0.550000 +v 0.000000 0.000000 0.000000 +v -0.194555 0.000000 0.000000 +v -0.550000 0.000000 -0.744555 +v -0.744555 0.000000 -0.550000 +v 0.000000 0.000000 0.000000 +v 0.000000 0.000000 0.194555 +v 0.550000 0.000000 0.744555 +v 0.744555 0.000000 0.550000 +v 0.000000 0.000000 0.000000 +v -0.550000 0.000000 0.744555 +v -0.744555 0.000000 0.550000 +vn -0.0000 1.0000 -0.0000 +vn -0.0000 -1.0000 -0.0000 +vt 1.000000 0.000000 +vt 1.000000 1.000000 +vt 0.000000 1.000000 +s 0 +f 1/1/1 3/2/1 2/3/1 +f 3/2/1 4/3/1 2/3/1 +f 6/1/2 7/2/2 2/3/2 +f 7/2/2 8/3/2 2/3/2 +f 10/1/2 3/2/2 11/3/2 +f 3/2/2 12/3/2 11/3/2 +f 14/1/1 7/2/1 11/3/1 +f 7/2/1 15/3/1 11/3/1 +f 3/2/1 5/2/1 4/3/1 +f 7/2/2 9/2/2 8/3/2 +f 3/2/2 13/2/2 12/3/2 +f 7/2/1 16/2/1 15/3/1 diff --git a/bin/darkwallet/src/app/node.rs b/bin/darkwallet/src/app/node.rs index 4cea48625ae4..4b70dce0b005 100644 --- a/bin/darkwallet/src/app/node.rs +++ b/bin/darkwallet/src/app/node.rs @@ -47,6 +47,10 @@ pub fn create_vector_art(name: &str) -> SceneNode { debug!(target: "app", "create_vector_art({name})"); let mut node = SceneNode::new(name, SceneNodeType::VectorArt); + let mut prop = Property::new("is_visible", PropertyType::Bool, PropertySubType::Null); + prop.set_defaults_bool(vec![true]).unwrap(); + node.add_property(prop).unwrap(); + let mut prop = Property::new("rect", PropertyType::Float32, PropertySubType::Pixel); prop.set_array_len(4); prop.allow_exprs(); @@ -474,3 +478,18 @@ pub fn create_chatview(name: &str) -> SceneNode { node } + +pub fn create_emoji_picker(name: &str) -> SceneNode { + debug!(target: "app", "create_emoji_picker({name})"); + let mut node = SceneNode::new(name, SceneNodeType::Image); + + let mut prop = Property::new("rect", PropertyType::Float32, PropertySubType::Pixel); + prop.set_array_len(4); + prop.allow_exprs(); + node.add_property(prop).unwrap(); + + let prop = Property::new("z_index", PropertyType::Uint32, PropertySubType::Null); + node.add_property(prop).unwrap(); + + node +} diff --git a/bin/darkwallet/src/app/schema/chat.rs b/bin/darkwallet/src/app/schema/chat.rs index f6a813cbeed8..e91a01106001 100644 --- a/bin/darkwallet/src/app/schema/chat.rs +++ b/bin/darkwallet/src/app/schema/chat.rs @@ -16,13 +16,14 @@ * along with this program. If not, see . */ +use darkfi::system::msleep; use sled_overlay::sled; use crate::{ app::{ node::{ - create_button, create_chatedit, create_chatview, create_editbox, create_image, - create_layer, create_text, create_vector_art, + create_button, create_chatedit, create_chatview, create_editbox, create_emoji_picker, + create_image, create_layer, create_text, create_vector_art, }, populate_tree, App, }, @@ -37,8 +38,8 @@ use crate::{ shape, text::TextShaperPtr, ui::{ - Button, ChatEdit, ChatView, EditBox, Image, Layer, ShapeVertex, Text, VectorArt, - VectorShape, Window, + Button, ChatEdit, ChatView, EditBox, EmojiPicker, Image, Layer, ShapeVertex, Text, + VectorArt, VectorShape, Window, }, ExecutorPtr, }; @@ -69,6 +70,7 @@ mod android_ui_consts { pub const EMOJI_SCALE: f32 = 40.; pub const EMOJI_NEG_Y: f32 = 85.; pub const EMOJIBTN_BOX: [f32; 4] = [20., 118., 80., 75.]; + pub const EMOJI_CLOSE_SCALE: f32 = 20.; pub const SENDARROW_NEG_X: f32 = 50.; pub const SENDARROW_NEG_Y: f32 = 80.; pub const SENDBTN_BOX: [f32; 4] = [86., 120., 80., 70.]; @@ -128,6 +130,7 @@ mod ui_consts { pub const EMOJI_SCALE: f32 = 20.; pub const EMOJI_NEG_Y: f32 = 34.; pub const EMOJIBTN_BOX: [f32; 4] = [16., 50., 44., 36.]; + pub const EMOJI_CLOSE_SCALE: f32 = 10.; pub const SENDARROW_NEG_X: f32 = 50.; pub const SENDARROW_NEG_Y: f32 = 32.; pub const SENDBTN_BOX: [f32; 4] = [72., 50., 45., 34.]; @@ -257,7 +260,6 @@ pub async fn make(app: &App, window: SceneNodePtr, channel: &str, db: &sled::Db) prop.set_f32(Role::App, 1, 0.).unwrap(); prop.set_expr(Role::App, 2, expr::load_var("w")).unwrap(); prop.set_f32(Role::App, 3, CHATEDIT_HEIGHT).unwrap(); - node.set_property_u32(Role::App, "z_index", 2).unwrap(); node.set_property_f32(Role::App, "baseline", CHANNEL_LABEL_BASELINE).unwrap(); node.set_property_f32(Role::App, "font_size", FONTSIZE).unwrap(); node.set_property_str(Role::App, "text", &("#".to_string() + channel)).unwrap(); @@ -283,6 +285,52 @@ pub async fn make(app: &App, window: SceneNodePtr, channel: &str, db: &sled::Db) .await; layer_node.clone().link(node); + // Create the emoji picker + let mut node = create_emoji_picker("emoji_picker"); + let prop = Property::new("dynamic_h", PropertyType::Float32, PropertySubType::Pixel); + node.add_property(prop).unwrap(); + let emoji_h_prop = node.get_property("dynamic_h").unwrap(); + let prop = node.get_property("rect").unwrap(); + prop.set_f32(Role::App, 0, 0.).unwrap(); + let code = cc.compile("h - dynamic_h").unwrap(); + prop.set_expr(Role::App, 1, code).unwrap(); + prop.set_expr(Role::App, 2, expr::load_var("w")).unwrap(); + prop.set_expr(Role::App, 3, expr::load_var("dynamic_h")).unwrap(); + prop.add_depend(&emoji_h_prop, 0, "dynamic_h"); + let emoji_h_prop = PropertyFloat32::wrap(&node, Role::App, "dynamic_h", 0).unwrap(); + let emoji_rect_prop = prop; + //node.set_property_f32(Role::App, "baseline", CHANNEL_LABEL_BASELINE).unwrap(); + //node.set_property_f32(Role::App, "font_size", FONTSIZE).unwrap(); + node.set_property_u32(Role::App, "z_index", 2).unwrap(); + let node = node + .setup(|me| { + EmojiPicker::new( + me, + window_scale.clone(), + app.render_api.clone(), + app.text_shaper.clone(), + app.ex.clone(), + ) + }) + .await; + layer_node.clone().link(node); + + // Main content view + let chat_layer_node = layer_node; + let layer_node = create_layer("content"); + let prop = layer_node.get_property("rect").unwrap(); + prop.set_f32(Role::App, 0, 0.).unwrap(); + prop.set_f32(Role::App, 1, 0.).unwrap(); + prop.set_expr(Role::App, 2, expr::load_var("w")).unwrap(); + let code = cc.compile("h - emoji_h").unwrap(); + prop.set_expr(Role::App, 3, code).unwrap(); + prop.add_depend(&emoji_rect_prop, 3, "emoji_h"); + layer_node.set_property_bool(Role::App, "is_visible", true).unwrap(); + layer_node.set_property_u32(Role::App, "z_index", 1).unwrap(); + let layer_node = + layer_node.setup(|me| Layer::new(me, app.render_api.clone(), app.ex.clone())).await; + chat_layer_node.link(layer_node.clone()); + // ChatView let node = create_chatview("chatty"); let prop = node.get_property("rect").unwrap(); @@ -468,6 +516,7 @@ pub async fn make(app: &App, window: SceneNodePtr, channel: &str, db: &sled::Db) // Create the emoji button let node = create_vector_art("emoji_btn_bg"); + let emoji_btn_is_visible = PropertyBool::wrap(&node, Role::App, "is_visible", 0).unwrap(); let prop = node.get_property("rect").unwrap(); prop.set_f32(Role::App, 0, EMOJI_BTN_X).unwrap(); let code = cc.compile("h - EMOJI_NEG_Y").unwrap(); @@ -480,6 +529,22 @@ pub async fn make(app: &App, window: SceneNodePtr, channel: &str, db: &sled::Db) node.setup(|me| VectorArt::new(me, shape, app.render_api.clone(), app.ex.clone())).await; layer_node.clone().link(node); + // Create the emoji button + let node = create_vector_art("emoji_close_btn_bg"); + node.set_property_bool(Role::App, "is_visible", false).unwrap(); + let prop = node.get_property("rect").unwrap(); + let emoji_close_is_visible = PropertyBool::wrap(&node, Role::App, "is_visible", 0).unwrap(); + prop.set_f32(Role::App, 0, EMOJI_BTN_X).unwrap(); + let code = cc.compile("h - EMOJI_NEG_Y").unwrap(); + prop.set_expr(Role::App, 1, code).unwrap(); + prop.set_f32(Role::App, 2, 500.).unwrap(); + prop.set_f32(Role::App, 3, 500.).unwrap(); + node.set_property_u32(Role::App, "z_index", 3).unwrap(); + let shape = shape::create_close_icon().scaled(EMOJI_CLOSE_SCALE); + let node = + node.setup(|me| VectorArt::new(me, shape, app.render_api.clone(), app.ex.clone())).await; + layer_node.clone().link(node); + // Text edit let node = create_chatedit("editz"); node.set_property_bool(Role::App, "is_active", true).unwrap(); @@ -635,6 +700,25 @@ pub async fn make(app: &App, window: SceneNodePtr, channel: &str, db: &sled::Db) let listen_click = app.ex.spawn(async move { while let Ok(_) = recvr.recv().await { info!(target: "app::chat", "clicked emoji"); + if emoji_btn_is_visible.get() { + assert!(!emoji_close_is_visible.get()); + assert!(emoji_h_prop.get() < 0.001); + emoji_btn_is_visible.set(false); + emoji_close_is_visible.set(true); + for i in 1..=20 { + emoji_h_prop.set((20 * i) as f32); + msleep(10).await; + } + } else { + assert!(emoji_close_is_visible.get()); + assert!(emoji_h_prop.get() > 0.); + emoji_btn_is_visible.set(true); + emoji_close_is_visible.set(false); + for i in 1..=20 { + emoji_h_prop.set((400 - 20 * i) as f32); + msleep(10).await; + } + } } }); app.tasks.lock().unwrap().push(listen_click); diff --git a/bin/darkwallet/src/prop/mod.rs b/bin/darkwallet/src/prop/mod.rs index b613db54ebc3..d625b00c6028 100644 --- a/bin/darkwallet/src/prop/mod.rs +++ b/bin/darkwallet/src/prop/mod.rs @@ -319,6 +319,11 @@ impl Property { } Ok(()) } + pub fn set_defaults_bool(&mut self, defaults: Vec) -> Result<()> { + self.check_defaults_len(defaults.len())?; + self.defaults = defaults.into_iter().map(|v| PropertyValue::Bool(v)).collect(); + Ok(()) + } pub fn set_defaults_u32(&mut self, defaults: Vec) -> Result<()> { self.check_defaults_len(defaults.len())?; self.defaults = defaults.into_iter().map(|v| PropertyValue::Uint32(v)).collect(); diff --git a/bin/darkwallet/src/scene.rs b/bin/darkwallet/src/scene.rs index 0491d112ac76..188a08424702 100644 --- a/bin/darkwallet/src/scene.rs +++ b/bin/darkwallet/src/scene.rs @@ -476,4 +476,5 @@ pub enum Pimpl { ChatView(ui::ChatViewPtr), Image(ui::ImagePtr), Button(ui::ButtonPtr), + EmojiPicker(ui::EmojiPickerPtr), } diff --git a/bin/darkwallet/src/shape/close.rs b/bin/darkwallet/src/shape/close.rs new file mode 100644 index 000000000000..4b6404d5b940 --- /dev/null +++ b/bin/darkwallet/src/shape/close.rs @@ -0,0 +1,27 @@ +use crate::ui::{ShapeVertex, VectorShape}; +pub fn create_close_icon() -> VectorShape { + VectorShape { + verts: vec![ + ShapeVertex::from_xy(0.0, 0.0, [0., 1., 1., 1.]), + ShapeVertex::from_xy(0.0, -0.194555, [0., 1., 1., 1.]), + ShapeVertex::from_xy(0.194555, 0.0, [0., 1., 1., 1.]), + ShapeVertex::from_xy(0.55, -0.744555, [0., 1., 1., 1.]), + ShapeVertex::from_xy(0.744555, -0.55, [0., 1., 1., 1.]), + ShapeVertex::from_xy(0.0, 0.0, [0., 1., 1., 1.]), + ShapeVertex::from_xy(-0.194555, 0.0, [0., 1., 1., 1.]), + ShapeVertex::from_xy(-0.55, -0.744555, [0., 1., 1., 1.]), + ShapeVertex::from_xy(-0.744555, -0.55, [0., 1., 1., 1.]), + ShapeVertex::from_xy(0.0, 0.0, [0., 1., 1., 1.]), + ShapeVertex::from_xy(0.0, 0.194555, [0., 1., 1., 1.]), + ShapeVertex::from_xy(0.55, 0.744555, [0., 1., 1., 1.]), + ShapeVertex::from_xy(0.744555, 0.55, [0., 1., 1., 1.]), + ShapeVertex::from_xy(0.0, 0.0, [0., 1., 1., 1.]), + ShapeVertex::from_xy(-0.55, 0.744555, [0., 1., 1., 1.]), + ShapeVertex::from_xy(-0.744555, 0.55, [0., 1., 1., 1.]), + ], + indices: vec![ + 0, 2, 1, 2, 3, 1, 5, 6, 1, 6, 7, 1, 9, 2, 10, 2, 11, 10, 13, 6, 10, 6, 14, 10, 2, 4, 3, + 6, 8, 7, 2, 12, 11, 6, 15, 14, + ], + } +} diff --git a/bin/darkwallet/src/shape/mod.rs b/bin/darkwallet/src/shape/mod.rs index 99e55c8e6aae..a134462bbdaf 100644 --- a/bin/darkwallet/src/shape/mod.rs +++ b/bin/darkwallet/src/shape/mod.rs @@ -21,6 +21,9 @@ use crate::ui::{ShapeVertex, VectorShape}; mod back_arrow; pub use back_arrow::create_back_arrow; +mod close; +pub use close::create_close_icon; + mod send_arrow; pub use send_arrow::create_send_arrow; diff --git a/bin/darkwallet/src/ui/emoji_picker.rs b/bin/darkwallet/src/ui/emoji_picker.rs new file mode 100644 index 000000000000..777c38e9de70 --- /dev/null +++ b/bin/darkwallet/src/ui/emoji_picker.rs @@ -0,0 +1,188 @@ +/* This file is part of DarkFi (https://dark.fi) + * + * Copyright (C) 2020-2024 Dyne.org foundation + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +use async_trait::async_trait; +use image::ImageReader; +use rand::{rngs::OsRng, Rng}; +use std::{ + io::Cursor, + sync::{Arc, Mutex as SyncMutex, OnceLock, Weak}, +}; + +use crate::{ + gfx::{ + GfxDrawCall, GfxDrawInstruction, GfxDrawMesh, GfxTextureId, ManagedTexturePtr, Rectangle, + RenderApi, + }, + mesh::{MeshBuilder, MeshInfo, COLOR_WHITE}, + prop::{PropertyFloat32, PropertyPtr, PropertyRect, PropertyStr, PropertyUint32, Role}, + scene::{Pimpl, SceneNodePtr, SceneNodeWeak}, + text::{self, GlyphPositionIter, TextShaper, TextShaperPtr}, + ExecutorPtr, +}; + +use super::{DrawUpdate, OnModify, UIObject}; + +macro_rules! d { + ($($arg:tt)*) => { + debug!(target: "ui::emoji_picker", $($arg)*); + } +} + +pub type EmojiPickerPtr = Arc; + +pub struct EmojiPicker { + node: SceneNodeWeak, + render_api: RenderApi, + text_shaper: TextShaperPtr, + tasks: OnceLock>>, + + dc_key: u64, + + rect: PropertyRect, + z_index: PropertyUint32, + + window_scale: PropertyFloat32, + parent_rect: SyncMutex>, +} + +impl EmojiPicker { + pub async fn new( + node: SceneNodeWeak, + window_scale: PropertyFloat32, + render_api: RenderApi, + text_shaper: TextShaperPtr, + ex: ExecutorPtr, + ) -> Pimpl { + d!("EmojiPicker::new()"); + + let node_ref = &node.upgrade().unwrap(); + let rect = PropertyRect::wrap(node_ref, Role::Internal, "rect").unwrap(); + let z_index = PropertyUint32::wrap(node_ref, Role::Internal, "z_index", 0).unwrap(); + + let node_name = node_ref.name.clone(); + let node_id = node_ref.id; + + let self_ = Arc::new(Self { + node, + render_api, + text_shaper, + tasks: OnceLock::new(), + + dc_key: OsRng.gen(), + + rect, + z_index, + + window_scale, + parent_rect: SyncMutex::new(None), + }); + + Pimpl::EmojiPicker(self_) + } + + async fn redraw(self: Arc) { + let Some(parent_rect) = self.parent_rect.lock().unwrap().clone() else { return }; + + let Some(draw_update) = self.get_draw_calls(parent_rect).await else { + error!(target: "ui::image", "Emoji picker failed to draw"); + return; + }; + self.render_api.replace_draw_calls(draw_update.draw_calls); + debug!(target: "ui::image", "replace draw calls done"); + } + + /* + fn regen_mesh(&self) -> MeshInfo { + let rect = self.rect.get(); + let uv = self.uv.get(); + let mesh_rect = Rectangle::from([0., 0., rect.w, rect.h]); + let mut mesh = MeshBuilder::new(); + mesh.draw_box(&mesh_rect, COLOR_WHITE, &uv); + mesh.alloc(&self.render_api) + } + */ + + async fn get_draw_calls(&self, parent_rect: Rectangle) -> Option { + if let Err(e) = self.rect.eval(&parent_rect) { + warn!(target: "ui::emoji_picker", "Rect eval failed: {e}"); + return None + } + let rect = self.rect.get(); + + /* + let mesh = self.regen_mesh(); + let texture = self.texture.lock().unwrap().clone().expect("Node missing texture_id!"); + + let mesh = GfxDrawMesh { + vertex_buffer: mesh.vertex_buffer, + index_buffer: mesh.index_buffer, + texture: Some(texture), + num_elements: mesh.num_elements, + }; + */ + + Some(DrawUpdate { + key: self.dc_key, + draw_calls: vec![( + self.dc_key, + GfxDrawCall { + instrs: vec![ + GfxDrawInstruction::Move(rect.pos()), + //GfxDrawInstruction::Draw(mesh), + ], + dcs: vec![], + z_index: self.z_index.get(), + }, + )], + }) + } +} + +#[async_trait] +impl UIObject for EmojiPicker { + fn z_index(&self) -> u32 { + self.z_index.get() + } + + async fn start(self: Arc, ex: ExecutorPtr) { + let me = Arc::downgrade(&self); + + let node_ref = &self.node.upgrade().unwrap(); + let node_name = node_ref.name.clone(); + let node_id = node_ref.id; + + let mut on_modify = OnModify::new(ex, node_name, node_id, me.clone()); + on_modify.when_change(self.rect.prop(), Self::redraw); + on_modify.when_change(self.z_index.prop(), Self::redraw); + + self.tasks.set(on_modify.tasks); + } + + async fn draw(&self, parent_rect: Rectangle) -> Option { + debug!(target: "ui::image", "Image::draw()"); + *self.parent_rect.lock().unwrap() = Some(parent_rect); + self.get_draw_calls(parent_rect).await + } +} + +impl Drop for EmojiPicker { + fn drop(&mut self) { + self.render_api.replace_draw_calls(vec![(self.dc_key, Default::default())]); + } +} diff --git a/bin/darkwallet/src/ui/mod.rs b/bin/darkwallet/src/ui/mod.rs index 7df19b0b024e..fc1bd7166963 100644 --- a/bin/darkwallet/src/ui/mod.rs +++ b/bin/darkwallet/src/ui/mod.rs @@ -38,6 +38,8 @@ mod chatedit; pub use chatedit::{ChatEdit, ChatEditPtr}; mod editbox; pub use editbox::{EditBox, EditBoxPtr}; +mod emoji_picker; +pub use emoji_picker::{EmojiPicker, EmojiPickerPtr}; mod image; pub use image::{Image, ImagePtr}; mod vector_art; @@ -185,6 +187,7 @@ pub fn get_ui_object_ptr(node: &SceneNode3) -> Arc { Pimpl::ChatView(obj) => obj.clone(), Pimpl::Image(obj) => obj.clone(), Pimpl::Button(obj) => obj.clone(), + Pimpl::EmojiPicker(obj) => obj.clone(), _ => panic!("unhandled type for get_ui_object"), } } @@ -198,6 +201,7 @@ pub fn get_ui_object3<'a>(node: &'a SceneNode3) -> &'a dyn UIObject { Pimpl::ChatView(obj) => obj.as_ref(), Pimpl::Image(obj) => obj.as_ref(), Pimpl::Button(obj) => obj.as_ref(), + Pimpl::EmojiPicker(obj) => obj.as_ref(), _ => panic!("unhandled type for get_ui_object"), } } diff --git a/bin/darkwallet/src/ui/vector_art/mod.rs b/bin/darkwallet/src/ui/vector_art/mod.rs index 6a3871732ac7..09bdb389740f 100644 --- a/bin/darkwallet/src/ui/vector_art/mod.rs +++ b/bin/darkwallet/src/ui/vector_art/mod.rs @@ -27,7 +27,7 @@ use crate::{ GfxBufferId, GfxDrawCall, GfxDrawInstruction, GfxDrawMesh, Rectangle, RenderApi, Vertex, }, mesh::Color, - prop::{PropertyFloat32, PropertyPtr, PropertyRect, PropertyUint32, Role}, + prop::{PropertyBool, PropertyFloat32, PropertyPtr, PropertyRect, PropertyUint32, Role}, scene::{Pimpl, SceneNodePtr, SceneNodeWeak}, util::enumerate, ExecutorPtr, @@ -48,6 +48,7 @@ pub struct VectorArt { shape: VectorShape, dc_key: u64, + is_visible: PropertyBool, rect: PropertyRect, z_index: PropertyUint32, @@ -64,6 +65,7 @@ impl VectorArt { debug!(target: "ui::vector_art", "VectorArt::new()"); let node_ref = &node.upgrade().unwrap(); + let is_visible = PropertyBool::wrap(node_ref, Role::Internal, "is_visible", 0).unwrap(); let rect = PropertyRect::wrap(node_ref, Role::Internal, "rect").unwrap(); let z_index = PropertyUint32::wrap(node_ref, Role::Internal, "z_index", 0).unwrap(); @@ -78,6 +80,7 @@ impl VectorArt { shape, dc_key: OsRng.gen(), + is_visible, rect, z_index, @@ -98,12 +101,11 @@ impl VectorArt { //debug!(target: "ui::vector_art", "replace draw calls done"); } - async fn get_draw_calls(&self, parent_rect: Rectangle) -> Option { - //debug!(target: "ui::vector_art", "VectorArt::draw_cached()"); - if let Err(e) = self.rect.eval(&parent_rect) { - warn!(target: "ui::vector_art", "Rect eval failure: {e}"); - return None + fn get_draw_instrs(&self) -> Vec { + if !self.is_visible.get() { + return vec![] } + let rect = self.rect.get(); let verts = self.shape.eval(rect.w, rect.h).expect("bad shape"); @@ -117,18 +119,23 @@ impl VectorArt { num_elements: self.shape.indices.len() as i32, }; + vec![GfxDrawInstruction::Move(rect.pos()), GfxDrawInstruction::Draw(mesh)] + } + + async fn get_draw_calls(&self, parent_rect: Rectangle) -> Option { + //debug!(target: "ui::vector_art", "VectorArt::draw_cached()"); + if let Err(e) = self.rect.eval(&parent_rect) { + warn!(target: "ui::vector_art", "Rect eval failure: {e}"); + return None + } + + let instrs = self.get_draw_instrs(); + Some(DrawUpdate { key: self.dc_key, draw_calls: vec![( self.dc_key, - GfxDrawCall { - instrs: vec![ - GfxDrawInstruction::Move(rect.pos()), - GfxDrawInstruction::Draw(mesh), - ], - dcs: vec![], - z_index: self.z_index.get(), - }, + GfxDrawCall { instrs, dcs: vec![], z_index: self.z_index.get() }, )], }) } @@ -148,6 +155,7 @@ impl UIObject for VectorArt { let node_id = node_ref.id; let mut on_modify = OnModify::new(ex, node_name, node_id, me.clone()); + on_modify.when_change(self.is_visible.prop(), Self::redraw); on_modify.when_change(self.rect.prop(), Self::redraw); on_modify.when_change(self.z_index.prop(), Self::redraw);