1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
mod action;
mod state;
#[cfg(test)]
mod tests;
use input::{InputOptions, KeyCode, KeyEvent, KeyModifiers, StandardEvent};
use view::{LineSegment, ViewLine};
pub(crate) use self::action::Action as SearchBarAction;
use crate::{
components::{
search_bar::state::State,
shared::{EditAction, EditableLine},
},
events::Event,
};
const DEFAULT_LABEL: &str = "/";
const INPUT_OPTIONS_EDITING: InputOptions = InputOptions::RESIZE;
const INPUT_OPTIONS_SEARCHING: InputOptions = InputOptions::RESIZE
.union(InputOptions::SEARCH)
.union(InputOptions::HELP);
pub(crate) struct SearchBar {
editable_line: EditableLine,
state: State,
}
impl SearchBar {
pub(crate) fn new() -> Self {
let mut editable_line = EditableLine::new();
editable_line.set_label(LineSegment::new(DEFAULT_LABEL));
Self {
editable_line,
state: State::Deactivated,
}
}
pub(crate) fn start_search(&mut self, initial_value: Option<&str>) {
if let Some(value) = initial_value {
self.editable_line.set_content(value);
}
self.editable_line.set_read_only(false);
self.state = State::Editing;
}
pub(crate) fn reset(&mut self) {
self.state = State::Deactivated;
}
pub(crate) const fn input_options(&self) -> Option<&InputOptions> {
match self.state {
State::Deactivated => None,
State::Editing => Some(&INPUT_OPTIONS_EDITING),
State::Searching => Some(&INPUT_OPTIONS_SEARCHING),
}
}
pub(crate) const fn read_event(&self, event: Event) -> Option<Event> {
match self.state {
State::Deactivated | State::Searching => None,
State::Editing => Some(event),
}
}
pub(crate) fn handle_event(&mut self, event: Event) -> SearchBarAction {
if !self.state.is_active() {
return SearchBarAction::None;
}
match event {
Event::Standard(StandardEvent::SearchNext) => {
SearchBarAction::Next(String::from(self.editable_line.get_content()))
},
Event::Standard(StandardEvent::SearchPrevious) => {
SearchBarAction::Previous(String::from(self.editable_line.get_content()))
},
Event::Standard(StandardEvent::SearchFinish)
| Event::Key(KeyEvent {
code: KeyCode::Enter,
modifiers: KeyModifiers::NONE,
}) => {
self.editable_line.set_read_only(true);
self.state = State::Searching;
SearchBarAction::Start(String::from(self.editable_line.get_content()))
},
Event::Standard(StandardEvent::SearchStart) => {
self.state = State::Deactivated;
SearchBarAction::Cancel
},
Event::Key(KeyEvent {
code: KeyCode::Esc,
modifiers: KeyModifiers::NONE,
}) => {
self.reset();
SearchBarAction::Cancel
},
_ => {
if self.state == State::Editing && self.editable_line.handle_event(event) == EditAction::ContentUpdate {
SearchBarAction::Update(String::from(self.editable_line.get_content()))
}
else {
SearchBarAction::None
}
},
}
}
pub(crate) fn search_value(&self) -> Option<&str> {
match self.state {
State::Deactivated => None,
State::Editing | State::Searching => {
let content = self.editable_line.get_content();
if content.is_empty() { None } else { Some(content) }
},
}
}
pub(crate) const fn is_active(&self) -> bool {
self.state.is_active()
}
pub(crate) fn is_editing(&self) -> bool {
self.state == State::Editing
}
pub(crate) fn build_view_line(&self) -> ViewLine {
ViewLine::from(self.editable_line.line_segments())
}
}
|