Radish alpha
r
rad:z39mP9rQAaGmERfUMPULfPUi473tY
Radicle terminal user interface
Radicle
Git
bin: Fix search cursor on apply / cancel
Merged did:key:z6MkswQE...2C1V opened 1 year ago
5 files changed +94 -23 4596d0b8 263146b8
modified bin/commands/inbox/select/ui.rs
@@ -172,12 +172,10 @@ impl Browser {
                }),
            search: TextField::default()
                .to_widget(tx.clone())
-
                .on_event(|key, s, _| match key {
-
                    Key::Esc => Some(Message::CloseSearch),
-
                    Key::Char('\n') => Some(Message::ApplySearch),
-
                    _ => Some(Message::UpdateSearch {
+
                .on_event(|_, s, _| {
+
                    Some(Message::UpdateSearch {
                        value: s.and_then(|i| i.unwrap_string()).unwrap_or_default(),
-
                    }),
+
                    })
                })
                .on_update(|state: &State| {
                    TextFieldProps::default()
@@ -202,8 +200,17 @@ impl View for Browser {
            .unwrap_or(&default);

        if props.show_search {
-
            self.search.handle_event(key);
-
            None
+
            match key {
+
                Key::Esc => {
+
                    self.search.reset();
+
                    Some(Message::CloseSearch)
+
                }
+
                Key::Char('\n') => Some(Message::ApplySearch),
+
                _ => {
+
                    self.search.handle_event(key);
+
                    None
+
                }
+
            }
        } else {
            match key {
                Key::Char('/') => Some(Message::OpenSearch),
modified bin/commands/issue/select/ui.rs
@@ -187,12 +187,10 @@ impl Browser {
                }),
            search: TextField::default()
                .to_widget(tx.clone())
-
                .on_event(|key, s, _| match key {
-
                    Key::Esc => Some(Message::CloseSearch),
-
                    Key::Char('\n') => Some(Message::ApplySearch),
-
                    _ => Some(Message::UpdateSearch {
+
                .on_event(|_, s, _| {
+
                    Some(Message::UpdateSearch {
                        value: s.and_then(|i| i.unwrap_string()).unwrap_or_default(),
-
                    }),
+
                    })
                })
                .on_update(|state: &State| {
                    TextFieldProps::default()
@@ -217,8 +215,17 @@ impl View for Browser {
            .unwrap_or(&default);

        if props.show_search {
-
            self.search.handle_event(key);
-
            None
+
            match key {
+
                Key::Esc => {
+
                    self.search.reset();
+
                    Some(Message::CloseSearch)
+
                }
+
                Key::Char('\n') => Some(Message::ApplySearch),
+
                _ => {
+
                    self.search.handle_event(key);
+
                    None
+
                }
+
            }
        } else {
            match key {
                Key::Char('/') => Some(Message::OpenSearch),
modified bin/commands/patch/select/ui.rs
@@ -189,12 +189,10 @@ impl Browser {
                }),
            search: TextField::default()
                .to_widget(tx.clone())
-
                .on_event(|key, s, _| match key {
-
                    Key::Esc => Some(Message::CloseSearch),
-
                    Key::Char('\n') => Some(Message::ApplySearch),
-
                    _ => Some(Message::UpdateSearch {
+
                .on_event(|_, s, _| {
+
                    Some(Message::UpdateSearch {
                        value: s.and_then(|i| i.unwrap_string()).unwrap_or_default(),
-
                    }),
+
                    })
                })
                .on_update(|state: &State| {
                    TextFieldProps::default()
@@ -219,8 +217,17 @@ impl View for Browser {
            .unwrap_or(&default);

        if props.show_search {
-
            self.search.handle_event(key);
-
            None
+
            match key {
+
                Key::Esc => {
+
                    self.search.reset();
+
                    Some(Message::CloseSearch)
+
                }
+
                Key::Char('\n') => Some(Message::ApplySearch),
+
                _ => {
+
                    self.search.handle_event(key);
+
                    None
+
                }
+
            }
        } else {
            match key {
                Key::Esc | Key::Ctrl('c') => Some(Message::Exit { selection: None }),
modified src/ui/widget.rs
@@ -128,6 +128,9 @@ pub trait View {
        None
    }

+
    /// Should reset the internal state and call `reset` on all children.
+
    fn reset(&mut self) {}
+

    /// Should handle key events and call `handle_event` on all children.
    fn handle_event(&mut self, _props: Option<&ViewProps>, _key: Key) -> Option<Self::Message> {
        None
@@ -168,6 +171,11 @@ impl<S: 'static, M: 'static> Widget<S, M> {
        }
    }

+
    /// Calls `reset` on the wrapped view.
+
    pub fn reset(&mut self) {
+
        self.view.reset()
+
    }
+

    /// Calls `handle_event` on the wrapped view as well as the `on_event` callback.
    /// Sends any message returned by either the view or the callback.
    pub fn handle_event(&mut self, key: Key) {
modified src/ui/widget/input.rs
@@ -93,7 +93,39 @@ impl<S, M> TextField<S, M> {
        self.move_cursor_right();
    }

-
    fn delete_char(&mut self) {
+
    fn delete_char_right(&mut self) {
+
        self.state.text = Some(self.state.text.clone().unwrap_or_default());
+

+
        // Method "remove" is not used on the saved text for deleting the selected char.
+
        // Reason: Using remove on String works on bytes instead of the chars.
+
        // Using remove would require special care because of char boundaries.
+

+
        let current_index = self.state.cursor_position;
+
        let from_left_to_current_index = current_index;
+

+
        // Getting all characters before the selected character.
+
        let before_char_to_delete = self
+
            .state
+
            .text
+
            .as_ref()
+
            .unwrap()
+
            .chars()
+
            .take(from_left_to_current_index);
+
        // Getting all characters after selected character.
+
        let after_char_to_delete = self
+
            .state
+
            .text
+
            .as_ref()
+
            .unwrap()
+
            .chars()
+
            .skip(current_index.saturating_add(1));
+

+
        // Put all characters together except the selected one.
+
        // By leaving the selected one out, it is forgotten and therefore deleted.
+
        self.state.text = Some(before_char_to_delete.chain(after_char_to_delete).collect());
+
    }
+

+
    fn delete_char_left(&mut self) {
        self.state.text = Some(self.state.text.clone().unwrap_or_default());

        let is_not_cursor_leftmost = self.state.cursor_position != 0;
@@ -149,6 +181,13 @@ where
            .map(|text| ViewState::String(text.to_string()))
    }

+
    fn reset(&mut self) {
+
        self.state = TextFieldState {
+
            text: None,
+
            cursor_position: 0,
+
        };
+
    }
+

    fn handle_event(&mut self, _props: Option<&ViewProps>, key: Key) -> Option<Self::Message> {
        match key {
            Key::Char(to_insert)
@@ -159,7 +198,10 @@ where
                self.enter_char(to_insert);
            }
            Key::Backspace => {
-
                self.delete_char();
+
                self.delete_char_left();
+
            }
+
            Key::Delete => {
+
                self.delete_char_right();
            }
            Key::Left => {
                self.move_cursor_left();