瀏覽代碼

wallet: make paste popup work properly by hiding itself whenever chatedit changes

darkfi 1 年之前
父節點
當前提交
f0a2ce1dfa

+ 2 - 0
bin/darkwallet/src/app/node.rs

@@ -312,6 +312,7 @@ pub fn create_chatedit(name: &str) -> SceneNode {
     node.add_property(prop).unwrap();
     node.add_property(prop).unwrap();
 
 
     let mut prop = Property::new("cursor_pos", PropertyType::Uint32, PropertySubType::Pixel);
     let mut prop = Property::new("cursor_pos", PropertyType::Uint32, PropertySubType::Pixel);
+    prop.set_ui_text("Cursor Pos", "Cursor position within the text");
     prop.set_range_u32(0, u32::MAX);
     prop.set_range_u32(0, u32::MAX);
     node.add_property(prop).unwrap();
     node.add_property(prop).unwrap();
 
 
@@ -404,6 +405,7 @@ pub fn create_chatedit(name: &str) -> SceneNode {
 
 
     node.add_signal("enter_pressed", "Enter key pressed", vec![]).unwrap();
     node.add_signal("enter_pressed", "Enter key pressed", vec![]).unwrap();
     node.add_signal("keyboard_request", "Request to show keyboard", vec![]).unwrap();
     node.add_signal("keyboard_request", "Request to show keyboard", vec![]).unwrap();
+    node.add_signal("paste_request", "Request to show paste dialog", vec![]).unwrap();
 
 
     // Used by emoji_picker
     // Used by emoji_picker
     node.add_method("insert_text", vec![("text", "Text", CallArgType::Str)], None).unwrap();
     node.add_method("insert_text", vec![("text", "Text", CallArgType::Str)], None).unwrap();

+ 178 - 27
bin/darkwallet/src/app/schema/chat.rs

@@ -784,6 +784,7 @@ pub async fn make(
 
 
     let editz_text = PropertyStr::wrap(&node, Role::App, "text", 0).unwrap();
     let editz_text = PropertyStr::wrap(&node, Role::App, "text", 0).unwrap();
     let editz_select_text = node.get_property("select_text").unwrap();
     let editz_select_text = node.get_property("select_text").unwrap();
+    let editz_cursor_pos = node.get_property("cursor_pos").unwrap();
 
 
     //let editbox_focus = PropertyBool::wrap(node, Role::App, "is_focused", 0).unwrap();
     //let editbox_focus = PropertyBool::wrap(node, Role::App, "is_focused", 0).unwrap();
     //let darkirc_backend = app.darkirc_backend.clone();
     //let darkirc_backend = app.darkirc_backend.clone();
@@ -992,31 +993,6 @@ pub async fn make(
 
 
     let cmd_hint_is_visible =
     let cmd_hint_is_visible =
         PropertyBool::wrap(&cmd_layer_node, Role::App, "is_visible", 0).unwrap();
         PropertyBool::wrap(&cmd_layer_node, Role::App, "is_visible", 0).unwrap();
-    let editz_text_sub = editz_text.prop().subscribe_modify();
-    let editz_text_task = app.ex.spawn(async move {
-        while let Ok(_) = editz_text_sub.receive().await {
-            let text = editz_text.get();
-            info!(target: "app::chat", "text changed: {text}");
-            // We want to avoid setting the property multiple times to the same value
-            // because then it triggers unnecessary redraw work.
-            if let Some(first_char) = text.chars().next() {
-                if first_char == '/' {
-                    if !cmd_hint_is_visible.get() {
-                        cmd_hint_is_visible.set(true);
-                    }
-                } else {
-                    if cmd_hint_is_visible.get() {
-                        cmd_hint_is_visible.set(false);
-                    }
-                }
-            } else {
-                if cmd_hint_is_visible.get() {
-                    cmd_hint_is_visible.set(false);
-                }
-            }
-        }
-    });
-    app.tasks.lock().unwrap().push(editz_text_task);
 
 
     // Create the actionbar bg
     // Create the actionbar bg
     let node = create_vector_art("cmd_hint_bg");
     let node = create_vector_art("cmd_hint_bg");
@@ -1173,7 +1149,7 @@ pub async fn make(
     layer_node.set_property_u32(Role::App, "priority", 1).unwrap();
     layer_node.set_property_u32(Role::App, "priority", 1).unwrap();
     let layer_node =
     let layer_node =
         layer_node.setup(|me| Layer::new(me, app.render_api.clone(), app.ex.clone())).await;
         layer_node.setup(|me| Layer::new(me, app.render_api.clone(), app.ex.clone())).await;
-    content_layer_node.link(layer_node.clone());
+    content_layer_node.clone().link(layer_node.clone());
 
 
     let actions_is_visible = PropertyBool::wrap(&layer_node, Role::App, "is_visible", 0).unwrap();
     let actions_is_visible = PropertyBool::wrap(&layer_node, Role::App, "is_visible", 0).unwrap();
 
 
@@ -1325,6 +1301,7 @@ pub async fn make(
     let (slot, recvr) = Slot::new("paste_clicked");
     let (slot, recvr) = Slot::new("paste_clicked");
     node.register("click", slot).unwrap();
     node.register("click", slot).unwrap();
     let actions_is_visible2 = actions_is_visible.clone();
     let actions_is_visible2 = actions_is_visible.clone();
+    let chatedit_node2 = chatedit_node.clone();
     let listen_click = app.ex.spawn(async move {
     let listen_click = app.ex.spawn(async move {
         let mut clip = Clipboard::new();
         let mut clip = Clipboard::new();
         while let Ok(_) = recvr.recv().await {
         while let Ok(_) = recvr.recv().await {
@@ -1332,7 +1309,7 @@ pub async fn make(
                 info!(target: "app::chat", "clicked paste: {text}");
                 info!(target: "app::chat", "clicked paste: {text}");
                 let mut data = vec![];
                 let mut data = vec![];
                 text.encode(&mut data).unwrap();
                 text.encode(&mut data).unwrap();
-                chatedit_node.call_method("insert_text", data).await.unwrap();
+                chatedit_node2.call_method("insert_text", data).await.unwrap();
             } else {
             } else {
                 info!(target: "app::chat", "clicked paste but clip is empty");
                 info!(target: "app::chat", "clicked paste but clip is empty");
             }
             }
@@ -1366,18 +1343,192 @@ pub async fn make(
     let node = node.setup(|me| Button::new(me, app.ex.clone())).await;
     let node = node.setup(|me| Button::new(me, app.ex.clone())).await;
     layer_node.clone().link(node);
     layer_node.clone().link(node);
 
 
+    // Paste overlay popup
+    let layer_node = create_layer("paste_layer");
+    let prop = layer_node.get_property("rect").unwrap();
+    prop.set_f32(Role::App, 0, 40.).unwrap();
+    let code = cc.compile("editz_bg_top_y - ACTION_POPUP_Y_OFF").unwrap();
+    //let code = cc.compile("h - 60 - 80").unwrap();
+    //let code = cc.compile("h - 60 - 300").unwrap();
+    prop.set_expr(Role::App, 1, code).unwrap();
+    prop.set_f32(Role::App, 2, ACTION_SELECT_ALL_RECT.rhs()).unwrap();
+    prop.set_f32(Role::App, 3, ACTION_SELECT_ALL_RECT.h).unwrap();
+    prop.add_depend(&editbox_bg_rect_prop, 1, "editz_bg_top_y");
+    layer_node.set_property_bool(Role::App, "is_visible", false).unwrap();
+    layer_node.set_property_u32(Role::App, "z_index", 8).unwrap();
+    // Priority higher than chatview but lower than chatedit
+    layer_node.set_property_u32(Role::App, "priority", 1).unwrap();
+    let layer_node =
+        layer_node.setup(|me| Layer::new(me, app.render_api.clone(), app.ex.clone())).await;
+    content_layer_node.link(layer_node.clone());
+
+    let pasta_is_visible = PropertyBool::wrap(&layer_node, Role::App, "is_visible", 0).unwrap();
+
+    let (slot, recvr) = Slot::new("reqpasta");
+    chatedit_node.register("paste_request", slot).unwrap();
+    let pasta_is_visible2 = pasta_is_visible.clone();
+    let listen_click = app.ex.spawn(async move {
+        while let Ok(_) = recvr.recv().await {
+            pasta_is_visible2.set(true);
+        }
+    });
+    app.tasks.lock().unwrap().push(listen_click);
+
+    // Create the paste bg
+    let node = create_vector_art("paste_bg");
+    let prop = node.get_property("rect").unwrap();
+    prop.set_f32(Role::App, 0, 0.).unwrap();
+    prop.set_f32(Role::App, 1, 0.).unwrap();
+    prop.set_f32(Role::App, 2, ACTION_SELECT_ALL_RECT.rhs()).unwrap();
+    prop.set_f32(Role::App, 3, ACTION_SELECT_ALL_RECT.h).unwrap();
+    node.set_property_u32(Role::App, "z_index", 0).unwrap();
+
+    let mut shape = VectorShape::new();
+
+    let color1 = [0., 0.13, 0.08, 0.75];
+    let color2 = [0., 0., 0., 1.];
+    let gradient = [color1.clone(), color1, color2.clone(), color2];
+    let hicolor = [0., 0.94, 1., 1.];
+
+    // Paste box
+    shape.add_gradient_box(
+        expr::const_f32(0.),
+        expr::const_f32(0.),
+        expr::const_f32(ACTION_PASTE_RECT.w),
+        expr::const_f32(ACTION_PASTE_RECT.h),
+        gradient.clone(),
+    );
+
+    // Paste outline
+    shape.add_outline(
+        expr::const_f32(0.),
+        expr::const_f32(0.),
+        expr::const_f32(ACTION_PASTE_RECT.w),
+        expr::const_f32(ACTION_PASTE_RECT.h),
+        1.,
+        hicolor.clone(),
+    );
+
+    let node =
+        node.setup(|me| VectorArt::new(me, shape, app.render_api.clone(), app.ex.clone())).await;
+    layer_node.clone().link(node);
+
+    // Create some text
+    let node = create_text("paste_label");
+    let prop = node.get_property("rect").unwrap();
+    prop.set_f32(Role::App, 0, ACTION_LABEL_POS.x).unwrap();
+    prop.set_f32(Role::App, 1, ACTION_LABEL_POS.y).unwrap();
+    prop.set_f32(Role::App, 2, ACTION_SELECT_ALL_RECT.rhs()).unwrap();
+    prop.set_f32(Role::App, 3, ACTION_SELECT_ALL_RECT.h).unwrap();
+    node.set_property_f32(Role::App, "baseline", 0.).unwrap();
+    node.set_property_f32(Role::App, "font_size", FONTSIZE).unwrap();
+    node.set_property_str(Role::App, "text", "paste").unwrap();
+    //node.set_property_bool(Role::App, "debug", true).unwrap();
+    //node.set_property_str(Role::App, "text", "anon1").unwrap();
+    let prop = node.get_property("text_color").unwrap();
+    prop.set_f32(Role::App, 0, 0.).unwrap();
+    prop.set_f32(Role::App, 1, 0.94).unwrap();
+    prop.set_f32(Role::App, 2, 1.).unwrap();
+    prop.set_f32(Role::App, 3, 1.).unwrap();
+    node.set_property_u32(Role::App, "z_index", 1).unwrap();
+
+    let node = node
+        .setup(|me| {
+            Text::new(
+                me,
+                window_scale.clone(),
+                app.render_api.clone(),
+                app.text_shaper.clone(),
+                app.ex.clone(),
+            )
+        })
+        .await;
+    layer_node.clone().link(node);
+
+    // Paste button
+    let node = create_button("paste_btn");
+    node.set_property_bool(Role::App, "is_active", true).unwrap();
+    let prop = node.get_property("rect").unwrap();
+    prop.set_f32(Role::App, 0, 0.).unwrap();
+    prop.set_f32(Role::App, 1, 0.).unwrap();
+    prop.set_f32(Role::App, 2, ACTION_PASTE_RECT.w).unwrap();
+    prop.set_f32(Role::App, 3, ACTION_PASTE_RECT.h).unwrap();
+
+    let node = node.setup(|me| Button::new(me, app.ex.clone())).await;
+    layer_node.clone().link(node.clone());
+
+    let (slot, recvr) = Slot::new("paste_clicked");
+    node.register("click", slot).unwrap();
+    let chatedit_node2 = chatedit_node.clone();
+    let pasta_is_visible2 = pasta_is_visible.clone();
+    let listen_click = app.ex.spawn(async move {
+        let mut clip = Clipboard::new();
+        while let Ok(_) = recvr.recv().await {
+            if let Some(text) = clip.get() {
+                info!(target: "app::chat", "clicked paste: {text}");
+                let mut data = vec![];
+                text.encode(&mut data).unwrap();
+                chatedit_node2.call_method("insert_text", data).await.unwrap();
+            } else {
+                info!(target: "app::chat", "clicked paste but clip is empty");
+            }
+            pasta_is_visible2.set(false);
+        }
+    });
+    app.tasks.lock().unwrap().push(listen_click);
+
+    let editz_cpos_sub = editz_cursor_pos.subscribe_modify();
+    let pasta_is_visible2 = pasta_is_visible.clone();
+    let editz_cpos_task = app.ex.spawn(async move {
+        while let Ok(_) = editz_cpos_sub.receive().await {
+            pasta_is_visible2.set(false);
+        }
+    });
+    app.tasks.lock().unwrap().push(editz_cpos_task);
+
     let editz_select_sub = editz_select_text.subscribe_modify();
     let editz_select_sub = editz_select_text.subscribe_modify();
+    let pasta_is_visible2 = pasta_is_visible.clone();
     let editz_select_task = app.ex.spawn(async move {
     let editz_select_task = app.ex.spawn(async move {
         while let Ok(_) = editz_select_sub.receive().await {
         while let Ok(_) = editz_select_sub.receive().await {
             if editz_select_text.is_null(0).unwrap() {
             if editz_select_text.is_null(0).unwrap() {
                 info!(target: "app::chat", "selection changed: null");
                 info!(target: "app::chat", "selection changed: null");
                 actions_is_visible.set(false);
                 actions_is_visible.set(false);
+                pasta_is_visible2.set(false);
             } else {
             } else {
                 let select_text = editz_select_text.get_str(0).unwrap();
                 let select_text = editz_select_text.get_str(0).unwrap();
                 info!(target: "app::chat", "selection changed: {select_text}");
                 info!(target: "app::chat", "selection changed: {select_text}");
                 actions_is_visible.set(true);
                 actions_is_visible.set(true);
+                pasta_is_visible2.set(false);
             }
             }
         }
         }
     });
     });
     app.tasks.lock().unwrap().push(editz_select_task);
     app.tasks.lock().unwrap().push(editz_select_task);
+
+    let editz_text_sub = editz_text.prop().subscribe_modify();
+    let editz_text_task = app.ex.spawn(async move {
+        while let Ok(_) = editz_text_sub.receive().await {
+            pasta_is_visible.set(false);
+
+            let text = editz_text.get();
+            info!(target: "app::chat", "text changed: {text}");
+            // We want to avoid setting the property multiple times to the same value
+            // because then it triggers unnecessary redraw work.
+            if let Some(first_char) = text.chars().next() {
+                if first_char == '/' {
+                    if !cmd_hint_is_visible.get() {
+                        cmd_hint_is_visible.set(true);
+                    }
+                } else {
+                    if cmd_hint_is_visible.get() {
+                        cmd_hint_is_visible.set(false);
+                    }
+                }
+            } else {
+                if cmd_hint_is_visible.get() {
+                    cmd_hint_is_visible.set(false);
+                }
+            }
+        }
+    });
+    app.tasks.lock().unwrap().push(editz_text_task);
 }
 }

+ 41 - 21
bin/darkwallet/src/ui/chatedit.rs

@@ -170,6 +170,7 @@ impl TextWrap {
 
 
     fn get_word_boundary(&mut self, pos: TextPos) -> (TextPos, TextPos) {
     fn get_word_boundary(&mut self, pos: TextPos) -> (TextPos, TextPos) {
         let rendered = self.get_render();
         let rendered = self.get_render();
+        let final_pos = rendered.glyphs.len();
 
 
         // Find word start
         // Find word start
         let mut pos_start = pos;
         let mut pos_start = pos;
@@ -183,8 +184,8 @@ impl TextWrap {
         }
         }
 
 
         // Find word end
         // Find word end
-        let mut pos_end = pos + 1;
-        while pos_end < rendered.glyphs.len() {
+        let mut pos_end = std::cmp::min(pos + 1, final_pos);
+        while pos_end < final_pos {
             let glyph_str = &rendered.glyphs[pos_end].substr;
             let glyph_str = &rendered.glyphs[pos_end].substr;
             if is_whitespace(glyph_str) {
             if is_whitespace(glyph_str) {
                 break
                 break
@@ -646,10 +647,10 @@ impl ChatEdit {
         //    .lock()
         //    .lock()
         //    .editable
         //    .editable
         //    .set_text("".to_string(), "king!😁🍆jelly 🍆1234".to_string());
         //    .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_
         //self_
         //    .text_wrap
         //    .text_wrap
         //    .lock()
         //    .lock()
@@ -689,7 +690,6 @@ impl ChatEdit {
         let linespacing = self.linespacing.get();
         let linespacing = self.linespacing.get();
         let baseline = self.baseline.get();
         let baseline = self.baseline.get();
         let scroll = self.scroll.get();
         let scroll = self.scroll.get();
-        let cursor_pos = self.cursor_pos.get() as usize;
         let cursor_color = self.cursor_color.get();
         let cursor_color = self.cursor_color.get();
         let debug = self.debug.get();
         let debug = self.debug.get();
 
 
@@ -1275,23 +1275,23 @@ impl ChatEdit {
         // begin selection
         // begin selection
         let select = &mut text_wrap.select;
         let select = &mut text_wrap.select;
         select.clear();
         select.clear();
-        select.push(Selection::new(word_start, word_end));
+        if word_start != word_end {
+            select.push(Selection::new(word_start, word_end));
+
+            self.is_phone_select.store(true, Ordering::Relaxed);
+            // redraw() will now hide the cursor
+            self.hide_cursor.store(true, Ordering::Relaxed);
+        }
 
 
         debug!(target: "ui::chatview", "Selected {select:?} from {touch_pos:?}");
         debug!(target: "ui::chatview", "Selected {select:?} from {touch_pos:?}");
         self.update_select_text(&mut text_wrap);
         self.update_select_text(&mut text_wrap);
-
-        self.is_phone_select.store(true, Ordering::Relaxed);
-        // redraw() will now hide the cursor
-        self.hide_cursor.store(true, Ordering::Relaxed);
     }
     }
 
 
-    // Call this whenever the selection changes
+    /// Call this whenever the selection changes to update the external property
     fn update_select_text(&self, text_wrap: &mut TextWrap) {
     fn update_select_text(&self, text_wrap: &mut TextWrap) {
         let select = &text_wrap.select;
         let select = &text_wrap.select;
         let Some(select) = select.first().cloned() else {
         let Some(select) = select.first().cloned() else {
-            if !self.select_text.is_null(0).unwrap() {
-                self.select_text.set_null(Role::Internal, 0).unwrap();
-            }
+            self.select_text.set_null(Role::Internal, 0).unwrap();
             return
             return
         };
         };
 
 
@@ -1304,6 +1304,12 @@ impl ChatEdit {
         self.select_text.set_str(Role::Internal, 0, text).unwrap();
         self.select_text.set_str(Role::Internal, 0, text).unwrap();
     }
     }
 
 
+    /// Call this whenever the cursor pos changes to update the external property
+    fn update_cursor_pos(&self, text_wrap: &mut TextWrap) {
+        let cursor_off = text_wrap.editable.get_text_before().len() as u32;
+        self.cursor_pos.set(cursor_off);
+    }
+
     /*
     /*
     fn copy_highlighted(&self) -> Result<()> {
     fn copy_highlighted(&self) -> Result<()> {
         let start = self.selected.get_u32(0)? as usize;
         let start = self.selected.get_u32(0)? as usize;
@@ -1439,9 +1445,14 @@ impl ChatEdit {
         match &touch_state {
         match &touch_state {
             TouchStateAction::Inactive => return false,
             TouchStateAction::Inactive => return false,
             TouchStateAction::StartSelect => {
             TouchStateAction::StartSelect => {
-                self.abs_to_local(&mut touch_pos);
-                self.start_touch_select(touch_pos);
-                self.redraw().await;
+                if self.text.get().is_empty() {
+                    let node = self.node.upgrade().unwrap();
+                    node.trigger("paste_request", vec![]).await.unwrap();
+                } else {
+                    self.abs_to_local(&mut touch_pos);
+                    self.start_touch_select(touch_pos);
+                    self.redraw().await;
+                }
                 debug!(target: "ui::chatedit::touch", "touch state: StartSelect -> Select");
                 debug!(target: "ui::chatedit::touch", "touch state: StartSelect -> Select");
                 self.touch_info.lock().state = TouchStateAction::Select;
                 self.touch_info.lock().state = TouchStateAction::Select;
             }
             }
@@ -1541,10 +1552,14 @@ impl ChatEdit {
         {
         {
             let mut text_wrap = self.text_wrap.lock();
             let mut text_wrap = self.text_wrap.lock();
             let cursor_pos = text_wrap.set_cursor_with_point(touch_pos, width);
             let cursor_pos = text_wrap.set_cursor_with_point(touch_pos, width);
+            self.update_cursor_pos(&mut text_wrap);
 
 
             let select = &mut text_wrap.select;
             let select = &mut text_wrap.select;
+            let select_is_empty = select.is_empty();
             select.clear();
             select.clear();
-            self.update_select_text(&mut text_wrap);
+            if !select_is_empty {
+                self.update_select_text(&mut text_wrap);
+            }
         }
         }
 
 
         self.is_phone_select.store(false, Ordering::Relaxed);
         self.is_phone_select.store(false, Ordering::Relaxed);
@@ -1966,12 +1981,16 @@ impl UIObject for ChatEdit {
         {
         {
             let mut text_wrap = self.text_wrap.lock();
             let mut text_wrap = self.text_wrap.lock();
             let cursor_pos = text_wrap.set_cursor_with_point(mouse_pos, width);
             let cursor_pos = text_wrap.set_cursor_with_point(mouse_pos, width);
+            self.update_cursor_pos(&mut text_wrap);
             debug!(target: "ui::editbox", "Mouse move cursor pos to {cursor_pos}");
             debug!(target: "ui::editbox", "Mouse move cursor pos to {cursor_pos}");
 
 
             // begin selection
             // begin selection
             let select = &mut text_wrap.select;
             let select = &mut text_wrap.select;
+            let select_is_empty = select.is_empty();
             select.clear();
             select.clear();
-            self.update_select_text(&mut text_wrap);
+            if !select_is_empty {
+                self.update_select_text(&mut text_wrap);
+            }
 
 
             self.mouse_btn_held.store(true, Ordering::Relaxed);
             self.mouse_btn_held.store(true, Ordering::Relaxed);
         }
         }
@@ -2016,6 +2035,7 @@ impl UIObject for ChatEdit {
         {
         {
             let mut text_wrap = self.text_wrap.lock();
             let mut text_wrap = self.text_wrap.lock();
             let cursor_pos = text_wrap.set_cursor_with_point(mouse_pos, width);
             let cursor_pos = text_wrap.set_cursor_with_point(mouse_pos, width);
+            self.update_cursor_pos(&mut text_wrap);
 
 
             // modify current selection
             // modify current selection
             let select = &mut text_wrap.select;
             let select = &mut text_wrap.select;

+ 1 - 1
bin/darkwallet/src/ui/editbox/editable.rs

@@ -239,7 +239,7 @@ impl Editable {
         self.before_text += &final_text;
         self.before_text += &final_text;
     }
     }
 
 
-    fn get_text_before(&self) -> String {
+    pub fn get_text_before(&self) -> String {
         let text =
         let text =
             self.before_text.clone() + &self.composer.commit_text + &self.composer.compose_text;
             self.before_text.clone() + &self.composer.commit_text + &self.composer.compose_text;
         text
         text