>>(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();
-}
diff --git a/bin/darkwallet/src/chatview.rs b/bin/darkwallet/src/chatview.rs
deleted file mode 100644
index 37ebc7a33..000000000
--- a/bin/darkwallet/src/chatview.rs
+++ /dev/null
@@ -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 .
- */
-
-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(filename: P) -> Vec
-where
- P: AsRef,
-{
- //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;
-
-pub struct ChatView {
- node_name: String,
- debug: PropertyBool,
- // Used for mouse interaction
- world_rect: Mutex>,
- mouse_pos: Mutex>,
- text_shaper: TextShaper,
- scroll: AtomicF32,
- lines: Vec,
- glyph_lines: Mutex>>,
- atlas: Mutex>,
-}
-
-impl ChatView {
- pub fn new(
- scene_graph: &mut SceneGraph,
- node_id: SceneNodeId,
- font_faces: Vec,
- ) -> Result {
- 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,
- ) -> 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, _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);
- }
-}
diff --git a/bin/darkwallet/src/darkirc.rs b/bin/darkwallet/src/darkirc.rs
index 608b38873..b897d5857 100644
--- a/bin/darkwallet/src/darkirc.rs
+++ b/bin/darkwallet/src/darkirc.rs
@@ -39,7 +39,7 @@ use darkfi_serial::{
use crate::{
net::ZeroMQAdapter,
scene::{SceneGraph, SceneGraphPtr2},
- text2::TextShaper,
+ text::TextShaper,
ExecutorPtr,
};
diff --git a/bin/darkwallet/src/editbox.rs b/bin/darkwallet/src/editbox.rs
deleted file mode 100644
index 31c0711c2..000000000
--- a/bin/darkwallet/src/editbox.rs
+++ /dev/null
@@ -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 .
- */
-
-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,
- /// 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;
-
-pub struct EditBox {
- node_name: String,
- is_active: PropertyBool,
- debug: PropertyBool,
- baseline: PropertyFloat32,
- scroll: PropertyFloat32,
- cursor_pos: PropertyUint32,
- selected: Arc,
- text: PropertyStr,
- font_size: PropertyFloat32,
- text_color: PropertyColor,
- cursor_color: PropertyColor,
- hi_bg_color: PropertyColor,
- // Used for mouse clicks
- world_rect: Mutex>,
- glyphs: Mutex>,
- text_shaper: TextShaper,
- key_repeat: Mutex,
- mouse_btn_held: AtomicBool,
- window_scale: f32,
- screen_size: Arc,
- atlas: Mutex>,
-}
-
-impl EditBox {
- pub fn new(
- scene_graph: &mut SceneGraph,
- node_id: SceneNodeId,
- font_faces: Vec,
- ) -> Result {
- 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,
- ) -> 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,
- glyphs: &Vec,
- ) -> 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) {
- 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, 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, key: String, mods: KeyMods) {
- let mut repeater = self.key_repeat.lock().unwrap();
- repeater.key_up(&key);
- }
-
- fn mouse_button_down(self: Arc, 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, 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, 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, w: f32, h: f32) {}
-}
-
-enum MouseClickGlyph {
- Lhs,
- Pos(u32),
- Rhs(u32),
-}
diff --git a/bin/darkwallet/src/gfx.rs b/bin/darkwallet/src/gfx.rs
deleted file mode 100644
index c34338644..000000000
--- a/bin/darkwallet/src/gfx.rs
+++ /dev/null
@@ -1,1352 +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 .
- */
-
-use darkfi_serial::{Decodable, Encodable, SerialDecodable, SerialEncodable};
-use freetype as ft;
-use miniquad::{
- conf, window, Backend, Bindings, BlendFactor, BlendState, BlendValue, BufferId, BufferLayout,
- BufferSource, BufferType, BufferUsage, Equation, EventHandler, KeyCode, KeyMods, MouseButton,
- PassAction, Pipeline, PipelineParams, RenderingBackend, ShaderMeta, ShaderSource, TextureId,
- UniformDesc, UniformType, VertexAttribute, VertexFormat,
-};
-use std::{
- array::IntoIter,
- collections::HashMap,
- fmt,
- io::Cursor,
- sync::{mpsc, Mutex, MutexGuard},
- time::{Duration, Instant},
-};
-
-use crate::{
- chatview, editbox,
- error::{Error, Result},
- expr::{SExprMachine, SExprVal},
- keysym::{KeyCodeAsStr, MouseButtonAsU8},
- prop::{Property, PropertySubType, PropertyType},
- res::{ResourceId, ResourceManager},
- scene::{
- MethodResponseFn, Pimpl, SceneGraph, SceneGraphPtr, SceneNode, SceneNodeId, SceneNodeInfo,
- SceneNodeType,
- },
- shader,
-};
-
-type Color = [f32; 4];
-
-pub const COLOR_RED: Color = [1., 0., 0., 1.];
-pub const COLOR_DARKGREY: Color = [0.2, 0.2, 0.2, 1.];
-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(Debug, SerialEncodable, SerialDecodable)]
-#[repr(C)]
-struct Vertex {
- pos: [f32; 2],
- color: [f32; 4],
- uv: [f32; 2],
-}
-
-#[derive(SerialEncodable, SerialDecodable)]
-#[repr(C)]
-struct Face {
- idxs: [u32; 3],
-}
-
-impl fmt::Debug for Face {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "{:?}", self.idxs)
- }
-}
-
-struct Mesh {
- pub verts: Vec,
- pub faces: Vec,
- pub vertex_buffer: BufferId,
- pub index_buffer: BufferId,
-}
-
-pub struct Point {
- pub x: T,
- pub y: T,
-}
-
-#[derive(Debug, Clone)]
-pub struct Rectangle<
- T: Copy + std::ops::Add