Introduce disabled state for TextInput

This commit is contained in:
Dan Mishin 2023-03-03 10:01:49 +03:00 committed by Héctor Ramón Jiménez
parent ca828f03f5
commit f10e936f00
No known key found for this signature in database
GPG key ID: 140CC052C94F138E
17 changed files with 410 additions and 254 deletions

View file

@ -141,8 +141,8 @@ mod numeric_input {
.map(u32::to_string) .map(u32::to_string)
.as_deref() .as_deref()
.unwrap_or(""), .unwrap_or(""),
Event::InputChanged,
) )
.on_change(Event::InputChanged)
.padding(10), .padding(10),
button("+", Event::IncrementPressed), button("+", Event::IncrementPressed),
] ]

View file

@ -100,11 +100,10 @@ impl Program for Controls {
.size(14) .size(14)
.style(Color::WHITE), .style(Color::WHITE),
) )
.push(text_input( .push(
"Placeholder", text_input("Placeholder", text)
text, .on_change(Message::TextChanged),
Message::TextChanged, ),
)),
), ),
) )
.into() .into()

View file

@ -214,12 +214,9 @@ impl Sandbox for App {
column![ column![
scrollable(options).height(Length::Fill), scrollable(options).height(Length::Fill),
row![ row![
text_input( text_input("Add a new option", &self.input)
"Add a new option", .on_change(Message::InputChanged)
&self.input, .on_submit(Message::AddItem(self.input.clone())),
Message::InputChanged,
)
.on_submit(Message::AddItem(self.input.clone())),
button(text(format!("Toggle Order ({})", self.order))) button(text(format!("Toggle Order ({})", self.order)))
.on_press(Message::ToggleOrder) .on_press(Message::ToggleOrder)
] ]

View file

@ -133,18 +133,16 @@ impl Application for App {
column![ column![
column![ column![
text("Email").size(12), text("Email").size(12),
text_input( text_input("abc@123.com", &self.email,)
"abc@123.com", .on_change(Message::Email)
&self.email, .on_submit(Message::Submit)
Message::Email .padding(5),
)
.on_submit(Message::Submit)
.padding(5),
] ]
.spacing(5), .spacing(5),
column![ column![
text("Password").size(12), text("Password").size(12),
text_input("", &self.password, Message::Password) text_input("", &self.password)
.on_change(Message::Password)
.on_submit(Message::Submit) .on_submit(Message::Submit)
.password() .password()
.padding(5), .padding(5),

View file

@ -49,13 +49,11 @@ impl Sandbox for QRGenerator {
.size(70) .size(70)
.style(Color::from([0.5, 0.5, 0.5])); .style(Color::from([0.5, 0.5, 0.5]));
let input = text_input( let input =
"Type the data of your QR code here...", text_input("Type the data of your QR code here...", &self.data)
&self.data, .on_change(Message::DataChanged)
Message::DataChanged, .size(30)
) .padding(15);
.size(30)
.padding(15);
let mut content = column![title, input] let mut content = column![title, input]
.width(700) .width(700)

View file

@ -90,13 +90,10 @@ impl Sandbox for Styling {
}, },
); );
let text_input = text_input( let text_input = text_input("Type something...", &self.input_value)
"Type something...", .on_change(Message::InputChanged)
&self.input_value, .padding(10)
Message::InputChanged, .size(20);
)
.padding(10)
.size(20);
let button = button("Submit") let button = button("Submit")
.padding(10) .padding(10)

View file

@ -0,0 +1,11 @@
[package]
name = "text_input"
authors = ["Dan Mishin <jungletryne@yandex.com>"]
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
iced = { path = "../..", features = ["tokio"] }
tokio = { version = "1.26.0", features = ["time"] }

View file

@ -0,0 +1,15 @@
# Text Input
This example shows basic usage of text edit.
The button delays the change of the text field state to allow testing of the corner cases.
<div align="center">
<a href="https://gfycat.com/everycarelessisabellinewheatear">
<img src="https://thumbs.gfycat.com/EveryCarelessIsabellinewheatear-max-1mb.gif" height="400px">
</a>
</div>
You can run it with cargo run:
```bash
cargo run --package text_input
```

View file

@ -0,0 +1,94 @@
use crate::Message::{StartTimer, TextEditModeChange};
use iced::widget::{button, column, container, row, text, text_input};
use iced::{
executor, window, Application, Command, Element, Length, Renderer,
Settings, Theme,
};
use tokio::time::{sleep, Duration};
fn main() -> iced::Result {
let settings = Settings {
window: window::Settings {
size: (700, 100),
..window::Settings::default()
},
..Settings::default()
};
Example::run(settings)
}
#[derive(Default)]
struct Example {
data: String,
text_edit_enabled: bool,
}
#[derive(Debug, Clone)]
enum Message {
StartTimer,
TextEditModeChange,
TextInputChanged(String),
}
impl Application for Example {
type Executor = executor::Default;
type Message = Message;
type Theme = Theme;
type Flags = ();
fn new(_flags: Self::Flags) -> (Self, Command<Self::Message>) {
(Self::default(), Command::none())
}
fn title(&self) -> String {
"TextInput example".into()
}
fn update(&mut self, message: Self::Message) -> Command<Self::Message> {
match message {
Message::TextEditModeChange => {
self.text_edit_enabled = !self.text_edit_enabled;
Command::none()
}
Message::TextInputChanged(updated_text) => {
self.data = updated_text;
Command::none()
}
StartTimer => {
let timer_f = async {
sleep(Duration::from_secs(3)).await;
};
Command::perform(timer_f, |_| TextEditModeChange)
}
}
}
fn view(&self) -> Element<'_, Self::Message, Renderer<Self::Theme>> {
let placeholder = if self.text_edit_enabled {
"Enabled TextEdit"
} else {
"Disabled TextEdit"
};
let mut txt_input = text_input(placeholder, &self.data);
if self.text_edit_enabled {
txt_input = txt_input.on_change(Message::TextInputChanged);
}
let btn = button("Enable/Disable").on_press(StartTimer);
let label = text(
"The mode will be changed after 3s when the button is pressed",
);
let content = row![txt_input, btn].spacing(10);
let content = column![content, label].spacing(10);
container(content)
.width(Length::Shrink)
.height(Length::Shrink)
.padding(20)
.into()
}
}

View file

@ -119,13 +119,15 @@ impl Application for App {
column![ column![
subtitle( subtitle(
"Title", "Title",
text_input("", &self.editing.title, Message::Title) text_input("", &self.editing.title)
.on_change(Message::Title)
.on_submit(Message::Add) .on_submit(Message::Add)
.into() .into()
), ),
subtitle( subtitle(
"Message", "Message",
text_input("", &self.editing.body, Message::Body) text_input("", &self.editing.body)
.on_change(Message::Body)
.on_submit(Message::Add) .on_submit(Message::Add)
.into() .into()
), ),

View file

@ -204,15 +204,12 @@ impl Application for Todos {
.style(Color::from([0.5, 0.5, 0.5])) .style(Color::from([0.5, 0.5, 0.5]))
.horizontal_alignment(alignment::Horizontal::Center); .horizontal_alignment(alignment::Horizontal::Center);
let input = text_input( let input = text_input("What needs to be done?", input_value)
"What needs to be done?", .on_change(Message::InputChanged)
input_value, .id(INPUT_ID.clone())
Message::InputChanged, .padding(15)
) .size(30)
.id(INPUT_ID.clone()) .on_submit(Message::CreateTask);
.padding(15)
.size(30)
.on_submit(Message::CreateTask);
let controls = view_controls(tasks, *filter); let controls = view_controls(tasks, *filter);
let filtered_tasks = let filtered_tasks =
@ -375,14 +372,12 @@ impl Task {
.into() .into()
} }
TaskState::Editing => { TaskState::Editing => {
let text_input = text_input( let text_input =
"Describe your task...", text_input("Describe your task...", &self.description)
&self.description, .id(Self::text_input_id(i))
TaskMessage::DescriptionEdited, .on_change(TaskMessage::DescriptionEdited)
) .on_submit(TaskMessage::FinishEdition)
.id(Self::text_input_id(i)) .padding(10);
.on_submit(TaskMessage::FinishEdition)
.padding(10);
row![ row![
text_input, text_input,

View file

@ -570,13 +570,10 @@ impl<'a> Step {
bytes: include_bytes!("../fonts/icons.ttf"), bytes: include_bytes!("../fonts/icons.ttf"),
}; };
let mut text_input = text_input( let mut text_input = text_input("Type something to continue...", value)
"Type something to continue...", .on_change(StepMessage::InputChanged)
value, .padding(10)
StepMessage::InputChanged, .size(30);
)
.padding(10)
.size(30);
if is_showing_icon { if is_showing_icon {
text_input = text_input.icon(text_input::Icon { text_input = text_input.icon(text_input::Icon {

View file

@ -125,12 +125,9 @@ impl Application for WebSocket {
}; };
let new_message_input = { let new_message_input = {
let mut input = text_input( let mut input = text_input("Type a message...", &self.new_message)
"Type a message...", .on_change(Message::NewMessageChanged)
&self.new_message, .padding(10);
Message::NewMessageChanged,
)
.padding(10);
let mut button = button( let mut button = button(
text("Send") text("Send")

View file

@ -171,14 +171,13 @@ where
pub fn text_input<'a, Message, Renderer>( pub fn text_input<'a, Message, Renderer>(
placeholder: &str, placeholder: &str,
value: &str, value: &str,
on_change: impl Fn(String) -> Message + 'a,
) -> widget::TextInput<'a, Message, Renderer> ) -> widget::TextInput<'a, Message, Renderer>
where where
Message: Clone, Message: Clone,
Renderer: crate::text::Renderer, Renderer: crate::text::Renderer,
Renderer::Theme: widget::text_input::StyleSheet, Renderer::Theme: widget::text_input::StyleSheet,
{ {
widget::TextInput::new(placeholder, value, on_change) widget::TextInput::new(placeholder, value)
} }
/// Creates a new [`Slider`]. /// Creates a new [`Slider`].

View file

@ -46,8 +46,8 @@ pub use iced_style::text_input::{Appearance, StyleSheet};
/// let input = TextInput::new( /// let input = TextInput::new(
/// "This is the placeholder...", /// "This is the placeholder...",
/// value, /// value,
/// Message::TextInputChanged,
/// ) /// )
/// .on_change(|_| Message::TextInputChanged)
/// .padding(10); /// .padding(10);
/// ``` /// ```
/// ![Text input drawn by `iced_wgpu`](https://github.com/iced-rs/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/text_input.png?raw=true) /// ![Text input drawn by `iced_wgpu`](https://github.com/iced-rs/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/text_input.png?raw=true)
@ -65,7 +65,7 @@ where
width: Length, width: Length,
padding: Padding, padding: Padding,
size: Option<f32>, size: Option<f32>,
on_change: Box<dyn Fn(String) -> Message + 'a>, on_change: Option<Box<dyn Fn(String) -> Message + 'a>>,
on_paste: Option<Box<dyn Fn(String) -> Message + 'a>>, on_paste: Option<Box<dyn Fn(String) -> Message + 'a>>,
on_submit: Option<Message>, on_submit: Option<Message>,
icon: Option<Icon<Renderer::Font>>, icon: Option<Icon<Renderer::Font>>,
@ -82,12 +82,8 @@ where
/// ///
/// It expects: /// It expects:
/// - a placeholder, /// - a placeholder,
/// - the current value, and /// - the current value
/// - a function that produces a message when the [`TextInput`] changes. pub fn new(placeholder: &str, value: &str) -> Self {
pub fn new<F>(placeholder: &str, value: &str, on_change: F) -> Self
where
F: 'a + Fn(String) -> Message,
{
TextInput { TextInput {
id: None, id: None,
placeholder: String::from(placeholder), placeholder: String::from(placeholder),
@ -97,7 +93,7 @@ where
width: Length::Fill, width: Length::Fill,
padding: Padding::new(5.0), padding: Padding::new(5.0),
size: None, size: None,
on_change: Box::new(on_change), on_change: None,
on_paste: None, on_paste: None,
on_submit: None, on_submit: None,
icon: None, icon: None,
@ -175,6 +171,16 @@ where
self self
} }
/// Sets the callback which is called when the text gets changed
/// If not specified, the widget will be disabled
pub fn on_change<F>(mut self, callback: F) -> Self
where
F: 'a + Fn(String) -> Message,
{
self.on_change = Some(Box::new(callback));
self
}
/// Draws the [`TextInput`] with the given [`Renderer`], overriding its /// Draws the [`TextInput`] with the given [`Renderer`], overriding its
/// [`Value`] if provided. /// [`Value`] if provided.
/// ///
@ -201,6 +207,7 @@ where
self.is_secure, self.is_secure,
self.icon.as_ref(), self.icon.as_ref(),
&self.style, &self.style,
self.on_change.is_none(),
) )
} }
} }
@ -277,7 +284,7 @@ where
self.size, self.size,
&self.font, &self.font,
self.is_secure, self.is_secure,
self.on_change.as_ref(), self.on_change.as_deref(),
self.on_paste.as_deref(), self.on_paste.as_deref(),
&self.on_submit, &self.on_submit,
|| tree.state.downcast_mut::<State>(), || tree.state.downcast_mut::<State>(),
@ -307,6 +314,7 @@ where
self.is_secure, self.is_secure,
self.icon.as_ref(), self.icon.as_ref(),
&self.style, &self.style,
self.on_change.is_none(),
) )
} }
@ -492,7 +500,7 @@ pub fn update<'a, Message, Renderer>(
size: Option<f32>, size: Option<f32>,
font: &Renderer::Font, font: &Renderer::Font,
is_secure: bool, is_secure: bool,
on_change: &dyn Fn(String) -> Message, on_change: Option<&dyn Fn(String) -> Message>,
on_paste: Option<&dyn Fn(String) -> Message>, on_paste: Option<&dyn Fn(String) -> Message>,
on_submit: &Option<Message>, on_submit: &Option<Message>,
state: impl FnOnce() -> &'a mut State, state: impl FnOnce() -> &'a mut State,
@ -501,11 +509,14 @@ where
Message: Clone, Message: Clone,
Renderer: text::Renderer, Renderer: text::Renderer,
{ {
let is_disabled = on_change.is_none();
match event { match event {
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
| Event::Touch(touch::Event::FingerPressed { .. }) => { | Event::Touch(touch::Event::FingerPressed { .. }) => {
let state = state(); let state = state();
let is_clicked = layout.bounds().contains(cursor_position); let is_clicked = layout.bounds().contains(cursor_position);
let is_clicked = is_clicked && !is_disabled;
state.is_focused = if is_clicked { state.is_focused = if is_clicked {
state.is_focused.or_else(|| { state.is_focused.or_else(|| {
@ -635,20 +646,22 @@ where
let state = state(); let state = state();
if let Some(focus) = &mut state.is_focused { if let Some(focus) = &mut state.is_focused {
if state.is_pasting.is_none() if let Some(on_change) = on_change {
&& !state.keyboard_modifiers.command() if state.is_pasting.is_none()
&& !c.is_control() && !state.keyboard_modifiers.command()
{ && !c.is_control()
let mut editor = Editor::new(value, &mut state.cursor); {
let mut editor = Editor::new(value, &mut state.cursor);
editor.insert(c); editor.insert(c);
let message = (on_change)(editor.contents()); let message = (on_change)(editor.contents());
shell.publish(message); shell.publish(message);
focus.updated_at = Instant::now(); focus.updated_at = Instant::now();
return event::Status::Captured; return event::Status::Captured;
}
} }
} }
} }
@ -656,181 +669,188 @@ where
let state = state(); let state = state();
if let Some(focus) = &mut state.is_focused { if let Some(focus) = &mut state.is_focused {
let modifiers = state.keyboard_modifiers; if let Some(on_change) = on_change {
focus.updated_at = Instant::now(); let modifiers = state.keyboard_modifiers;
focus.updated_at = Instant::now();
match key_code { match key_code {
keyboard::KeyCode::Enter keyboard::KeyCode::Enter
| keyboard::KeyCode::NumpadEnter => { | keyboard::KeyCode::NumpadEnter => {
if let Some(on_submit) = on_submit.clone() { if let Some(on_submit) = on_submit.clone() {
shell.publish(on_submit); shell.publish(on_submit);
}
}
keyboard::KeyCode::Backspace => {
if platform::is_jump_modifier_pressed(modifiers)
&& state.cursor.selection(value).is_none()
{
if is_secure {
let cursor_pos = state.cursor.end(value);
state.cursor.select_range(0, cursor_pos);
} else {
state.cursor.select_left_by_words(value);
} }
} }
keyboard::KeyCode::Backspace => {
let mut editor = Editor::new(value, &mut state.cursor); if platform::is_jump_modifier_pressed(modifiers)
editor.backspace(); && state.cursor.selection(value).is_none()
{
let message = (on_change)(editor.contents()); if is_secure {
shell.publish(message); let cursor_pos = state.cursor.end(value);
} state.cursor.select_range(0, cursor_pos);
keyboard::KeyCode::Delete => { } else {
if platform::is_jump_modifier_pressed(modifiers) state.cursor.select_left_by_words(value);
&& state.cursor.selection(value).is_none()
{
if is_secure {
let cursor_pos = state.cursor.end(value);
state
.cursor
.select_range(cursor_pos, value.len());
} else {
state.cursor.select_right_by_words(value);
}
}
let mut editor = Editor::new(value, &mut state.cursor);
editor.delete();
let message = (on_change)(editor.contents());
shell.publish(message);
}
keyboard::KeyCode::Left => {
if platform::is_jump_modifier_pressed(modifiers)
&& !is_secure
{
if modifiers.shift() {
state.cursor.select_left_by_words(value);
} else {
state.cursor.move_left_by_words(value);
}
} else if modifiers.shift() {
state.cursor.select_left(value)
} else {
state.cursor.move_left(value);
}
}
keyboard::KeyCode::Right => {
if platform::is_jump_modifier_pressed(modifiers)
&& !is_secure
{
if modifiers.shift() {
state.cursor.select_right_by_words(value);
} else {
state.cursor.move_right_by_words(value);
}
} else if modifiers.shift() {
state.cursor.select_right(value)
} else {
state.cursor.move_right(value);
}
}
keyboard::KeyCode::Home => {
if modifiers.shift() {
state
.cursor
.select_range(state.cursor.start(value), 0);
} else {
state.cursor.move_to(0);
}
}
keyboard::KeyCode::End => {
if modifiers.shift() {
state.cursor.select_range(
state.cursor.start(value),
value.len(),
);
} else {
state.cursor.move_to(value.len());
}
}
keyboard::KeyCode::C
if state.keyboard_modifiers.command() =>
{
if let Some((start, end)) =
state.cursor.selection(value)
{
clipboard
.write(value.select(start, end).to_string());
}
}
keyboard::KeyCode::X
if state.keyboard_modifiers.command() =>
{
if let Some((start, end)) =
state.cursor.selection(value)
{
clipboard
.write(value.select(start, end).to_string());
}
let mut editor = Editor::new(value, &mut state.cursor);
editor.delete();
let message = (on_change)(editor.contents());
shell.publish(message);
}
keyboard::KeyCode::V => {
if state.keyboard_modifiers.command() {
let content = match state.is_pasting.take() {
Some(content) => content,
None => {
let content: String = clipboard
.read()
.unwrap_or_default()
.chars()
.filter(|c| !c.is_control())
.collect();
Value::new(&content)
} }
}; }
let mut editor = let mut editor =
Editor::new(value, &mut state.cursor); Editor::new(value, &mut state.cursor);
editor.backspace();
editor.paste(content.clone()); let message = (on_change)(editor.contents());
let message = if let Some(paste) = &on_paste {
(paste)(editor.contents())
} else {
(on_change)(editor.contents())
};
shell.publish(message); shell.publish(message);
state.is_pasting = Some(content);
} else {
state.is_pasting = None;
} }
} keyboard::KeyCode::Delete => {
keyboard::KeyCode::A if platform::is_jump_modifier_pressed(modifiers)
if state.keyboard_modifiers.command() => && state.cursor.selection(value).is_none()
{ {
state.cursor.select_all(value); if is_secure {
} let cursor_pos = state.cursor.end(value);
keyboard::KeyCode::Escape => { state
state.is_focused = None; .cursor
state.is_dragging = false; .select_range(cursor_pos, value.len());
state.is_pasting = None; } else {
state.cursor.select_right_by_words(value);
}
}
state.keyboard_modifiers = let mut editor =
keyboard::Modifiers::default(); Editor::new(value, &mut state.cursor);
editor.delete();
let message = (on_change)(editor.contents());
shell.publish(message);
}
keyboard::KeyCode::Left => {
if platform::is_jump_modifier_pressed(modifiers)
&& !is_secure
{
if modifiers.shift() {
state.cursor.select_left_by_words(value);
} else {
state.cursor.move_left_by_words(value);
}
} else if modifiers.shift() {
state.cursor.select_left(value)
} else {
state.cursor.move_left(value);
}
}
keyboard::KeyCode::Right => {
if platform::is_jump_modifier_pressed(modifiers)
&& !is_secure
{
if modifiers.shift() {
state.cursor.select_right_by_words(value);
} else {
state.cursor.move_right_by_words(value);
}
} else if modifiers.shift() {
state.cursor.select_right(value)
} else {
state.cursor.move_right(value);
}
}
keyboard::KeyCode::Home => {
if modifiers.shift() {
state
.cursor
.select_range(state.cursor.start(value), 0);
} else {
state.cursor.move_to(0);
}
}
keyboard::KeyCode::End => {
if modifiers.shift() {
state.cursor.select_range(
state.cursor.start(value),
value.len(),
);
} else {
state.cursor.move_to(value.len());
}
}
keyboard::KeyCode::C
if state.keyboard_modifiers.command() =>
{
if let Some((start, end)) =
state.cursor.selection(value)
{
clipboard.write(
value.select(start, end).to_string(),
);
}
}
keyboard::KeyCode::X
if state.keyboard_modifiers.command() =>
{
if let Some((start, end)) =
state.cursor.selection(value)
{
clipboard.write(
value.select(start, end).to_string(),
);
}
let mut editor =
Editor::new(value, &mut state.cursor);
editor.delete();
let message = (on_change)(editor.contents());
shell.publish(message);
}
keyboard::KeyCode::V => {
if state.keyboard_modifiers.command() {
let content = match state.is_pasting.take() {
Some(content) => content,
None => {
let content: String = clipboard
.read()
.unwrap_or_default()
.chars()
.filter(|c| !c.is_control())
.collect();
Value::new(&content)
}
};
let mut editor =
Editor::new(value, &mut state.cursor);
editor.paste(content.clone());
let message = if let Some(paste) = &on_paste {
(paste)(editor.contents())
} else {
(on_change)(editor.contents())
};
shell.publish(message);
state.is_pasting = Some(content);
} else {
state.is_pasting = None;
}
}
keyboard::KeyCode::A
if state.keyboard_modifiers.command() =>
{
state.cursor.select_all(value);
}
keyboard::KeyCode::Escape => {
state.is_focused = None;
state.is_dragging = false;
state.is_pasting = None;
state.keyboard_modifiers =
keyboard::Modifiers::default();
}
keyboard::KeyCode::Tab
| keyboard::KeyCode::Up
| keyboard::KeyCode::Down => {
return event::Status::Ignored;
}
_ => {}
} }
keyboard::KeyCode::Tab
| keyboard::KeyCode::Up
| keyboard::KeyCode::Down => {
return event::Status::Ignored;
}
_ => {}
} }
return event::Status::Captured; return event::Status::Captured;
@ -900,6 +920,7 @@ pub fn draw<Renderer>(
is_secure: bool, is_secure: bool,
icon: Option<&Icon<Renderer::Font>>, icon: Option<&Icon<Renderer::Font>>,
style: &<Renderer::Theme as StyleSheet>::Style, style: &<Renderer::Theme as StyleSheet>::Style,
is_disabled: bool,
) where ) where
Renderer: text::Renderer, Renderer: text::Renderer,
Renderer::Theme: StyleSheet, Renderer::Theme: StyleSheet,
@ -914,7 +935,9 @@ pub fn draw<Renderer>(
let is_mouse_over = bounds.contains(cursor_position); let is_mouse_over = bounds.contains(cursor_position);
let appearance = if state.is_focused() { let appearance = if is_disabled {
theme.disabled(style)
} else if state.is_focused() {
theme.focused(style) theme.focused(style)
} else if is_mouse_over { } else if is_mouse_over {
theme.hovered(style) theme.hovered(style)
@ -968,7 +991,7 @@ pub fn draw<Renderer>(
% 2 % 2
== 0; == 0;
let cursor = if is_cursor_visible { let cursor = if is_cursor_visible && !is_disabled {
Some(( Some((
renderer::Quad { renderer::Quad {
bounds: Rectangle { bounds: Rectangle {
@ -1057,6 +1080,8 @@ pub fn draw<Renderer>(
content: if text.is_empty() { placeholder } else { &text }, content: if text.is_empty() { placeholder } else { &text },
color: if text.is_empty() { color: if text.is_empty() {
theme.placeholder_color(style) theme.placeholder_color(style)
} else if is_disabled {
theme.disabled_color(style)
} else { } else {
theme.value_color(style) theme.value_color(style)
}, },

View file

@ -33,6 +33,9 @@ pub trait StyleSheet {
/// Produces the [`Color`] of the value of a text input. /// Produces the [`Color`] of the value of a text input.
fn value_color(&self, style: &Self::Style) -> Color; fn value_color(&self, style: &Self::Style) -> Color;
/// Produces the [`Color`] of the value of a disabled text input.
fn disabled_color(&self, style: &Self::Style) -> Color;
/// Produces the [`Color`] of the selection of a text input. /// Produces the [`Color`] of the selection of a text input.
fn selection_color(&self, style: &Self::Style) -> Color; fn selection_color(&self, style: &Self::Style) -> Color;
@ -40,4 +43,7 @@ pub trait StyleSheet {
fn hovered(&self, style: &Self::Style) -> Appearance { fn hovered(&self, style: &Self::Style) -> Appearance {
self.focused(style) self.focused(style)
} }
/// Produces the style of a disabled text input.
fn disabled(&self, style: &Self::Style) -> Appearance;
} }

View file

@ -1093,4 +1093,30 @@ impl text_input::StyleSheet for Theme {
palette.primary.weak.color palette.primary.weak.color
} }
fn disabled(&self, style: &Self::Style) -> text_input::Appearance {
if let TextInput::Custom(custom) = style {
return custom.disabled(self);
}
let palette = self.extended_palette();
text_input::Appearance {
background: palette.background.base.color.into(),
border_radius: 2.0,
border_width: 1.0,
border_color: palette.background.weak.color,
icon_color: palette.background.weak.color,
}
}
fn disabled_color(&self, style: &Self::Style) -> Color {
if let TextInput::Custom(custom) = style {
return custom.value_color(self);
}
let palette = self.extended_palette();
palette.secondary.strong.color
}
} }