Introduce disabled state for TextInput
This commit is contained in:
parent
ca828f03f5
commit
f10e936f00
17 changed files with 410 additions and 254 deletions
|
|
@ -141,8 +141,8 @@ mod numeric_input {
|
|||
.map(u32::to_string)
|
||||
.as_deref()
|
||||
.unwrap_or(""),
|
||||
Event::InputChanged,
|
||||
)
|
||||
.on_change(Event::InputChanged)
|
||||
.padding(10),
|
||||
button("+", Event::IncrementPressed),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -100,11 +100,10 @@ impl Program for Controls {
|
|||
.size(14)
|
||||
.style(Color::WHITE),
|
||||
)
|
||||
.push(text_input(
|
||||
"Placeholder",
|
||||
text,
|
||||
Message::TextChanged,
|
||||
)),
|
||||
.push(
|
||||
text_input("Placeholder", text)
|
||||
.on_change(Message::TextChanged),
|
||||
),
|
||||
),
|
||||
)
|
||||
.into()
|
||||
|
|
|
|||
|
|
@ -214,12 +214,9 @@ impl Sandbox for App {
|
|||
column![
|
||||
scrollable(options).height(Length::Fill),
|
||||
row![
|
||||
text_input(
|
||||
"Add a new option",
|
||||
&self.input,
|
||||
Message::InputChanged,
|
||||
)
|
||||
.on_submit(Message::AddItem(self.input.clone())),
|
||||
text_input("Add a new option", &self.input)
|
||||
.on_change(Message::InputChanged)
|
||||
.on_submit(Message::AddItem(self.input.clone())),
|
||||
button(text(format!("Toggle Order ({})", self.order)))
|
||||
.on_press(Message::ToggleOrder)
|
||||
]
|
||||
|
|
|
|||
|
|
@ -133,18 +133,16 @@ impl Application for App {
|
|||
column![
|
||||
column![
|
||||
text("Email").size(12),
|
||||
text_input(
|
||||
"abc@123.com",
|
||||
&self.email,
|
||||
Message::Email
|
||||
)
|
||||
.on_submit(Message::Submit)
|
||||
.padding(5),
|
||||
text_input("abc@123.com", &self.email,)
|
||||
.on_change(Message::Email)
|
||||
.on_submit(Message::Submit)
|
||||
.padding(5),
|
||||
]
|
||||
.spacing(5),
|
||||
column![
|
||||
text("Password").size(12),
|
||||
text_input("", &self.password, Message::Password)
|
||||
text_input("", &self.password)
|
||||
.on_change(Message::Password)
|
||||
.on_submit(Message::Submit)
|
||||
.password()
|
||||
.padding(5),
|
||||
|
|
|
|||
|
|
@ -49,13 +49,11 @@ impl Sandbox for QRGenerator {
|
|||
.size(70)
|
||||
.style(Color::from([0.5, 0.5, 0.5]));
|
||||
|
||||
let input = text_input(
|
||||
"Type the data of your QR code here...",
|
||||
&self.data,
|
||||
Message::DataChanged,
|
||||
)
|
||||
.size(30)
|
||||
.padding(15);
|
||||
let input =
|
||||
text_input("Type the data of your QR code here...", &self.data)
|
||||
.on_change(Message::DataChanged)
|
||||
.size(30)
|
||||
.padding(15);
|
||||
|
||||
let mut content = column![title, input]
|
||||
.width(700)
|
||||
|
|
|
|||
|
|
@ -90,13 +90,10 @@ impl Sandbox for Styling {
|
|||
},
|
||||
);
|
||||
|
||||
let text_input = text_input(
|
||||
"Type something...",
|
||||
&self.input_value,
|
||||
Message::InputChanged,
|
||||
)
|
||||
.padding(10)
|
||||
.size(20);
|
||||
let text_input = text_input("Type something...", &self.input_value)
|
||||
.on_change(Message::InputChanged)
|
||||
.padding(10)
|
||||
.size(20);
|
||||
|
||||
let button = button("Submit")
|
||||
.padding(10)
|
||||
|
|
|
|||
11
examples/text_input/Cargo.toml
Normal file
11
examples/text_input/Cargo.toml
Normal 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"] }
|
||||
15
examples/text_input/README.md
Normal file
15
examples/text_input/README.md
Normal 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
|
||||
```
|
||||
94
examples/text_input/src/main.rs
Normal file
94
examples/text_input/src/main.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
|
|
@ -119,13 +119,15 @@ impl Application for App {
|
|||
column![
|
||||
subtitle(
|
||||
"Title",
|
||||
text_input("", &self.editing.title, Message::Title)
|
||||
text_input("", &self.editing.title)
|
||||
.on_change(Message::Title)
|
||||
.on_submit(Message::Add)
|
||||
.into()
|
||||
),
|
||||
subtitle(
|
||||
"Message",
|
||||
text_input("", &self.editing.body, Message::Body)
|
||||
text_input("", &self.editing.body)
|
||||
.on_change(Message::Body)
|
||||
.on_submit(Message::Add)
|
||||
.into()
|
||||
),
|
||||
|
|
|
|||
|
|
@ -204,15 +204,12 @@ impl Application for Todos {
|
|||
.style(Color::from([0.5, 0.5, 0.5]))
|
||||
.horizontal_alignment(alignment::Horizontal::Center);
|
||||
|
||||
let input = text_input(
|
||||
"What needs to be done?",
|
||||
input_value,
|
||||
Message::InputChanged,
|
||||
)
|
||||
.id(INPUT_ID.clone())
|
||||
.padding(15)
|
||||
.size(30)
|
||||
.on_submit(Message::CreateTask);
|
||||
let input = text_input("What needs to be done?", input_value)
|
||||
.on_change(Message::InputChanged)
|
||||
.id(INPUT_ID.clone())
|
||||
.padding(15)
|
||||
.size(30)
|
||||
.on_submit(Message::CreateTask);
|
||||
|
||||
let controls = view_controls(tasks, *filter);
|
||||
let filtered_tasks =
|
||||
|
|
@ -375,14 +372,12 @@ impl Task {
|
|||
.into()
|
||||
}
|
||||
TaskState::Editing => {
|
||||
let text_input = text_input(
|
||||
"Describe your task...",
|
||||
&self.description,
|
||||
TaskMessage::DescriptionEdited,
|
||||
)
|
||||
.id(Self::text_input_id(i))
|
||||
.on_submit(TaskMessage::FinishEdition)
|
||||
.padding(10);
|
||||
let text_input =
|
||||
text_input("Describe your task...", &self.description)
|
||||
.id(Self::text_input_id(i))
|
||||
.on_change(TaskMessage::DescriptionEdited)
|
||||
.on_submit(TaskMessage::FinishEdition)
|
||||
.padding(10);
|
||||
|
||||
row![
|
||||
text_input,
|
||||
|
|
|
|||
|
|
@ -570,13 +570,10 @@ impl<'a> Step {
|
|||
bytes: include_bytes!("../fonts/icons.ttf"),
|
||||
};
|
||||
|
||||
let mut text_input = text_input(
|
||||
"Type something to continue...",
|
||||
value,
|
||||
StepMessage::InputChanged,
|
||||
)
|
||||
.padding(10)
|
||||
.size(30);
|
||||
let mut text_input = text_input("Type something to continue...", value)
|
||||
.on_change(StepMessage::InputChanged)
|
||||
.padding(10)
|
||||
.size(30);
|
||||
|
||||
if is_showing_icon {
|
||||
text_input = text_input.icon(text_input::Icon {
|
||||
|
|
|
|||
|
|
@ -125,12 +125,9 @@ impl Application for WebSocket {
|
|||
};
|
||||
|
||||
let new_message_input = {
|
||||
let mut input = text_input(
|
||||
"Type a message...",
|
||||
&self.new_message,
|
||||
Message::NewMessageChanged,
|
||||
)
|
||||
.padding(10);
|
||||
let mut input = text_input("Type a message...", &self.new_message)
|
||||
.on_change(Message::NewMessageChanged)
|
||||
.padding(10);
|
||||
|
||||
let mut button = button(
|
||||
text("Send")
|
||||
|
|
|
|||
|
|
@ -171,14 +171,13 @@ where
|
|||
pub fn text_input<'a, Message, Renderer>(
|
||||
placeholder: &str,
|
||||
value: &str,
|
||||
on_change: impl Fn(String) -> Message + 'a,
|
||||
) -> widget::TextInput<'a, Message, Renderer>
|
||||
where
|
||||
Message: Clone,
|
||||
Renderer: crate::text::Renderer,
|
||||
Renderer::Theme: widget::text_input::StyleSheet,
|
||||
{
|
||||
widget::TextInput::new(placeholder, value, on_change)
|
||||
widget::TextInput::new(placeholder, value)
|
||||
}
|
||||
|
||||
/// Creates a new [`Slider`].
|
||||
|
|
|
|||
|
|
@ -46,8 +46,8 @@ pub use iced_style::text_input::{Appearance, StyleSheet};
|
|||
/// let input = TextInput::new(
|
||||
/// "This is the placeholder...",
|
||||
/// value,
|
||||
/// Message::TextInputChanged,
|
||||
/// )
|
||||
/// .on_change(|_| Message::TextInputChanged)
|
||||
/// .padding(10);
|
||||
/// ```
|
||||
/// 
|
||||
|
|
@ -65,7 +65,7 @@ where
|
|||
width: Length,
|
||||
padding: Padding,
|
||||
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_submit: Option<Message>,
|
||||
icon: Option<Icon<Renderer::Font>>,
|
||||
|
|
@ -82,12 +82,8 @@ where
|
|||
///
|
||||
/// It expects:
|
||||
/// - a placeholder,
|
||||
/// - the current value, and
|
||||
/// - a function that produces a message when the [`TextInput`] changes.
|
||||
pub fn new<F>(placeholder: &str, value: &str, on_change: F) -> Self
|
||||
where
|
||||
F: 'a + Fn(String) -> Message,
|
||||
{
|
||||
/// - the current value
|
||||
pub fn new(placeholder: &str, value: &str) -> Self {
|
||||
TextInput {
|
||||
id: None,
|
||||
placeholder: String::from(placeholder),
|
||||
|
|
@ -97,7 +93,7 @@ where
|
|||
width: Length::Fill,
|
||||
padding: Padding::new(5.0),
|
||||
size: None,
|
||||
on_change: Box::new(on_change),
|
||||
on_change: None,
|
||||
on_paste: None,
|
||||
on_submit: None,
|
||||
icon: None,
|
||||
|
|
@ -175,6 +171,16 @@ where
|
|||
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
|
||||
/// [`Value`] if provided.
|
||||
///
|
||||
|
|
@ -201,6 +207,7 @@ where
|
|||
self.is_secure,
|
||||
self.icon.as_ref(),
|
||||
&self.style,
|
||||
self.on_change.is_none(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -277,7 +284,7 @@ where
|
|||
self.size,
|
||||
&self.font,
|
||||
self.is_secure,
|
||||
self.on_change.as_ref(),
|
||||
self.on_change.as_deref(),
|
||||
self.on_paste.as_deref(),
|
||||
&self.on_submit,
|
||||
|| tree.state.downcast_mut::<State>(),
|
||||
|
|
@ -307,6 +314,7 @@ where
|
|||
self.is_secure,
|
||||
self.icon.as_ref(),
|
||||
&self.style,
|
||||
self.on_change.is_none(),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -492,7 +500,7 @@ pub fn update<'a, Message, Renderer>(
|
|||
size: Option<f32>,
|
||||
font: &Renderer::Font,
|
||||
is_secure: bool,
|
||||
on_change: &dyn Fn(String) -> Message,
|
||||
on_change: Option<&dyn Fn(String) -> Message>,
|
||||
on_paste: Option<&dyn Fn(String) -> Message>,
|
||||
on_submit: &Option<Message>,
|
||||
state: impl FnOnce() -> &'a mut State,
|
||||
|
|
@ -501,11 +509,14 @@ where
|
|||
Message: Clone,
|
||||
Renderer: text::Renderer,
|
||||
{
|
||||
let is_disabled = on_change.is_none();
|
||||
|
||||
match event {
|
||||
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
|
||||
| Event::Touch(touch::Event::FingerPressed { .. }) => {
|
||||
let state = state();
|
||||
let is_clicked = layout.bounds().contains(cursor_position);
|
||||
let is_clicked = is_clicked && !is_disabled;
|
||||
|
||||
state.is_focused = if is_clicked {
|
||||
state.is_focused.or_else(|| {
|
||||
|
|
@ -635,20 +646,22 @@ where
|
|||
let state = state();
|
||||
|
||||
if let Some(focus) = &mut state.is_focused {
|
||||
if state.is_pasting.is_none()
|
||||
&& !state.keyboard_modifiers.command()
|
||||
&& !c.is_control()
|
||||
{
|
||||
let mut editor = Editor::new(value, &mut state.cursor);
|
||||
if let Some(on_change) = on_change {
|
||||
if state.is_pasting.is_none()
|
||||
&& !state.keyboard_modifiers.command()
|
||||
&& !c.is_control()
|
||||
{
|
||||
let mut editor = Editor::new(value, &mut state.cursor);
|
||||
|
||||
editor.insert(c);
|
||||
editor.insert(c);
|
||||
|
||||
let message = (on_change)(editor.contents());
|
||||
shell.publish(message);
|
||||
let message = (on_change)(editor.contents());
|
||||
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();
|
||||
|
||||
if let Some(focus) = &mut state.is_focused {
|
||||
let modifiers = state.keyboard_modifiers;
|
||||
focus.updated_at = Instant::now();
|
||||
if let Some(on_change) = on_change {
|
||||
let modifiers = state.keyboard_modifiers;
|
||||
focus.updated_at = Instant::now();
|
||||
|
||||
match key_code {
|
||||
keyboard::KeyCode::Enter
|
||||
| keyboard::KeyCode::NumpadEnter => {
|
||||
if let Some(on_submit) = on_submit.clone() {
|
||||
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);
|
||||
match key_code {
|
||||
keyboard::KeyCode::Enter
|
||||
| keyboard::KeyCode::NumpadEnter => {
|
||||
if let Some(on_submit) = on_submit.clone() {
|
||||
shell.publish(on_submit);
|
||||
}
|
||||
}
|
||||
|
||||
let mut editor = Editor::new(value, &mut state.cursor);
|
||||
editor.backspace();
|
||||
|
||||
let message = (on_change)(editor.contents());
|
||||
shell.publish(message);
|
||||
}
|
||||
keyboard::KeyCode::Delete => {
|
||||
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(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)
|
||||
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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let mut editor =
|
||||
Editor::new(value, &mut state.cursor);
|
||||
editor.backspace();
|
||||
|
||||
editor.paste(content.clone());
|
||||
|
||||
let message = if let Some(paste) = &on_paste {
|
||||
(paste)(editor.contents())
|
||||
} else {
|
||||
(on_change)(editor.contents())
|
||||
};
|
||||
let message = (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;
|
||||
keyboard::KeyCode::Delete => {
|
||||
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(cursor_pos, value.len());
|
||||
} else {
|
||||
state.cursor.select_right_by_words(value);
|
||||
}
|
||||
}
|
||||
|
||||
state.keyboard_modifiers =
|
||||
keyboard::Modifiers::default();
|
||||
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 =
|
||||
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;
|
||||
|
|
@ -900,6 +920,7 @@ pub fn draw<Renderer>(
|
|||
is_secure: bool,
|
||||
icon: Option<&Icon<Renderer::Font>>,
|
||||
style: &<Renderer::Theme as StyleSheet>::Style,
|
||||
is_disabled: bool,
|
||||
) where
|
||||
Renderer: text::Renderer,
|
||||
Renderer::Theme: StyleSheet,
|
||||
|
|
@ -914,7 +935,9 @@ pub fn draw<Renderer>(
|
|||
|
||||
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)
|
||||
} else if is_mouse_over {
|
||||
theme.hovered(style)
|
||||
|
|
@ -968,7 +991,7 @@ pub fn draw<Renderer>(
|
|||
% 2
|
||||
== 0;
|
||||
|
||||
let cursor = if is_cursor_visible {
|
||||
let cursor = if is_cursor_visible && !is_disabled {
|
||||
Some((
|
||||
renderer::Quad {
|
||||
bounds: Rectangle {
|
||||
|
|
@ -1057,6 +1080,8 @@ pub fn draw<Renderer>(
|
|||
content: if text.is_empty() { placeholder } else { &text },
|
||||
color: if text.is_empty() {
|
||||
theme.placeholder_color(style)
|
||||
} else if is_disabled {
|
||||
theme.disabled_color(style)
|
||||
} else {
|
||||
theme.value_color(style)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -33,6 +33,9 @@ pub trait StyleSheet {
|
|||
/// Produces the [`Color`] of the value of a text input.
|
||||
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.
|
||||
fn selection_color(&self, style: &Self::Style) -> Color;
|
||||
|
||||
|
|
@ -40,4 +43,7 @@ pub trait StyleSheet {
|
|||
fn hovered(&self, style: &Self::Style) -> Appearance {
|
||||
self.focused(style)
|
||||
}
|
||||
|
||||
/// Produces the style of a disabled text input.
|
||||
fn disabled(&self, style: &Self::Style) -> Appearance;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1093,4 +1093,30 @@ impl text_input::StyleSheet for Theme {
|
|||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue