1use crate::ext::ColorExt;
7use crate::widget::text_input::{Appearance, StyleSheet};
8use iced_core::Color;
9
10#[derive(Default)]
11pub enum TextInput {
12 #[default]
13 Default,
14 EditableText,
15 ExpandableSearch,
16 Search,
17 Inline,
18 Custom {
19 active: Box<dyn Fn(&crate::Theme) -> Appearance>,
20 error: Box<dyn Fn(&crate::Theme) -> Appearance>,
21 hovered: Box<dyn Fn(&crate::Theme) -> Appearance>,
22 focused: Box<dyn Fn(&crate::Theme) -> Appearance>,
23 disabled: Box<dyn Fn(&crate::Theme) -> Appearance>,
24 },
25}
26
27impl StyleSheet for crate::Theme {
28 type Style = TextInput;
29
30 fn active(&self, style: &Self::Style) -> Appearance {
31 let palette = self.cosmic();
32 let container = self.current_container();
33
34 let mut background: Color = container.component.base.into();
35 background.a = 0.25;
36
37 let corner = palette.corner_radii;
38 let label_color = palette.palette.neutral_9;
39 match style {
40 TextInput::Default => Appearance {
41 background: background.into(),
42 border_radius: corner.radius_s.into(),
43 border_width: 2.0,
44 border_offset: None,
45 border_color: container.component.divider.into(),
46 icon_color: None,
47 text_color: None,
48 placeholder_color: {
49 let color: Color = container.on.into();
50 color.blend_alpha(background, 0.7)
51 },
52 selected_text_color: palette.on_accent_color().into(),
53 selected_fill: palette.accent_color().into(),
54 label_color: label_color.into(),
55 },
56 TextInput::EditableText => Appearance {
57 background: Color::TRANSPARENT.into(),
58 border_radius: corner.radius_0.into(),
59 border_width: 0.0,
60 border_offset: None,
61 border_color: Color::TRANSPARENT,
62 icon_color: None,
63 text_color: None,
64 placeholder_color: {
65 let color: Color = container.on.into();
66 color.blend_alpha(background, 0.7)
67 },
68 selected_text_color: palette.on_accent_color().into(),
69 selected_fill: palette.accent_color().into(),
70 label_color: label_color.into(),
71 },
72 TextInput::ExpandableSearch => Appearance {
73 background: Color::TRANSPARENT.into(),
74 border_radius: corner.radius_xl.into(),
75 border_width: 0.0,
76 border_offset: None,
77 border_color: Color::TRANSPARENT,
78 icon_color: None,
79 text_color: None,
80 placeholder_color: {
81 let color: Color = container.on.into();
82 color.blend_alpha(background, 0.7)
83 },
84 selected_text_color: palette.on_accent_color().into(),
85 selected_fill: palette.accent_color().into(),
86 label_color: label_color.into(),
87 },
88 TextInput::Search => Appearance {
89 background: background.into(),
90 border_radius: corner.radius_xl.into(),
91 border_width: 2.0,
92 border_offset: None,
93 border_color: container.component.divider.into(),
94 icon_color: None,
95 text_color: None,
96 placeholder_color: {
97 let color: Color = container.on.into();
98 color.blend_alpha(background, 0.7)
99 },
100 selected_text_color: palette.on_accent_color().into(),
101 selected_fill: palette.accent_color().into(),
102 label_color: label_color.into(),
103 },
104 TextInput::Inline => Appearance {
105 background: Color::TRANSPARENT.into(),
106 border_radius: corner.radius_0.into(),
107 border_width: 0.0,
108 border_offset: None,
109 border_color: Color::TRANSPARENT,
110 icon_color: None,
111 text_color: None,
112 placeholder_color: {
113 let color: Color = container.on.into();
114 color.blend_alpha(background, 0.7)
115 },
116 selected_text_color: palette.on_accent_color().into(),
117 selected_fill: palette.accent_color().into(),
118 label_color: label_color.into(),
119 },
120 TextInput::Custom { active, .. } => active(self),
121 }
122 }
123
124 fn error(&self, style: &Self::Style) -> Appearance {
125 let palette = self.cosmic();
126 let container = self.current_container();
127
128 let mut background: Color = container.component.base.into();
129 background.a = 0.25;
130
131 let corner = palette.corner_radii;
132 let label_color = palette.palette.neutral_9;
133
134 match style {
135 TextInput::Default => Appearance {
136 background: background.into(),
137 border_radius: corner.radius_s.into(),
138 border_width: 2.0,
139 border_offset: Some(2.0),
140 border_color: Color::from(palette.destructive_color()),
141 icon_color: None,
142 text_color: None,
143 placeholder_color: {
144 let color: Color = container.on.into();
145 color.blend_alpha(background, 0.7)
146 },
147 selected_text_color: palette.on_accent_color().into(),
148 selected_fill: palette.accent_color().into(),
149 label_color: label_color.into(),
150 },
151 TextInput::Search | TextInput::ExpandableSearch => Appearance {
152 background: background.into(),
153 border_radius: corner.radius_xl.into(),
154 border_width: 0.0,
155 border_offset: None,
156 border_color: Color::TRANSPARENT,
157 icon_color: None,
158 text_color: None,
159 placeholder_color: {
160 let color: Color = container.on.into();
161 color.blend_alpha(background, 0.7)
162 },
163 selected_text_color: palette.on_accent_color().into(),
164 selected_fill: palette.accent_color().into(),
165 label_color: label_color.into(),
166 },
167 TextInput::EditableText | TextInput::Inline => Appearance {
168 background: Color::TRANSPARENT.into(),
169 border_radius: corner.radius_0.into(),
170 border_width: 0.0,
171 border_offset: None,
172 border_color: Color::TRANSPARENT,
173 icon_color: None,
174 text_color: None,
175 placeholder_color: {
176 let color: Color = container.on.into();
177 color.blend_alpha(background, 0.7)
178 },
179 selected_text_color: palette.on_accent_color().into(),
180 selected_fill: palette.accent_color().into(),
181 label_color: label_color.into(),
182 },
183 TextInput::Custom { error, .. } => error(self),
184 }
185 }
186
187 fn hovered(&self, style: &Self::Style) -> Appearance {
188 let palette = self.cosmic();
189 let container = self.current_container();
190
191 let mut background: Color = container.component.base.into();
192 background.a = 0.25;
193
194 let corner = palette.corner_radii;
195 let label_color = palette.palette.neutral_9;
196
197 match style {
198 TextInput::Default => Appearance {
199 background: background.into(),
200 border_radius: corner.radius_s.into(),
201 border_width: 2.0,
202 border_offset: None,
203 border_color: palette.accent.base.into(),
204 icon_color: None,
205 text_color: None,
206 placeholder_color: {
207 let color: Color = container.on.into();
208 color.blend_alpha(background, 0.7)
209 },
210 selected_text_color: palette.on_accent_color().into(),
211 selected_fill: palette.accent_color().into(),
212 label_color: label_color.into(),
213 },
214 TextInput::Search => Appearance {
215 background: background.into(),
216 border_radius: corner.radius_xl.into(),
217 border_offset: None,
218 border_width: 2.0,
219 border_color: palette.accent.base.into(),
220 icon_color: None,
221 text_color: None,
222 placeholder_color: {
223 let color: Color = container.on.into();
224 color.blend_alpha(background, 0.7)
225 },
226 selected_text_color: palette.on_accent_color().into(),
227 selected_fill: palette.accent_color().into(),
228 label_color: label_color.into(),
229 },
230 TextInput::ExpandableSearch => Appearance {
231 background: background.into(),
232 border_radius: corner.radius_xl.into(),
233 border_offset: None,
234 border_width: 0.0,
235 border_color: Color::TRANSPARENT,
236 icon_color: None,
237 text_color: None,
238 placeholder_color: {
239 let color: Color = container.on.into();
240 color.blend_alpha(background, 0.7)
241 },
242 selected_text_color: palette.on_accent_color().into(),
243 selected_fill: palette.accent_color().into(),
244 label_color: label_color.into(),
245 },
246 TextInput::EditableText => Appearance {
247 background: Color::TRANSPARENT.into(),
248 border_radius: corner.radius_0.into(),
249 border_width: 0.0,
250 border_offset: None,
251 border_color: Color::TRANSPARENT,
252 icon_color: None,
253 text_color: None,
254 placeholder_color: {
255 let color: Color = container.on.into();
256 color.blend_alpha(background, 0.7)
257 },
258 selected_text_color: palette.on_accent_color().into(),
259 selected_fill: palette.accent_color().into(),
260 label_color: label_color.into(),
261 },
262 TextInput::Inline => Appearance {
263 background: Color::from(container.component.hover).into(),
264 border_radius: corner.radius_0.into(),
265 border_width: 0.0,
266 border_offset: None,
267 border_color: Color::TRANSPARENT,
268 icon_color: None,
269 text_color: None,
270 placeholder_color: {
271 let color: Color = container.on.into();
272 color.blend_alpha(background, 0.7)
273 },
274 selected_text_color: palette.on_accent_color().into(),
275 selected_fill: palette.accent_color().into(),
276 label_color: label_color.into(),
277 },
278 TextInput::Custom { hovered, .. } => hovered(self),
279 }
280 }
281
282 fn focused(&self, style: &Self::Style) -> Appearance {
283 let palette = self.cosmic();
284 let container = self.current_container();
285
286 let mut background: Color = container.component.base.into();
287 background.a = 0.25;
288
289 let corner = palette.corner_radii;
290 let label_color = palette.palette.neutral_9;
291
292 match style {
293 TextInput::Default => Appearance {
294 background: background.into(),
295 border_radius: corner.radius_s.into(),
296 border_width: 2.0,
297 border_offset: Some(2.0),
298 border_color: palette.accent.base.into(),
299 icon_color: None,
300 text_color: None,
301 placeholder_color: {
302 let color: Color = container.on.into();
303 color.blend_alpha(background, 0.7)
304 },
305 selected_text_color: palette.on_accent_color().into(),
306 selected_fill: palette.accent_color().into(),
307 label_color: label_color.into(),
308 },
309 TextInput::Search | TextInput::ExpandableSearch => Appearance {
310 background: background.into(),
311 border_radius: corner.radius_xl.into(),
312 border_width: 2.0,
313 border_offset: Some(2.0),
314 border_color: palette.accent.base.into(),
315 icon_color: None,
316 text_color: None,
317 placeholder_color: {
318 let color: Color = container.on.into();
319 color.blend_alpha(background, 0.7)
320 },
321 selected_text_color: palette.on_accent_color().into(),
322 selected_fill: palette.accent_color().into(),
323 label_color: label_color.into(),
324 },
325 TextInput::EditableText => Appearance {
326 background: Color::TRANSPARENT.into(),
327 border_radius: corner.radius_0.into(),
328 border_width: 0.0,
329 border_offset: None,
330 border_color: Color::TRANSPARENT,
331 icon_color: None,
332 text_color: None,
333 placeholder_color: {
334 let color: Color = container.on.into();
335 color.blend_alpha(background, 0.7)
336 },
337 selected_text_color: palette.on_accent_color().into(),
338 selected_fill: palette.accent_color().into(),
339 label_color: label_color.into(),
340 },
341 TextInput::Inline => Appearance {
342 background: Color::TRANSPARENT.into(),
343 border_radius: corner.radius_0.into(),
344 border_width: 0.0,
345 border_offset: None,
346 border_color: Color::TRANSPARENT,
347 icon_color: None,
348 text_color: None,
349 placeholder_color: {
350 let color: Color = container.on.into();
351 color.blend_alpha(background, 0.7)
352 },
353 selected_text_color: palette.on_accent_color().into(),
354 selected_fill: palette.accent_color().into(),
355 label_color: label_color.into(),
356 },
357 TextInput::Custom { focused, .. } => focused(self),
358 }
359 }
360
361 fn disabled(&self, style: &Self::Style) -> Appearance {
362 if let TextInput::Custom { disabled, .. } = style {
363 return disabled(self);
364 }
365
366 self.active(style)
367 }
368}