bubbletea_widgets/textinput/
view.rs1use super::model::Model;
4use super::types::EchoMode;
5
6impl Model {
7 pub fn view(&self) -> String {
10 if self.value.is_empty() && !self.placeholder.is_empty() {
12 return self.placeholder_view();
13 }
14
15 let value_slice = if self.offset_right <= self.value.len() {
16 &self.value[self.offset..self.offset_right]
17 } else {
18 &self.value[self.offset..]
19 };
20
21 let pos = self.pos.saturating_sub(self.offset);
22 let value_str: String = value_slice.iter().collect();
23 let display_value = self.echo_transform(&value_str);
24
25 let mut v = String::new();
26
27 if pos < display_value.len() {
29 v.push_str(&self.text_style.render(&display_value[..pos]));
30 } else {
31 v.push_str(&self.text_style.render(&display_value));
32 }
33
34 if pos < display_value.len() {
36 let char_at_pos = display_value.chars().nth(pos).unwrap_or(' ');
37 let mut cur = self.cursor.clone();
38 cur.set_char(&char_at_pos.to_string());
39 v.push_str(&cur.view());
40
41 if pos + 1 < display_value.len() {
43 v.push_str(&self.text_style.render(&display_value[pos + 1..]));
44 }
45
46 v.push_str(&self.completion_view(0));
47 } else {
48 if self.focus && self.can_accept_suggestion() {
50 let suggestion = &self.matched_suggestions[self.current_suggestion_index];
51 if self.value.len() < suggestion.len() {
52 let next_char = suggestion[pos];
53 let mut cur = self.cursor.clone();
54 cur.set_char(&next_char.to_string());
55 v.push_str(&cur.view());
56 v.push_str(&self.completion_view(1));
57 } else {
58 let mut cur = self.cursor.clone();
59 cur.set_char(" ");
60 v.push_str(&cur.view());
61 }
62 } else {
63 let mut cur = self.cursor.clone();
64 cur.set_char(" ");
65 v.push_str(&cur.view());
66 }
67 }
68
69 let val_width = display_value.chars().count();
71 if self.width > 0 && val_width <= self.width as usize {
72 let padding = (self.width as usize).saturating_sub(val_width);
73 if val_width + padding <= self.width as usize && pos < display_value.len() {
74 }
76 v.push_str(&self.text_style.render(&" ".repeat(padding)));
77 }
78
79 format!("{}{}", self.prompt_style.render(&self.prompt), v)
80 }
81
82 pub(super) fn placeholder_view(&self) -> String {
84 let mut v = String::new();
85
86 let placeholder_chars: Vec<char> = self.placeholder.chars().collect();
87 let p = if self.width > 0 {
88 let mut p_vec = vec![' '; self.width as usize + 1];
89 for (i, &ch) in placeholder_chars.iter().enumerate() {
90 if i < p_vec.len() {
91 p_vec[i] = ch;
92 }
93 }
94 p_vec
95 } else {
96 placeholder_chars
97 };
98
99 if !p.is_empty() {
100 let mut cur = self.cursor.clone();
101 cur.set_char(&p[0].to_string());
102 v.push_str(&cur.view());
103 }
104
105 if self.width < 1 && p.len() <= 1 {
106 return format!("{}{}", self.prompt_style.render(&self.prompt), v);
107 }
108
109 if self.width > 0 {
110 let min_width = self.placeholder.chars().count();
111 let avail_width = (self.width as usize).saturating_sub(min_width) + 1;
112
113 if p.len() > 1 {
114 let end_idx = std::cmp::min(p.len(), min_width);
115 let text: String = p[1..end_idx].iter().collect();
116 v.push_str(&self.placeholder_style.render(&text));
117 }
118 v.push_str(&self.placeholder_style.render(&" ".repeat(avail_width)));
119 } else if p.len() > 1 {
120 let text: String = p[1..].iter().collect();
122 v.push_str(&self.placeholder_style.render(&text));
123 }
124
125 format!("{}{}", self.prompt_style.render(&self.prompt), v)
126 }
127
128 pub(super) fn echo_transform(&self, v: &str) -> String {
130 match self.echo_mode {
131 EchoMode::EchoPassword => {
132 let width = v.chars().count();
133 self.echo_character.to_string().repeat(width)
134 }
135 EchoMode::EchoNone => String::new(),
136 EchoMode::EchoNormal => v.to_string(),
137 }
138 }
139
140 pub(super) fn completion_view(&self, offset: usize) -> String {
142 if self.can_accept_suggestion() {
143 let suggestion = &self.matched_suggestions[self.current_suggestion_index];
144 if self.value.len() + offset < suggestion.len() {
145 let remaining: String = suggestion[self.value.len() + offset..].iter().collect();
146 return self.completion_style.render(&remaining);
147 }
148 }
149 String::new()
150 }
151}