wallet: UI Text properties can be edited realtime

This commit is contained in:
darkfi
2024-07-01 09:43:21 +02:00
parent da0381d3c0
commit 1ff57a6ecf
6 changed files with 148 additions and 91 deletions

View File

@@ -413,9 +413,11 @@ impl Stage {
sendr: async_channel::Sender<TextureId>,
) {
let texture = self.ctx.new_texture_from_rgba8(width, height, &data);
debug!(target: "gfx2", "Invoked method: new_texture({}, {}, ...) -> {:?}\n{}",
width, height, texture,
ansi_texture(width as usize, height as usize, &data));
debug!(target: "gfx2", "Invoked method: new_texture({}, {}, ...) -> {:?}",
width, height, texture);
//debug!(target: "gfx2", "Invoked method: new_texture({}, {}, ...) -> {:?}\n{}",
// width, height, texture,
// ansi_texture(width as usize, height as usize, &data));
sendr.try_send(texture).unwrap();
}
fn method_delete_texture(&mut self, texture: TextureId) {

View File

@@ -37,20 +37,21 @@ mod util;
use crate::{net::ZeroMQAdapter, scene::SceneGraph, text2::TextShaper};
#[cfg(target_os = "android")]
fn panic_hook(panic_info: &std::panic::PanicInfo) {
error!("panic occurred: {panic_info}");
//error!("panic: {}", std::backtrace::Backtrace::force_capture().to_string());
std::process::exit(1);
}
fn main() {
// Exit the application on panic right away
std::panic::set_hook(Box::new(panic_hook));
#[cfg(target_os = "android")]
{
android_logger::init_once(
android_logger::Config::default().with_max_level(LevelFilter::Debug).with_tag("darkfi"),
);
std::panic::set_hook(Box::new(panic_hook));
}
#[cfg(target_os = "linux")]

View File

@@ -12,6 +12,13 @@ pub const COLOR_GREEN: Color = [0., 1., 0., 1.];
pub const COLOR_BLUE: Color = [0., 0., 1., 1.];
pub const COLOR_WHITE: Color = [1., 1., 1., 1.];
#[derive(Clone)]
pub struct MeshInfo {
pub vertex_buffer: BufferId,
pub index_buffer: BufferId,
pub num_elements: i32,
}
pub struct MeshBuilder {
verts: Vec<Vertex>,
indices: Vec<u16>,
@@ -67,18 +74,14 @@ impl MeshBuilder {
self.draw_box(&Rectangle::new(x1, y2 - thickness, dist_x, thickness), color, &uv);
}
// Needed by OpenGL
pub fn num_elements(&self) -> i32 {
self.indices.len() as i32
}
pub async fn alloc(self, render_api: &RenderApi) -> Result<(BufferId, BufferId)> {
pub async fn alloc(self, render_api: &RenderApi) -> Result<MeshInfo> {
//debug!(target: "mesh", "allocating {} verts:", self.verts.len());
//for vert in &self.verts {
// debug!(target: "mesh", " {:?}", vert);
//}
let num_elements = self.indices.len() as i32;
let vertex_buffer = render_api.new_vertex_buffer(self.verts).await?;
let index_buffer = render_api.new_index_buffer(self.indices).await?;
Ok((vertex_buffer, index_buffer))
Ok(MeshInfo { vertex_buffer, index_buffer, num_elements })
}
}

View File

@@ -7,7 +7,7 @@ use crate::{
};
pub struct PropertyBool {
prop: Arc<Property>,
prop: PropertyPtr,
idx: usize,
}
@@ -28,10 +28,14 @@ impl PropertyBool {
pub fn set(&self, val: bool) {
self.prop.set_bool(self.idx, val).unwrap()
}
pub fn prop(&self) -> PropertyPtr {
self.prop.clone()
}
}
pub struct PropertyUint32 {
prop: Arc<Property>,
prop: PropertyPtr,
idx: usize,
}
@@ -59,10 +63,14 @@ impl PropertyUint32 {
pub fn set(&self, val: u32) {
self.prop.set_u32(self.idx, val).unwrap()
}
pub fn prop(&self) -> PropertyPtr {
self.prop.clone()
}
}
pub struct PropertyFloat32 {
prop: Arc<Property>,
prop: PropertyPtr,
idx: usize,
}
@@ -83,10 +91,14 @@ impl PropertyFloat32 {
pub fn set(&self, val: f32) {
self.prop.set_f32(self.idx, val).unwrap()
}
pub fn prop(&self) -> PropertyPtr {
self.prop.clone()
}
}
pub struct PropertyStr {
prop: Arc<Property>,
prop: PropertyPtr,
idx: usize,
}
@@ -107,10 +119,14 @@ impl PropertyStr {
pub fn set<S: Into<String>>(&self, val: S) {
self.prop.set_str(self.idx, val.into()).unwrap()
}
pub fn prop(&self) -> PropertyPtr {
self.prop.clone()
}
}
pub struct PropertyColor {
prop: Arc<Property>,
prop: PropertyPtr,
}
impl PropertyColor {
@@ -142,4 +158,8 @@ impl PropertyColor {
self.prop.set_f32(2, val[2]).unwrap();
self.prop.set_f32(3, val[3]).unwrap();
}
pub fn prop(&self) -> PropertyPtr {
self.prop.clone()
}
}

View File

@@ -16,7 +16,7 @@ use std::{
use crate::{
error::Result,
gfx2::{Rectangle, RenderApiPtr},
gfx2::{Rectangle, RenderApi, RenderApiPtr},
util::ansi_texture,
};
@@ -53,7 +53,7 @@ pub struct RenderedAtlas {
const ATLAS_GAP: usize = 2;
pub async fn make_texture_atlas(
render_api: RenderApiPtr,
render_api: &RenderApi,
font_size: f32,
glyphs: &Vec<Glyph>,
) -> Result<RenderedAtlas> {
@@ -337,7 +337,9 @@ impl TextShaper {
let mut prev_cluster = 0;
//for (i, (position, info)) in positions.iter().zip(infos).enumerate() {
for (i, (position, info)) in glyph_pos_iter.iter().zip(glyph_infos_iter.iter()).enumerate() {
for (i, (position, info)) in
glyph_pos_iter.iter().zip(glyph_infos_iter.iter()).enumerate()
{
let glyph_id = info.codepoint as u32;
// Index within this substr
let curr_cluster = info.cluster as usize;
@@ -450,6 +452,7 @@ impl TextShaper {
bearing_x,
bearing_y,
has_fixed_sizes,
has_color: face.has_color(),
});
cache.insert(cache_key, Arc::downgrade(&sprite));
@@ -510,6 +513,7 @@ pub struct Sprite {
pub bearing_x: f32,
pub bearing_y: f32,
pub has_fixed_sizes: bool,
pub has_color: bool,
}
pub struct Glyph {

View File

@@ -1,37 +1,46 @@
use async_lock::Mutex;
//use async_lock::Mutex;
use miniquad::{BufferId, TextureId};
use rand::{rngs::OsRng, Rng};
use std::sync::{Arc, Weak};
use std::sync::{Arc, Mutex, Weak};
use crate::{
gfx2::{DrawCall, DrawInstruction, DrawMesh, Rectangle, RenderApiPtr, Vertex},
mesh::{MeshBuilder, COLOR_BLUE, COLOR_WHITE},
prop::{PropertyPtr, PropertyUint32},
gfx2::{DrawCall, DrawInstruction, DrawMesh, Rectangle, RenderApi, RenderApiPtr, Vertex},
mesh::{Color, MeshBuilder, MeshInfo, COLOR_BLUE, COLOR_WHITE},
prop::{
PropertyBool, PropertyColor, PropertyFloat32, PropertyPtr, PropertyStr, PropertyUint32,
},
scene::{Pimpl, SceneGraph, SceneGraphPtr2, SceneNodeId},
text2::{self, Glyph, GlyphPositionIter, RenderedAtlas, SpritePtr, TextShaperPtr},
text2::{self, Glyph, GlyphPositionIter, RenderedAtlas, SpritePtr, TextShaper, TextShaperPtr},
};
use super::{eval_rect, get_parent_rect, read_rect, DrawUpdate, OnModify, Stoppable};
pub type TextPtr = Arc<Text>;
#[derive(Clone)]
struct TextRenderInfo {
glyph_sprites: Vec<SpritePtr>,
mesh: MeshInfo,
texture_id: TextureId,
}
pub struct Text {
sg: SceneGraphPtr2,
render_api: RenderApiPtr,
text_shaper: TextShaperPtr,
tasks: Vec<smol::Task<()>>,
glyph_sprites: Mutex<Vec<SpritePtr>>,
texture_id: TextureId,
vertex_buffer: BufferId,
index_buffer: BufferId,
num_elements: i32,
render_info: Mutex<TextRenderInfo>,
dc_key: u64,
node_id: SceneNodeId,
rect: PropertyPtr,
z_index: PropertyUint32,
text: PropertyStr,
font_size: PropertyFloat32,
color: PropertyColor,
baseline: PropertyFloat32,
debug: PropertyBool,
}
impl Text {
@@ -46,83 +55,99 @@ impl Text {
let node = scene_graph.get_node(node_id).unwrap();
let node_name = node.name.clone();
let rect = node.get_property("rect").expect("Text::rect");
let z_index_prop = node.get_property("z_index").expect("Text::z_index");
let z_index = PropertyUint32::from(z_index_prop.clone(), 0).unwrap();
let text = node.get_property("text").expect("Text::text");
let font_size = node.get_property("font_size").expect("Text::font_size");
let color = node.get_property("color").expect("Text::color");
let debug = node.get_property("debug").expect("Text::debug");
let baseline = node.get_property("baseline").expect("Text::baseline");
let z_index = PropertyUint32::wrap(node, "z_index", 0).unwrap();
let text = PropertyStr::wrap(node, "text", 0).unwrap();
let font_size = PropertyFloat32::wrap(node, "font_size", 0).unwrap();
let color = PropertyColor::wrap(node, "color").unwrap();
let debug = PropertyBool::wrap(node, "debug", 0).unwrap();
let baseline = PropertyFloat32::wrap(node, "baseline", 0).unwrap();
drop(scene_graph);
let text_str = text.get_str(0).unwrap();
let font_size_val = font_size.get_f32(0).unwrap();
debug!(target: "ui::text", "Rendering label '{}'", text_str);
let glyphs = text_shaper.shape(text_str, font_size_val).await;
let atlas =
text2::make_texture_atlas(render_api.clone(), font_size_val, &glyphs).await.unwrap();
let (x1, y1) = (0., 0.);
let (x2, y2) = (1., 1.);
let verts = vec![
// top left
Vertex { pos: [x1, y1], color: [1., 0., 0., 1.], uv: [0., 0.] },
// top right
Vertex { pos: [x2, y1], color: [1., 0., 1., 1.], uv: [1., 0.] },
// bottom left
Vertex { pos: [x1, y2], color: [0., 0., 1., 1.], uv: [0., 1.] },
// bottom right
Vertex { pos: [x2, y2], color: [1., 1., 0., 1.], uv: [1., 1.] },
];
let indices = vec![0, 2, 1, 1, 2, 3];
assert_eq!(atlas.uv_rects.len(), glyphs.len());
let baseline_y = baseline.get_f32(0).unwrap();
let mut mesh = MeshBuilder::new();
let mut glyph_pos_iter = GlyphPositionIter::new(font_size_val, &glyphs, baseline_y);
for (uv_rect, glyph_rect) in atlas.uv_rects.into_iter().zip(glyph_pos_iter) {
//mesh.draw_outline(&glyph_rect, COLOR_BLUE, 2.);
mesh.draw_box(&glyph_rect, COLOR_WHITE, &uv_rect);
}
let num_elements = mesh.num_elements();
let (vertex_buffer, index_buffer) = mesh.alloc(&render_api).await.unwrap();
let sprites = glyphs.into_iter().map(|glyph| glyph.sprite).collect();
let render_info = Self::regen_mesh(
&render_api,
&text_shaper,
text.get(),
font_size.get(),
color.get(),
baseline.get(),
)
.await;
let self_ = Arc::new_cyclic(|me: &Weak<Self>| {
let mut on_modify = OnModify::new(ex, node_name, node_id, me.clone());
on_modify.when_change(rect.clone(), Self::redraw);
on_modify.when_change(z_index_prop, Self::redraw);
on_modify.when_change(text, Self::redraw);
on_modify.when_change(font_size, Self::redraw);
on_modify.when_change(color, Self::redraw);
on_modify.when_change(debug, Self::redraw);
on_modify.when_change(baseline, Self::redraw);
on_modify.when_change(z_index.prop(), Self::redraw);
on_modify.when_change(text.prop(), Self::redraw);
on_modify.when_change(font_size.prop(), Self::redraw);
on_modify.when_change(color.prop(), Self::redraw);
on_modify.when_change(debug.prop(), Self::redraw);
on_modify.when_change(baseline.prop(), Self::redraw);
Self {
sg,
render_api,
text_shaper,
tasks: on_modify.tasks,
//glyphs: Mutex::new((glyphs, atlas)),
glyph_sprites: Mutex::new(sprites),
texture_id: atlas.texture_id,
vertex_buffer,
index_buffer,
num_elements,
render_info: Mutex::new(render_info),
dc_key: OsRng.gen(),
node_id,
rect,
z_index,
text,
font_size,
color,
baseline,
debug,
}
});
Pimpl::Text(self_)
}
async fn regen_mesh(
render_api: &RenderApi,
text_shaper: &TextShaper,
text: String,
font_size: f32,
text_color: Color,
baseline: f32,
) -> TextRenderInfo {
debug!(target: "ui::text", "Rendering label '{}'", text);
let glyphs = text_shaper.shape(text, font_size).await;
let atlas = text2::make_texture_atlas(render_api, font_size, &glyphs).await.unwrap();
let mut mesh = MeshBuilder::new();
let mut glyph_pos_iter = GlyphPositionIter::new(font_size, &glyphs, baseline);
for ((uv_rect, glyph_rect), glyph) in
atlas.uv_rects.into_iter().zip(glyph_pos_iter).zip(glyphs.iter())
{
//mesh.draw_outline(&glyph_rect, COLOR_BLUE, 2.);
let mut color = text_color.clone();
if glyph.sprite.has_color {
color = COLOR_WHITE;
}
mesh.draw_box(&glyph_rect, color, &uv_rect);
}
let mesh = mesh.alloc(&render_api).await.unwrap();
let glyph_sprites = glyphs.into_iter().map(|glyph| glyph.sprite).collect();
TextRenderInfo { glyph_sprites, mesh, texture_id: atlas.texture_id }
}
async fn redraw(self: Arc<Self>) {
let render_info = Self::regen_mesh(
&self.render_api,
&self.text_shaper,
self.text.get(),
self.font_size.get(),
self.color.get(),
self.baseline.get(),
)
.await;
*self.render_info.lock().unwrap() = render_info;
let sg = self.sg.lock().await;
let node = sg.get_node(self.node_id).unwrap();
@@ -143,11 +168,13 @@ impl Text {
// Only used for debug messages
let node = sg.get_node(self.node_id).unwrap();
let render_info = self.render_info.lock().unwrap().clone();
let mesh = DrawMesh {
vertex_buffer: self.vertex_buffer,
index_buffer: self.index_buffer,
texture: Some(self.texture_id),
num_elements: self.num_elements,
vertex_buffer: render_info.mesh.vertex_buffer,
index_buffer: render_info.mesh.index_buffer,
texture: Some(render_info.texture_id),
num_elements: render_info.mesh.num_elements,
};
if let Err(err) = eval_rect(self.rect.clone(), parent_rect) {