wallet: remove old unused modules from proj

This commit is contained in:
darkfi
2024-08-09 10:38:49 +02:00
parent d626f6e802
commit dc71754be6
26 changed files with 43 additions and 3799 deletions

View File

@@ -32,13 +32,13 @@ use crate::{
darkirc::{DarkIrcBackendPtr, Privmsg},
error::Error,
expr::Op,
gfx2::{GraphicsEventPublisherPtr, RenderApiPtr, Vertex},
gfx::{GraphicsEventPublisherPtr, RenderApiPtr, Vertex},
prop::{Property, PropertyBool, PropertyStr, PropertySubType, PropertyType, Role},
scene::{
CallArgType, MethodResponseFn, Pimpl, SceneGraph, SceneGraphPtr2, SceneNodeId,
SceneNodeType, Slot,
},
text2::TextShaperPtr,
text::TextShaperPtr,
ui::{chatview, Button, ChatView, EditBox, Image, Mesh, RenderLayer, Stoppable, Text, Window},
ExecutorPtr,
};

View File

@@ -1,436 +0,0 @@
/* 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 <https://www.gnu.org/licenses/>.
*/
use darkfi_serial::Encodable;
use crate::{
expr::Op,
gfx::Rectangle,
prop::{Property, PropertySubType, PropertyType},
scene::{
SceneGraph, SceneNodeId,
SceneNodeType,
},
};
struct Buffer {
verts: Vec<u8>,
faces: Vec<u8>,
verts_len: u32,
}
impl Buffer {
pub fn new() -> Self {
Self { verts: vec![], faces: vec![], verts_len: 0 }
}
fn vertex(&mut self, x: f32, y: f32, r: f32, g: f32, b: f32, a: f32, u: f32, v: f32) {
// xy
x.encode(&mut self.verts).unwrap();
y.encode(&mut self.verts).unwrap();
// rgba
r.encode(&mut self.verts).unwrap();
g.encode(&mut self.verts).unwrap();
b.encode(&mut self.verts).unwrap();
a.encode(&mut self.verts).unwrap();
// uv
u.encode(&mut self.verts).unwrap();
v.encode(&mut self.verts).unwrap();
self.verts_len += 1
}
fn face(&mut self, i1: u32, i2: u32, i3: u32) {
i1.encode(&mut self.faces).unwrap();
i2.encode(&mut self.faces).unwrap();
i3.encode(&mut self.faces).unwrap();
}
pub fn draw_box(&mut self, rect: Rectangle<f32>, color: [f32; 4]) {
let k = self.verts_len;
let x = rect.x;
let y = rect.y;
let w = rect.w;
let h = rect.h;
let r = color[0];
let g = color[1];
let b = color[2];
let a = color[3];
self.vertex(x, y, r, g, b, a, 0., 0.);
self.vertex(x + w, y, r, g, b, a, 1., 0.);
self.vertex(x, y + h, r, g, b, a, 0., 1.);
self.vertex(x + w, y + h, r, g, b, a, 1., 1.);
self.face(k, k + 2, k + 1);
self.face(k + 1, k + 2, k + 3);
}
pub fn draw_outline(
&mut self,
rect: Rectangle<f32>,
color: [f32; 4],
pad: f32,
layer_w: f32,
layer_h: f32,
) {
let pad_x = pad / layer_w;
let pad_y = pad / layer_h;
let x = rect.x;
let y = rect.y;
let w = rect.w;
let h = rect.h;
// left
self.draw_box(Rectangle { x, y, w: pad_x, h }, color);
// top
self.draw_box(Rectangle { x, y, w, h: pad_y }, color);
// right
let rhs = x + w;
self.draw_box(Rectangle { x: rhs - pad_x, y, w: pad_x, h }, color);
// bottom
let bhs = y + h;
self.draw_box(Rectangle { x, y: bhs - pad_y, w, h: pad_y }, color);
}
}
pub fn create_layer(sg: &mut SceneGraph, name: &str) -> SceneNodeId {
let win_id = sg.lookup_node("/window").unwrap().id;
let node = sg.add_node(name, SceneNodeType::RenderLayer);
let mut prop = Property::new("is_visible", PropertyType::Bool, PropertySubType::Null);
node.add_property(prop).unwrap();
let mut prop = Property::new("rect", PropertyType::Float32, PropertySubType::Pixel);
prop.set_array_len(4);
prop.allow_exprs();
node.add_property(prop).unwrap();
node.id
}
pub fn create_mesh(sg: &mut SceneGraph, name: &str) -> SceneNodeId {
let node = sg.add_node(name, SceneNodeType::RenderMesh);
let mut prop = Property::new("rect", PropertyType::Float32, PropertySubType::Pixel);
prop.set_array_len(4);
prop.allow_exprs();
node.add_property(prop).unwrap();
let mut prop = Property::new("z_index", PropertyType::Uint32, PropertySubType::Null);
node.add_property(prop).unwrap();
node.id
}
fn create_text(sg: &mut SceneGraph, name: &str, layer_node_id: SceneNodeId) -> SceneNodeId {
let node = sg.add_node(name, SceneNodeType::RenderText);
let mut prop = Property::new("rect", PropertyType::Float32, PropertySubType::Pixel);
prop.set_array_len(4);
prop.allow_exprs();
node.add_property(prop).unwrap();
let mut prop = Property::new("baseline", PropertyType::Float32, PropertySubType::Pixel);
node.add_property(prop).unwrap();
let mut prop = Property::new("font_size", PropertyType::Float32, PropertySubType::Pixel);
node.add_property(prop).unwrap();
let mut prop = Property::new("text", PropertyType::Str, PropertySubType::Null);
node.add_property(prop).unwrap();
let mut prop = Property::new("color", PropertyType::Float32, PropertySubType::Color);
prop.set_array_len(4);
prop.set_range_f32(0., 1.);
node.add_property(prop).unwrap();
let mut prop = Property::new("z_index", PropertyType::Uint32, PropertySubType::Null);
node.add_property(prop).unwrap();
let mut prop = Property::new("debug", PropertyType::Bool, PropertySubType::Null);
node.add_property(prop).unwrap();
let node_id = node.id;
sg.link(node_id, layer_node_id).unwrap();
node_id
}
fn create_editbox(sg: &mut SceneGraph, name: &str, layer_node_id: SceneNodeId) -> SceneNodeId {
let node = sg.add_node(name, SceneNodeType::EditBox);
let mut prop = Property::new("is_active", PropertyType::Bool, PropertySubType::Null);
node.add_property(prop).unwrap();
let mut prop = Property::new("rect", PropertyType::Float32, PropertySubType::Pixel);
prop.set_array_len(4);
prop.allow_exprs();
node.add_property(prop).unwrap();
let mut prop = Property::new("baseline", PropertyType::Float32, PropertySubType::Pixel);
node.add_property(prop).unwrap();
let mut prop = Property::new("scroll", PropertyType::Float32, PropertySubType::Pixel);
node.add_property(prop).unwrap();
let mut prop = Property::new("cursor_pos", PropertyType::Uint32, PropertySubType::Pixel);
node.add_property(prop).unwrap();
let mut prop = Property::new("font_size", PropertyType::Float32, PropertySubType::Pixel);
node.add_property(prop).unwrap();
let mut prop = Property::new("text", PropertyType::Str, PropertySubType::Null);
node.add_property(prop).unwrap();
let mut prop = Property::new("text_color", PropertyType::Float32, PropertySubType::Color);
prop.set_array_len(4);
prop.set_range_f32(0., 1.);
node.add_property(prop).unwrap();
let mut prop = Property::new("cursor_color", PropertyType::Float32, PropertySubType::Color);
prop.set_array_len(4);
prop.set_range_f32(0., 1.);
node.add_property(prop).unwrap();
let mut prop = Property::new("hi_bg_color", PropertyType::Float32, PropertySubType::Color);
prop.set_array_len(4);
prop.set_range_f32(0., 1.);
node.add_property(prop).unwrap();
let mut prop = Property::new("selected", PropertyType::Uint32, PropertySubType::Color);
prop.set_array_len(2);
prop.allow_null_values();
node.add_property(prop).unwrap();
let mut prop = Property::new("z_index", PropertyType::Uint32, PropertySubType::Null);
node.add_property(prop).unwrap();
let mut prop = Property::new("debug", PropertyType::Bool, PropertySubType::Null);
node.add_property(prop).unwrap();
let node_id = node.id;
sg.link(node_id, layer_node_id).unwrap();
node_id
}
fn create_chatview(sg: &mut SceneGraph, name: &str, layer_node_id: SceneNodeId) -> SceneNodeId {
let node = sg.add_node(name, SceneNodeType::ChatView);
let mut prop = Property::new("rect", PropertyType::Float32, PropertySubType::Pixel);
prop.set_array_len(4);
prop.allow_exprs();
node.add_property(prop).unwrap();
let mut prop = Property::new("z_index", PropertyType::Uint32, PropertySubType::Null);
node.add_property(prop).unwrap();
let mut prop = Property::new("debug", PropertyType::Bool, PropertySubType::Null);
node.add_property(prop).unwrap();
let node_id = node.id;
let mut arg_data = vec![];
node_id.encode(&mut arg_data).unwrap();
//let (tx, rx) = mpsc::sync_channel::<Result<Vec<u8>>>(0);
let response_fn = Box::new(move |_result| {
//tx.send(result).unwrap();
});
let win_node = sg.lookup_node_mut("/window").unwrap();
win_node.call_method("create_chat_view", arg_data, response_fn).unwrap();
//let _result = rx.recv().unwrap();
sg.link(node_id, layer_node_id).unwrap();
node_id
}
pub fn setup(sg: &mut SceneGraph) {
let layer_node_id = create_layer(sg, "view");
let node = sg.get_node(layer_node_id).unwrap();
let prop = node.get_property("rect").unwrap();
prop.set_u32(0, 0).unwrap();
prop.set_u32(1, 0).unwrap();
let code = vec![Op::Float32ToUint32((Box::new(Op::LoadVar("sw".to_string()))))];
prop.set_expr(2, code).unwrap();
let code = vec![Op::Float32ToUint32((Box::new(Op::LoadVar("sh".to_string()))))];
prop.set_expr(3, code).unwrap();
// Make the black background
// Maybe we should use a RenderPass for this instead
let node_id = create_mesh(sg, "bg");
let node = sg.get_node(node_id).unwrap();
let prop = node.get_property("rect").unwrap();
prop.set_f32(0, 0.).unwrap();
prop.set_f32(1, 0.).unwrap();
let code = vec![Op::LoadVar("lw".to_string())];
prop.set_expr(2, code).unwrap();
let code = vec![Op::LoadVar("lh".to_string())];
prop.set_expr(3, code).unwrap();
let prop = node.get_property("data").unwrap();
let mut buff = Buffer::new();
buff.draw_box(Rectangle { x: 0., y: 0., w: 1., h: 1. }, [0.05, 0.05, 0.05, 1.]);
prop.set_buf(0, buff.verts).unwrap();
prop.set_buf(1, buff.faces).unwrap();
// Make the chatedit bg
let node_id = create_mesh(sg, "chateditbg");
let node = sg.get_node(node_id).unwrap();
let prop = node.get_property("rect").unwrap();
let prop = node.get_property("rect").unwrap();
prop.set_f32(0, 140.).unwrap();
let code =
vec![Op::Sub((Box::new(Op::LoadVar("lh".to_string())), Box::new(Op::ConstFloat32(60.))))];
prop.set_expr(1, code).unwrap();
let code =
vec![Op::Sub((Box::new(Op::LoadVar("lw".to_string())), Box::new(Op::ConstFloat32(140.))))];
prop.set_expr(2, code).unwrap();
prop.set_f32(3, 60.).unwrap();
let prop = node.get_property("data").unwrap();
let mut buff = Buffer::new();
buff.draw_box(Rectangle { x: 0., y: 0., w: 1., h: 1. }, [0., 0.13, 0.08, 1.]);
// FIXME: layer dim is passed here manually!
// we should just use separate objs
buff.draw_outline(
Rectangle { x: 0., y: 0., w: 1., h: 1. },
[0.22, 0.22, 0.22, 1.],
1.,
1000.,
50.,
);
prop.set_buf(0, buff.verts).unwrap();
prop.set_buf(1, buff.faces).unwrap();
// Make the nicktext border
let node_id = create_mesh(sg, "nickbg");
let node = sg.get_node(node_id).unwrap();
let prop = node.get_property("rect").unwrap();
prop.set_f32(0, 0.).unwrap();
let code =
vec![Op::Sub((Box::new(Op::LoadVar("lh".to_string())), Box::new(Op::ConstFloat32(60.))))];
prop.set_expr(1, code).unwrap();
prop.set_f32(2, 130.).unwrap();
prop.set_f32(3, 60.).unwrap();
let prop = node.get_property("data").unwrap();
let mut buff = Buffer::new();
// FIXME: layer dim is passed here manually!
// we should just use separate objs
buff.draw_outline(
Rectangle { x: 0., y: 0., w: 1., h: 1. },
[0., 0.13, 0.08, 1.],
1.,
1000.,
50.,
);
prop.set_buf(0, buff.verts).unwrap();
prop.set_buf(1, buff.faces).unwrap();
// Nickname
let node_id = create_text(sg, "nick", layer_node_id);
let node = sg.get_node(node_id).unwrap();
let prop = node.get_property("rect").unwrap();
prop.set_f32(0, 20.).unwrap();
let code =
vec![Op::Sub((Box::new(Op::LoadVar("lh".to_string())), Box::new(Op::ConstFloat32(60.))))];
prop.set_expr(1, code).unwrap();
prop.set_f32(2, 120.).unwrap();
prop.set_f32(3, 60.).unwrap();
node.set_property_f32("baseline", 40.).unwrap();
node.set_property_f32("font_size", 20.).unwrap();
node.set_property_str("text", "anon1").unwrap();
let prop = node.get_property("color").unwrap();
prop.set_f32(0, 0.).unwrap();
prop.set_f32(1, 1.).unwrap();
prop.set_f32(2, 0.).unwrap();
prop.set_f32(3, 1.).unwrap();
node.set_property_u32("z_index", 1).unwrap();
// Text edit
let node_id = create_editbox(sg, "editz", layer_node_id);
let node = sg.get_node(node_id).unwrap();
node.set_property_bool("is_active", true).unwrap();
let prop = node.get_property("rect").unwrap();
prop.set_f32(0, 150.).unwrap();
let code =
vec![Op::Sub((Box::new(Op::LoadVar("lh".to_string())), Box::new(Op::ConstFloat32(60.))))];
prop.set_expr(1, code).unwrap();
let code =
vec![Op::Sub((Box::new(Op::LoadVar("lw".to_string())), Box::new(Op::ConstFloat32(120.))))];
prop.set_expr(2, code).unwrap();
prop.set_f32(3, 60.).unwrap();
node.set_property_f32("baseline", 40.).unwrap();
node.set_property_f32("font_size", 20.).unwrap();
//node.set_property_str("text", "hello king!😁🍆jelly 🍆1234").unwrap();
node.set_property_str("text", "hello king! jelly 1234").unwrap();
let prop = node.get_property("text_color").unwrap();
prop.set_f32(0, 1.).unwrap();
prop.set_f32(1, 1.).unwrap();
prop.set_f32(2, 1.).unwrap();
prop.set_f32(3, 1.).unwrap();
let prop = node.get_property("cursor_color").unwrap();
prop.set_f32(0, 1.).unwrap();
prop.set_f32(1, 0.5).unwrap();
prop.set_f32(2, 0.5).unwrap();
prop.set_f32(3, 1.).unwrap();
let prop = node.get_property("hi_bg_color").unwrap();
prop.set_f32(0, 1.).unwrap();
prop.set_f32(1, 1.).unwrap();
prop.set_f32(2, 1.).unwrap();
prop.set_f32(3, 0.5).unwrap();
let prop = node.get_property("selected").unwrap();
prop.set_null(0).unwrap();
prop.set_null(1).unwrap();
node.set_property_u32("z_index", 1).unwrap();
let node_id = node.id;
let mut arg_data = vec![];
node_id.encode(&mut arg_data).unwrap();
//let (tx, rx) = mpsc::sync_channel::<Result<Vec<u8>>>(0);
let response_fn = Box::new(move |result| {
//tx.send(result).unwrap();
});
let win_node = sg.lookup_node_mut("/window").unwrap();
win_node.call_method("create_edit_box", arg_data, response_fn).unwrap();
//let _result = rx.recv().unwrap();
// ChatView
let node_id = create_chatview(sg, "chatty", layer_node_id);
let node = sg.get_node(node_id).unwrap();
let prop = node.get_property("rect").unwrap();
prop.set_f32(0, 0.).unwrap();
prop.set_f32(1, 0.).unwrap();
let code = vec![Op::LoadVar("lw".to_string())];
prop.set_expr(2, code).unwrap();
let code =
vec![Op::Sub((Box::new(Op::LoadVar("lh".to_string())), Box::new(Op::ConstFloat32(50.))))];
prop.set_expr(3, code).unwrap();
node.set_property_u32("z_index", 1).unwrap();
// On android lets scale the UI up
let win_node = sg.lookup_node_mut("/window").unwrap();
win_node.set_property_f32("scale", 1.6).unwrap();
let layer_node = sg.get_node(layer_node_id).unwrap();
layer_node.set_property_bool("is_visible", true).unwrap();
}

View File

@@ -1,339 +0,0 @@
/* 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 <https://www.gnu.org/licenses/>.
*/
use atomic_float::AtomicF32;
use miniquad::{TextureId, UniformType};
use std::{
collections::HashMap,
io::{BufRead, BufReader},
path::Path,
sync::{
atomic::Ordering,
Arc, Mutex,
},
};
use crate::{
error::Result,
gfx::{
FreetypeFace, Point, Rectangle, RenderContext,
COLOR_RED, COLOR_WHITE,
},
prop::PropertyBool,
scene::{Pimpl, SceneGraph, SceneNodeId},
text::{Glyph, TextShaper},
};
fn read_lines<P>(filename: P) -> Vec<String>
where
P: AsRef<Path>,
{
//let file = File::open(filename).unwrap();
//BufReader::new(file).lines().map(|l| l.unwrap()).collect()
// Just so we can package for android easily
// Later this will be all replaced anyway
let file = include_bytes!("../chat.txt");
BufReader::new(&file[..]).lines().map(|l| l.unwrap()).collect()
}
pub type ChatViewPtr = Arc<ChatView>;
pub struct ChatView {
node_name: String,
debug: PropertyBool,
// Used for mouse interaction
world_rect: Mutex<Rectangle<f32>>,
mouse_pos: Mutex<Point<f32>>,
text_shaper: TextShaper,
scroll: AtomicF32,
lines: Vec<String>,
glyph_lines: Mutex<Vec<Vec<Glyph>>>,
atlas: Mutex<HashMap<(u32, [u8; 4]), TextureId>>,
}
impl ChatView {
pub fn new(
scene_graph: &mut SceneGraph,
node_id: SceneNodeId,
font_faces: Vec<FreetypeFace>,
) -> Result<Pimpl> {
let node = scene_graph.get_node(node_id).unwrap();
let node_name = node.name.clone();
let debug = PropertyBool::wrap(node, "debug", 0)?;
let text_shaper = TextShaper { font_faces };
let lines = read_lines("chat.txt");
let mut glyph_lines = vec![];
glyph_lines.resize(lines.len(), vec![]);
let self_ = Arc::new(Self {
node_name: node_name.clone(),
debug,
world_rect: Mutex::new(Rectangle { x: 0., y: 0., w: 0., h: 0. }),
mouse_pos: Mutex::new(Point { x: 0., y: 0. }),
text_shaper,
scroll: AtomicF32::new(0.),
lines,
glyph_lines: Mutex::new(glyph_lines),
atlas: Mutex::new(HashMap::new()),
});
/*
let weak_self = Arc::downgrade(&self_);
let slot_move = Slot {
name: format!("{}::mouse_move", node_name),
func: Box::new(move |data| {
let mut cur = Cursor::new(&data);
let x = f32::decode(&mut cur).unwrap();
let y = f32::decode(&mut cur).unwrap();
let self_ = weak_self.upgrade();
if let Some(self_) = self_ {
let pos = &mut *self_.mouse_pos.lock().unwrap();
pos.x = x;
pos.y = y;
}
}),
};
let weak_self = Arc::downgrade(&self_);
let slot_wheel = Slot {
name: format!("{}::mouse_wheel", node_name),
func: Box::new(move |data| {
let mut cur = Cursor::new(&data);
let x = f32::decode(&mut cur).unwrap();
let y = f32::decode(&mut cur).unwrap();
let self_ = weak_self.upgrade();
if let Some(self_) = self_ {
self_.mouse_scroll(x, y);
}
}),
};
*/
let mouse_node =
scene_graph.lookup_node_mut("/window/input/mouse").expect("no mouse attached!");
//mouse_node.register("wheel", slot_wheel);
//mouse_node.register("move", slot_move);
// Save any properties we use
Ok(Pimpl::ChatView(self_))
}
pub fn render<'a>(
&self,
render: &mut RenderContext<'a>,
node_id: SceneNodeId,
layer_rect: &Rectangle<f32>,
) -> Result<()> {
let debug = self.debug.get();
let node = render.scene_graph.get_node(node_id).unwrap();
let rect = RenderContext::get_dim(node, layer_rect)?;
// Used for detecting mouse clicks
let mut world_rect = rect.clone();
world_rect.x += layer_rect.x as f32;
world_rect.y += layer_rect.y as f32;
*self.world_rect.lock().unwrap() = world_rect;
let layer_w = layer_rect.w as f32;
let layer_h = layer_rect.h as f32;
let off_x = rect.x / layer_w;
let off_y = rect.y / layer_h;
// Use absolute pixel scale
let scale_x = 1. / layer_w;
let scale_y = 1. / layer_h;
let model = glam::Mat4::from_translation(glam::Vec3::new(off_x, off_y, 0.)) *
glam::Mat4::from_scale(glam::Vec3::new(scale_x, scale_y, 1.));
let mut uniforms_data = [0u8; 128];
let data: [u8; 64] = unsafe { std::mem::transmute_copy(&render.proj) };
uniforms_data[0..64].copy_from_slice(&data);
let data: [u8; 64] = unsafe { std::mem::transmute_copy(&model) };
uniforms_data[64..].copy_from_slice(&data);
assert_eq!(128, 2 * UniformType::Mat4.size());
render.ctx.apply_uniforms_from_bytes(uniforms_data.as_ptr(), uniforms_data.len());
// Used for scaling the font size
let window = render.scene_graph.lookup_node("/window").expect("no window attached!");
let window_scale = window.get_property_f32("scale")?;
let font_size = window_scale * 20.;
let bound = Rectangle { x: 0., y: 0., w: rect.w, h: rect.h };
let glyph_lines = &mut self.glyph_lines.lock().unwrap();
let atlas = &mut self.atlas.lock().unwrap();
let scroll = self.scroll.load(Ordering::Relaxed);
for (i, (line, glyph_line)) in self.lines.iter().zip(glyph_lines.iter_mut()).enumerate() {
let line = line.replace("\t", " ");
// Split time and nick from the line
let mut iter = line.split_whitespace();
let Some(time) = iter.next() else {
error!("line missing time");
continue
};
let Some(nick) = iter.next() else {
error!("line missing nick");
continue
};
let Some(line) = iter.remainder() else {
error!("line missing remainder");
continue
};
let linespacing = window_scale * 30.;
let off_y = linespacing * i as f32 + scroll;
if off_y + linespacing < 0. || off_y - linespacing > rect.h {
continue;
}
if glyph_line.is_empty() {
*glyph_line = self.text_shaper.shape(line.to_string(), font_size, COLOR_WHITE);
}
let times_color = [0.4, 0.4, 0.4, 1.];
let times_color_u8 =
[(255. * 0.4) as u8, (255. * 0.4) as u8, (255. * 0.4) as u8, (255. * 1.) as u8];
let glyphs_time = self.text_shaper.shape(time.to_string(), font_size, times_color);
let mut rhs = 0.;
for glyph in glyphs_time {
let mut pos = glyph.pos.clone();
pos.y += off_y;
rhs = pos.x + pos.w;
assert_eq!(glyph.bmp.len() as u16, glyph.bmp_width * glyph.bmp_height * 4);
//debug!("gly {} {}", glyph.substr, glyph.bmp.len());
let texture = if atlas.contains_key(&(glyph.id, times_color_u8.clone())) {
*atlas.get(&(glyph.id, times_color_u8.clone())).unwrap()
} else {
let texture = render.ctx.new_texture_from_rgba8(
glyph.bmp_width,
glyph.bmp_height,
&glyph.bmp,
);
atlas.insert((glyph.id, times_color_u8.clone()), texture);
texture
};
render.render_clipped_box_with_texture2(&bound, &pos, COLOR_WHITE, texture);
//render.ctx.delete_texture(texture);
}
let nick_colors = [
[0.00, 0.94, 1.00, 1.],
[0.36, 1.00, 0.69, 1.],
[0.29, 1.00, 0.45, 1.],
[0.00, 0.73, 0.38, 1.],
[0.21, 0.67, 0.67, 1.],
[0.56, 0.61, 1.00, 1.],
[0.84, 0.48, 1.00, 1.],
[1.00, 0.61, 0.94, 1.],
[1.00, 0.36, 0.48, 1.],
[1.00, 0.30, 0.00, 1.],
];
let nick_colors_u8 = [
[(255. * 0.00) as u8, (255. * 0.94) as u8, (255. * 1.00) as u8, (255. * 1.) as u8],
[(255. * 0.36) as u8, (255. * 1.00) as u8, (255. * 0.69) as u8, (255. * 1.) as u8],
[(255. * 0.29) as u8, (255. * 1.00) as u8, (255. * 0.45) as u8, (255. * 1.) as u8],
[(255. * 0.00) as u8, (255. * 0.73) as u8, (255. * 0.38) as u8, (255. * 1.) as u8],
[(255. * 0.21) as u8, (255. * 0.67) as u8, (255. * 0.67) as u8, (255. * 1.) as u8],
[(255. * 0.56) as u8, (255. * 0.61) as u8, (255. * 1.00) as u8, (255. * 1.) as u8],
[(255. * 0.84) as u8, (255. * 0.48) as u8, (255. * 1.00) as u8, (255. * 1.) as u8],
[(255. * 1.00) as u8, (255. * 0.61) as u8, (255. * 0.94) as u8, (255. * 1.) as u8],
[(255. * 1.00) as u8, (255. * 0.36) as u8, (255. * 0.48) as u8, (255. * 1.) as u8],
[(255. * 1.00) as u8, (255. * 0.30) as u8, (255. * 0.00) as u8, (255. * 1.) as u8],
];
let nick_color = nick_colors[nick.len() % nick_colors.len()];
let nick_color_u8 = nick_colors_u8[nick.len() % nick_colors.len()];
let glyphs_nick = self.text_shaper.shape(nick.to_string(), font_size, nick_color);
let off_x = rhs + window_scale * 20.;
for glyph in glyphs_nick {
let mut pos = glyph.pos.clone();
pos.x += off_x;
pos.y += off_y;
rhs = pos.x + pos.w;
assert_eq!(glyph.bmp.len() as u16, glyph.bmp_width * glyph.bmp_height * 4);
//debug!("gly {} {}", glyph.substr, glyph.bmp.len());
//let texture = render.ctx.new_texture_from_rgba8(glyph.bmp_width, glyph.bmp_height, &glyph.bmp);
let texture = if atlas.contains_key(&(glyph.id, nick_color_u8.clone())) {
*atlas.get(&(glyph.id, nick_color_u8.clone())).unwrap()
} else {
let texture = render.ctx.new_texture_from_rgba8(
glyph.bmp_width,
glyph.bmp_height,
&glyph.bmp,
);
atlas.insert((glyph.id, nick_color_u8.clone()), texture);
texture
};
render.render_clipped_box_with_texture2(&bound, &pos, COLOR_WHITE, texture);
//render.ctx.delete_texture(texture);
}
let off_x = rhs + window_scale * 20.;
for glyph in glyph_line {
let mut pos = glyph.pos.clone();
pos.x += off_x;
pos.y += off_y;
assert_eq!(glyph.bmp.len() as u16, glyph.bmp_width * glyph.bmp_height * 4);
//debug!("gly {} {}", glyph.substr, glyph.bmp.len());
//let texture = render.ctx.new_texture_from_rgba8(glyph.bmp_width, glyph.bmp_height, &glyph.bmp);
let texture = if atlas.contains_key(&(glyph.id, [255, 255, 255, 255])) {
*atlas.get(&(glyph.id, [255, 255, 255, 255])).unwrap()
} else {
let texture = render.ctx.new_texture_from_rgba8(
glyph.bmp_width,
glyph.bmp_height,
&glyph.bmp,
);
atlas.insert((glyph.id, [255, 255, 255, 255]), texture);
texture
};
render.render_clipped_box_with_texture2(&bound, &pos, COLOR_WHITE, texture);
//render.ctx.delete_texture(texture);
}
}
if debug {
render.outline(0., 0., rect.w, rect.h, COLOR_RED, 1.);
}
Ok(())
}
fn mouse_scroll(self: Arc<Self>, _x: f32, y: f32) {
let mouse_pos = &*self.mouse_pos.lock().unwrap();
if !self.world_rect.lock().unwrap().contains(mouse_pos) {
return;
}
drop(mouse_pos);
self.scroll.fetch_add(y * 10., Ordering::Relaxed);
// y = 1 for scroll up
// y = -1 for scroll down
//println!("{}", y);
}
}

View File

@@ -39,7 +39,7 @@ use darkfi_serial::{
use crate::{
net::ZeroMQAdapter,
scene::{SceneGraph, SceneGraphPtr2},
text2::TextShaper,
text::TextShaper,
ExecutorPtr,
};

View File

@@ -1,865 +0,0 @@
/* 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 <https://www.gnu.org/licenses/>.
*/
use log::{debug, info};
use miniquad::{window, KeyMods, MouseButton, TextureId, UniformType};
use std::{
collections::HashMap,
sync::{
atomic::{AtomicBool, Ordering},
Arc, Mutex,
},
time::Instant,
};
use crate::{
error::{Error, Result},
gfx::{
FreetypeFace, Point, Rectangle, RenderContext, COLOR_DARKGREY, COLOR_GREEN, COLOR_WHITE,
},
prop::{Property, PropertyBool, PropertyColor, PropertyFloat32, PropertyStr, PropertyUint32},
scene::{Pimpl, SceneGraph, SceneNodeId},
text::{Glyph, TextShaper},
};
const CURSOR_WIDTH: f32 = 4.;
struct PressedKeysSmoothRepeat {
/// When holding keys, we track from start and last sent time.
/// This is useful for initial delay and smooth scrolling.
pressed_keys: HashMap<String, RepeatingKeyTimer>,
/// Initial delay before allowing keys
start_delay: u32,
/// Minimum time between repeated keys
step_time: u32,
}
impl PressedKeysSmoothRepeat {
fn new(start_delay: u32, step_time: u32) -> Self {
Self { pressed_keys: HashMap::new(), start_delay, step_time }
}
fn key_down(&mut self, key: &str, repeat: bool) -> u32 {
if !repeat {
return 1;
}
// Insert key if not exists
if !self.pressed_keys.contains_key(key) {
self.pressed_keys.insert(key.to_string(), RepeatingKeyTimer::new());
}
let repeater = self.pressed_keys.get_mut(key).expect("repeat map");
repeater.update(self.start_delay, self.step_time)
}
fn key_up(&mut self, key: &str) {
self.pressed_keys.remove(key);
}
}
struct RepeatingKeyTimer {
start: Instant,
actions: u32,
}
impl RepeatingKeyTimer {
fn new() -> Self {
Self { start: Instant::now(), actions: 0 }
}
fn update(&mut self, start_delay: u32, step_time: u32) -> u32 {
let elapsed = self.start.elapsed().as_millis();
if elapsed < start_delay as u128 {
return 0
}
let total_actions = ((elapsed - start_delay as u128) / step_time as u128) as u32;
let remaining_actions = total_actions - self.actions;
self.actions = total_actions;
remaining_actions
}
}
pub type EditBoxPtr = Arc<EditBox>;
pub struct EditBox {
node_name: String,
is_active: PropertyBool,
debug: PropertyBool,
baseline: PropertyFloat32,
scroll: PropertyFloat32,
cursor_pos: PropertyUint32,
selected: Arc<Property>,
text: PropertyStr,
font_size: PropertyFloat32,
text_color: PropertyColor,
cursor_color: PropertyColor,
hi_bg_color: PropertyColor,
// Used for mouse clicks
world_rect: Mutex<Rectangle<f32>>,
glyphs: Mutex<Vec<Glyph>>,
text_shaper: TextShaper,
key_repeat: Mutex<PressedKeysSmoothRepeat>,
mouse_btn_held: AtomicBool,
window_scale: f32,
screen_size: Arc<Property>,
atlas: Mutex<HashMap<u32, TextureId>>,
}
impl EditBox {
pub fn new(
scene_graph: &mut SceneGraph,
node_id: SceneNodeId,
font_faces: Vec<FreetypeFace>,
) -> Result<Pimpl> {
let node = scene_graph.get_node(node_id).unwrap();
let node_name = node.name.clone();
let is_active = PropertyBool::wrap(node, "is_active", 0)?;
let debug = PropertyBool::wrap(node, "debug", 0)?;
let baseline = PropertyFloat32::wrap(node, "baseline", 0)?;
let scroll = PropertyFloat32::wrap(node, "scroll", 0)?;
let cursor_pos = PropertyUint32::wrap(node, "cursor_pos", 0)?;
let selected = node.get_property("selected").ok_or(Error::PropertyNotFound)?;
let text = PropertyStr::wrap(node, "text", 0)?;
let font_size = PropertyFloat32::wrap(node, "font_size", 0)?;
let text_color = PropertyColor::wrap(node, "text_color")?;
let cursor_color = PropertyColor::wrap(node, "cursor_color")?;
let hi_bg_color = PropertyColor::wrap(node, "hi_bg_color")?;
let text_shaper = TextShaper { font_faces };
// TODO: catch window resize event and regen glyphs
// Used for scaling the font size
let window_node = scene_graph.lookup_node("/window").expect("no window attached!");
let window_scale = window_node.get_property_f32("scale")?;
let screen_size = window_node.get_property("screen_size").ok_or(Error::PropertyNotFound)?;
let self_ = Arc::new(Self {
node_name: node_name.clone(),
is_active,
debug,
baseline,
scroll,
cursor_pos,
selected,
text,
font_size,
text_color,
cursor_color,
hi_bg_color,
world_rect: Mutex::new(Rectangle { x: 0., y: 0., w: 0., h: 0. }),
glyphs: Mutex::new(vec![]),
text_shaper,
key_repeat: Mutex::new(PressedKeysSmoothRepeat::new(400, 50)),
mouse_btn_held: AtomicBool::new(false),
window_scale,
screen_size,
atlas: Mutex::new(HashMap::new()),
});
self_.regen_glyphs().unwrap();
/*
let weak_self = Arc::downgrade(&self_);
let slot_key_down = Slot {
name: format!("{}::key_down", node_name),
func: Box::new(move |data| {
let mut cur = Cursor::new(&data);
let keymods = KeyMods {
shift: Decodable::decode(&mut cur).unwrap(),
ctrl: Decodable::decode(&mut cur).unwrap(),
alt: Decodable::decode(&mut cur).unwrap(),
logo: Decodable::decode(&mut cur).unwrap(),
};
let repeat = bool::decode(&mut cur).unwrap();
let key = String::decode(&mut cur).unwrap();
let self_ = weak_self.upgrade();
if let Some(self_) = self_ {
self_.key_down(key, keymods, repeat);
}
}),
};
let weak_self = Arc::downgrade(&self_);
let slot_key_up = Slot {
name: format!("{}::key_up", node_name),
func: Box::new(move |data| {
let mut cur = Cursor::new(&data);
let keymods = KeyMods {
shift: Decodable::decode(&mut cur).unwrap(),
ctrl: Decodable::decode(&mut cur).unwrap(),
alt: Decodable::decode(&mut cur).unwrap(),
logo: Decodable::decode(&mut cur).unwrap(),
};
let key = String::decode(&mut cur).unwrap();
let self_ = weak_self.upgrade();
if let Some(self_) = self_ {
self_.key_up(key, keymods);
}
}),
};
let keyb_node =
scene_graph
.lookup_node_mut("/window/input/keyboard")
.expect("no keyboard attached!");
keyb_node.register("key_down", slot_key_down).unwrap();
keyb_node.register("key_up", slot_key_up).unwrap();
let weak_self = Arc::downgrade(&self_);
let slot_btn_down = Slot {
name: format!("{}::mouse_button_down", node_name),
func: Box::new(move |data| {
let mut cur = Cursor::new(&data);
let button = MouseButton::from_u8(u8::decode(&mut cur).unwrap());
let x = f32::decode(&mut cur).unwrap();
let y = f32::decode(&mut cur).unwrap();
let self_ = weak_self.upgrade();
if let Some(self_) = self_ {
self_.mouse_button_down(button, x, y);
}
}),
};
let weak_self = Arc::downgrade(&self_);
let slot_btn_up = Slot {
name: format!("{}::mouse_button_up", node_name),
func: Box::new(move |data| {
let mut cur = Cursor::new(&data);
let button = MouseButton::from_u8(u8::decode(&mut cur).unwrap());
let x = f32::decode(&mut cur).unwrap();
let y = f32::decode(&mut cur).unwrap();
let self_ = weak_self.upgrade();
if let Some(self_) = self_ {
self_.mouse_button_up(button, x, y);
}
}),
};
let weak_self = Arc::downgrade(&self_);
let slot_move = Slot {
name: format!("{}::mouse_move", node_name),
func: Box::new(move |data| {
let mut cur = Cursor::new(&data);
let x = f32::decode(&mut cur).unwrap();
let y = f32::decode(&mut cur).unwrap();
let self_ = weak_self.upgrade();
if let Some(self_) = self_ {
self_.mouse_move(x, y);
}
}),
};
let mouse_node =
scene_graph
.lookup_node_mut("/window/input/mouse")
.expect("no mouse attached!");
mouse_node.register("button_down", slot_btn_down).unwrap();
mouse_node.register("button_up", slot_btn_up).unwrap();
mouse_node.register("move", slot_move).unwrap();
let weak_self = Arc::downgrade(&self_);
let slot_resize = Slot {
name: format!("{}::window_resize", node_name),
func: Box::new(move |data| {
let mut cur = Cursor::new(&data);
let w = f32::decode(&mut cur).unwrap();
let h = f32::decode(&mut cur).unwrap();
let self_ = weak_self.upgrade();
if let Some(self_) = self_ {
self_.window_resize(w, h);
}
}),
};
*/
let window_node = scene_graph.lookup_node_mut("/window").expect("no window attached!");
//window_node.register("resize", slot_resize).unwrap();
// Save any properties we use
Ok(Pimpl::EditBox(self_))
}
pub fn render<'a>(
&self,
render: &mut RenderContext<'a>,
node_id: SceneNodeId,
layer_rect: &Rectangle<f32>,
) -> Result<()> {
let node = render.scene_graph.get_node(node_id).unwrap();
let rect = RenderContext::get_dim(node, layer_rect)?;
// Used for detecting mouse clicks
let mut world_rect = rect.clone();
world_rect.x += layer_rect.x as f32;
world_rect.y += layer_rect.y as f32;
world_rect.x *= self.window_scale;
world_rect.y *= self.window_scale;
world_rect.w *= self.window_scale;
world_rect.h *= self.window_scale;
*self.world_rect.lock().unwrap() = world_rect;
let layer_w = layer_rect.w as f32;
let layer_h = layer_rect.h as f32;
let off_x = rect.x / layer_w;
let off_y = rect.y / layer_h;
// Use absolute pixel scale
let scale_x = 1. / layer_w;
let scale_y = 1. / layer_h;
//let model = glam::Mat4::IDENTITY;
let model = glam::Mat4::from_translation(glam::Vec3::new(off_x, off_y, 0.)) *
glam::Mat4::from_scale(glam::Vec3::new(scale_x, scale_y, 1.));
let mut uniforms_data = [0u8; 128];
let data: [u8; 64] = unsafe { std::mem::transmute_copy(&render.proj) };
uniforms_data[0..64].copy_from_slice(&data);
let data: [u8; 64] = unsafe { std::mem::transmute_copy(&model) };
uniforms_data[64..].copy_from_slice(&data);
assert_eq!(128, 2 * UniformType::Mat4.size());
render.ctx.apply_uniforms_from_bytes(uniforms_data.as_ptr(), uniforms_data.len());
self.apply_cursor_scrolling(&rect);
let node = render.scene_graph.get_node(node_id).unwrap();
let debug = self.debug.get();
let baseline = self.baseline.get();
let scroll = self.scroll.get();
let cursor_pos = self.cursor_pos.get() as usize;
let cursor_color = self.cursor_color.get();
let text_color = self.text_color.get();
let glyphs = &*self.glyphs.lock().unwrap();
let atlas = &mut self.atlas.lock().unwrap();
if !self.selected.is_null(0)? && !self.selected.is_null(1)? {
self.render_selected(render, &rect, glyphs)?;
}
let bound = Rectangle { x: 0., y: 0., w: rect.w, h: rect.h };
let mut rhs = 0.;
for (glyph_idx, glyph) in glyphs.iter().enumerate() {
let x1 = glyph.pos.x + scroll;
let y1 = glyph.pos.y + baseline;
let x2 = x1 + glyph.pos.w;
let y2 = y1 + glyph.pos.h;
let texture = if atlas.contains_key(&glyph.id) {
*atlas.get(&glyph.id).unwrap()
} else {
let texture = render.ctx.new_texture_from_rgba8(
glyph.bmp_width,
glyph.bmp_height,
&glyph.bmp,
);
atlas.insert(glyph.id, texture);
texture
};
//let texture = render.ctx.new_texture_from_rgba8(glyph.bmp_width, glyph.bmp_height, &glyph.bmp);
render.render_clipped_box_with_texture(&bound, x1, y1, x2, y2, COLOR_WHITE, texture);
//render.render_box_with_texture(x1, y1, x2, y2, COLOR_WHITE, texture);
//render.ctx.delete_texture(texture);
// Glyph outlines
//if debug {
// render.outline(x1, y1, x2, y2, COLOR_BLUE, 1.);
//}
if cursor_pos != 0 && cursor_pos == glyph_idx {
render.render_box(x1 - CURSOR_WIDTH, 0., x1, rect.h, cursor_color);
}
rhs = x2;
}
if cursor_pos == 0 {
render.render_box(0., 0., CURSOR_WIDTH, rect.h, cursor_color);
} else if cursor_pos == glyphs.len() {
render.render_box(rhs - CURSOR_WIDTH, 0., rhs, rect.h, cursor_color);
}
if debug {
let outline_color = if self.is_active.get() { COLOR_GREEN } else { COLOR_DARKGREY };
// Baseline
//render.hline(0., rhs, 0., COLOR_RED, 1.);
render.outline(0., 0., rect.w, rect.h, outline_color, 1.);
}
Ok(())
}
pub fn render_selected<'a>(
&self,
render: &mut RenderContext<'a>,
rect: &Rectangle<f32>,
glyphs: &Vec<Glyph>,
) -> Result<()> {
let start = self.selected.get_u32(0)? as usize;
let end = self.selected.get_u32(1)? as usize;
let sel_start = std::cmp::min(start, end);
let sel_end = std::cmp::max(start, end);
let scroll = self.scroll.get();
let hi_bg_color = self.hi_bg_color.get();
let mut start_x = 0.;
let mut end_x = 0.;
for (glyph_idx, glyph) in glyphs.iter().enumerate() {
let x1 = glyph.pos.x + scroll;
if glyph_idx == sel_start {
start_x = x1;
}
if glyph_idx == sel_end {
end_x = x1;
}
}
if sel_start == 0 {
start_x = scroll;
}
if sel_end == glyphs.len() {
let glyph = &glyphs.last().unwrap();
let x2 = glyph.pos.x + scroll + glyph.pos.w;
end_x = x2;
}
// Apply clipping
if start_x < 0. {
start_x = 0.;
}
if end_x > rect.w {
end_x = rect.w;
}
render.render_box(start_x, 0., end_x, rect.h, hi_bg_color);
Ok(())
}
fn delete_highlighted(&self) {
assert!(!self.selected.is_null(0).unwrap());
assert!(!self.selected.is_null(1).unwrap());
let start = self.selected.get_u32(0).unwrap() as usize;
let end = self.selected.get_u32(1).unwrap() as usize;
let sel_start = std::cmp::min(start, end);
let sel_end = std::cmp::max(start, end);
let glyphs = &*self.glyphs.lock().unwrap();
// Regen text
let mut text = String::new();
for (i, glyph) in glyphs.iter().enumerate() {
let mut substr = glyph.substr.clone();
if sel_start <= i && i < sel_end {
continue
}
text.push_str(&substr);
}
debug!(
"EditBox(\"{}\")::delete_highlighted() text=\"{}\", cursor_pos={}",
self.node_name, text, sel_start
);
self.text.set(text);
self.selected.set_null(0).unwrap();
self.selected.set_null(1).unwrap();
self.cursor_pos.set(sel_start as u32);
}
fn apply_cursor_scrolling(&self, rect: &Rectangle<f32>) {
let cursor_pos = self.cursor_pos.get() as usize;
let mut scroll = self.scroll.get();
let cursor_x = {
let glyphs = &*self.glyphs.lock().unwrap();
if cursor_pos == 0 {
0.
} else if cursor_pos == glyphs.len() {
let glyph = &glyphs.last().unwrap();
glyph.pos.x + glyph.pos.w
} else {
assert!(cursor_pos < glyphs.len());
let glyph = &glyphs[cursor_pos];
glyph.pos.x
}
};
let cursor_lhs = cursor_x + scroll;
let cursor_rhs = cursor_lhs + CURSOR_WIDTH;
if cursor_rhs > rect.w {
scroll = rect.w - cursor_x;
} else if cursor_lhs < 0. {
scroll = -cursor_x + CURSOR_WIDTH;
}
self.scroll.set(scroll);
}
fn regen_glyphs(&self) -> Result<()> {
let font_size = self.window_scale * self.font_size.get();
debug!("shape start");
let glyphs = self.text_shaper.shape(self.text.get(), font_size, self.text_color.get());
debug!("shape end");
if self.cursor_pos.get() > glyphs.len() as u32 {
self.cursor_pos.set(glyphs.len() as u32);
}
*self.glyphs.lock().unwrap() = glyphs;
Ok(())
}
fn key_down(self: Arc<Self>, key: String, mods: KeyMods, repeat: bool) {
if !self.is_active.get() {
return
}
let actions = {
let mut repeater = self.key_repeat.lock().unwrap();
repeater.key_down(&key, repeat)
};
for _ in 0..actions {
self.do_key_down(&key, &mods)
}
}
fn do_key_down(&self, key: &str, mods: &KeyMods) {
match key {
"Left" => {
let mut cursor_pos = self.cursor_pos.get();
// Start selection if shift is held
if !mods.shift {
self.selected.set_null(0).unwrap();
self.selected.set_null(1).unwrap();
} else if self.selected.is_null(0).unwrap() {
assert!(self.selected.is_null(1).unwrap());
self.selected.set_u32(0, cursor_pos).unwrap();
}
if cursor_pos > 0 {
cursor_pos -= 1;
debug!(
"EditBox(\"{}\")::key_down(Left) cursor_pos={}",
self.node_name, cursor_pos
);
self.cursor_pos.set(cursor_pos);
}
// Update selection
if mods.shift {
self.selected.set_u32(1, cursor_pos).unwrap();
}
}
"Right" => {
let mut cursor_pos = self.cursor_pos.get();
// Start selection if shift is held
if !mods.shift {
self.selected.set_null(0).unwrap();
self.selected.set_null(1).unwrap();
} else if self.selected.is_null(0).unwrap() {
assert!(self.selected.is_null(1).unwrap());
self.selected.set_u32(0, cursor_pos).unwrap();
}
let glyphs_len = self.glyphs.lock().unwrap().len() as u32;
if cursor_pos < glyphs_len {
cursor_pos += 1;
debug!(
"EditBox(\"{}\")::key_down(Right) cursor_pos={}",
self.node_name, cursor_pos
);
self.cursor_pos.set(cursor_pos);
}
// Update selection
if mods.shift {
self.selected.set_u32(1, cursor_pos).unwrap();
}
}
"Delete" => {
let cursor_pos = self.cursor_pos.get();
let _text = if !self.selected.is_null(0).unwrap() {
self.delete_highlighted();
} else {
let glyphs = &*self.glyphs.lock().unwrap();
if cursor_pos == glyphs.len() as u32 {
return;
}
// Regen text
let mut text = String::new();
for (i, glyph) in glyphs.iter().enumerate() {
let mut substr = glyph.substr.clone();
if cursor_pos as usize == i {
// Lmk if anyone knows a better way to do substr.pop_front()
let mut chars = substr.chars();
chars.next();
substr = chars.as_str().to_string();
}
text.push_str(&substr);
}
self.text.set(text);
};
self.regen_glyphs().unwrap();
}
"Backspace" => {
let cursor_pos = self.cursor_pos.get();
let _text = if !self.selected.is_null(0).unwrap() {
self.delete_highlighted();
} else {
if cursor_pos == 0 {
return;
}
let glyphs = &*self.glyphs.lock().unwrap();
let mut text = String::new();
for (i, glyph) in glyphs.iter().enumerate() {
let mut substr = glyph.substr.clone();
if cursor_pos as usize - 1 == i {
substr.pop().unwrap();
}
text.push_str(&substr);
}
self.text.set(text);
self.cursor_pos.set(cursor_pos - 1);
};
self.regen_glyphs().unwrap();
}
"C" => {
if mods.ctrl {
self.copy_highlighted_text().unwrap();
} else {
self.insert_char(key, mods);
}
}
"V" => {
if mods.ctrl {
if let Some(text) = window::clipboard_get() {
self.insert_text(text);
}
} else {
self.insert_char(key, mods);
}
}
_ => {
self.insert_char(key, mods);
}
}
}
fn insert_char(&self, key: &str, mods: &KeyMods) {
// First filter for only single digit keys
let allowed_keys = [
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q",
"R", "S", "T", "U", "V", "W", "X", "Y", "Z", " ", ":", ";", "'", "-", ".", "/", "=",
"(", "\\", ")", "`", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
];
if !allowed_keys.contains(&key) {
return
}
// If we want to only allow specific chars in a String here
//let ch = key.chars().next().unwrap();
// if !self.allowed_chars.chars().any(|c| c == ch) { return }
let key = if mods.shift { key.to_string() } else { key.to_lowercase() };
self.insert_text(key);
}
fn insert_text(&self, key: String) {
let mut text = String::new();
let cursor_pos = self.cursor_pos.get();
if cursor_pos == 0 {
text = key;
} else {
let glyphs = &*self.glyphs.lock().unwrap();
for (glyph_idx, glyph) in glyphs.iter().enumerate() {
text.push_str(&glyph.substr);
if cursor_pos == glyph_idx as u32 + 1 {
text.push_str(&key);
}
}
}
self.text.set(text);
// Not always true lol
self.cursor_pos.set(cursor_pos + 1);
self.regen_glyphs().unwrap();
}
fn copy_highlighted_text(&self) -> Result<()> {
let start = self.selected.get_u32(0)? as usize;
let end = self.selected.get_u32(1)? as usize;
let sel_start = std::cmp::min(start, end);
let sel_end = std::cmp::max(start, end);
let mut text = String::new();
let glyphs = &*self.glyphs.lock().unwrap();
for (glyph_idx, glyph) in glyphs.iter().enumerate() {
if sel_start <= glyph_idx && glyph_idx < sel_end {
text.push_str(&glyph.substr);
}
}
info!("Copied '{}'", text);
window::clipboard_set(&text);
Ok(())
}
fn key_up(self: Arc<Self>, key: String, mods: KeyMods) {
let mut repeater = self.key_repeat.lock().unwrap();
repeater.key_up(&key);
}
fn mouse_button_down(self: Arc<Self>, button: MouseButton, x: f32, y: f32) {
let mouse_pos = Point { x, y };
let rect = self.world_rect.lock().unwrap().clone();
debug!("mouse down event {} {} {} {} {} {}", x, y, rect.x, rect.y, rect.w, rect.h);
// clicking inside box will:
// 1. make it active
// 2. begin selection
if rect.contains(&mouse_pos) {
debug!("showkeyb!");
window::show_keyboard(true);
if !self.is_active.get() {
self.is_active.set(true);
println!("inside!");
// Send signal
}
let cpos = match self.find_closest_glyph_idx(x) {
MouseClickGlyph::Pos(cpos) => cpos,
_ => panic!("shouldn't be possible to reach here!"),
};
// set cursor pos
self.cursor_pos.set(cpos);
// begin selection
self.selected.set_u32(0, cpos).unwrap();
self.selected.set_u32(1, cpos).unwrap();
self.mouse_btn_held.store(true, Ordering::Relaxed);
}
// click outside the box will:
// 1. make it inactive
else {
if self.is_active.get() {
self.is_active.set(false);
// Send signal
}
}
}
fn mouse_button_up(self: Arc<Self>, button: MouseButton, x: f32, y: f32) {
// releasing mouse button will:
// 1. end selection
self.mouse_btn_held.store(false, Ordering::Relaxed);
}
fn mouse_move(self: Arc<Self>, x: f32, y: f32) {
if !self.mouse_btn_held.load(Ordering::Relaxed) {
return;
}
// if active and selection_active, then use x to modify the selection.
// also implement scrolling when cursor is to the left or right
// just scroll to the end
// also set cursor_pos too
let cpos = match self.find_closest_glyph_idx(x) {
MouseClickGlyph::Lhs => 0,
MouseClickGlyph::Pos(cpos) => cpos,
MouseClickGlyph::Rhs(cpos) => cpos,
};
self.cursor_pos.set(cpos);
self.selected.set_u32(1, cpos).unwrap();
}
// Uses screen x pos
fn find_closest_glyph_idx(&self, x: f32) -> MouseClickGlyph {
let rect = self.world_rect.lock().unwrap().clone();
let glyphs = &*self.glyphs.lock().unwrap();
let mouse_x = x - rect.x;
if mouse_x > rect.w {
// Highlight to the end
let cpos = glyphs.len() as u32;
return MouseClickGlyph::Rhs(cpos);
// Scroll to the right handled in render
} else if mouse_x < 0. {
return MouseClickGlyph::Lhs;
}
let scroll = self.scroll.get();
let mouse_x = x - rect.x;
let mut cpos = 0;
let lhs = 0.;
let mut last_d = (lhs - mouse_x).abs();
for (i, glyph) in glyphs.iter().skip(1).enumerate() {
// Because we skip the first item
let glyph_idx = (i + 1) as u32;
let x1 = glyph.pos.x + scroll;
let curr_d = (x1 - mouse_x).abs();
if curr_d < last_d {
last_d = curr_d;
cpos = glyph_idx;
}
}
// also check the right hand side
let rhs = {
let glyph = &glyphs.last().unwrap();
glyph.pos.x + scroll + glyph.pos.w
};
let curr_d = (rhs - mouse_x).abs();
if curr_d < last_d {
//last_d = curr_d;
cpos = glyphs.len() as u32;
}
MouseClickGlyph::Pos(cpos)
}
fn window_resize(self: Arc<Self>, w: f32, h: f32) {}
}
enum MouseClickGlyph {
Lhs,
Pos(u32),
Rhs(u32),
}

File diff suppressed because it is too large Load Diff

View File

@@ -32,11 +32,12 @@ use std::{
time::{Duration, Instant},
};
mod shader;
use crate::{
app::{AppPtr, AsyncRuntime},
error::{Error, Result},
pubsub::{Publisher, PublisherPtr, Subscription, SubscriptionId},
shader,
util::ansi_texture,
};
@@ -734,15 +735,15 @@ 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({}, {}, ...) -> {:?}",
//debug!(target: "gfx", "Invoked method: new_texture({}, {}, ...) -> {:?}",
// width, height, texture);
//debug!(target: "gfx2", "Invoked method: new_texture({}, {}, ...) -> {:?}\n{}",
//debug!(target: "gfx", "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) {
//debug!(target: "gfx2", "Invoked method: delete_texture({:?})", texture);
//debug!(target: "gfx", "Invoked method: delete_texture({:?})", texture);
self.ctx.delete_texture(texture);
}
fn method_new_vertex_buffer(
@@ -755,7 +756,7 @@ impl Stage {
BufferUsage::Immutable,
BufferSource::slice(&verts),
);
//debug!(target: "gfx2", "Invoked method: new_vertex_buffer({:?}) -> {:?}", verts, buffer);
//debug!(target: "gfx", "Invoked method: new_vertex_buffer({:?}) -> {:?}", verts, buffer);
sendr.try_send(buffer).unwrap();
}
fn method_new_index_buffer(
@@ -768,15 +769,15 @@ impl Stage {
BufferUsage::Immutable,
BufferSource::slice(&indices),
);
//debug!(target: "gfx2", "Invoked method: new_index_buffer({:?}) -> {:?}", indices, buffer);
//debug!(target: "gfx", "Invoked method: new_index_buffer({:?}) -> {:?}", indices, buffer);
sendr.try_send(buffer).unwrap();
}
fn method_delete_buffer(&mut self, buffer: BufferId) {
//debug!(target: "gfx2", "Invoked method: delete_buffer({:?})", buffer);
//debug!(target: "gfx", "Invoked method: delete_buffer({:?})", buffer);
self.ctx.delete_buffer(buffer);
}
fn method_replace_draw_calls(&mut self, dcs: Vec<(u64, DrawCall)>) {
//debug!(target: "gfx2", "Invoked method: replace_draw_calls({:?})", dcs);
//debug!(target: "gfx", "Invoked method: replace_draw_calls({:?})", dcs);
for (key, val) in dcs {
self.draw_calls.insert(key, val);
}

View File

@@ -1,434 +0,0 @@
/* 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 <https://www.gnu.org/licenses/>.
*/
use miniquad::{KeyCode, MouseButton};
pub trait KeyCodeAsStr {
fn to_str(&self) -> &str;
}
impl KeyCodeAsStr for KeyCode {
fn to_str(&self) -> &str {
match self {
Self::Space => " ",
Self::Apostrophe => "'",
Self::Comma => ",",
Self::Minus => "-",
Self::Period => ".",
Self::Slash => "/",
Self::Key0 => "0",
Self::Key1 => "1",
Self::Key2 => "2",
Self::Key3 => "3",
Self::Key4 => "4",
Self::Key5 => "5",
Self::Key6 => "6",
Self::Key7 => "7",
Self::Key8 => "8",
Self::Key9 => "9",
Self::Semicolon => ";",
Self::Equal => "=",
Self::A => "A",
Self::B => "B",
Self::C => "C",
Self::D => "D",
Self::E => "E",
Self::F => "F",
Self::G => "G",
Self::H => "H",
Self::I => "I",
Self::J => "J",
Self::K => "K",
Self::L => "L",
Self::M => "M",
Self::N => "N",
Self::O => "O",
Self::P => "P",
Self::Q => "Q",
Self::R => "R",
Self::S => "S",
Self::T => "T",
Self::U => "U",
Self::V => "V",
Self::W => "W",
Self::X => "X",
Self::Y => "Y",
Self::Z => "Z",
Self::LeftBracket => "(",
Self::Backslash => "\\",
Self::RightBracket => ")",
Self::GraveAccent => "`",
Self::World1 => "World1",
Self::World2 => "World2",
Self::Escape => "Escape",
Self::Enter => "\n",
Self::Tab => "Tab",
Self::Backspace => "Backspace",
Self::Insert => "Insert",
Self::Delete => "Delete",
Self::Right => "Right",
Self::Left => "Left",
Self::Down => "Down",
Self::Up => "Up",
Self::PageUp => "PageUp",
Self::PageDown => "PageDown",
Self::Home => "Home",
Self::End => "End",
Self::CapsLock => "CapsLock",
Self::ScrollLock => "ScrollLock",
Self::NumLock => "NumLock",
Self::PrintScreen => "PrintScreen",
Self::Pause => "Pause",
Self::F1 => "F1",
Self::F2 => "F2",
Self::F3 => "F3",
Self::F4 => "F4",
Self::F5 => "F5",
Self::F6 => "F6",
Self::F7 => "F7",
Self::F8 => "F8",
Self::F9 => "F9",
Self::F10 => "F10",
Self::F11 => "F11",
Self::F12 => "F12",
Self::F13 => "F13",
Self::F14 => "F14",
Self::F15 => "F15",
Self::F16 => "F16",
Self::F17 => "F17",
Self::F18 => "F18",
Self::F19 => "F19",
Self::F20 => "F20",
Self::F21 => "F21",
Self::F22 => "F22",
Self::F23 => "F23",
Self::F24 => "F24",
Self::F25 => "F25",
Self::Kp0 => "Kp0",
Self::Kp1 => "Kp1",
Self::Kp2 => "Kp2",
Self::Kp3 => "Kp3",
Self::Kp4 => "Kp4",
Self::Kp5 => "Kp5",
Self::Kp6 => "Kp6",
Self::Kp7 => "Kp7",
Self::Kp8 => "Kp8",
Self::Kp9 => "Kp9",
Self::KpDecimal => "KpDecimal",
Self::KpDivide => "KpDivide",
Self::KpMultiply => "KpMultiply",
Self::KpSubtract => "KpSubtract",
Self::KpAdd => "KpAdd",
Self::KpEnter => "KpEnter",
Self::KpEqual => "KpEqual",
Self::LeftShift => "LeftShift",
Self::LeftControl => "LeftControl",
Self::LeftAlt => "LeftAlt",
Self::LeftSuper => "LeftSuper",
Self::RightShift => "RightShift",
Self::RightControl => "RightControl",
Self::RightAlt => "RightAlt",
Self::RightSuper => "RightSuper",
Self::Menu => "Menu",
Self::Unknown => "Unknown",
}
}
}
pub trait KeyCodeAsU16 {
fn to_u16(&self) -> u16;
fn from_u16(keysym: u16) -> Self;
}
impl KeyCodeAsU16 for KeyCode {
fn to_u16(&self) -> u16 {
match self {
Self::Space => 0x0020,
Self::Apostrophe => 0x0027,
Self::Comma => 0x002c,
Self::Minus => 0x002d,
Self::Period => 0x002e,
Self::Slash => 0x002f,
Self::Key0 => 0x0030,
Self::Key1 => 0x0031,
Self::Key2 => 0x0032,
Self::Key3 => 0x0033,
Self::Key4 => 0x0034,
Self::Key5 => 0x0035,
Self::Key6 => 0x0036,
Self::Key7 => 0x0037,
Self::Key8 => 0x0038,
Self::Key9 => 0x0039,
Self::Semicolon => 0x003b,
Self::Equal => 0x003d,
Self::A => 0x0041,
Self::B => 0x0042,
Self::C => 0x0043,
Self::D => 0x0044,
Self::E => 0x0045,
Self::F => 0x0046,
Self::G => 0x0047,
Self::H => 0x0048,
Self::I => 0x0049,
Self::J => 0x004a,
Self::K => 0x004b,
Self::L => 0x004c,
Self::M => 0x004d,
Self::N => 0x004e,
Self::O => 0x004f,
Self::P => 0x0050,
Self::Q => 0x0051,
Self::R => 0x0052,
Self::S => 0x0053,
Self::T => 0x0054,
Self::U => 0x0055,
Self::V => 0x0056,
Self::W => 0x0057,
Self::X => 0x0058,
Self::Y => 0x0059,
Self::Z => 0x005a,
Self::LeftBracket => 0x005b,
Self::Backslash => 0x005c,
Self::RightBracket => 0x005d,
Self::GraveAccent => 0x0060,
Self::World1 => 0x0100,
Self::World2 => 0x0101,
Self::Escape => 0xff1b,
Self::Enter => 0xff0d,
Self::Tab => 0xff09,
Self::Backspace => 0xff08,
Self::Insert => 0xff63,
Self::Delete => 0xffff,
Self::Right => 0xff53,
Self::Left => 0xff51,
Self::Down => 0xff54,
Self::Up => 0xff52,
Self::PageUp => 0xff55,
Self::PageDown => 0xff56,
Self::Home => 0xff50,
Self::End => 0xff57,
Self::CapsLock => 0xffe5,
Self::ScrollLock => 0xff14,
Self::NumLock => 0xff7f,
Self::PrintScreen => 0xfd1d,
Self::Pause => 0xff13,
Self::F1 => 0xffbe,
Self::F2 => 0xffbf,
Self::F3 => 0xffc0,
Self::F4 => 0xffc1,
Self::F5 => 0xffc2,
Self::F6 => 0xffc3,
Self::F7 => 0xffc4,
Self::F8 => 0xffc5,
Self::F9 => 0xffc6,
Self::F10 => 0xffc7,
Self::F11 => 0xffc8,
Self::F12 => 0xffc9,
Self::F13 => 0xffca,
Self::F14 => 0xffcb,
Self::F15 => 0xffcc,
Self::F16 => 0xffcd,
Self::F17 => 0xffce,
Self::F18 => 0xffcf,
Self::F19 => 0xffd0,
Self::F20 => 0xffd1,
Self::F21 => 0xffd2,
Self::F22 => 0xffd3,
Self::F23 => 0xffd4,
Self::F24 => 0xffd5,
Self::F25 => 0xffd6,
Self::Kp0 => 0xffb0,
Self::Kp1 => 0xffb1,
Self::Kp2 => 0xffb2,
Self::Kp3 => 0xffb3,
Self::Kp4 => 0xffb4,
Self::Kp5 => 0xffb5,
Self::Kp6 => 0xffb6,
Self::Kp7 => 0xffb7,
Self::Kp8 => 0xffb8,
Self::Kp9 => 0xffb9,
Self::KpDecimal => 0xffae,
Self::KpDivide => 0xffaf,
Self::KpMultiply => 0xffaa,
Self::KpSubtract => 0xffad,
Self::KpAdd => 0xffab,
Self::KpEnter => 0xff8d,
Self::KpEqual => 0xffbd,
Self::LeftShift => 0xffe1,
Self::LeftControl => 0xffe3,
Self::LeftAlt => 0xffe9,
Self::LeftSuper => 0xffeb,
Self::RightShift => 0xffe2,
Self::RightControl => 0xffe4,
Self::RightAlt => 0xffea,
Self::RightSuper => 0xffec,
Self::Menu => 0xff67,
Self::Unknown => 0x01ff,
}
}
fn from_u16(keysym: u16) -> Self {
match keysym {
0x0020 => Self::Space,
0x0027 => Self::Apostrophe,
0x002c => Self::Comma,
0x002d => Self::Minus,
0x002e => Self::Period,
0x002f => Self::Slash,
0x0030 => Self::Key0,
0x0031 => Self::Key1,
0x0032 => Self::Key2,
0x0033 => Self::Key3,
0x0034 => Self::Key4,
0x0035 => Self::Key5,
0x0036 => Self::Key6,
0x0037 => Self::Key7,
0x0038 => Self::Key8,
0x0039 => Self::Key9,
0x003b => Self::Semicolon,
0x003d => Self::Equal,
0x0041 => Self::A,
0x0042 => Self::B,
0x0043 => Self::C,
0x0044 => Self::D,
0x0045 => Self::E,
0x0046 => Self::F,
0x0047 => Self::G,
0x0048 => Self::H,
0x0049 => Self::I,
0x004a => Self::J,
0x004b => Self::K,
0x004c => Self::L,
0x004d => Self::M,
0x004e => Self::N,
0x004f => Self::O,
0x0050 => Self::P,
0x0051 => Self::Q,
0x0052 => Self::R,
0x0053 => Self::S,
0x0054 => Self::T,
0x0055 => Self::U,
0x0056 => Self::V,
0x0057 => Self::W,
0x0058 => Self::X,
0x0059 => Self::Y,
0x005a => Self::Z,
0x005b => Self::LeftBracket,
0x005c => Self::Backslash,
0x005d => Self::RightBracket,
0x0060 => Self::GraveAccent,
0x0100 => Self::World1,
0x0101 => Self::World2,
0xff1b => Self::Escape,
0xff0d => Self::Enter,
0xff09 => Self::Tab,
0xff08 => Self::Backspace,
0xff63 => Self::Insert,
0xffff => Self::Delete,
0xff53 => Self::Right,
0xff51 => Self::Left,
0xff54 => Self::Down,
0xff52 => Self::Up,
0xff55 => Self::PageUp,
0xff56 => Self::PageDown,
0xff50 => Self::Home,
0xff57 => Self::End,
0xffe5 => Self::CapsLock,
0xff14 => Self::ScrollLock,
0xff7f => Self::NumLock,
0xfd1d => Self::PrintScreen,
0xff13 => Self::Pause,
0xffbe => Self::F1,
0xffbf => Self::F2,
0xffc0 => Self::F3,
0xffc1 => Self::F4,
0xffc2 => Self::F5,
0xffc3 => Self::F6,
0xffc4 => Self::F7,
0xffc5 => Self::F8,
0xffc6 => Self::F9,
0xffc7 => Self::F10,
0xffc8 => Self::F11,
0xffc9 => Self::F12,
0xffca => Self::F13,
0xffcb => Self::F14,
0xffcc => Self::F15,
0xffcd => Self::F16,
0xffce => Self::F17,
0xffcf => Self::F18,
0xffd0 => Self::F19,
0xffd1 => Self::F20,
0xffd2 => Self::F21,
0xffd3 => Self::F22,
0xffd4 => Self::F23,
0xffd5 => Self::F24,
0xffd6 => Self::F25,
0xffb0 => Self::Kp0,
0xffb1 => Self::Kp1,
0xffb2 => Self::Kp2,
0xffb3 => Self::Kp3,
0xffb4 => Self::Kp4,
0xffb5 => Self::Kp5,
0xffb6 => Self::Kp6,
0xffb7 => Self::Kp7,
0xffb8 => Self::Kp8,
0xffb9 => Self::Kp9,
0xffae => Self::KpDecimal,
0xffaf => Self::KpDivide,
0xffaa => Self::KpMultiply,
0xffad => Self::KpSubtract,
0xffab => Self::KpAdd,
0xff8d => Self::KpEnter,
0xffbd => Self::KpEqual,
0xffe1 => Self::LeftShift,
0xffe3 => Self::LeftControl,
0xffe9 => Self::LeftAlt,
0xffeb => Self::LeftSuper,
0xffe2 => Self::RightShift,
0xffe4 => Self::RightControl,
0xffea => Self::RightAlt,
0xffec => Self::RightSuper,
0xff67 => Self::Menu,
_ => Self::Unknown,
}
}
}
pub trait MouseButtonAsU8 {
fn to_u8(&self) -> u8;
fn from_u8(btn: u8) -> Self;
}
impl MouseButtonAsU8 for MouseButton {
fn to_u8(&self) -> u8 {
match self {
Self::Left => 0,
Self::Middle => 1,
Self::Right => 2,
Self::Unknown => 3,
}
}
fn from_u8(btn: u8) -> Self {
match btn {
0 => Self::Left,
1 => Self::Middle,
2 => Self::Right,
_ => Self::Unknown,
}
}
}

View File

@@ -51,32 +51,25 @@ use log::LevelFilter;
mod app;
mod darkirc;
use darkirc::DarkIrcBackend;
//mod chatapp;
//mod chatview;
//mod editbox;
mod error;
mod expr;
//mod gfx;
mod gfx2;
mod keysym;
mod gfx;
mod mesh;
mod net;
//mod plugin;
mod prop;
mod pubsub;
//mod py;
//mod res;
mod scene;
mod shader;
mod text2;
mod text;
mod ui;
mod util;
use crate::{
darkirc::DarkIrcBackend,
net::ZeroMQAdapter,
scene::{SceneGraph, SceneGraphPtr2},
text2::TextShaper,
text::TextShaper,
};
pub type ExecutorPtr = Arc<smol::Executor<'static>>;
@@ -134,8 +127,8 @@ fn main() {
let (method_req, method_rep) = mpsc::channel();
// The UI actually needs to be running for this to reply back.
// Otherwise calls will just hang.
let render_api = gfx2::RenderApi::new(method_req);
let event_pub = gfx2::GraphicsEventPublisher::new();
let render_api = gfx::RenderApi::new(method_req);
let event_pub = gfx::GraphicsEventPublisher::new();
let text_shaper = TextShaper::new();
@@ -202,8 +195,8 @@ fn main() {
});
async_runtime.push_task(ev_relay_task);
//let stage = gfx2::Stage::new(method_rep, event_pub);
gfx2::run_gui(app, async_runtime, method_rep, event_pub);
//let stage = gfx::Stage::new(method_rep, event_pub);
gfx::run_gui(app, async_runtime, method_rep, event_pub);
debug!(target: "main", "Started GFX backend");
}

View File

@@ -18,7 +18,7 @@
use crate::{
error::Result,
gfx2::{DrawMesh, Point, Rectangle, RenderApi, Vertex},
gfx::{DrawMesh, Point, Rectangle, RenderApi, Vertex},
};
use miniquad::{BufferId, TextureId};

View File

@@ -1,73 +0,0 @@
/* 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 <https://www.gnu.org/licenses/>.
*/
use crate::error::{Error, Result};
pub type ResourceId = u32;
pub struct ResourceManager<T> {
resources: Vec<(ResourceId, Option<T>)>,
freed: Vec<usize>,
id_counter: ResourceId,
}
impl<T> ResourceManager<T> {
pub fn new() -> Self {
Self { resources: vec![], freed: vec![], id_counter: 0 }
}
pub fn alloc(&mut self, rsrc: T) -> ResourceId {
let id = self.id_counter;
self.id_counter += 1;
if self.freed.is_empty() {
let idx = self.resources.len();
self.resources.push((id, Some(rsrc)));
} else {
let idx = self.freed.pop().unwrap();
let _ = std::mem::replace(&mut self.resources[idx], (id, Some(rsrc)));
}
id
}
pub fn get(&self, id: ResourceId) -> Option<&T> {
for (idx, (rsrc_id, rsrc)) in self.resources.iter().enumerate() {
if self.freed.contains(&idx) {
continue
}
if *rsrc_id == id {
return rsrc.as_ref()
}
}
None
}
pub fn free(&mut self, id: ResourceId) -> Result<()> {
for (idx, (rsrc_id, rsrc)) in self.resources.iter_mut().enumerate() {
if self.freed.contains(&idx) {
return Err(Error::ResourceNotFound)
}
if *rsrc_id == id {
*rsrc = None;
self.freed.push(idx);
return Ok(())
}
}
Err(Error::ResourceNotFound)
}
}

View File

@@ -1,251 +0,0 @@
/* 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 <https://www.gnu.org/licenses/>.
*/
use freetype as ft;
use crate::gfx::{FreetypeFace, Rectangle};
// From https://sourceforge.net/projects/freetype/files/freetype2/2.6/
//
// * An `FT_Face' object can only be safely used from one thread at
// a time.
//
// * An `FT_Library' object can now be used without modification
// from multiple threads at the same time.
//
// * `FT_Face' creation and destruction with the same `FT_Library'
// object can only be done from one thread at a time.
//
// One can use a single `FT_Library' object across threads as long
// as a mutex lock is used around `FT_New_Face' and `FT_Done_Face'.
// Any calls to `FT_Load_Glyph' and similar API are safe and do not
// need the lock to be held as long as the same `FT_Face' is not
// used from multiple threads at the same time.
// Harfbuzz is threadsafe.
// Notes:
// * All ft init and face creation should happen at startup.
// * FT faces protected behind an async Mutex
// * Glyph cache. Key is (glyph_id, font_size)
// * Glyph texture cache: (glyph_id, font_size, color)
#[derive(Clone)]
pub struct Glyph {
pub id: u32,
// Substring this glyph corresponds to
pub substr: String,
// The texture
pub bmp: Vec<u8>,
pub bmp_width: u16,
pub bmp_height: u16,
pub pos: Rectangle<f32>,
}
pub struct TextShaper {
pub font_faces: Vec<FreetypeFace>,
}
unsafe impl Send for TextShaper {}
unsafe impl Sync for TextShaper {}
impl TextShaper {
fn split_into_substrs(&self, text: String) -> Vec<(usize, String)> {
let mut current_idx = 0;
let mut current_str = String::new();
let mut substrs = vec![];
'next_char: for chr in text.chars() {
let idx = 'get_idx: {
for i in 0..self.font_faces.len() {
let ft_face = &self.font_faces[i];
if ft_face.get_char_index(chr as usize).is_some() {
break 'get_idx i
}
}
warn!("no font fallback for char: '{}'", chr);
// Skip this char
continue 'next_char
};
if current_idx != idx {
if !current_str.is_empty() {
// Push
substrs.push((current_idx, current_str.clone()));
}
current_str.clear();
current_idx = idx;
}
current_str.push(chr);
}
if !current_str.is_empty() {
// Push
substrs.push((current_idx, current_str));
}
substrs
}
pub fn shape(&self, text: String, font_size: f32, text_color: [f32; 4]) -> Vec<Glyph> {
let substrs = self.split_into_substrs(text.clone());
let mut glyphs: Vec<Glyph> = vec![];
let mut current_x = 0.;
let mut current_y = 0.;
for (face_idx, text) in substrs {
//debug!("substr {}", text);
let face = &self.font_faces[face_idx];
if face.has_fixed_sizes() {
// emojis required a fixed size
//face.set_char_size(109 * 64, 0, 72, 72).unwrap();
face.select_size(0).unwrap();
} else {
face.set_char_size(font_size as isize * 64, 0, 72, 72).unwrap();
}
let hb_font = harfbuzz_rs::Font::from_freetype_face(face.clone());
let buffer = harfbuzz_rs::UnicodeBuffer::new()
.set_cluster_level(harfbuzz_rs::ClusterLevel::MonotoneCharacters)
.add_str(&text);
let output = harfbuzz_rs::shape(&hb_font, buffer, &[]);
let positions = output.get_glyph_positions();
let infos = output.get_glyph_infos();
let mut prev_cluster = 0;
for (i, (position, info)) in positions.iter().zip(infos).enumerate() {
let gid = info.codepoint;
// Index within this substr
let curr_cluster = info.cluster as usize;
// Skip first time
if i != 0 {
let substr = text[prev_cluster..curr_cluster].to_string();
glyphs.last_mut().unwrap().substr = substr;
}
prev_cluster = curr_cluster;
let mut flags = ft::face::LoadFlag::DEFAULT;
if face.has_color() {
flags |= ft::face::LoadFlag::COLOR;
}
// FIXME: glyph 884 hangs on android
// For now just avoid using emojis on android
//debug!("load_glyph {}", gid);
face.load_glyph(gid, flags).unwrap();
//debug!("load_glyph {} [done]", gid);
let glyph = face.glyph();
glyph.render_glyph(ft::RenderMode::Normal).unwrap();
let bmp = glyph.bitmap();
let buffer = bmp.buffer();
let bmp_width = bmp.width() as usize;
let bmp_height = bmp.rows() as usize;
let bearing_x = glyph.bitmap_left() as f32;
let bearing_y = glyph.bitmap_top() as f32;
let pixel_mode = bmp.pixel_mode().unwrap();
let bmp = match pixel_mode {
ft::bitmap::PixelMode::Bgra => {
let mut tdata = vec![];
tdata.resize(4 * bmp_width * bmp_height, 0);
// Convert from BGRA to RGBA
for i in 0..bmp_width * bmp_height {
let idx = i * 4;
let b = buffer[idx];
let g = buffer[idx + 1];
let r = buffer[idx + 2];
let a = buffer[idx + 3];
tdata[idx] = r;
tdata[idx + 1] = g;
tdata[idx + 2] = b;
tdata[idx + 3] = a;
}
tdata
}
ft::bitmap::PixelMode::Gray => {
// Convert from greyscale to RGBA8
let tdata: Vec<_> = buffer
.iter()
.flat_map(|coverage| {
let r = (255. * text_color[0]) as u8;
let g = (255. * text_color[1]) as u8;
let b = (255. * text_color[2]) as u8;
let α = ((*coverage as f32) * text_color[3]) as u8;
vec![r, g, b, α]
})
.collect();
tdata
}
_ => panic!("unsupport pixel mode: {:?}", pixel_mode),
};
let pos = if face.has_fixed_sizes() {
// Downscale by height
let w = (bmp_width as f32 * font_size) / bmp_height as f32;
let h = font_size;
// Shouldn't this use the bearing?
let x = current_x;
let y = current_y - h;
current_x += w;
Rectangle { x, y, w, h }
} else {
let (w, h) = (bmp_width as f32, bmp_height as f32);
let off_x = position.x_offset as f32 / 64.;
let off_y = position.y_offset as f32 / 64.;
let x = current_x + off_x + bearing_x;
let y = current_y - off_y - bearing_y;
let x_advance = position.x_advance as f32 / 64.;
let y_advance = position.y_advance as f32 / 64.;
current_x += x_advance;
current_y += y_advance;
Rectangle { x, y, w, h }
};
let glyph = Glyph {
id: gid,
substr: String::new(),
bmp,
bmp_width: bmp_width as u16,
bmp_height: bmp_height as u16,
pos,
};
glyphs.push(glyph);
}
let substr = text[prev_cluster..].to_string();
glyphs.last_mut().unwrap().substr = substr;
}
glyphs
}
}

View File

@@ -2,7 +2,7 @@ use miniquad::TextureId;
use crate::{
error::{Error, Result},
gfx2::{Rectangle, RenderApi, RenderApiPtr},
gfx::{Rectangle, RenderApi, RenderApiPtr},
util::{ansi_texture, zip3},
};

View File

@@ -34,7 +34,7 @@ use std::{
use crate::{
error::Result,
gfx2::{Rectangle, RenderApi, RenderApiPtr},
gfx::{Rectangle, RenderApi, RenderApiPtr},
util::ansi_texture,
};

View File

@@ -2,7 +2,7 @@ use miniquad::TextureId;
use crate::{
error::Result,
gfx2::{Rectangle, RenderApi, RenderApiPtr},
gfx::{Rectangle, RenderApi, RenderApiPtr},
util::ansi_texture,
};

View File

@@ -24,7 +24,7 @@ use std::sync::{
};
use crate::{
gfx2::{
gfx::{
DrawCall, DrawInstruction, DrawMesh, GraphicsEventPublisherPtr, Point, Rectangle,
RenderApiPtr, Vertex,
},

View File

@@ -35,7 +35,7 @@ use std::{
use crate::{
error::Result,
gfx2::{
gfx::{
DrawCall, DrawInstruction, DrawMesh, GraphicsEventPublisherPtr, Point, Rectangle,
RenderApi, RenderApiPtr, Vertex,
},
@@ -46,7 +46,7 @@ use crate::{
},
pubsub::Subscription,
scene::{Pimpl, SceneGraph, SceneGraphPtr2, SceneNodeId},
text2::{self, Glyph, GlyphPositionIter, SpritePtr, TextShaper, TextShaperPtr},
text::{self, Glyph, GlyphPositionIter, SpritePtr, TextShaper, TextShaperPtr},
util::zip3,
ExecutorPtr,
};
@@ -107,7 +107,7 @@ const PRELOAD_PAGES: usize = 10;
#[derive(Clone)]
struct Page {
msgs: Vec<Message>,
atlas: text2::RenderedAtlas,
atlas: text::RenderedAtlas,
}
#[derive(Clone)]
@@ -120,7 +120,7 @@ type Page2Ptr = Arc<Page2>;
struct Page2 {
msgs: Vec<Message>,
atlas: SyncMutex<text2::RenderedAtlas>,
atlas: SyncMutex<text::RenderedAtlas>,
// One draw call per page.
// Resizing the canvas means we recalc wrapping and the mesh changes
mesh_inf: SyncMutex<Option<PageMeshInfo>>,
@@ -128,7 +128,7 @@ struct Page2 {
impl Page2 {
async fn new(msgs: Vec<Message>, render_api: &RenderApi) -> Arc<Self> {
let mut atlas = text2::Atlas::new(render_api);
let mut atlas = text::Atlas::new(render_api);
for msg in &msgs {
atlas.push(&msg.glyphs);
}
@@ -169,7 +169,7 @@ impl Page2 {
// Finally is just the message itself
let mut section = 2;
let mut lines = text2::wrap(clip.w, font_size, glyphs);
let mut lines = text::wrap(clip.w, font_size, glyphs);
// We are drawing bottom up but line wrap gives us lines in normal order
lines.reverse();
let last_idx = lines.len() - 1;

View File

@@ -29,7 +29,7 @@ use std::{
use crate::{
error::Result,
gfx2::{
gfx::{
DrawCall, DrawInstruction, DrawMesh, GraphicsEventPublisherPtr, Point, Rectangle,
RenderApi, RenderApiPtr, Vertex,
},
@@ -40,7 +40,7 @@ use crate::{
},
pubsub::Subscription,
scene::{Pimpl, SceneGraph, SceneGraphPtr2, SceneNodeId},
text2::{self, Glyph, GlyphPositionIter, SpritePtr, TextShaper, TextShaperPtr},
text::{self, Glyph, GlyphPositionIter, SpritePtr, TextShaper, TextShaperPtr},
util::zip3,
ExecutorPtr,
};
@@ -361,7 +361,7 @@ impl EditBox {
debug!(target: "ui::editbox", " cursor_pos={cursor_pos}, is_focused={is_focused}");
let glyphs = self.glyphs.lock().unwrap().clone();
let atlas = text2::make_texture_atlas(&self.render_api, &glyphs).await.unwrap();
let atlas = text::make_texture_atlas(&self.render_api, &glyphs).await.unwrap();
let mut mesh = MeshBuilder::with_clip(clip.clone());
self.draw_selected(&mut mesh, &glyphs, clip.h).unwrap();

View File

@@ -26,14 +26,14 @@ use std::{
};
use crate::{
gfx2::{DrawCall, DrawInstruction, DrawMesh, Rectangle, RenderApi, RenderApiPtr, Vertex},
gfx::{DrawCall, DrawInstruction, DrawMesh, Rectangle, RenderApi, RenderApiPtr, Vertex},
mesh::{Color, MeshBuilder, MeshInfo, COLOR_BLUE, COLOR_WHITE},
prop::{
PropertyBool, PropertyColor, PropertyFloat32, PropertyPtr, PropertyStr, PropertyUint32,
Role,
},
scene::{Pimpl, SceneGraph, SceneGraphPtr2, SceneNodeId},
text2::{self, Glyph, GlyphPositionIter, SpritePtr, TextShaper, TextShaperPtr},
text::{self, Glyph, GlyphPositionIter, SpritePtr, TextShaper, TextShaperPtr},
util::zip3,
ExecutorPtr,
};

View File

@@ -21,7 +21,7 @@ use rand::{rngs::OsRng, Rng};
use std::sync::{Arc, Weak};
use crate::{
gfx2::{DrawCall, DrawInstruction, Rectangle, RenderApiPtr},
gfx::{DrawCall, DrawInstruction, Rectangle, RenderApiPtr},
prop::{PropertyBool, PropertyPtr, Role},
scene::{Pimpl, SceneGraph, SceneGraphPtr2, SceneNodeId},
ExecutorPtr,

View File

@@ -20,7 +20,7 @@ use rand::{rngs::OsRng, Rng};
use std::sync::{Arc, Weak};
use crate::{
gfx2::{DrawCall, DrawInstruction, DrawMesh, Rectangle, RenderApiPtr, Vertex},
gfx::{DrawCall, DrawInstruction, DrawMesh, Rectangle, RenderApiPtr, Vertex},
prop::{PropertyPtr, PropertyUint32, Role},
scene::{Pimpl, SceneGraph, SceneGraphPtr2, SceneNodeId},
ExecutorPtr,

View File

@@ -22,7 +22,7 @@ use std::sync::{Arc, Weak};
use crate::{
error::{Error, Result},
expr::{SExprMachine, SExprVal},
gfx2::{DrawCall, Rectangle},
gfx::{DrawCall, Rectangle},
prop::{PropertyPtr, Role},
scene::{SceneGraph, SceneNode, SceneNodeId, SceneNodeType},
ExecutorPtr,

View File

@@ -22,14 +22,14 @@ use rand::{rngs::OsRng, Rng};
use std::sync::{Arc, Mutex as SyncMutex, Weak};
use crate::{
gfx2::{DrawCall, DrawInstruction, DrawMesh, Rectangle, RenderApi, RenderApiPtr, Vertex},
gfx::{DrawCall, DrawInstruction, DrawMesh, Rectangle, RenderApi, RenderApiPtr, Vertex},
mesh::{Color, MeshBuilder, MeshInfo, COLOR_BLUE, COLOR_WHITE},
prop::{
PropertyBool, PropertyColor, PropertyFloat32, PropertyPtr, PropertyStr, PropertyUint32,
Role,
},
scene::{Pimpl, SceneGraph, SceneGraphPtr2, SceneNodeId},
text2::{self, Glyph, GlyphPositionIter, SpritePtr, TextShaper, TextShaperPtr},
text::{self, Glyph, GlyphPositionIter, SpritePtr, TextShaper, TextShaperPtr},
util::zip3,
ExecutorPtr,
};
@@ -137,7 +137,7 @@ impl Text {
) -> 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, &glyphs).await.unwrap();
let atlas = text::make_texture_atlas(render_api, &glyphs).await.unwrap();
let mut mesh = MeshBuilder::new();
let mut glyph_pos_iter = GlyphPositionIter::new(font_size, &glyphs, baseline);

View File

@@ -19,7 +19,7 @@
use std::sync::{Arc, Weak};
use crate::{
gfx2::{DrawCall, GraphicsEventPublisherPtr, Rectangle, RenderApiPtr},
gfx::{DrawCall, GraphicsEventPublisherPtr, Rectangle, RenderApiPtr},
prop::{PropertyPtr, Role},
scene::{Pimpl, SceneGraph, SceneGraphPtr2, SceneNodeId},
ExecutorPtr,