diff --git a/bin/darkwallet/src/app/node.rs b/bin/darkwallet/src/app/node.rs index 5287a68c8bb8..8bcde7ad220b 100644 --- a/bin/darkwallet/src/app/node.rs +++ b/bin/darkwallet/src/app/node.rs @@ -313,6 +313,11 @@ pub fn create_chatedit(name: &str) -> SceneNode { prop.set_range_f32(0., 1.); node.add_property(prop).unwrap(); + let mut prop = Property::new("text_cmd_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.); @@ -350,6 +355,11 @@ pub fn create_chatedit(name: &str) -> SceneNode { prop.set_range_f32(0., 1.); node.add_property(prop).unwrap(); + let mut prop = Property::new("cmd_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("select_text", PropertyType::Str, PropertySubType::Null); prop.allow_null_values(); prop.set_defaults_null().unwrap(); diff --git a/bin/darkwallet/src/app/schema/chat.rs b/bin/darkwallet/src/app/schema/chat.rs index 414dbf4aeb2d..1630de1c064a 100644 --- a/bin/darkwallet/src/app/schema/chat.rs +++ b/bin/darkwallet/src/app/schema/chat.rs @@ -688,6 +688,11 @@ pub async fn make( prop.set_f32(Role::App, 1, 0.96).unwrap(); prop.set_f32(Role::App, 2, 1.).unwrap(); prop.set_f32(Role::App, 3, 1.).unwrap(); + let prop = node.get_property("text_cmd_color").unwrap(); + prop.set_f32(Role::App, 0, 0.64).unwrap(); + prop.set_f32(Role::App, 1, 1.).unwrap(); + prop.set_f32(Role::App, 2, 0.83).unwrap(); + prop.set_f32(Role::App, 3, 1.).unwrap(); let prop = node.get_property("cursor_color").unwrap(); prop.set_f32(Role::App, 0, 0.816).unwrap(); prop.set_f32(Role::App, 1, 0.627).unwrap(); @@ -695,10 +700,10 @@ pub async fn make( prop.set_f32(Role::App, 3, 1.).unwrap(); node.set_property_f32(Role::App, "cursor_ascent", CHATEDIT_CURSOR_ASCENT).unwrap(); node.set_property_f32(Role::App, "cursor_descent", CHATEDIT_CURSOR_DESCENT).unwrap(); - let prop = node.get_property("hi_bg_color").unwrap(); node.set_property_f32(Role::App, "select_ascent", CHATEDIT_SELECT_ASCENT).unwrap(); node.set_property_f32(Role::App, "select_descent", CHATEDIT_SELECT_DESCENT).unwrap(); node.set_property_f32(Role::App, "handle_descent", CHATEDIT_HANDLE_DESCENT).unwrap(); + let prop = node.get_property("hi_bg_color").unwrap(); if LIGHTMODE { prop.set_f32(Role::App, 0, 0.5).unwrap(); prop.set_f32(Role::App, 1, 0.5).unwrap(); @@ -710,6 +715,18 @@ pub async fn make( prop.set_f32(Role::App, 2, 0.22).unwrap(); prop.set_f32(Role::App, 3, 1.).unwrap(); } + let prop = node.get_property("cmd_bg_color").unwrap(); + if LIGHTMODE { + prop.set_f32(Role::App, 0, 0.5).unwrap(); + prop.set_f32(Role::App, 1, 0.5).unwrap(); + prop.set_f32(Role::App, 2, 0.5).unwrap(); + prop.set_f32(Role::App, 3, 1.).unwrap(); + } else { + prop.set_f32(Role::App, 0, 0.).unwrap(); + prop.set_f32(Role::App, 1, 0.30).unwrap(); + prop.set_f32(Role::App, 2, 0.25).unwrap(); + prop.set_f32(Role::App, 3, 1.).unwrap(); + } node.set_property_u32(Role::App, "z_index", 3).unwrap(); //node.set_property_bool(Role::App, "debug", true).unwrap(); diff --git a/bin/darkwallet/src/ui/chatedit.rs b/bin/darkwallet/src/ui/chatedit.rs index a3fd1dedbae3..6057d1727537 100644 --- a/bin/darkwallet/src/ui/chatedit.rs +++ b/bin/darkwallet/src/ui/chatedit.rs @@ -40,7 +40,7 @@ use crate::{ GfxDrawCall, GfxDrawInstruction, GfxDrawMesh, GfxTextureId, GraphicsEventPublisherPtr, Point, Rectangle, RenderApi, Vertex, }, - mesh::{MeshBuilder, MeshInfo, COLOR_BLUE, COLOR_RED, COLOR_WHITE}, + mesh::{Color, MeshBuilder, MeshInfo, COLOR_BLUE, COLOR_RED, COLOR_WHITE}, prop::{ PropertyBool, PropertyColor, PropertyFloat32, PropertyPtr, PropertyRect, PropertyStr, PropertyUint32, Role, @@ -48,7 +48,7 @@ use crate::{ pubsub::Subscription, scene::{MethodCallSub, Pimpl, SceneNodePtr, SceneNodeWeak}, text::{self, Glyph, GlyphPositionIter, TextShaperPtr}, - util::{enumerate_ref, is_whitespace, min_f32, zip3}, + util::{enumerate_ref, is_whitespace, min_f32, zip4}, ExecutorPtr, }; @@ -442,6 +442,12 @@ impl TouchInfo { } } +enum ColoringState { + Start, + IsCommand, + Normal, +} + pub type ChatEditPtr = Arc; pub struct ChatEdit { @@ -474,6 +480,7 @@ pub struct ChatEdit { text: PropertyStr, text_color: PropertyColor, text_hi_color: PropertyColor, + text_cmd_color: PropertyColor, cursor_color: PropertyColor, cursor_width: PropertyFloat32, cursor_ascent: PropertyFloat32, @@ -481,6 +488,7 @@ pub struct ChatEdit { cursor_blink_time: PropertyUint32, cursor_idle_time: PropertyUint32, hi_bg_color: PropertyColor, + cmd_bg_color: PropertyColor, select_ascent: PropertyFloat32, select_descent: PropertyFloat32, handle_descent: PropertyFloat32, @@ -533,6 +541,8 @@ impl ChatEdit { let text = PropertyStr::wrap(node_ref, Role::Internal, "text", 0).unwrap(); let text_color = PropertyColor::wrap(node_ref, Role::Internal, "text_color").unwrap(); let text_hi_color = PropertyColor::wrap(node_ref, Role::Internal, "text_hi_color").unwrap(); + let text_cmd_color = + PropertyColor::wrap(node_ref, Role::Internal, "text_cmd_color").unwrap(); let cursor_color = PropertyColor::wrap(node_ref, Role::Internal, "cursor_color").unwrap(); let cursor_width = PropertyFloat32::wrap(node_ref, Role::Internal, "cursor_width", 0).unwrap(); @@ -541,6 +551,7 @@ impl ChatEdit { let cursor_descent = PropertyFloat32::wrap(node_ref, Role::Internal, "cursor_descent", 0).unwrap(); let hi_bg_color = PropertyColor::wrap(node_ref, Role::Internal, "hi_bg_color").unwrap(); + let cmd_bg_color = PropertyColor::wrap(node_ref, Role::Internal, "cmd_bg_color").unwrap(); let select_ascent = PropertyFloat32::wrap(node_ref, Role::Internal, "select_ascent", 0).unwrap(); let select_descent = @@ -589,6 +600,7 @@ impl ChatEdit { text, text_color, text_hi_color, + text_cmd_color, cursor_color, cursor_width, cursor_ascent, @@ -596,6 +608,7 @@ impl ChatEdit { cursor_blink_time, cursor_idle_time, hi_bg_color, + cmd_bg_color, select_ascent, select_descent, handle_descent, @@ -630,10 +643,10 @@ impl ChatEdit { // .lock() // .editable // .set_text("".to_string(), "king!๐Ÿ˜๐Ÿ†jelly ๐Ÿ†1234".to_string()); - self_.text_wrap.lock().editable.set_text( - "".to_string(), - "A berry is a small, pulpy, and often edible fruit. Typically, berries are juicy, rounded, brightly colored, sweet, sour or tart, and do not have a stone or pit, although many pips or seeds may be present. Common examples of berries in the culinary sense are strawberries, raspberries, blueberries, blackberries, white currants, blackcurrants, and redcurrants. In Britain, soft fruit is a horticultural term for such fruits. The common usage of the term berry is different from the scientific or botanical definition of a berry, which refers to a fruit produced from the ovary of a single flower where the outer layer of the ovary wall develops into an edible fleshy portion (pericarp). The botanical definition includes many fruits that are not commonly known or referred to as berries, such as grapes, tomatoes, cucumbers, eggplants, bananas, and chili peppers.".to_string() - ); + //self_.text_wrap.lock().editable.set_text( + // "".to_string(), + // "A berry is a small, pulpy, and often edible fruit. Typically, berries are juicy, rounded, brightly colored, sweet, sour or tart, and do not have a stone or pit, although many pips or seeds may be present. Common examples of berries in the culinary sense are strawberries, raspberries, blueberries, blackberries, white currants, blackcurrants, and redcurrants. In Britain, soft fruit is a horticultural term for such fruits. The common usage of the term berry is different from the scientific or botanical definition of a berry, which refers to a fruit produced from the ovary of a single flower where the outer layer of the ovary wall develops into an edible fleshy portion (pericarp). The botanical definition includes many fruits that are not commonly known or referred to as berries, such as grapes, tomatoes, cucumbers, eggplants, bananas, and chili peppers.".to_string() + //); //self_ // .text_wrap // .lock() @@ -669,6 +682,7 @@ impl ChatEdit { let window_scale = self.window_scale.get(); let text_color = self.text_color.get(); let text_hi_color = self.text_hi_color.get(); + let text_cmd_color = self.text_cmd_color.get(); let linespacing = self.linespacing.get(); let baseline = self.baseline.get(); let scroll = self.scroll.get(); @@ -725,7 +739,16 @@ impl ChatEdit { for (line_idx, wrap_line) in wrapped_lines.lines.iter().enumerate() { let select_marks = self.mark_selected_glyphs(&wrap_line, &selections); - self.draw_selected(&mut mesh, &select_marks, &wrap_line, curr_y); + let hi_bg_color = self.hi_bg_color.get(); + self.draw_text_bg_box(&mut mesh, &select_marks, &wrap_line, curr_y, hi_bg_color); + + let cmd_marks = if line_idx == 0 { + self.mark_command_glyphs(&wrap_line) + } else { + vec![false; wrap_line.len()] + }; + let cmd_bg_color = self.cmd_bg_color.get(); + self.draw_text_bg_box(&mut mesh, &cmd_marks, &wrap_line, curr_y, cmd_bg_color); /* if rendered.has_underline() { @@ -741,9 +764,12 @@ impl ChatEdit { let pos_iter = wrap_line.pos_iter(); - for (_, mut glyph_rect, glyph, is_selected) in - zip3(pos_iter, wrap_line.glyphs.iter(), select_marks.into_iter()) - { + for (_, mut glyph_rect, glyph, is_selected, is_cmd) in zip4( + pos_iter, + wrap_line.glyphs.iter(), + select_marks.into_iter(), + cmd_marks.into_iter(), + ) { let uv_rect = atlas.fetch_uv(glyph.glyph_id).expect("missing glyph UV rect"); glyph_rect.y += curr_y; @@ -752,7 +778,10 @@ impl ChatEdit { let mut color = text_color.clone(); if is_selected { color = text_hi_color.clone(); + } else if is_cmd { + color = text_cmd_color.clone(); } + if glyph.sprite.has_color { color = COLOR_WHITE; } @@ -849,18 +878,46 @@ impl ChatEdit { marks } - fn draw_selected( + fn mark_command_glyphs(&self, wrap_line: &WrappedLine) -> Vec { + let mut state = ColoringState::Start; + let mut marks = vec![false; wrap_line.len()]; + for (idx, glyph) in wrap_line.glyphs.iter().enumerate() { + match state { + ColoringState::Start => { + if glyph.substr == "/" { + state = ColoringState::IsCommand + } else { + state = ColoringState::Normal + } + } + ColoringState::IsCommand => { + if is_whitespace(&glyph.substr) { + state = ColoringState::Normal + } + } + _ => {} + } + + match state { + ColoringState::IsCommand => marks[idx] = true, + _ => {} + } + } + marks + } + + fn draw_text_bg_box( &self, mesh: &mut MeshBuilder, select_marks: &Vec, wrap_line: &WrappedLine, y_off: f32, + color: Color, ) { let font_size = self.font_size.get(); let baseline = self.baseline.get(); let select_ascent = self.select_ascent.get(); let select_descent = self.select_descent.get(); - let hi_bg_color = self.hi_bg_color.get(); if select_marks.iter().all(|b| !b) { return @@ -901,7 +958,7 @@ impl ChatEdit { w: end_x - start_x, h: select_ascent + select_descent, }; - mesh.draw_box(&select_rect, hi_bg_color, &Rectangle::zero()); + mesh.draw_box(&select_rect, color, &Rectangle::zero()); } fn draw_phone_select_handle( diff --git a/bin/darkwallet/src/util.rs b/bin/darkwallet/src/util.rs index db86e36be41d..136a645d5909 100644 --- a/bin/darkwallet/src/util.rs +++ b/bin/darkwallet/src/util.rs @@ -117,7 +117,6 @@ where } } -#[allow(dead_code)] pub fn zip3(i1: I1, i2: I2, i3: I3) -> TupleIterStruct3 where I1: Iterator, @@ -127,6 +126,52 @@ where TupleIterStruct3 { idx: 0, i1, i2, i3 } } +pub struct TupleIterStruct4 { + idx: usize, + i1: I1, + i2: I2, + i3: I3, + i4: I4, +} + +impl Iterator for TupleIterStruct4 +where + I1: Iterator, + I2: Iterator, + I3: Iterator, + I4: Iterator, +{ + type Item = (usize, I1::Item, I2::Item, I3::Item, I4::Item); + + fn next(&mut self) -> Option { + let Some(x1) = self.i1.next() else { return None }; + let Some(x2) = self.i2.next() else { return None }; + let Some(x3) = self.i3.next() else { return None }; + let Some(x4) = self.i4.next() else { return None }; + + let res = (self.idx, x1, x2, x3, x4); + self.idx += 1; + + Some(res) + } +} + +#[allow(dead_code)] +pub fn zip4( + i1: I1, + i2: I2, + i3: I3, + i4: I4, +) -> TupleIterStruct4 +where + I1: Iterator, + I2: Iterator, + I3: Iterator, + I4: Iterator, +{ + TupleIterStruct4 { idx: 0, i1, i2, i3, i4 } +} + pub fn enumerate(v: Vec) -> impl Iterator { v.into_iter().enumerate() }