Skip to content

Commit

Permalink
wallet/chatedit: when typing commands like /nick, highlight them with…
Browse files Browse the repository at this point in the history
… special coloring
  • Loading branch information
darkfi committed Jan 7, 2025
1 parent 5a6ebc8 commit da4867c
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 15 deletions.
10 changes: 10 additions & 0 deletions bin/darkwallet/src/app/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.);
Expand Down Expand Up @@ -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();
Expand Down
19 changes: 18 additions & 1 deletion bin/darkwallet/src/app/schema/chat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -688,17 +688,22 @@ 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();
prop.set_f32(Role::App, 2, 1.).unwrap();
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();
Expand All @@ -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();

Expand Down
83 changes: 70 additions & 13 deletions bin/darkwallet/src/ui/chatedit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,15 @@ 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,
},
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,
};

Expand Down Expand Up @@ -442,6 +442,12 @@ impl TouchInfo {
}
}

enum ColoringState {
Start,
IsCommand,
Normal,
}

pub type ChatEditPtr = Arc<ChatEdit>;

pub struct ChatEdit {
Expand Down Expand Up @@ -474,13 +480,15 @@ 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,
cursor_descent: PropertyFloat32,
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,
Expand Down Expand Up @@ -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();
Expand All @@ -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 =
Expand Down Expand Up @@ -589,13 +600,15 @@ impl ChatEdit {
text,
text_color,
text_hi_color,
text_cmd_color,
cursor_color,
cursor_width,
cursor_ascent,
cursor_descent,
cursor_blink_time,
cursor_idle_time,
hi_bg_color,
cmd_bg_color,
select_ascent,
select_descent,
handle_descent,
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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() {
Expand All @@ -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;
Expand All @@ -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;
}
Expand Down Expand Up @@ -849,18 +878,46 @@ impl ChatEdit {
marks
}

fn draw_selected(
fn mark_command_glyphs(&self, wrap_line: &WrappedLine) -> Vec<bool> {
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<bool>,
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
Expand Down Expand Up @@ -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(
Expand Down
47 changes: 46 additions & 1 deletion bin/darkwallet/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@ where
}
}

#[allow(dead_code)]
pub fn zip3<X1, X2, X3, I1, I2, I3>(i1: I1, i2: I2, i3: I3) -> TupleIterStruct3<I1, I2, I3>
where
I1: Iterator<Item = X1>,
Expand All @@ -127,6 +126,52 @@ where
TupleIterStruct3 { idx: 0, i1, i2, i3 }
}

pub struct TupleIterStruct4<I1, I2, I3, I4> {
idx: usize,
i1: I1,
i2: I2,
i3: I3,
i4: I4,
}

impl<I1, I2, I3, I4> Iterator for TupleIterStruct4<I1, I2, I3, I4>
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<Self::Item> {
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<X1, X2, X3, X4, I1, I2, I3, I4>(
i1: I1,
i2: I2,
i3: I3,
i4: I4,
) -> TupleIterStruct4<I1, I2, I3, I4>
where
I1: Iterator<Item = X1>,
I2: Iterator<Item = X2>,
I3: Iterator<Item = X3>,
I4: Iterator<Item = X4>,
{
TupleIterStruct4 { idx: 0, i1, i2, i3, i4 }
}

pub fn enumerate<X>(v: Vec<X>) -> impl Iterator<Item = (usize, X)> {
v.into_iter().enumerate()
}
Expand Down

0 comments on commit da4867c

Please sign in to comment.