Skip to content

Commit

Permalink
Adding some comment on the number_input
Browse files Browse the repository at this point in the history
  • Loading branch information
Ultraxime committed Dec 3, 2024
1 parent 177c88c commit 794ce06
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 10 deletions.
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
rev: v5.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
Expand Down Expand Up @@ -36,7 +36,7 @@ repos:
- id: clippy

- repo: https://github.com/DevinR528/cargo-sort
rev: v1.0.9
rev: v1.1.0
hooks:
- id: cargo-sort
- repo: local
Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [0.10.0] - 2024-09-18

### Added
### Added
- Typed Input @Ultraxime

### Changes
Expand Down
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,4 +164,3 @@ Also included in this feature, are two widgets `sidebar::column::FlushColumn` an
### Color palette

This crate adds a predefined color palette based on the [CSS color palette](https://www.w3schools.com/cssref/css_colors.asp).

65 changes: 59 additions & 6 deletions src/widget/number_input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,8 @@ where
shell.publish((self.on_change)(self.value.clone()));
}

/// Returns the lower value possible
/// if the bound is excluded the bound is increased by the step
fn min(&self) -> T {
match &self.min {
Bound::Included(n) => n.clone(),
Expand All @@ -300,6 +302,8 @@ where
}
}

/// Returns the higher value possible
/// if the bound is excluded the bound is decreased by the step
fn max(&self) -> T {
match &self.max {
Bound::Included(n) => n.clone(),
Expand All @@ -308,6 +312,7 @@ where
}
}

/// Checks if the value is within the bounds
fn valid(&self, value: &T) -> bool {
(match &self.min {
Bound::Included(n) if *n > *value => false,
Expand All @@ -320,14 +325,18 @@ where
})
}

/// Checks if the value can be increased by the step
fn can_increase(&self) -> bool {
self.valid(&(self.value.clone() + self.step.clone())) || self.value < self.max()
}

/// Checks if the value can be decreased by the step
fn can_decrease(&self) -> bool {
self.valid(&(self.value.clone() - self.step.clone())) || self.value > self.min()
}

/// Checks if the [`NumberInput`] is disabled
/// Meaning that the bounds are too tight for the value to change
fn disabled(&self) -> bool {
match (&self.min, &self.max) {
(Bound::Included(n) | Bound::Excluded(n), Bound::Included(m) | Bound::Excluded(m)) => {
Expand Down Expand Up @@ -360,7 +369,7 @@ where
self
}

/// Sets the [`Id`] of the underlying [`TextInput`].
/// Sets the [`Id`](text_input::Id) of the underlying [`TextInput`](iced::widget::TextInput).
#[must_use]
pub fn id(mut self, id: impl Into<text_input::Id>) -> Self {
self.content = self.content.id(id.into());
Expand Down Expand Up @@ -536,9 +545,11 @@ where
.state
.downcast_mut::<text_input::State<Renderer::Paragraph>>();

// We use a secondary shell to select handle the event of the underlying [`TypedInput`]
let mut messages = Vec::new();
let mut sub_shell = Shell::new(&mut messages);

// Function to forward the event to the underlying [`TypedInput`]
let mut forward_to_text = |widget: &mut Self, child, clipboard| {
widget.content.on_event(
child,
Expand All @@ -552,6 +563,7 @@ where
)
};

// Check if the value that would result from the input is valid and within bound
let mut check_value = |value: &str| {
if let Ok(value) = T::from_str(value) {
self.valid(&value)
Expand Down Expand Up @@ -581,12 +593,20 @@ where
let cursor = text_input.cursor();

match key.as_ref() {
// Enter
keyboard::Key::Named(keyboard::key::Named::Enter) => {
forward_to_text(self, child, clipboard)
}
// Copy and selecting all
keyboard::Key::Character("c" | "a") if modifiers.command() => {
forward_to_text(self, child, clipboard)
}
// Cut
keyboard::Key::Character("x") if modifiers.command() => {
// We need a selection to cut
if let Some((start, end)) = cursor.selection(&Value::new(&value)) {
let _ = value.drain(start..end);
// We check that once this part is cut, it's still a number
if check_value(&value) {
forward_to_text(self, child, clipboard)
} else {
Expand All @@ -596,111 +616,139 @@ where
return event::Status::Ignored;
}
}
// Paste
keyboard::Key::Character("v") if modifiers.command() => {
// We need something to paste
let Some(paste) =
clipboard.read(iced::advanced::clipboard::Kind::Standard)
else {
return event::Status::Ignored;
};
// We replace the selection or paste the text at the cursor
match cursor.state(&Value::new(&value)) {
cursor::State::Index(idx) => {
let () = value.insert_str(idx, &paste);
}
cursor::State::Selection { start, end } if end >= start => {
let () = value.replace_range(start..end, &paste);
}
// we need to invert the selection to be sure the end is after the start
cursor::State::Selection { start, end } => {
let () = value.replace_range(end..start, &paste);
}
}

// We check if it's now a valid number
if check_value(&value) {
forward_to_text(self, child, clipboard)
} else {
return event::Status::Ignored;
}
}
keyboard::Key::Named(keyboard::key::Named::Enter) => {
forward_to_text(self, child, clipboard)
}
// Backspace
keyboard::Key::Named(keyboard::key::Named::Backspace) => {
// We remove either the selection or the character before the cursor
match cursor.state(&Value::new(&value)) {
cursor::State::Selection { start, end } => {
cursor::State::Selection { start, end } if end >= start => {
let _ = value.drain(start..end);
}
// we need to invert the selection to be sure the end is after the start
cursor::State::Selection { start, end } => {
let _ = value.drain(end..start);
}
// We need the cursor not at the start
cursor::State::Index(idx) if idx > 0 => {
let _ = value.remove(idx - 1);
}
cursor::State::Index(_) => return event::Status::Ignored,
}

// We check if it's now a valid number
if check_value(&value) {
forward_to_text(self, child, clipboard)
} else {
return event::Status::Ignored;
}
}
// Delete
keyboard::Key::Named(keyboard::key::Named::Delete) => {
// We remove either the selection or the character after the cursor
match cursor.state(&Value::new(&value)) {
cursor::State::Selection { start, end } => {
cursor::State::Selection { start, end } if end >= start => {
let _ = value.drain(start..end);
}
// we need to invert the selection to be sure the end is after the start
cursor::State::Selection { start, end } => {
let _ = value.drain(end..start);
}
// We need the cursor not at the end
cursor::State::Index(idx) if idx < value.len() => {
let _ = value.remove(idx);
}
cursor::State::Index(_) => return event::Status::Ignored,
}

// We check if it's now a valid number
if check_value(&value) {
forward_to_text(self, child, clipboard)
} else {
return event::Status::Ignored;
}
}
// Arrow Down, decrease by step
keyboard::Key::Named(keyboard::key::Named::ArrowDown)
if can_decrease =>
{
self.decrease_value(shell);

event::Status::Captured
}
// Arrow Up, increase by step
keyboard::Key::Named(keyboard::key::Named::ArrowUp) if can_increase => {
self.increase_value(shell);

event::Status::Captured
}
// Mouvement of the cursor
keyboard::Key::Named(
keyboard::key::Named::ArrowLeft
| keyboard::key::Named::ArrowRight
| keyboard::key::Named::Home
| keyboard::key::Named::End,
) => forward_to_text(self, child, clipboard),
// Everything else
_ => match text {
// If we are trying to input text
Some(text) => {
// We replace the selection or insert the text at the cursor
match cursor.state(&Value::new(&value)) {
cursor::State::Index(idx) => {
let () = value.insert_str(idx, text);
}
cursor::State::Selection { start, end } if end >= start => {
let () = value.replace_range(start..end, text);
}
// we need to invert the selection to be sure the end is after the start
cursor::State::Selection { start, end } => {
let () = value.replace_range(end..start, text);
}
}

// We check if it's now a valid number
if check_value(&value) {
forward_to_text(self, child, clipboard)
} else {
return event::Status::Ignored;
}
}
// If we are not trying to input text
None => return event::Status::Ignored,
},
}
}
}
}
// Mouse scroll event
Event::Mouse(mouse::Event::WheelScrolled { delta })
if mouse_over_widget && !self.ignore_scroll_events =>
{
Expand All @@ -715,6 +763,7 @@ where
}
event::Status::Captured
}
// Clicking on the buttons up or down
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
if mouse_over_button && !self.ignore_buttons =>
{
Expand All @@ -727,6 +776,7 @@ where
}
event::Status::Captured
}
// Releasing the buttons
Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
if mouse_over_button =>
{
Expand All @@ -737,8 +787,11 @@ where
}
event::Status::Captured
}
// Any other event are just forwarded
_ => forward_to_text(self, child, clipboard),
};

// We forward the shell of the [`TypedInput`] to the application
if let Some(redraw) = sub_shell.redraw_request() {
shell.request_redraw(redraw);
}
Expand Down

0 comments on commit 794ce06

Please sign in to comment.