From 346af3f8b0baa418fd37b878bc2930ff0bd57cc0 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 11 Sep 2023 02:47:24 +0200 Subject: Make `FontSystem` global and simplify `Paragraph` API --- widget/src/pick_list.rs | 22 ++++++++-------------- widget/src/text_input.rs | 17 +++++++---------- 2 files changed, 15 insertions(+), 24 deletions(-) (limited to 'widget/src') diff --git a/widget/src/pick_list.rs b/widget/src/pick_list.rs index 056a5e65..4b89d6ff 100644 --- a/widget/src/pick_list.rs +++ b/widget/src/pick_list.rs @@ -415,23 +415,17 @@ where for (option, paragraph) in options.iter().zip(state.options.iter_mut()) { let label = option.to_string(); - renderer.update_paragraph( - paragraph, - Text { - content: &label, - ..option_text - }, - ); + paragraph.update(Text { + content: &label, + ..option_text + }); } if let Some(placeholder) = placeholder { - renderer.update_paragraph( - &mut state.placeholder, - Text { - content: placeholder, - ..option_text - }, - ); + state.placeholder.update(Text { + content: placeholder, + ..option_text + }); } let max_width = match width { diff --git a/widget/src/text_input.rs b/widget/src/text_input.rs index 7d5ae806..f9a2d419 100644 --- a/widget/src/text_input.rs +++ b/widget/src/text_input.rs @@ -523,18 +523,15 @@ where shaping: text::Shaping::Advanced, }; - renderer.update_paragraph(&mut state.placeholder, placeholder_text); + state.placeholder.update(placeholder_text); let secure_value = is_secure.then(|| value.secure()); let value = secure_value.as_ref().unwrap_or(value); - renderer.update_paragraph( - &mut state.value, - Text { - content: &value.to_string(), - ..placeholder_text - }, - ); + state.value.update(Text { + content: &value.to_string(), + ..placeholder_text + }); if let Some(icon) = icon { let icon_text = Text { @@ -548,7 +545,7 @@ where shaping: text::Shaping::Advanced, }; - renderer.update_paragraph(&mut state.icon, icon_text); + state.icon.update(icon_text); let icon_width = state.icon.min_width(); @@ -1461,7 +1458,7 @@ fn replace_paragraph( let mut children_layout = layout.children(); let text_bounds = children_layout.next().unwrap().bounds(); - state.value = renderer.create_paragraph(Text { + state.value = Renderer::Paragraph::with_text(Text { font, line_height, content: &value.to_string(), -- cgit From 6448429103c9c82b90040ac5a5a097bdded23f82 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 12 Sep 2023 14:51:00 +0200 Subject: Draft `Editor` API and `TextEditor` widget --- widget/src/helpers.rs | 15 ++ widget/src/lib.rs | 5 +- widget/src/text_editor.rs | 457 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 475 insertions(+), 2 deletions(-) create mode 100644 widget/src/text_editor.rs (limited to 'widget/src') diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index 3c9c2b29..61541eac 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -16,6 +16,7 @@ use crate::runtime::Command; use crate::scrollable::{self, Scrollable}; use crate::slider::{self, Slider}; use crate::text::{self, Text}; +use crate::text_editor::{self, TextEditor}; use crate::text_input::{self, TextInput}; use crate::toggler::{self, Toggler}; use crate::tooltip::{self, Tooltip}; @@ -206,6 +207,20 @@ where TextInput::new(placeholder, value) } +/// Creates a new [`TextEditor`]. +/// +/// [`TextEditor`]: crate::TextEditor +pub fn text_editor<'a, Message, Renderer>( + content: &'a text_editor::Content, +) -> TextEditor<'a, Message, Renderer> +where + Message: Clone, + Renderer: core::text::Renderer, + Renderer::Theme: text_editor::StyleSheet, +{ + TextEditor::new(content) +} + /// Creates a new [`Slider`]. /// /// [`Slider`]: crate::Slider diff --git a/widget/src/lib.rs b/widget/src/lib.rs index 7e204171..f8e5e865 100644 --- a/widget/src/lib.rs +++ b/widget/src/lib.rs @@ -4,8 +4,8 @@ )] #![forbid(unsafe_code, rust_2018_idioms)] #![deny( - missing_debug_implementations, - missing_docs, + // missing_debug_implementations, + // missing_docs, unused_results, clippy::extra_unused_lifetimes, clippy::from_over_into, @@ -41,6 +41,7 @@ pub mod scrollable; pub mod slider; pub mod space; pub mod text; +pub mod text_editor; pub mod text_input; pub mod toggler; pub mod tooltip; diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs new file mode 100644 index 00000000..d09f2c3e --- /dev/null +++ b/widget/src/text_editor.rs @@ -0,0 +1,457 @@ +use crate::core::event::{self, Event}; +use crate::core::keyboard; +use crate::core::layout::{self, Layout}; +use crate::core::mouse; +use crate::core::renderer; +use crate::core::text::editor::{Cursor, Editor as _}; +use crate::core::text::{self, LineHeight}; +use crate::core::widget::{self, Widget}; +use crate::core::{ + Clipboard, Color, Element, Length, Padding, Pixels, Point, Rectangle, + Shell, Vector, +}; + +use std::cell::RefCell; + +pub use crate::style::text_editor::{Appearance, StyleSheet}; +pub use text::editor::Action; + +pub struct TextEditor<'a, Message, Renderer = crate::Renderer> +where + Renderer: text::Renderer, + Renderer::Theme: StyleSheet, +{ + content: &'a Content, + font: Option, + text_size: Option, + line_height: LineHeight, + width: Length, + height: Length, + padding: Padding, + style: ::Style, + on_edit: Option Message + 'a>>, +} + +impl<'a, Message, Renderer> TextEditor<'a, Message, Renderer> +where + Renderer: text::Renderer, + Renderer::Theme: StyleSheet, +{ + pub fn new(content: &'a Content) -> Self { + Self { + content, + font: None, + text_size: None, + line_height: LineHeight::default(), + width: Length::Fill, + height: Length::Fill, + padding: Padding::new(5.0), + style: Default::default(), + on_edit: None, + } + } + + pub fn on_edit(mut self, on_edit: impl Fn(Action) -> Message + 'a) -> Self { + self.on_edit = Some(Box::new(on_edit)); + self + } + + pub fn font(mut self, font: impl Into) -> Self { + self.font = Some(font.into()); + self + } + + pub fn padding(mut self, padding: impl Into) -> Self { + self.padding = padding.into(); + self + } +} + +pub struct Content(RefCell>) +where + R: text::Renderer; + +struct Internal +where + R: text::Renderer, +{ + editor: R::Editor, + is_dirty: bool, +} + +impl Content +where + R: text::Renderer, +{ + pub fn new() -> Self { + Self::with("") + } + + pub fn with(text: &str) -> Self { + Self(RefCell::new(Internal { + editor: R::Editor::with_text(text), + is_dirty: true, + })) + } + + pub fn edit(&mut self, action: Action) { + let internal = self.0.get_mut(); + + internal.editor.perform(action); + internal.is_dirty = true; + } +} + +impl Default for Content +where + Renderer: text::Renderer, +{ + fn default() -> Self { + Self::new() + } +} + +struct State { + is_focused: bool, + is_dragging: bool, + last_click: Option, +} + +impl<'a, Message, Renderer> Widget + for TextEditor<'a, Message, Renderer> +where + Renderer: text::Renderer, + Renderer::Theme: StyleSheet, +{ + fn tag(&self) -> widget::tree::Tag { + widget::tree::Tag::of::() + } + + fn state(&self) -> widget::tree::State { + widget::tree::State::new(State { + is_focused: false, + is_dragging: false, + last_click: None, + }) + } + + fn width(&self) -> Length { + self.width + } + + fn height(&self) -> Length { + self.height + } + + fn layout( + &self, + _tree: &mut widget::Tree, + renderer: &Renderer, + limits: &layout::Limits, + ) -> iced_renderer::core::layout::Node { + let mut internal = self.content.0.borrow_mut(); + + internal.editor.update( + limits.pad(self.padding).max(), + self.font.unwrap_or_else(|| renderer.default_font()), + self.text_size.unwrap_or_else(|| renderer.default_size()), + self.line_height, + ); + + layout::Node::new(limits.max()) + } + + fn on_event( + &mut self, + tree: &mut widget::Tree, + event: Event, + layout: Layout<'_>, + cursor: mouse::Cursor, + _renderer: &Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + _viewport: &Rectangle, + ) -> event::Status { + let Some(on_edit) = self.on_edit.as_ref() else { + return event::Status::Ignored; + }; + + let state = tree.state.downcast_mut::(); + + let Some(update) = Update::from_event( + event, + state, + layout.bounds(), + self.padding, + cursor, + ) else { + return event::Status::Ignored; + }; + + match update { + Update::Focus { click, action } => { + state.is_focused = true; + state.last_click = Some(click); + shell.publish(on_edit(action)); + } + Update::Unfocus => { + state.is_focused = false; + state.is_dragging = false; + } + Update::Click { click, action } => { + state.last_click = Some(click); + state.is_dragging = true; + shell.publish(on_edit(action)); + } + Update::StopDragging => { + state.is_dragging = false; + } + Update::Edit(action) => { + shell.publish(on_edit(action)); + } + Update::Copy => {} + Update::Paste => if let Some(_contents) = clipboard.read() {}, + } + + event::Status::Captured + } + + fn draw( + &self, + tree: &widget::Tree, + renderer: &mut Renderer, + theme: &::Theme, + style: &renderer::Style, + layout: Layout<'_>, + cursor: mouse::Cursor, + _viewport: &Rectangle, + ) { + let bounds = layout.bounds(); + + let internal = self.content.0.borrow(); + let state = tree.state.downcast_ref::(); + + let is_disabled = self.on_edit.is_none(); + let is_mouse_over = cursor.is_over(bounds); + + let appearance = if is_disabled { + theme.disabled(&self.style) + } else if state.is_focused { + theme.focused(&self.style) + } else if is_mouse_over { + theme.hovered(&self.style) + } else { + theme.active(&self.style) + }; + + renderer.fill_quad( + renderer::Quad { + bounds, + border_radius: appearance.border_radius, + border_width: appearance.border_width, + border_color: appearance.border_color, + }, + appearance.background, + ); + + renderer.fill_editor( + &internal.editor, + bounds.position() + + Vector::new(self.padding.left, self.padding.top), + style.text_color, + ); + + if state.is_focused { + match internal.editor.cursor() { + Cursor::Caret(position) => { + renderer.fill_quad( + renderer::Quad { + bounds: Rectangle { + x: position.x + bounds.x + self.padding.left, + y: position.y + bounds.y + self.padding.top, + width: 1.0, + height: self + .line_height + .to_absolute(self.text_size.unwrap_or_else( + || renderer.default_size(), + )) + .into(), + }, + border_radius: 0.0.into(), + border_width: 0.0, + border_color: Color::TRANSPARENT, + }, + theme.value_color(&self.style), + ); + } + Cursor::Selection(ranges) => { + for range in ranges { + renderer.fill_quad( + renderer::Quad { + bounds: range + Vector::new(bounds.x, bounds.y), + border_radius: 0.0.into(), + border_width: 0.0, + border_color: Color::TRANSPARENT, + }, + theme.selection_color(&self.style), + ); + } + } + } + } + } + + fn mouse_interaction( + &self, + _state: &widget::Tree, + layout: Layout<'_>, + cursor: mouse::Cursor, + _viewport: &Rectangle, + _renderer: &Renderer, + ) -> mouse::Interaction { + let is_disabled = self.on_edit.is_none(); + + if cursor.is_over(layout.bounds()) { + if is_disabled { + mouse::Interaction::NotAllowed + } else { + mouse::Interaction::Text + } + } else { + mouse::Interaction::default() + } + } +} + +impl<'a, Message, Renderer> From> + for Element<'a, Message, Renderer> +where + Message: 'a, + Renderer: text::Renderer, + Renderer::Theme: StyleSheet, +{ + fn from(text_editor: TextEditor<'a, Message, Renderer>) -> Self { + Self::new(text_editor) + } +} + +enum Update { + Focus { click: mouse::Click, action: Action }, + Unfocus, + Click { click: mouse::Click, action: Action }, + StopDragging, + Edit(Action), + Copy, + Paste, +} + +impl Update { + fn from_event( + event: Event, + state: &State, + bounds: Rectangle, + padding: Padding, + cursor: mouse::Cursor, + ) -> Option { + match event { + Event::Mouse(event) => match event { + mouse::Event::ButtonPressed(mouse::Button::Left) => { + if let Some(cursor_position) = cursor.position_in(bounds) { + let cursor_position = cursor_position + - Vector::new(padding.top, padding.left); + + if state.is_focused { + let click = mouse::Click::new( + cursor_position, + state.last_click, + ); + + let action = match click.kind() { + mouse::click::Kind::Single => { + Action::Click(cursor_position) + } + mouse::click::Kind::Double => { + Action::SelectWord + } + mouse::click::Kind::Triple => { + Action::SelectLine + } + }; + + Some(Update::Click { click, action }) + } else { + Some(Update::Focus { + click: mouse::Click::new(cursor_position, None), + action: Action::Click(cursor_position), + }) + } + } else if state.is_focused { + Some(Update::Unfocus) + } else { + None + } + } + mouse::Event::ButtonReleased(mouse::Button::Left) => { + Some(Update::StopDragging) + } + mouse::Event::CursorMoved { .. } if state.is_dragging => { + let cursor_position = cursor.position_in(bounds)? + - Vector::new(padding.top, padding.left); + + Some(Self::Edit(Action::Drag(cursor_position))) + } + _ => None, + }, + Event::Keyboard(event) => match event { + keyboard::Event::KeyPressed { + key_code, + modifiers, + } if state.is_focused => match key_code { + keyboard::KeyCode::Left => { + if platform::is_jump_modifier_pressed(modifiers) { + Some(Self::Edit(Action::MoveLeftWord)) + } else { + Some(Self::Edit(Action::MoveLeft)) + } + } + keyboard::KeyCode::Right => { + if platform::is_jump_modifier_pressed(modifiers) { + Some(Self::Edit(Action::MoveRightWord)) + } else { + Some(Self::Edit(Action::MoveRight)) + } + } + keyboard::KeyCode::Up => Some(Self::Edit(Action::MoveUp)), + keyboard::KeyCode::Down => { + Some(Self::Edit(Action::MoveDown)) + } + keyboard::KeyCode::Backspace => { + Some(Self::Edit(Action::Backspace)) + } + keyboard::KeyCode::Delete => { + Some(Self::Edit(Action::Delete)) + } + keyboard::KeyCode::Escape => Some(Self::Unfocus), + _ => None, + }, + keyboard::Event::CharacterReceived(c) if state.is_focused => { + Some(Self::Edit(Action::Insert(c))) + } + _ => None, + }, + _ => None, + } + } +} + +mod platform { + use crate::core::keyboard; + + pub fn is_jump_modifier_pressed(modifiers: keyboard::Modifiers) -> bool { + if cfg!(target_os = "macos") { + modifiers.alt() + } else { + modifiers.control() + } + } +} -- cgit From 1455911b636f19810e12eeb12a6eed11c5244cfe Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 12 Sep 2023 15:03:23 +0200 Subject: Add `Enter` variant to `Action` in `text::Editor` --- widget/src/text_editor.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'widget/src') diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index d09f2c3e..fcbd3dad 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -425,6 +425,7 @@ impl Update { keyboard::KeyCode::Down => { Some(Self::Edit(Action::MoveDown)) } + keyboard::KeyCode::Enter => Some(Self::Edit(Action::Enter)), keyboard::KeyCode::Backspace => { Some(Self::Edit(Action::Backspace)) } -- cgit From 40eb648f1e1e2ceb2782eddacbbc966f44de6961 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 13 Sep 2023 15:00:33 +0200 Subject: Implement `Cursor::Selection` calculation in `Editor::cursor` --- widget/src/text_editor.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'widget/src') diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index fcbd3dad..12e66f68 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -288,7 +288,11 @@ where for range in ranges { renderer.fill_quad( renderer::Quad { - bounds: range + Vector::new(bounds.x, bounds.y), + bounds: range + + Vector::new( + bounds.x + self.padding.left, + bounds.y + self.padding.top, + ), border_radius: 0.0.into(), border_width: 0.0, border_color: Color::TRANSPARENT, -- cgit From d502c9f16fc78bf6b5253152751480c5b5e5999c Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 13 Sep 2023 15:16:47 +0200 Subject: Unify `Focus` and `Click` updates in `widget::text_editor` --- widget/src/text_editor.rs | 48 ++++++++++++++++------------------------------- 1 file changed, 16 insertions(+), 32 deletions(-) (limited to 'widget/src') diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index 12e66f68..a8069069 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -189,16 +189,12 @@ where }; match update { - Update::Focus { click, action } => { - state.is_focused = true; - state.last_click = Some(click); - shell.publish(on_edit(action)); - } Update::Unfocus => { state.is_focused = false; state.is_dragging = false; } Update::Click { click, action } => { + state.is_focused = true; state.last_click = Some(click); state.is_dragging = true; shell.publish(on_edit(action)); @@ -340,9 +336,8 @@ where } enum Update { - Focus { click: mouse::Click, action: Action }, - Unfocus, Click { click: mouse::Click, action: Action }, + Unfocus, StopDragging, Edit(Action), Copy, @@ -364,31 +359,20 @@ impl Update { let cursor_position = cursor_position - Vector::new(padding.top, padding.left); - if state.is_focused { - let click = mouse::Click::new( - cursor_position, - state.last_click, - ); - - let action = match click.kind() { - mouse::click::Kind::Single => { - Action::Click(cursor_position) - } - mouse::click::Kind::Double => { - Action::SelectWord - } - mouse::click::Kind::Triple => { - Action::SelectLine - } - }; - - Some(Update::Click { click, action }) - } else { - Some(Update::Focus { - click: mouse::Click::new(cursor_position, None), - action: Action::Click(cursor_position), - }) - } + let click = mouse::Click::new( + cursor_position, + state.last_click, + ); + + let action = match click.kind() { + mouse::click::Kind::Single => { + Action::Click(cursor_position) + } + mouse::click::Kind::Double => Action::SelectWord, + mouse::click::Kind::Triple => Action::SelectLine, + }; + + Some(Update::Click { click, action }) } else if state.is_focused { Some(Update::Unfocus) } else { -- cgit From f4c51a96d50953d5fb6e9eb62194f226e2cbfd3c Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 13 Sep 2023 16:11:43 +0200 Subject: Introduce `Motion` concept in `core::text::editor` --- widget/src/text_editor.rs | 77 +++++++++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 33 deletions(-) (limited to 'widget/src') diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index a8069069..38c243bd 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -14,7 +14,7 @@ use crate::core::{ use std::cell::RefCell; pub use crate::style::text_editor::{Appearance, StyleSheet}; -pub use text::editor::Action; +pub use text::editor::{Action, Motion}; pub struct TextEditor<'a, Message, Renderer = crate::Renderer> where @@ -189,16 +189,16 @@ where }; match update { - Update::Unfocus => { - state.is_focused = false; - state.is_dragging = false; - } Update::Click { click, action } => { state.is_focused = true; - state.last_click = Some(click); state.is_dragging = true; + state.last_click = Some(click); shell.publish(on_edit(action)); } + Update::Unfocus => { + state.is_focused = false; + state.is_dragging = false; + } Update::StopDragging => { state.is_dragging = false; } @@ -352,6 +352,9 @@ impl Update { padding: Padding, cursor: mouse::Cursor, ) -> Option { + let edit = |action| Some(Update::Edit(action)); + let move_ = |motion| Some(Update::Edit(Action::Move(motion))); + match event { Event::Mouse(event) => match event { mouse::Event::ButtonPressed(mouse::Button::Left) => { @@ -386,7 +389,7 @@ impl Update { let cursor_position = cursor.position_in(bounds)? - Vector::new(padding.top, padding.left); - Some(Self::Edit(Action::Drag(cursor_position))) + edit(Action::Drag(cursor_position)) } _ => None, }, @@ -394,37 +397,31 @@ impl Update { keyboard::Event::KeyPressed { key_code, modifiers, - } if state.is_focused => match key_code { - keyboard::KeyCode::Left => { - if platform::is_jump_modifier_pressed(modifiers) { - Some(Self::Edit(Action::MoveLeftWord)) + } if state.is_focused => { + if let Some(motion) = motion(key_code) { + let motion = if modifiers.control() { + motion.widen() } else { - Some(Self::Edit(Action::MoveLeft)) - } - } - keyboard::KeyCode::Right => { - if platform::is_jump_modifier_pressed(modifiers) { - Some(Self::Edit(Action::MoveRightWord)) + motion + }; + + return edit(if modifiers.shift() { + Action::Select(motion) } else { - Some(Self::Edit(Action::MoveRight)) - } - } - keyboard::KeyCode::Up => Some(Self::Edit(Action::MoveUp)), - keyboard::KeyCode::Down => { - Some(Self::Edit(Action::MoveDown)) - } - keyboard::KeyCode::Enter => Some(Self::Edit(Action::Enter)), - keyboard::KeyCode::Backspace => { - Some(Self::Edit(Action::Backspace)) + Action::Move(motion) + }); } - keyboard::KeyCode::Delete => { - Some(Self::Edit(Action::Delete)) + + match key_code { + keyboard::KeyCode::Enter => edit(Action::Enter), + keyboard::KeyCode::Backspace => edit(Action::Backspace), + keyboard::KeyCode::Delete => edit(Action::Delete), + keyboard::KeyCode::Escape => Some(Self::Unfocus), + _ => None, } - keyboard::KeyCode::Escape => Some(Self::Unfocus), - _ => None, - }, + } keyboard::Event::CharacterReceived(c) if state.is_focused => { - Some(Self::Edit(Action::Insert(c))) + edit(Action::Insert(c)) } _ => None, }, @@ -433,6 +430,20 @@ impl Update { } } +fn motion(key_code: keyboard::KeyCode) -> Option { + match key_code { + keyboard::KeyCode::Left => Some(Motion::Left), + keyboard::KeyCode::Right => Some(Motion::Right), + keyboard::KeyCode::Up => Some(Motion::Up), + keyboard::KeyCode::Down => Some(Motion::Down), + keyboard::KeyCode::Home => Some(Motion::Home), + keyboard::KeyCode::End => Some(Motion::End), + keyboard::KeyCode::PageUp => Some(Motion::PageUp), + keyboard::KeyCode::PageDown => Some(Motion::PageDown), + _ => None, + } +} + mod platform { use crate::core::keyboard; -- cgit From f14ef7a6069cf45ae11261d7d20df6a5d7870dde Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 13 Sep 2023 16:31:56 +0200 Subject: Fix `clippy` lints --- widget/src/helpers.rs | 6 +++--- widget/src/text_editor.rs | 30 ++++++++++++++++++++---------- 2 files changed, 23 insertions(+), 13 deletions(-) (limited to 'widget/src') diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index 61541eac..e3f31513 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -210,9 +210,9 @@ where /// Creates a new [`TextEditor`]. /// /// [`TextEditor`]: crate::TextEditor -pub fn text_editor<'a, Message, Renderer>( - content: &'a text_editor::Content, -) -> TextEditor<'a, Message, Renderer> +pub fn text_editor( + content: &text_editor::Content, +) -> TextEditor<'_, Message, Renderer> where Message: Clone, Renderer: core::text::Renderer, diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index 38c243bd..48de6409 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -7,8 +7,8 @@ use crate::core::text::editor::{Cursor, Editor as _}; use crate::core::text::{self, LineHeight}; use crate::core::widget::{self, Widget}; use crate::core::{ - Clipboard, Color, Element, Length, Padding, Pixels, Point, Rectangle, - Shell, Vector, + Clipboard, Color, Element, Length, Padding, Pixels, Rectangle, Shell, + Vector, }; use std::cell::RefCell; @@ -205,8 +205,12 @@ where Update::Edit(action) => { shell.publish(on_edit(action)); } - Update::Copy => {} - Update::Paste => if let Some(_contents) = clipboard.read() {}, + Update::Copy => todo!(), + Update::Paste => { + if let Some(_contents) = clipboard.read() { + todo!() + } + } } event::Status::Captured @@ -353,7 +357,6 @@ impl Update { cursor: mouse::Cursor, ) -> Option { let edit = |action| Some(Update::Edit(action)); - let move_ = |motion| Some(Update::Edit(Action::Move(motion))); match event { Event::Mouse(event) => match event { @@ -399,11 +402,12 @@ impl Update { modifiers, } if state.is_focused => { if let Some(motion) = motion(key_code) { - let motion = if modifiers.control() { - motion.widen() - } else { - motion - }; + let motion = + if platform::is_jump_modifier_pressed(modifiers) { + motion.widen() + } else { + motion + }; return edit(if modifiers.shift() { Action::Select(motion) @@ -417,6 +421,12 @@ impl Update { keyboard::KeyCode::Backspace => edit(Action::Backspace), keyboard::KeyCode::Delete => edit(Action::Delete), keyboard::KeyCode::Escape => Some(Self::Unfocus), + keyboard::KeyCode::C => Some(Self::Copy), + keyboard::KeyCode::V + if modifiers.command() && !modifiers.alt() => + { + Some(Self::Paste) + } _ => None, } } -- cgit From 8e6e37e0cee79a2f293abedd18a6a7249575bb63 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 14 Sep 2023 19:05:50 +0200 Subject: Fix broken intra-doc links --- widget/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'widget/src') diff --git a/widget/src/lib.rs b/widget/src/lib.rs index f8e5e865..4c318d75 100644 --- a/widget/src/lib.rs +++ b/widget/src/lib.rs @@ -93,6 +93,8 @@ pub use space::Space; #[doc(no_inline)] pub use text::Text; #[doc(no_inline)] +pub use text_editor::TextEditor; +#[doc(no_inline)] pub use text_input::TextInput; #[doc(no_inline)] pub use toggler::Toggler; -- cgit From f7fc13d98c52a9260b1ab55394a0c3d2693318ed Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 14 Sep 2023 22:55:54 +0200 Subject: Fix `Copy` action being triggered without any modifiers --- widget/src/text_editor.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'widget/src') diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index 48de6409..114d35ef 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -421,7 +421,9 @@ impl Update { keyboard::KeyCode::Backspace => edit(Action::Backspace), keyboard::KeyCode::Delete => edit(Action::Delete), keyboard::KeyCode::Escape => Some(Self::Unfocus), - keyboard::KeyCode::C => Some(Self::Copy), + keyboard::KeyCode::C if modifiers.command() => { + Some(Self::Copy) + } keyboard::KeyCode::V if modifiers.command() && !modifiers.alt() => { -- cgit From c6d0443627c22dcf1576303e5a426aa3622f1b7d Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 16 Sep 2023 15:27:25 +0200 Subject: Implement methods to query the contents of a `TextEditor` --- widget/src/text_editor.rs | 48 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) (limited to 'widget/src') diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index 114d35ef..ec7a6d1d 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -100,6 +100,54 @@ where internal.editor.perform(action); internal.is_dirty = true; } + + pub fn line_count(&self) -> usize { + self.0.borrow().editor.line_count() + } + + pub fn line( + &self, + index: usize, + ) -> Option + '_> { + std::cell::Ref::filter_map(self.0.borrow(), |internal| { + internal.editor.line(index) + }) + .ok() + } + + pub fn lines( + &self, + ) -> impl Iterator + '_> { + struct Lines<'a, Renderer: text::Renderer> { + internal: std::cell::Ref<'a, Internal>, + current: usize, + } + + impl<'a, Renderer: text::Renderer> Iterator for Lines<'a, Renderer> { + type Item = std::cell::Ref<'a, str>; + + fn next(&mut self) -> Option { + let line = std::cell::Ref::filter_map( + std::cell::Ref::clone(&self.internal), + |internal| internal.editor.line(self.current), + ) + .ok()?; + + self.current += 1; + + Some(line) + } + } + + Lines { + internal: self.0.borrow(), + current: 0, + } + } + + pub fn selection(&self) -> Option { + self.0.borrow().editor.selection() + } } impl Default for Content -- cgit From d051f21597bb333ac10183aaa3214a292e9aa365 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 16 Sep 2023 15:40:16 +0200 Subject: Implement `Copy` and `Paste` actions for `text::Editor` --- widget/src/text_editor.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'widget/src') diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index ec7a6d1d..0bb6b7d3 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -12,6 +12,7 @@ use crate::core::{ }; use std::cell::RefCell; +use std::sync::Arc; pub use crate::style::text_editor::{Appearance, StyleSheet}; pub use text::editor::{Action, Motion}; @@ -253,10 +254,14 @@ where Update::Edit(action) => { shell.publish(on_edit(action)); } - Update::Copy => todo!(), + Update::Copy => { + if let Some(selection) = self.content.selection() { + clipboard.write(selection); + } + } Update::Paste => { - if let Some(_contents) = clipboard.read() { - todo!() + if let Some(contents) = clipboard.read() { + shell.publish(on_edit(Action::Paste(Arc::new(contents)))); } } } -- cgit From 45c5cfe5774ac99a6e1b1d1014418f68b21b41cf Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 16 Sep 2023 19:05:31 +0200 Subject: Avoid drag on double or triple click for now in `TextEditor` --- widget/src/text_editor.rs | 52 +++++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 24 deletions(-) (limited to 'widget/src') diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index 0bb6b7d3..68e3c656 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -162,8 +162,8 @@ where struct State { is_focused: bool, - is_dragging: bool, last_click: Option, + drag_click: Option, } impl<'a, Message, Renderer> Widget @@ -179,8 +179,8 @@ where fn state(&self) -> widget::tree::State { widget::tree::State::new(State { is_focused: false, - is_dragging: false, last_click: None, + drag_click: None, }) } @@ -238,18 +238,27 @@ where }; match update { - Update::Click { click, action } => { + Update::Click(click) => { + let action = match click.kind() { + mouse::click::Kind::Single => { + Action::Click(click.position()) + } + mouse::click::Kind::Double => Action::SelectWord, + mouse::click::Kind::Triple => Action::SelectLine, + }; + state.is_focused = true; - state.is_dragging = true; state.last_click = Some(click); + state.drag_click = Some(click.kind()); + shell.publish(on_edit(action)); } Update::Unfocus => { state.is_focused = false; - state.is_dragging = false; + state.drag_click = None; } - Update::StopDragging => { - state.is_dragging = false; + Update::Release => { + state.drag_click = None; } Update::Edit(action) => { shell.publish(on_edit(action)); @@ -393,9 +402,9 @@ where } enum Update { - Click { click: mouse::Click, action: Action }, + Click(mouse::Click), Unfocus, - StopDragging, + Release, Edit(Action), Copy, Paste, @@ -423,15 +432,7 @@ impl Update { state.last_click, ); - let action = match click.kind() { - mouse::click::Kind::Single => { - Action::Click(cursor_position) - } - mouse::click::Kind::Double => Action::SelectWord, - mouse::click::Kind::Triple => Action::SelectLine, - }; - - Some(Update::Click { click, action }) + Some(Update::Click(click)) } else if state.is_focused { Some(Update::Unfocus) } else { @@ -439,14 +440,17 @@ impl Update { } } mouse::Event::ButtonReleased(mouse::Button::Left) => { - Some(Update::StopDragging) + Some(Update::Release) } - mouse::Event::CursorMoved { .. } if state.is_dragging => { - let cursor_position = cursor.position_in(bounds)? - - Vector::new(padding.top, padding.left); + mouse::Event::CursorMoved { .. } => match state.drag_click { + Some(mouse::click::Kind::Single) => { + let cursor_position = cursor.position_in(bounds)? + - Vector::new(padding.top, padding.left); - edit(Action::Drag(cursor_position)) - } + edit(Action::Drag(cursor_position)) + } + _ => None, + }, _ => None, }, Event::Keyboard(event) => match event { -- cgit From 76dc82e8e8b5201ec10f8d00d851c1decf998583 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 17 Sep 2023 15:29:14 +0200 Subject: Draft `Highlighter` API --- widget/src/helpers.rs | 2 +- widget/src/text_editor.rs | 64 ++++++++++++++++++++++++++++++++++++----------- 2 files changed, 50 insertions(+), 16 deletions(-) (limited to 'widget/src') diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index e3f31513..e0b58722 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -212,7 +212,7 @@ where /// [`TextEditor`]: crate::TextEditor pub fn text_editor( content: &text_editor::Content, -) -> TextEditor<'_, Message, Renderer> +) -> TextEditor<'_, core::text::highlighter::PlainText, Message, Renderer> where Message: Clone, Renderer: core::text::Renderer, diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index 68e3c656..b17e1156 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -4,6 +4,7 @@ use crate::core::layout::{self, Layout}; use crate::core::mouse; use crate::core::renderer; use crate::core::text::editor::{Cursor, Editor as _}; +use crate::core::text::highlighter::{self, Highlighter}; use crate::core::text::{self, LineHeight}; use crate::core::widget::{self, Widget}; use crate::core::{ @@ -12,13 +13,15 @@ use crate::core::{ }; use std::cell::RefCell; +use std::ops::DerefMut; use std::sync::Arc; -pub use crate::style::text_editor::{Appearance, StyleSheet}; +pub use crate::style::text_editor::{Appearance, Highlight, StyleSheet}; pub use text::editor::{Action, Motion}; -pub struct TextEditor<'a, Message, Renderer = crate::Renderer> +pub struct TextEditor<'a, Highlighter, Message, Renderer = crate::Renderer> where + Highlighter: text::Highlighter, Renderer: text::Renderer, Renderer::Theme: StyleSheet, { @@ -31,9 +34,11 @@ where padding: Padding, style: ::Style, on_edit: Option Message + 'a>>, + highlighter_settings: Highlighter::Settings, } -impl<'a, Message, Renderer> TextEditor<'a, Message, Renderer> +impl<'a, Message, Renderer> + TextEditor<'a, highlighter::PlainText, Message, Renderer> where Renderer: text::Renderer, Renderer::Theme: StyleSheet, @@ -49,9 +54,19 @@ where padding: Padding::new(5.0), style: Default::default(), on_edit: None, + highlighter_settings: (), } } +} +impl<'a, Highlighter, Message, Renderer> + TextEditor<'a, Highlighter, Message, Renderer> +where + Highlighter: text::Highlighter, + Highlighter::Highlight: Highlight, + Renderer: text::Renderer, + Renderer::Theme: StyleSheet, +{ pub fn on_edit(mut self, on_edit: impl Fn(Action) -> Message + 'a) -> Self { self.on_edit = Some(Box::new(on_edit)); self @@ -160,20 +175,23 @@ where } } -struct State { +struct State { is_focused: bool, last_click: Option, drag_click: Option, + highlighter: RefCell, } -impl<'a, Message, Renderer> Widget - for TextEditor<'a, Message, Renderer> +impl<'a, Highlighter, Message, Renderer> Widget + for TextEditor<'a, Highlighter, Message, Renderer> where + Highlighter: text::Highlighter, + Highlighter::Highlight: Highlight, Renderer: text::Renderer, Renderer::Theme: StyleSheet, { fn tag(&self) -> widget::tree::Tag { - widget::tree::Tag::of::() + widget::tree::Tag::of::>() } fn state(&self) -> widget::tree::State { @@ -181,6 +199,9 @@ where is_focused: false, last_click: None, drag_click: None, + highlighter: RefCell::new(Highlighter::new( + &self.highlighter_settings, + )), }) } @@ -194,17 +215,19 @@ where fn layout( &self, - _tree: &mut widget::Tree, + tree: &mut widget::Tree, renderer: &Renderer, limits: &layout::Limits, ) -> iced_renderer::core::layout::Node { let mut internal = self.content.0.borrow_mut(); + let state = tree.state.downcast_mut::>(); internal.editor.update( limits.pad(self.padding).max(), self.font.unwrap_or_else(|| renderer.default_font()), self.text_size.unwrap_or_else(|| renderer.default_size()), self.line_height, + state.highlighter.borrow_mut().deref_mut(), ); layout::Node::new(limits.max()) @@ -225,7 +248,7 @@ where return event::Status::Ignored; }; - let state = tree.state.downcast_mut::(); + let state = tree.state.downcast_mut::>(); let Some(update) = Update::from_event( event, @@ -290,8 +313,14 @@ where ) { let bounds = layout.bounds(); - let internal = self.content.0.borrow(); - let state = tree.state.downcast_ref::(); + let mut internal = self.content.0.borrow_mut(); + let state = tree.state.downcast_ref::>(); + + internal.editor.highlight( + self.font.unwrap_or_else(|| renderer.default_font()), + state.highlighter.borrow_mut().deref_mut(), + |highlight| highlight.format(theme), + ); let is_disabled = self.on_edit.is_none(); let is_mouse_over = cursor.is_over(bounds); @@ -389,14 +418,19 @@ where } } -impl<'a, Message, Renderer> From> +impl<'a, Highlighter, Message, Renderer> + From> for Element<'a, Message, Renderer> where + Highlighter: text::Highlighter, + Highlighter::Highlight: Highlight, Message: 'a, Renderer: text::Renderer, Renderer::Theme: StyleSheet, { - fn from(text_editor: TextEditor<'a, Message, Renderer>) -> Self { + fn from( + text_editor: TextEditor<'a, Highlighter, Message, Renderer>, + ) -> Self { Self::new(text_editor) } } @@ -411,9 +445,9 @@ enum Update { } impl Update { - fn from_event( + fn from_event( event: Event, - state: &State, + state: &State, bounds: Rectangle, padding: Padding, cursor: mouse::Cursor, -- cgit From d3011992a76e83e12f74402c2ade616cdc7f1497 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 17 Sep 2023 19:03:58 +0200 Subject: Implement basic syntax highlighting with `syntect` in `editor` example --- widget/src/text_editor.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'widget/src') diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index b17e1156..03adbb59 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -81,6 +81,24 @@ where self.padding = padding.into(); self } + + pub fn highlight( + self, + settings: H::Settings, + ) -> TextEditor<'a, H, Message, Renderer> { + TextEditor { + content: self.content, + font: self.font, + text_size: self.text_size, + line_height: self.line_height, + width: self.width, + height: self.height, + padding: self.padding, + style: self.style, + on_edit: self.on_edit, + highlighter_settings: settings, + } + } } pub struct Content(RefCell>) -- cgit From 2897986f2ded7318894a52572bec3d62754ebfaa Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 17 Sep 2023 19:27:51 +0200 Subject: Notify `Highlighter` of topmost line change --- widget/src/text_editor.rs | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) (limited to 'widget/src') diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index 03adbb59..c30e185f 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -17,7 +17,7 @@ use std::ops::DerefMut; use std::sync::Arc; pub use crate::style::text_editor::{Appearance, Highlight, StyleSheet}; -pub use text::editor::{Action, Motion}; +pub use text::editor::{Action, Edit, Motion}; pub struct TextEditor<'a, Highlighter, Message, Renderer = crate::Renderer> where @@ -301,7 +301,7 @@ where Update::Release => { state.drag_click = None; } - Update::Edit(action) => { + Update::Action(action) => { shell.publish(on_edit(action)); } Update::Copy => { @@ -311,7 +311,9 @@ where } Update::Paste => { if let Some(contents) = clipboard.read() { - shell.publish(on_edit(Action::Paste(Arc::new(contents)))); + shell.publish(on_edit(Action::Edit(Edit::Paste( + Arc::new(contents), + )))); } } } @@ -457,7 +459,7 @@ enum Update { Click(mouse::Click), Unfocus, Release, - Edit(Action), + Action(Action), Copy, Paste, } @@ -470,7 +472,8 @@ impl Update { padding: Padding, cursor: mouse::Cursor, ) -> Option { - let edit = |action| Some(Update::Edit(action)); + let action = |action| Some(Update::Action(action)); + let edit = |edit| action(Action::Edit(edit)); match event { Event::Mouse(event) => match event { @@ -499,7 +502,7 @@ impl Update { let cursor_position = cursor.position_in(bounds)? - Vector::new(padding.top, padding.left); - edit(Action::Drag(cursor_position)) + action(Action::Drag(cursor_position)) } _ => None, }, @@ -518,7 +521,7 @@ impl Update { motion }; - return edit(if modifiers.shift() { + return action(if modifiers.shift() { Action::Select(motion) } else { Action::Move(motion) @@ -526,9 +529,9 @@ impl Update { } match key_code { - keyboard::KeyCode::Enter => edit(Action::Enter), - keyboard::KeyCode::Backspace => edit(Action::Backspace), - keyboard::KeyCode::Delete => edit(Action::Delete), + keyboard::KeyCode::Enter => edit(Edit::Enter), + keyboard::KeyCode::Backspace => edit(Edit::Backspace), + keyboard::KeyCode::Delete => edit(Edit::Delete), keyboard::KeyCode::Escape => Some(Self::Unfocus), keyboard::KeyCode::C if modifiers.command() => { Some(Self::Copy) @@ -542,7 +545,7 @@ impl Update { } } keyboard::Event::CharacterReceived(c) if state.is_focused => { - edit(Action::Insert(c)) + edit(Edit::Insert(c)) } _ => None, }, -- cgit From 8446fe6de52fa68077d23d39f728f79a29b52f00 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 18 Sep 2023 14:38:54 +0200 Subject: Implement theme selector in `editor` example --- widget/src/text_editor.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'widget/src') diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index c30e185f..0cde2c98 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -193,11 +193,12 @@ where } } -struct State { +struct State { is_focused: bool, last_click: Option, drag_click: Option, highlighter: RefCell, + highlighter_settings: Highlighter::Settings, } impl<'a, Highlighter, Message, Renderer> Widget @@ -220,6 +221,7 @@ where highlighter: RefCell::new(Highlighter::new( &self.highlighter_settings, )), + highlighter_settings: self.highlighter_settings.clone(), }) } @@ -240,6 +242,15 @@ where let mut internal = self.content.0.borrow_mut(); let state = tree.state.downcast_mut::>(); + if state.highlighter_settings != self.highlighter_settings { + state + .highlighter + .borrow_mut() + .update(&self.highlighter_settings); + + state.highlighter_settings = self.highlighter_settings.clone(); + } + internal.editor.update( limits.pad(self.padding).max(), self.font.unwrap_or_else(|| renderer.default_font()), -- cgit From e7326f0af6f16cf2ff04fbac93bf296a044923f4 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 18 Sep 2023 19:07:41 +0200 Subject: Flesh out the `editor` example a bit more --- widget/src/text_editor.rs | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'widget/src') diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index 0cde2c98..970ec031 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -182,6 +182,10 @@ where pub fn selection(&self) -> Option { self.0.borrow().editor.selection() } + + pub fn cursor_position(&self) -> (usize, usize) { + self.0.borrow().editor.cursor_position() + } } impl Default for Content -- cgit From 4e757a26d0c1c58001f31cf0592131cd5ad886ad Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 19 Sep 2023 01:18:06 +0200 Subject: Implement `Scroll` action in `text::editor` --- widget/src/text_editor.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'widget/src') diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index 970ec031..ad12a076 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -521,6 +521,18 @@ impl Update { } _ => None, }, + mouse::Event::WheelScrolled { delta } => { + action(Action::Scroll { + lines: match delta { + mouse::ScrollDelta::Lines { y, .. } => { + -y as i32 * 4 + } + mouse::ScrollDelta::Pixels { y, .. } => { + -y.signum() as i32 + } + }, + }) + } _ => None, }, Event::Keyboard(event) => match event { -- cgit From f806d001e6fb44b5a45029ca257261e6e0d4d4b2 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 19 Sep 2023 20:48:50 +0200 Subject: Introduce new `iced_highlighter` subcrate --- widget/src/text_editor.rs | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) (limited to 'widget/src') diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index ad12a076..c384b8a2 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -16,7 +16,7 @@ use std::cell::RefCell; use std::ops::DerefMut; use std::sync::Arc; -pub use crate::style::text_editor::{Appearance, Highlight, StyleSheet}; +pub use crate::style::text_editor::{Appearance, StyleSheet}; pub use text::editor::{Action, Edit, Motion}; pub struct TextEditor<'a, Highlighter, Message, Renderer = crate::Renderer> @@ -35,6 +35,10 @@ where style: ::Style, on_edit: Option Message + 'a>>, highlighter_settings: Highlighter::Settings, + highlighter_format: fn( + &Highlighter::Highlight, + &Renderer::Theme, + ) -> highlighter::Format, } impl<'a, Message, Renderer> @@ -55,6 +59,9 @@ where style: Default::default(), on_edit: None, highlighter_settings: (), + highlighter_format: |_highlight, _theme| { + highlighter::Format::default() + }, } } } @@ -63,7 +70,6 @@ impl<'a, Highlighter, Message, Renderer> TextEditor<'a, Highlighter, Message, Renderer> where Highlighter: text::Highlighter, - Highlighter::Highlight: Highlight, Renderer: text::Renderer, Renderer::Theme: StyleSheet, { @@ -85,6 +91,10 @@ where pub fn highlight( self, settings: H::Settings, + to_format: fn( + &H::Highlight, + &Renderer::Theme, + ) -> highlighter::Format, ) -> TextEditor<'a, H, Message, Renderer> { TextEditor { content: self.content, @@ -97,6 +107,7 @@ where style: self.style, on_edit: self.on_edit, highlighter_settings: settings, + highlighter_format: to_format, } } } @@ -203,13 +214,13 @@ struct State { drag_click: Option, highlighter: RefCell, highlighter_settings: Highlighter::Settings, + highlighter_format_address: usize, } impl<'a, Highlighter, Message, Renderer> Widget for TextEditor<'a, Highlighter, Message, Renderer> where Highlighter: text::Highlighter, - Highlighter::Highlight: Highlight, Renderer: text::Renderer, Renderer::Theme: StyleSheet, { @@ -226,6 +237,7 @@ where &self.highlighter_settings, )), highlighter_settings: self.highlighter_settings.clone(), + highlighter_format_address: self.highlighter_format as usize, }) } @@ -246,6 +258,13 @@ where let mut internal = self.content.0.borrow_mut(); let state = tree.state.downcast_mut::>(); + if state.highlighter_format_address != self.highlighter_format as usize + { + state.highlighter.borrow_mut().change_line(0); + + state.highlighter_format_address = self.highlighter_format as usize; + } + if state.highlighter_settings != self.highlighter_settings { state .highlighter @@ -354,7 +373,7 @@ where internal.editor.highlight( self.font.unwrap_or_else(|| renderer.default_font()), state.highlighter.borrow_mut().deref_mut(), - |highlight| highlight.format(theme), + |highlight| (self.highlighter_format)(highlight, theme), ); let is_disabled = self.on_edit.is_none(); @@ -458,7 +477,6 @@ impl<'a, Highlighter, Message, Renderer> for Element<'a, Message, Renderer> where Highlighter: text::Highlighter, - Highlighter::Highlight: Highlight, Message: 'a, Renderer: text::Renderer, Renderer::Theme: StyleSheet, -- cgit From 29fb4eab878a7ba399cae6ab1ec18a71e369ee59 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 20 Sep 2023 01:23:50 +0200 Subject: Scroll `TextEditor` only if `cursor.is_over(bounds)` --- widget/src/text_editor.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'widget/src') diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index c384b8a2..4191e02c 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -539,7 +539,9 @@ impl Update { } _ => None, }, - mouse::Event::WheelScrolled { delta } => { + mouse::Event::WheelScrolled { delta } + if cursor.is_over(bounds) => + { action(Action::Scroll { lines: match delta { mouse::ScrollDelta::Lines { y, .. } => { -- cgit From da5dd2526a2d9ee27e9405ed19c0f7a641160c54 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 21 Sep 2023 06:07:19 +0200 Subject: Round `ScrollDelta::Lines` in `TextEditor` --- widget/src/text_editor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'widget/src') diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index 4191e02c..ac927fbc 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -545,7 +545,7 @@ impl Update { action(Action::Scroll { lines: match delta { mouse::ScrollDelta::Lines { y, .. } => { - -y as i32 * 4 + -y.round() as i32 * 4 } mouse::ScrollDelta::Pixels { y, .. } => { -y.signum() as i32 -- cgit From 7373dd856b8837c2d91067b45e43b8f0e767c917 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 21 Sep 2023 06:13:08 +0200 Subject: Scroll at least one line on macOS in `TextEditor` --- widget/src/text_editor.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'widget/src') diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index ac927fbc..76f3cc18 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -545,7 +545,11 @@ impl Update { action(Action::Scroll { lines: match delta { mouse::ScrollDelta::Lines { y, .. } => { - -y.round() as i32 * 4 + if y > 0.0 { + -(y * 4.0).min(1.0) as i32 + } else { + 0 + } } mouse::ScrollDelta::Pixels { y, .. } => { -y.signum() as i32 -- cgit From 68d49459ce0e8b28e56b71970cb26e66ac1b01b4 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 21 Sep 2023 06:17:47 +0200 Subject: Fix vertical scroll for `TextEditor` --- widget/src/text_editor.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'widget/src') diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index 76f3cc18..e8187b9c 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -545,8 +545,9 @@ impl Update { action(Action::Scroll { lines: match delta { mouse::ScrollDelta::Lines { y, .. } => { - if y > 0.0 { - -(y * 4.0).min(1.0) as i32 + if y.abs() > 0.0 { + (y.signum() * -(y.abs() * 4.0).max(1.0)) + as i32 } else { 0 } -- cgit From 70e49df4289b925d24f92ce5c91ef2b03dbc54e3 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 22 Sep 2023 05:50:31 +0200 Subject: Fix selection clipping out of bounds in `TextEditor` --- widget/src/text_editor.rs | 57 +++++++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 24 deletions(-) (limited to 'widget/src') diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index e8187b9c..c142c22d 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -406,38 +406,47 @@ where style.text_color, ); + let translation = Vector::new( + bounds.x + self.padding.left, + bounds.y + self.padding.top, + ); + if state.is_focused { match internal.editor.cursor() { Cursor::Caret(position) => { - renderer.fill_quad( - renderer::Quad { - bounds: Rectangle { - x: position.x + bounds.x + self.padding.left, - y: position.y + bounds.y + self.padding.top, - width: 1.0, - height: self - .line_height - .to_absolute(self.text_size.unwrap_or_else( - || renderer.default_size(), - )) - .into(), + let position = position + translation; + + if bounds.contains(position) { + renderer.fill_quad( + renderer::Quad { + bounds: Rectangle { + x: position.x, + y: position.y, + width: 1.0, + height: self + .line_height + .to_absolute( + self.text_size.unwrap_or_else( + || renderer.default_size(), + ), + ) + .into(), + }, + border_radius: 0.0.into(), + border_width: 0.0, + border_color: Color::TRANSPARENT, }, - border_radius: 0.0.into(), - border_width: 0.0, - border_color: Color::TRANSPARENT, - }, - theme.value_color(&self.style), - ); + theme.value_color(&self.style), + ); + } } Cursor::Selection(ranges) => { - for range in ranges { + for range in ranges.into_iter().filter_map(|range| { + bounds.intersection(&(range + translation)) + }) { renderer.fill_quad( renderer::Quad { - bounds: range - + Vector::new( - bounds.x + self.padding.left, - bounds.y + self.padding.top, - ), + bounds: range, border_radius: 0.0.into(), border_width: 0.0, border_color: Color::TRANSPARENT, -- cgit From 8cc19de254c37d3123d5ea1b6513f1f34d35c7c8 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 22 Sep 2023 06:00:51 +0200 Subject: Add `text` helper method for `text_editor::Content` --- widget/src/text_editor.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'widget/src') diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index c142c22d..6d25967e 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -190,6 +190,27 @@ where } } + pub fn text(&self) -> String { + let mut text = self.lines().enumerate().fold( + String::new(), + |mut contents, (i, line)| { + if i > 0 { + contents.push('\n'); + } + + contents.push_str(&line); + + contents + }, + ); + + if !text.ends_with('\n') { + text.push('\n'); + } + + text + } + pub fn selection(&self) -> Option { self.0.borrow().editor.selection() } -- cgit From 625cd745f38215b1cb8f629cdc6d2fa41c9a739a Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 27 Oct 2023 05:04:14 +0200 Subject: Write documentation for the new text APIs --- widget/src/lib.rs | 4 ++-- widget/src/text_editor.rs | 36 ++++++++++++++++++++++++++++++++---- 2 files changed, 34 insertions(+), 6 deletions(-) (limited to 'widget/src') diff --git a/widget/src/lib.rs b/widget/src/lib.rs index 97e4ac58..e3335a98 100644 --- a/widget/src/lib.rs +++ b/widget/src/lib.rs @@ -4,8 +4,8 @@ )] #![forbid(unsafe_code, rust_2018_idioms)] #![deny( - // missing_debug_implementations, - // missing_docs, + //missing_debug_implementations, + missing_docs, unused_results, rustdoc::broken_intra_doc_links )] diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index 6d25967e..da1905dc 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -1,3 +1,4 @@ +//! Display a multi-line text input for text editing. use crate::core::event::{self, Event}; use crate::core::keyboard; use crate::core::layout::{self, Layout}; @@ -19,6 +20,7 @@ use std::sync::Arc; pub use crate::style::text_editor::{Appearance, StyleSheet}; pub use text::editor::{Action, Edit, Motion}; +/// A multi-line text input. pub struct TextEditor<'a, Highlighter, Message, Renderer = crate::Renderer> where Highlighter: text::Highlighter, @@ -47,6 +49,7 @@ where Renderer: text::Renderer, Renderer::Theme: StyleSheet, { + /// Creates new [`TextEditor`] with the given [`Content`]. pub fn new(content: &'a Content) -> Self { Self { content, @@ -73,21 +76,34 @@ where Renderer: text::Renderer, Renderer::Theme: StyleSheet, { - pub fn on_edit(mut self, on_edit: impl Fn(Action) -> Message + 'a) -> Self { + /// Sets the message that should be produced when some action is performed in + /// the [`TextEditor`]. + /// + /// If this method is not called, the [`TextEditor`] will be disabled. + pub fn on_action( + mut self, + on_edit: impl Fn(Action) -> Message + 'a, + ) -> Self { self.on_edit = Some(Box::new(on_edit)); self } + /// Sets the [`Font`] of the [`TextEditor`]. + /// + /// [`Font`]: text::Renderer::Font pub fn font(mut self, font: impl Into) -> Self { self.font = Some(font.into()); self } + /// Sets the [`Padding`] of the [`TextEditor`]. pub fn padding(mut self, padding: impl Into) -> Self { self.padding = padding.into(); self } + /// Highlights the [`TextEditor`] with the given [`Highlighter`] and + /// a strategy to turn its highlights into some text format. pub fn highlight( self, settings: H::Settings, @@ -112,6 +128,7 @@ where } } +/// The content of a [`TextEditor`]. pub struct Content(RefCell>) where R: text::Renderer; @@ -128,28 +145,33 @@ impl Content where R: text::Renderer, { + /// Creates an empty [`Content`]. pub fn new() -> Self { - Self::with("") + Self::with_text("") } - pub fn with(text: &str) -> Self { + /// Creates a [`Content`] with the given text. + pub fn with_text(text: &str) -> Self { Self(RefCell::new(Internal { editor: R::Editor::with_text(text), is_dirty: true, })) } - pub fn edit(&mut self, action: Action) { + /// Performs an [`Action`] on the [`Content`]. + pub fn perform(&mut self, action: Action) { let internal = self.0.get_mut(); internal.editor.perform(action); internal.is_dirty = true; } + /// Returns the amount of lines of the [`Content`]. pub fn line_count(&self) -> usize { self.0.borrow().editor.line_count() } + /// Returns the text of the line at the given index, if it exists. pub fn line( &self, index: usize, @@ -160,6 +182,7 @@ where .ok() } + /// Returns an iterator of the text of the lines in the [`Content`]. pub fn lines( &self, ) -> impl Iterator + '_> { @@ -190,6 +213,9 @@ where } } + /// Returns the text of the [`Content`]. + /// + /// Lines are joined with `'\n'`. pub fn text(&self) -> String { let mut text = self.lines().enumerate().fold( String::new(), @@ -211,10 +237,12 @@ where text } + /// Returns the selected text of the [`Content`]. pub fn selection(&self) -> Option { self.0.borrow().editor.selection() } + /// Returns the current cursor position of the [`Content`]. pub fn cursor_position(&self) -> (usize, usize) { self.0.borrow().editor.cursor_position() } -- cgit From e579d8553088c7d17784e7ff8f6e21360c2bd9ef Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 27 Oct 2023 05:08:06 +0200 Subject: Implement missing debug implementations in `iced_widget` --- widget/src/lib.rs | 2 +- widget/src/text_editor.rs | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'widget/src') diff --git a/widget/src/lib.rs b/widget/src/lib.rs index e3335a98..2f959370 100644 --- a/widget/src/lib.rs +++ b/widget/src/lib.rs @@ -4,7 +4,7 @@ )] #![forbid(unsafe_code, rust_2018_idioms)] #![deny( - //missing_debug_implementations, + missing_debug_implementations, missing_docs, unused_results, rustdoc::broken_intra_doc_links diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index da1905dc..ac24920f 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -14,6 +14,7 @@ use crate::core::{ }; use std::cell::RefCell; +use std::fmt; use std::ops::DerefMut; use std::sync::Arc; @@ -21,6 +22,7 @@ pub use crate::style::text_editor::{Appearance, StyleSheet}; pub use text::editor::{Action, Edit, Motion}; /// A multi-line text input. +#[allow(missing_debug_implementations)] pub struct TextEditor<'a, Highlighter, Message, Renderer = crate::Renderer> where Highlighter: text::Highlighter, @@ -257,6 +259,21 @@ where } } +impl fmt::Debug for Content +where + Renderer: text::Renderer, + Renderer::Editor: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let internal = self.0.borrow(); + + f.debug_struct("Content") + .field("editor", &internal.editor) + .field("is_dirty", &internal.is_dirty) + .finish() + } +} + struct State { is_focused: bool, last_click: Option, -- cgit From c8eca4e6bfae82013e6bb08e9d8bf66560b36564 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 27 Oct 2023 16:37:58 +0200 Subject: Improve `TextEditor` scroll interaction with a touchpad --- widget/src/text_editor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'widget/src') diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index ac24920f..1708a2e5 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -628,7 +628,7 @@ impl Update { } } mouse::ScrollDelta::Pixels { y, .. } => { - -y.signum() as i32 + (-y / 4.0) as i32 } }, }) -- cgit From 5759096a4c33935fcdf5f96606143e4f21159186 Mon Sep 17 00:00:00 2001 From: Remmirad Date: Wed, 31 May 2023 15:46:21 +0200 Subject: Implement texture filtering options --- widget/src/image.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'widget/src') diff --git a/widget/src/image.rs b/widget/src/image.rs index a0e89920..9f0b16b7 100644 --- a/widget/src/image.rs +++ b/widget/src/image.rs @@ -13,7 +13,7 @@ use crate::core::{ use std::hash::Hash; -pub use image::Handle; +pub use image::{Handle, TextureFilter, FilterMethod}; /// Creates a new [`Viewer`] with the given image `Handle`. pub fn viewer(handle: Handle) -> Viewer { -- cgit From 4b32a488808e371313ce78e727c9d98ab2eb759e Mon Sep 17 00:00:00 2001 From: Remmirad Date: Fri, 4 Aug 2023 13:50:16 +0200 Subject: Fix clippy + fmt --- widget/src/image.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'widget/src') diff --git a/widget/src/image.rs b/widget/src/image.rs index 9f0b16b7..684f200c 100644 --- a/widget/src/image.rs +++ b/widget/src/image.rs @@ -13,7 +13,7 @@ use crate::core::{ use std::hash::Hash; -pub use image::{Handle, TextureFilter, FilterMethod}; +pub use image::{FilterMethod, Handle, TextureFilter}; /// Creates a new [`Viewer`] with the given image `Handle`. pub fn viewer(handle: Handle) -> Viewer { -- cgit From a5125d6fea824df1191777fe3eb53a2f748208b9 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 11 Nov 2023 07:02:01 +0100 Subject: Refactor texture image filtering - Support only `Linear` or `Nearest` - Simplify `Layer` groups - Move `FilterMethod` to `Image` and `image::Viewer` --- widget/src/image.rs | 29 +++++++++++++++++++++-------- widget/src/image/viewer.rs | 5 ++++- 2 files changed, 25 insertions(+), 9 deletions(-) (limited to 'widget/src') diff --git a/widget/src/image.rs b/widget/src/image.rs index 684f200c..67699102 100644 --- a/widget/src/image.rs +++ b/widget/src/image.rs @@ -13,7 +13,7 @@ use crate::core::{ use std::hash::Hash; -pub use image::{FilterMethod, Handle, TextureFilter}; +pub use image::{FilterMethod, Handle}; /// Creates a new [`Viewer`] with the given image `Handle`. pub fn viewer(handle: Handle) -> Viewer { @@ -37,6 +37,7 @@ pub struct Image { width: Length, height: Length, content_fit: ContentFit, + filter_method: FilterMethod, } impl Image { @@ -47,6 +48,7 @@ impl Image { width: Length::Shrink, height: Length::Shrink, content_fit: ContentFit::Contain, + filter_method: FilterMethod::default(), } } @@ -65,11 +67,15 @@ impl Image { /// Sets the [`ContentFit`] of the [`Image`]. /// /// Defaults to [`ContentFit::Contain`] - pub fn content_fit(self, content_fit: ContentFit) -> Self { - Self { - content_fit, - ..self - } + pub fn content_fit(mut self, content_fit: ContentFit) -> Self { + self.content_fit = content_fit; + self + } + + /// Sets the [`FilterMethod`] of the [`Image`]. + pub fn filter_method(mut self, filter_method: FilterMethod) -> Self { + self.filter_method = filter_method; + self } } @@ -119,6 +125,7 @@ pub fn draw( layout: Layout<'_>, handle: &Handle, content_fit: ContentFit, + filter_method: FilterMethod, ) where Renderer: image::Renderer, Handle: Clone + Hash, @@ -141,7 +148,7 @@ pub fn draw( ..bounds }; - renderer.draw(handle.clone(), drawing_bounds + offset); + renderer.draw(handle.clone(), filter_method, drawing_bounds + offset); }; if adjusted_fit.width > bounds.width || adjusted_fit.height > bounds.height @@ -191,7 +198,13 @@ where _cursor: mouse::Cursor, _viewport: &Rectangle, ) { - draw(renderer, layout, &self.handle, self.content_fit); + draw( + renderer, + layout, + &self.handle, + self.content_fit, + self.filter_method, + ); } } diff --git a/widget/src/image/viewer.rs b/widget/src/image/viewer.rs index 44624fc8..68015ba8 100644 --- a/widget/src/image/viewer.rs +++ b/widget/src/image/viewer.rs @@ -22,19 +22,21 @@ pub struct Viewer { max_scale: f32, scale_step: f32, handle: Handle, + filter_method: image::FilterMethod, } impl Viewer { /// Creates a new [`Viewer`] with the given [`State`]. pub fn new(handle: Handle) -> Self { Viewer { + handle, padding: 0.0, width: Length::Shrink, height: Length::Shrink, min_scale: 0.25, max_scale: 10.0, scale_step: 0.10, - handle, + filter_method: image::FilterMethod::default(), } } @@ -329,6 +331,7 @@ where image::Renderer::draw( renderer, self.handle.clone(), + self.filter_method, Rectangle { x: bounds.x, y: bounds.y, -- cgit From 781ef1f94c4859aeeb852f801b72be095b8ff82b Mon Sep 17 00:00:00 2001 From: Bingus Date: Thu, 14 Sep 2023 13:58:36 -0700 Subject: Added support for custom shader widget for iced_wgpu backend. --- widget/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'widget/src') diff --git a/widget/src/lib.rs b/widget/src/lib.rs index 2f959370..e052f398 100644 --- a/widget/src/lib.rs +++ b/widget/src/lib.rs @@ -97,6 +97,9 @@ pub use tooltip::Tooltip; #[doc(no_inline)] pub use vertical_slider::VerticalSlider; +#[cfg(feature = "wgpu")] +pub use renderer::widget::shader::{self, Shader}; + #[cfg(feature = "svg")] pub mod svg; -- cgit From 91fca024b629b7ddf4de533a75f01593954362f6 Mon Sep 17 00:00:00 2001 From: Bingus Date: Thu, 21 Sep 2023 13:24:54 -0700 Subject: Reexport Transformation from widget::shader --- widget/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'widget/src') diff --git a/widget/src/lib.rs b/widget/src/lib.rs index e052f398..5220e83a 100644 --- a/widget/src/lib.rs +++ b/widget/src/lib.rs @@ -98,7 +98,7 @@ pub use tooltip::Tooltip; pub use vertical_slider::VerticalSlider; #[cfg(feature = "wgpu")] -pub use renderer::widget::shader::{self, Shader}; +pub use renderer::widget::shader::{self, Shader, Transformation}; #[cfg(feature = "svg")] pub mod svg; -- cgit From 9489e29e6619b14ed9f41a8887c4b34158266f71 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 14 Nov 2023 12:49:49 +0100 Subject: Re-organize `custom` module as `pipeline` module ... and move `Shader` widget to `iced_widget` crate --- widget/src/lib.rs | 6 +- widget/src/shader.rs | 219 +++++++++++++++++++++++++++++++++++++++++++ widget/src/shader/event.rs | 26 +++++ widget/src/shader/program.rs | 62 ++++++++++++ 4 files changed, 312 insertions(+), 1 deletion(-) create mode 100644 widget/src/shader.rs create mode 100644 widget/src/shader/event.rs create mode 100644 widget/src/shader/program.rs (limited to 'widget/src') diff --git a/widget/src/lib.rs b/widget/src/lib.rs index 5220e83a..07378d83 100644 --- a/widget/src/lib.rs +++ b/widget/src/lib.rs @@ -98,7 +98,11 @@ pub use tooltip::Tooltip; pub use vertical_slider::VerticalSlider; #[cfg(feature = "wgpu")] -pub use renderer::widget::shader::{self, Shader, Transformation}; +pub mod shader; + +#[cfg(feature = "wgpu")] +#[doc(no_inline)] +pub use shader::Shader; #[cfg(feature = "svg")] pub mod svg; diff --git a/widget/src/shader.rs b/widget/src/shader.rs new file mode 100644 index 00000000..9d482537 --- /dev/null +++ b/widget/src/shader.rs @@ -0,0 +1,219 @@ +//! A custom shader widget for wgpu applications. +mod event; +mod program; + +pub use event::Event; +pub use program::Program; + +use crate::core; +use crate::core::layout::{self, Layout}; +use crate::core::mouse; +use crate::core::renderer; +use crate::core::widget::tree::{self, Tree}; +use crate::core::widget::{self, Widget}; +use crate::core::window; +use crate::core::{Clipboard, Element, Length, Rectangle, Shell, Size}; +use crate::renderer::wgpu::primitive::pipeline; + +use std::marker::PhantomData; + +pub use pipeline::{Primitive, Storage}; + +/// A widget which can render custom shaders with Iced's `wgpu` backend. +/// +/// Must be initialized with a [`Program`], which describes the internal widget state & how +/// its [`Program::Primitive`]s are drawn. +#[allow(missing_debug_implementations)] +pub struct Shader> { + width: Length, + height: Length, + program: P, + _message: PhantomData, +} + +impl> Shader { + /// Create a new custom [`Shader`]. + pub fn new(program: P) -> Self { + Self { + width: Length::Fixed(100.0), + height: Length::Fixed(100.0), + program, + _message: PhantomData, + } + } + + /// Set the `width` of the custom [`Shader`]. + pub fn width(mut self, width: impl Into) -> Self { + self.width = width.into(); + self + } + + /// Set the `height` of the custom [`Shader`]. + pub fn height(mut self, height: impl Into) -> Self { + self.height = height.into(); + self + } +} + +impl Widget for Shader +where + P: Program, + Renderer: pipeline::Renderer, +{ + fn tag(&self) -> tree::Tag { + struct Tag(T); + tree::Tag::of::>() + } + + fn state(&self) -> tree::State { + tree::State::new(P::State::default()) + } + + fn width(&self) -> Length { + self.width + } + + fn height(&self) -> Length { + self.height + } + + fn layout( + &self, + _tree: &mut Tree, + _renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + let limits = limits.width(self.width).height(self.height); + let size = limits.resolve(Size::ZERO); + + layout::Node::new(size) + } + + fn on_event( + &mut self, + tree: &mut Tree, + event: crate::core::Event, + layout: Layout<'_>, + cursor: mouse::Cursor, + _renderer: &Renderer, + _clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + _viewport: &Rectangle, + ) -> event::Status { + let bounds = layout.bounds(); + + let custom_shader_event = match event { + core::Event::Mouse(mouse_event) => Some(Event::Mouse(mouse_event)), + core::Event::Keyboard(keyboard_event) => { + Some(Event::Keyboard(keyboard_event)) + } + core::Event::Touch(touch_event) => Some(Event::Touch(touch_event)), + core::Event::Window(window::Event::RedrawRequested(instant)) => { + Some(Event::RedrawRequested(instant)) + } + _ => None, + }; + + if let Some(custom_shader_event) = custom_shader_event { + let state = tree.state.downcast_mut::(); + + let (event_status, message) = self.program.update( + state, + custom_shader_event, + bounds, + cursor, + shell, + ); + + if let Some(message) = message { + shell.publish(message); + } + + return event_status; + } + + event::Status::Ignored + } + + fn mouse_interaction( + &self, + tree: &Tree, + layout: Layout<'_>, + cursor: mouse::Cursor, + _viewport: &Rectangle, + _renderer: &Renderer, + ) -> mouse::Interaction { + let bounds = layout.bounds(); + let state = tree.state.downcast_ref::(); + + self.program.mouse_interaction(state, bounds, cursor) + } + + fn draw( + &self, + tree: &widget::Tree, + renderer: &mut Renderer, + _theme: &Renderer::Theme, + _style: &renderer::Style, + layout: Layout<'_>, + cursor_position: mouse::Cursor, + _viewport: &Rectangle, + ) { + let bounds = layout.bounds(); + let state = tree.state.downcast_ref::(); + + renderer.draw_pipeline_primitive( + bounds, + self.program.draw(state, cursor_position, bounds), + ); + } +} + +impl<'a, Message, Renderer, P> From> + for Element<'a, Message, Renderer> +where + Message: 'a, + Renderer: pipeline::Renderer, + P: Program + 'a, +{ + fn from(custom: Shader) -> Element<'a, Message, Renderer> { + Element::new(custom) + } +} + +impl Program for &T +where + T: Program, +{ + type State = T::State; + type Primitive = T::Primitive; + + fn update( + &self, + state: &mut Self::State, + event: Event, + bounds: Rectangle, + cursor: mouse::Cursor, + shell: &mut Shell<'_, Message>, + ) -> (event::Status, Option) { + T::update(self, state, event, bounds, cursor, shell) + } + + fn draw( + &self, + state: &Self::State, + cursor: mouse::Cursor, + bounds: Rectangle, + ) -> Self::Primitive { + T::draw(self, state, cursor, bounds) + } + + fn mouse_interaction( + &self, + state: &Self::State, + bounds: Rectangle, + cursor: mouse::Cursor, + ) -> mouse::Interaction { + T::mouse_interaction(self, state, bounds, cursor) + } +} diff --git a/widget/src/shader/event.rs b/widget/src/shader/event.rs new file mode 100644 index 00000000..e4d2b03d --- /dev/null +++ b/widget/src/shader/event.rs @@ -0,0 +1,26 @@ +//! Handle events of a custom shader widget. +use crate::core::keyboard; +use crate::core::mouse; +use crate::core::touch; + +use std::time::Instant; + +pub use crate::core::event::Status; + +/// A [`Shader`] event. +/// +/// [`Shader`]: crate::widget::shader::Shader; +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Event { + /// A mouse event. + Mouse(mouse::Event), + + /// A touch event. + Touch(touch::Event), + + /// A keyboard event. + Keyboard(keyboard::Event), + + /// A window requested a redraw. + RedrawRequested(Instant), +} diff --git a/widget/src/shader/program.rs b/widget/src/shader/program.rs new file mode 100644 index 00000000..0319844d --- /dev/null +++ b/widget/src/shader/program.rs @@ -0,0 +1,62 @@ +use crate::core::event; +use crate::core::mouse; +use crate::core::{Rectangle, Shell}; +use crate::renderer::wgpu::primitive::pipeline; +use crate::shader; + +/// The state and logic of a [`Shader`] widget. +/// +/// A [`Program`] can mutate the internal state of a [`Shader`] widget +/// and produce messages for an application. +/// +/// [`Shader`]: crate::widget::shader::Shader +pub trait Program { + /// The internal state of the [`Program`]. + type State: Default + 'static; + + /// The type of primitive this [`Program`] can draw. + type Primitive: pipeline::Primitive + 'static; + + /// Update the internal [`State`] of the [`Program`]. This can be used to reflect state changes + /// based on mouse & other events. You can use the [`Shell`] to publish messages, request a + /// redraw for the window, etc. + /// + /// By default, this method does and returns nothing. + /// + /// [`State`]: Self::State + fn update( + &self, + _state: &mut Self::State, + _event: shader::Event, + _bounds: Rectangle, + _cursor: mouse::Cursor, + _shell: &mut Shell<'_, Message>, + ) -> (event::Status, Option) { + (event::Status::Ignored, None) + } + + /// Draws the [`Primitive`]. + /// + /// [`Primitive`]: Self::Primitive + fn draw( + &self, + state: &Self::State, + cursor: mouse::Cursor, + bounds: Rectangle, + ) -> Self::Primitive; + + /// Returns the current mouse interaction of the [`Program`]. + /// + /// The interaction returned will be in effect even if the cursor position is out of + /// bounds of the [`Shader`]'s program. + /// + /// [`Shader`]: crate::widget::shader::Shader + fn mouse_interaction( + &self, + _state: &Self::State, + _bounds: Rectangle, + _cursor: mouse::Cursor, + ) -> mouse::Interaction { + mouse::Interaction::default() + } +} -- cgit From c2baf18cbff721e25c3103b6235292530b419c54 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 14 Nov 2023 12:52:03 +0100 Subject: Use `Instant` from `iced_core` instead of `std` This is needed for Wasm compatibility. --- widget/src/shader/event.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'widget/src') diff --git a/widget/src/shader/event.rs b/widget/src/shader/event.rs index e4d2b03d..a5a7acaa 100644 --- a/widget/src/shader/event.rs +++ b/widget/src/shader/event.rs @@ -1,10 +1,9 @@ //! Handle events of a custom shader widget. use crate::core::keyboard; use crate::core::mouse; +use crate::core::time::Instant; use crate::core::touch; -use std::time::Instant; - pub use crate::core::event::Status; /// A [`Shader`] event. -- cgit From 280d3736d59b62c4087fe980c187953cc2be83d2 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 14 Nov 2023 13:23:28 +0100 Subject: Fix broken intra-doc links --- widget/src/shader/event.rs | 2 +- widget/src/shader/program.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'widget/src') diff --git a/widget/src/shader/event.rs b/widget/src/shader/event.rs index a5a7acaa..1cc484fb 100644 --- a/widget/src/shader/event.rs +++ b/widget/src/shader/event.rs @@ -8,7 +8,7 @@ pub use crate::core::event::Status; /// A [`Shader`] event. /// -/// [`Shader`]: crate::widget::shader::Shader; +/// [`Shader`]: crate::Shader #[derive(Debug, Clone, Copy, PartialEq)] pub enum Event { /// A mouse event. diff --git a/widget/src/shader/program.rs b/widget/src/shader/program.rs index 0319844d..6dd50404 100644 --- a/widget/src/shader/program.rs +++ b/widget/src/shader/program.rs @@ -9,7 +9,7 @@ use crate::shader; /// A [`Program`] can mutate the internal state of a [`Shader`] widget /// and produce messages for an application. /// -/// [`Shader`]: crate::widget::shader::Shader +/// [`Shader`]: crate::Shader pub trait Program { /// The internal state of the [`Program`]. type State: Default + 'static; @@ -50,7 +50,7 @@ pub trait Program { /// The interaction returned will be in effect even if the cursor position is out of /// bounds of the [`Shader`]'s program. /// - /// [`Shader`]: crate::widget::shader::Shader + /// [`Shader`]: crate::Shader fn mouse_interaction( &self, _state: &Self::State, -- cgit From 91d7df52cdedd1b5c431fdb51a6356e827765b60 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 14 Nov 2023 13:25:49 +0100 Subject: Create `shader` function helper in `iced_widget` --- widget/src/helpers.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'widget/src') diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index e0b58722..115198fb 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -385,6 +385,17 @@ where crate::Canvas::new(program) } +/// Creates a new [`Shader`]. +/// +/// [`Shader`]: crate::Shader +#[cfg(feature = "wgpu")] +pub fn shader(program: P) -> crate::Shader +where + P: crate::shader::Program, +{ + crate::Shader::new(program) +} + /// Focuses the previous focusable widget. pub fn focus_previous() -> Command where -- cgit From 63f36b04638f14af3455ead8b82d581a438a28a3 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 14 Nov 2023 14:04:54 +0100 Subject: Export `wgpu` crate in `shader` module in `iced_widget` --- widget/src/shader.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'widget/src') diff --git a/widget/src/shader.rs b/widget/src/shader.rs index 9d482537..fe6214db 100644 --- a/widget/src/shader.rs +++ b/widget/src/shader.rs @@ -17,6 +17,7 @@ use crate::renderer::wgpu::primitive::pipeline; use std::marker::PhantomData; +pub use crate::renderer::wgpu::wgpu; pub use pipeline::{Primitive, Storage}; /// A widget which can render custom shaders with Iced's `wgpu` backend. -- cgit From 811aa673e9e832ebe38bf56a087f32fdc7aba59c Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 14 Nov 2023 15:48:01 +0100 Subject: Improve module hierarchy of `custom_shader` example --- widget/src/shader.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'widget/src') diff --git a/widget/src/shader.rs b/widget/src/shader.rs index fe6214db..ca140627 100644 --- a/widget/src/shader.rs +++ b/widget/src/shader.rs @@ -17,6 +17,7 @@ use crate::renderer::wgpu::primitive::pipeline; use std::marker::PhantomData; +pub use crate::graphics::Transformation; pub use crate::renderer::wgpu::wgpu; pub use pipeline::{Primitive, Storage}; -- cgit From 25006b9c6f2ae909d86871d3a13631d518c07158 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 21 Nov 2023 14:41:22 +0100 Subject: Fix `Overlay` composition Translations were not easily composable. --- widget/src/lazy.rs | 5 +++-- widget/src/lazy/component.rs | 3 ++- widget/src/lazy/responsive.rs | 6 ++++-- widget/src/overlay/menu.rs | 1 + widget/src/tooltip.rs | 1 + 5 files changed, 11 insertions(+), 5 deletions(-) (limited to 'widget/src') diff --git a/widget/src/lazy.rs b/widget/src/lazy.rs index 589dd938..167a055d 100644 --- a/widget/src/lazy.rs +++ b/widget/src/lazy.rs @@ -18,7 +18,7 @@ use crate::core::widget::tree::{self, Tree}; use crate::core::widget::{self, Widget}; use crate::core::Element; use crate::core::{ - self, Clipboard, Hasher, Length, Point, Rectangle, Shell, Size, + self, Clipboard, Hasher, Length, Point, Rectangle, Shell, Size, Vector, }; use crate::runtime::overlay::Nested; @@ -333,9 +333,10 @@ where renderer: &Renderer, bounds: Size, position: Point, + translation: Vector, ) -> layout::Node { self.with_overlay_maybe(|overlay| { - overlay.layout(renderer, bounds, position) + overlay.layout(renderer, bounds, position, translation) }) .unwrap_or_default() } diff --git a/widget/src/lazy/component.rs b/widget/src/lazy/component.rs index d454b72b..ad0c3823 100644 --- a/widget/src/lazy/component.rs +++ b/widget/src/lazy/component.rs @@ -577,9 +577,10 @@ where renderer: &Renderer, bounds: Size, position: Point, + translation: Vector, ) -> layout::Node { self.with_overlay_maybe(|overlay| { - overlay.layout(renderer, bounds, position) + overlay.layout(renderer, bounds, position, translation) }) .unwrap_or_default() } diff --git a/widget/src/lazy/responsive.rs b/widget/src/lazy/responsive.rs index ed471988..86d37b6c 100644 --- a/widget/src/lazy/responsive.rs +++ b/widget/src/lazy/responsive.rs @@ -6,7 +6,8 @@ use crate::core::renderer; use crate::core::widget; use crate::core::widget::tree::{self, Tree}; use crate::core::{ - self, Clipboard, Element, Length, Point, Rectangle, Shell, Size, Widget, + self, Clipboard, Element, Length, Point, Rectangle, Shell, Size, Vector, + Widget, }; use crate::horizontal_space; use crate::runtime::overlay::Nested; @@ -367,9 +368,10 @@ where renderer: &Renderer, bounds: Size, position: Point, + translation: Vector, ) -> layout::Node { self.with_overlay_maybe(|overlay| { - overlay.layout(renderer, bounds, position) + overlay.layout(renderer, bounds, position, translation) }) .unwrap_or_default() } diff --git a/widget/src/overlay/menu.rs b/widget/src/overlay/menu.rs index b293f9fa..5098fa17 100644 --- a/widget/src/overlay/menu.rs +++ b/widget/src/overlay/menu.rs @@ -236,6 +236,7 @@ where renderer: &Renderer, bounds: Size, position: Point, + _translation: Vector, ) -> layout::Node { let space_below = bounds.height - (position.y + self.target_height); let space_above = position.y; diff --git a/widget/src/tooltip.rs b/widget/src/tooltip.rs index b041d2e9..d5ee3de2 100644 --- a/widget/src/tooltip.rs +++ b/widget/src/tooltip.rs @@ -325,6 +325,7 @@ where renderer: &Renderer, bounds: Size, position: Point, + _translation: Vector, ) -> layout::Node { let viewport = Rectangle::with_size(bounds); -- cgit From f67387f2d80e744618a5f4f107509ba24802146c Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 21 Nov 2023 18:11:31 +0100 Subject: Invalidate layout when `Tooltip` changes `overlay` --- widget/src/tooltip.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'widget/src') diff --git a/widget/src/tooltip.rs b/widget/src/tooltip.rs index b041d2e9..9745bc1c 100644 --- a/widget/src/tooltip.rs +++ b/widget/src/tooltip.rs @@ -157,11 +157,19 @@ where ) -> event::Status { let state = tree.state.downcast_mut::(); + let was_idle = *state == State::Idle; + *state = cursor .position_over(layout.bounds()) .map(|cursor_position| State::Hovered { cursor_position }) .unwrap_or_default(); + let is_idle = *state == State::Idle; + + if was_idle != is_idle { + shell.invalidate_layout(); + } + self.content.as_widget_mut().on_event( &mut tree.children[0], event, @@ -289,7 +297,7 @@ pub enum Position { Right, } -#[derive(Debug, Clone, Copy, Default)] +#[derive(Debug, Clone, Copy, PartialEq, Default)] enum State { #[default] Idle, -- cgit From ab7dae554cac801aeed5d9aa4d3850d50be86263 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 28 Nov 2023 23:13:38 +0100 Subject: Provide actual bounds to `Shader` primitives ... and allow for proper translation and scissoring. --- widget/src/shader.rs | 1 - 1 file changed, 1 deletion(-) (limited to 'widget/src') diff --git a/widget/src/shader.rs b/widget/src/shader.rs index ca140627..fe6214db 100644 --- a/widget/src/shader.rs +++ b/widget/src/shader.rs @@ -17,7 +17,6 @@ use crate::renderer::wgpu::primitive::pipeline; use std::marker::PhantomData; -pub use crate::graphics::Transformation; pub use crate::renderer::wgpu::wgpu; pub use pipeline::{Primitive, Storage}; -- cgit From 936d480267578d7e80675e78ec1880aaaaab72d6 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 1 Dec 2023 16:04:27 +0100 Subject: Clip text to `viewport` bounds instead of layout bounds --- widget/src/checkbox.rs | 4 +++- widget/src/column.rs | 20 +++++++++------- widget/src/combo_box.rs | 3 ++- widget/src/container.rs | 32 +++++++++++++------------ widget/src/overlay/menu.rs | 1 + widget/src/pick_list.rs | 6 ++++- widget/src/radio.rs | 3 ++- widget/src/row.rs | 20 +++++++++------- widget/src/text_editor.rs | 3 ++- widget/src/text_input.rs | 59 ++++++++++++++++++++++------------------------ widget/src/toggler.rs | 3 ++- 11 files changed, 84 insertions(+), 70 deletions(-) (limited to 'widget/src') diff --git a/widget/src/checkbox.rs b/widget/src/checkbox.rs index d7fdf339..a0d9559b 100644 --- a/widget/src/checkbox.rs +++ b/widget/src/checkbox.rs @@ -266,7 +266,7 @@ where style: &renderer::Style, layout: Layout<'_>, cursor: mouse::Cursor, - _viewport: &Rectangle, + viewport: &Rectangle, ) { let is_mouse_over = cursor.is_over(layout.bounds()); @@ -315,6 +315,7 @@ where }, bounds.center(), custom_style.icon_color, + *viewport, ); } } @@ -330,6 +331,7 @@ where crate::text::Appearance { color: custom_style.text_color, }, + viewport, ); } } diff --git a/widget/src/column.rs b/widget/src/column.rs index 42e90ac1..abb522be 100644 --- a/widget/src/column.rs +++ b/widget/src/column.rs @@ -224,15 +224,17 @@ where cursor: mouse::Cursor, viewport: &Rectangle, ) { - for ((child, state), layout) in self - .children - .iter() - .zip(&tree.children) - .zip(layout.children()) - { - child - .as_widget() - .draw(state, renderer, theme, style, layout, cursor, viewport); + if let Some(viewport) = layout.bounds().intersection(viewport) { + for ((child, state), layout) in self + .children + .iter() + .zip(&tree.children) + .zip(layout.children()) + { + child.as_widget().draw( + state, renderer, theme, style, layout, cursor, &viewport, + ); + } } } diff --git a/widget/src/combo_box.rs b/widget/src/combo_box.rs index 768c2402..31ec27fc 100644 --- a/widget/src/combo_box.rs +++ b/widget/src/combo_box.rs @@ -622,7 +622,7 @@ where _style: &renderer::Style, layout: Layout<'_>, cursor: mouse::Cursor, - _viewport: &Rectangle, + viewport: &Rectangle, ) { let is_focused = { let text_input_state = tree.children[0] @@ -645,6 +645,7 @@ where layout, cursor, selection, + viewport, ); } diff --git a/widget/src/container.rs b/widget/src/container.rs index ee7a4965..5dd7705b 100644 --- a/widget/src/container.rs +++ b/widget/src/container.rs @@ -252,21 +252,23 @@ where ) { let style = theme.appearance(&self.style); - draw_background(renderer, &style, layout.bounds()); - - self.content.as_widget().draw( - tree, - renderer, - theme, - &renderer::Style { - text_color: style - .text_color - .unwrap_or(renderer_style.text_color), - }, - layout.children().next().unwrap(), - cursor, - viewport, - ); + if let Some(viewport) = layout.bounds().intersection(viewport) { + draw_background(renderer, &style, layout.bounds()); + + self.content.as_widget().draw( + tree, + renderer, + theme, + &renderer::Style { + text_color: style + .text_color + .unwrap_or(renderer_style.text_color), + }, + layout.children().next().unwrap(), + cursor, + &viewport, + ); + } } fn overlay<'b>( diff --git a/widget/src/overlay/menu.rs b/widget/src/overlay/menu.rs index 5098fa17..e45b44ae 100644 --- a/widget/src/overlay/menu.rs +++ b/widget/src/overlay/menu.rs @@ -544,6 +544,7 @@ where } else { appearance.text_color }, + *viewport, ); } } diff --git a/widget/src/pick_list.rs b/widget/src/pick_list.rs index 00c1a7ff..022ca8d9 100644 --- a/widget/src/pick_list.rs +++ b/widget/src/pick_list.rs @@ -235,7 +235,7 @@ where _style: &renderer::Style, layout: Layout<'_>, cursor: mouse::Cursor, - _viewport: &Rectangle, + viewport: &Rectangle, ) { let font = self.font.unwrap_or_else(|| renderer.default_font()); draw( @@ -253,6 +253,7 @@ where &self.handle, &self.style, || tree.state.downcast_ref::>(), + viewport, ); } @@ -631,6 +632,7 @@ pub fn draw<'a, T, Renderer>( handle: &Handle, style: &::Style, state: impl FnOnce() -> &'a State, + viewport: &Rectangle, ) where Renderer: text::Renderer, Renderer::Theme: StyleSheet, @@ -715,6 +717,7 @@ pub fn draw<'a, T, Renderer>( bounds.center_y(), ), style.handle_color, + *viewport, ); } @@ -743,6 +746,7 @@ pub fn draw<'a, T, Renderer>( } else { style.placeholder_color }, + *viewport, ); } } diff --git a/widget/src/radio.rs b/widget/src/radio.rs index 57acc033..ae2365dd 100644 --- a/widget/src/radio.rs +++ b/widget/src/radio.rs @@ -291,7 +291,7 @@ where style: &renderer::Style, layout: Layout<'_>, cursor: mouse::Cursor, - _viewport: &Rectangle, + viewport: &Rectangle, ) { let is_mouse_over = cursor.is_over(layout.bounds()); @@ -349,6 +349,7 @@ where crate::text::Appearance { color: custom_style.text_color, }, + viewport, ); } } diff --git a/widget/src/row.rs b/widget/src/row.rs index 7ca90fbb..d52b8c43 100644 --- a/widget/src/row.rs +++ b/widget/src/row.rs @@ -213,15 +213,17 @@ where cursor: mouse::Cursor, viewport: &Rectangle, ) { - for ((child, state), layout) in self - .children - .iter() - .zip(&tree.children) - .zip(layout.children()) - { - child - .as_widget() - .draw(state, renderer, theme, style, layout, cursor, viewport); + if let Some(viewport) = layout.bounds().intersection(viewport) { + for ((child, state), layout) in self + .children + .iter() + .zip(&tree.children) + .zip(layout.children()) + { + child.as_widget().draw( + state, renderer, theme, style, layout, cursor, &viewport, + ); + } } } diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index 1708a2e5..63d48868 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -429,7 +429,7 @@ where style: &renderer::Style, layout: Layout<'_>, cursor: mouse::Cursor, - _viewport: &Rectangle, + viewport: &Rectangle, ) { let bounds = layout.bounds(); @@ -470,6 +470,7 @@ where bounds.position() + Vector::new(self.padding.left, self.padding.top), style.text_color, + *viewport, ); let translation = Vector::new( diff --git a/widget/src/text_input.rs b/widget/src/text_input.rs index 27efe755..b56e3167 100644 --- a/widget/src/text_input.rs +++ b/widget/src/text_input.rs @@ -238,6 +238,7 @@ where layout: Layout<'_>, cursor: mouse::Cursor, value: Option<&Value>, + viewport: &Rectangle, ) { draw( renderer, @@ -250,6 +251,7 @@ where self.is_secure, self.icon.as_ref(), &self.style, + viewport, ); } } @@ -362,7 +364,7 @@ where _style: &renderer::Style, layout: Layout<'_>, cursor: mouse::Cursor, - _viewport: &Rectangle, + viewport: &Rectangle, ) { draw( renderer, @@ -375,6 +377,7 @@ where self.is_secure, self.icon.as_ref(), &self.style, + viewport, ); } @@ -1055,6 +1058,7 @@ pub fn draw( is_secure: bool, icon: Option<&Icon>, style: &::Style, + viewport: &Rectangle, ) where Renderer: text::Renderer, Renderer::Theme: StyleSheet, @@ -1096,6 +1100,7 @@ pub fn draw( &state.icon, icon_layout.bounds().center(), appearance.icon_color, + *viewport, ); } @@ -1189,39 +1194,31 @@ pub fn draw( (None, 0.0) }; - let text_width = state.value.min_width(); - - let render = |renderer: &mut Renderer| { - if let Some((cursor, color)) = cursor { - renderer.fill_quad(cursor, color); - } else { - renderer.with_translation(Vector::ZERO, |_| {}); - } - - renderer.fill_paragraph( - if text.is_empty() { - &state.placeholder - } else { - &state.value - }, - Point::new(text_bounds.x, text_bounds.center_y()), - if text.is_empty() { - theme.placeholder_color(style) - } else if is_disabled { - theme.disabled_color(style) - } else { - theme.value_color(style) - }, - ); - }; - - if text_width > text_bounds.width { - renderer.with_layer(text_bounds, |renderer| { - renderer.with_translation(Vector::new(-offset, 0.0), render); + if let Some((cursor, color)) = cursor { + renderer.with_translation(Vector::new(-offset, 0.0), |renderer| { + renderer.fill_quad(cursor, color) }); } else { - render(renderer); + renderer.with_translation(Vector::ZERO, |_| {}); } + + renderer.fill_paragraph( + if text.is_empty() { + &state.placeholder + } else { + &state.value + }, + Point::new(text_bounds.x, text_bounds.center_y()) + - Vector::new(offset, 0.0), + if text.is_empty() { + theme.placeholder_color(style) + } else if is_disabled { + theme.disabled_color(style) + } else { + theme.value_color(style) + }, + text_bounds, + ); } /// Computes the current [`mouse::Interaction`] of the [`TextInput`]. diff --git a/widget/src/toggler.rs b/widget/src/toggler.rs index 476c8330..d8723080 100644 --- a/widget/src/toggler.rs +++ b/widget/src/toggler.rs @@ -266,7 +266,7 @@ where style: &renderer::Style, layout: Layout<'_>, cursor: mouse::Cursor, - _viewport: &Rectangle, + viewport: &Rectangle, ) { /// Makes sure that the border radius of the toggler looks good at every size. const BORDER_RADIUS_RATIO: f32 = 32.0 / 13.0; @@ -287,6 +287,7 @@ where label_layout, tree.state.downcast_ref(), crate::text::Appearance::default(), + viewport, ); } -- cgit From 43a7cc2222750b1cada1663b29278b29d3ea232c Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 1 Dec 2023 16:10:37 +0100 Subject: Fix `clippy` lint --- widget/src/text_input.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'widget/src') diff --git a/widget/src/text_input.rs b/widget/src/text_input.rs index b56e3167..ab0e2412 100644 --- a/widget/src/text_input.rs +++ b/widget/src/text_input.rs @@ -1196,7 +1196,7 @@ pub fn draw( if let Some((cursor, color)) = cursor { renderer.with_translation(Vector::new(-offset, 0.0), |renderer| { - renderer.fill_quad(cursor, color) + renderer.fill_quad(cursor, color); }); } else { renderer.with_translation(Vector::ZERO, |_| {}); -- cgit From 07b0aed5d35013a17deea7ce7824c744decef568 Mon Sep 17 00:00:00 2001 From: Bingus Date: Wed, 6 Dec 2023 14:52:53 -0800 Subject: Added the ability to change the style of a TextEditor --- widget/src/text_editor.rs | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'widget/src') diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index 63d48868..a2a186f0 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -128,6 +128,15 @@ where highlighter_format: to_format, } } + + /// Sets the style of the [`TextEditor`]. + pub fn style( + mut self, + style: impl Into<::Style>, + ) -> Self { + self.style = style.into(); + self + } } /// The content of a [`TextEditor`]. -- cgit From b54f27d30deec672012c4a63c65d34641b40a9d5 Mon Sep 17 00:00:00 2001 From: hicaru Date: Tue, 12 Dec 2023 14:02:15 +0500 Subject: added svg hover, for styles impl --- widget/src/svg.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'widget/src') diff --git a/widget/src/svg.rs b/widget/src/svg.rs index 2d01d1ab..f9b16e1a 100644 --- a/widget/src/svg.rs +++ b/widget/src/svg.rs @@ -145,7 +145,7 @@ where theme: &Renderer::Theme, _style: &renderer::Style, layout: Layout<'_>, - _cursor: mouse::Cursor, + cursor: mouse::Cursor, _viewport: &Rectangle, ) { let Size { width, height } = renderer.dimensions(&self.handle); @@ -153,6 +153,7 @@ where let bounds = layout.bounds(); let adjusted_fit = self.content_fit.fit(image_size, bounds.size()); + let is_mouse_over = cursor.is_over(bounds); let render = |renderer: &mut Renderer| { let offset = Vector::new( @@ -166,7 +167,11 @@ where ..bounds }; - let appearance = theme.appearance(&self.style); + let appearance = if is_mouse_over { + theme.appearance(&self.style) + } else { + theme.hovered(&self.style) + }; renderer.draw( self.handle.clone(), -- cgit From 116fb666b05d57df6f70631b11fc8732ed33f71b Mon Sep 17 00:00:00 2001 From: Joao Freitas <51237625+jhff@users.noreply.github.com> Date: Fri, 15 Dec 2023 10:02:13 +0000 Subject: Add deadband distance before initiating drag action on pane grid --- widget/src/pane_grid.rs | 69 ++++++++++++++++++++++++++++++++----------- widget/src/pane_grid/state.rs | 17 +++++++++++ 2 files changed, 68 insertions(+), 18 deletions(-) (limited to 'widget/src') diff --git a/widget/src/pane_grid.rs b/widget/src/pane_grid.rs index 2d25a543..7057fe59 100644 --- a/widget/src/pane_grid.rs +++ b/widget/src/pane_grid.rs @@ -531,6 +531,8 @@ pub fn update<'a, Message, T: Draggable>( on_drag: &Option Message + 'a>>, on_resize: &Option<(f32, Box Message + 'a>)>, ) -> event::Status { + const DRAG_DEADBAND_DISTANCE: f32 = 10.0; + let mut event_status = event::Status::Ignored; match event { @@ -572,7 +574,6 @@ pub fn update<'a, Message, T: Draggable>( shell, contents, on_click, - on_drag, ); } } @@ -584,7 +585,6 @@ pub fn update<'a, Message, T: Draggable>( shell, contents, on_click, - on_drag, ); } } @@ -637,7 +637,49 @@ pub fn update<'a, Message, T: Draggable>( } Event::Mouse(mouse::Event::CursorMoved { .. }) | Event::Touch(touch::Event::FingerMoved { .. }) => { - if let Some((_, on_resize)) = on_resize { + if let Some((_, origin)) = action.clicked_pane() { + if let Some(on_drag) = &on_drag { + let bounds = layout.bounds(); + + if let Some(cursor_position) = cursor.position_over(bounds) + { + let mut clicked_region = contents + .zip(layout.children()) + .filter(|(_, layout)| { + layout.bounds().contains(cursor_position) + }); + + if let Some(((pane, content), layout)) = + clicked_region.next() + { + if content + .can_be_dragged_at(layout, cursor_position) + { + let pane_position = layout.position(); + + let new_origin = cursor_position + - Vector::new( + pane_position.x, + pane_position.y, + ); + + if new_origin.distance(origin) + > DRAG_DEADBAND_DISTANCE + { + *action = state::Action::Dragging { + pane, + origin, + }; + + shell.publish(on_drag(DragEvent::Picked { + pane, + })); + } + } + } + } + } + } else if let Some((_, on_resize)) = on_resize { if let Some((split, _)) = action.picked_split() { let bounds = layout.bounds(); @@ -712,7 +754,6 @@ fn click_pane<'a, Message, T>( shell: &mut Shell<'_, Message>, contents: impl Iterator, on_click: &Option Message + 'a>>, - on_drag: &Option Message + 'a>>, ) where T: Draggable, { @@ -720,23 +761,15 @@ fn click_pane<'a, Message, T>( .zip(layout.children()) .filter(|(_, layout)| layout.bounds().contains(cursor_position)); - if let Some(((pane, content), layout)) = clicked_region.next() { + if let Some(((pane, _), layout)) = clicked_region.next() { if let Some(on_click) = &on_click { shell.publish(on_click(pane)); } - if let Some(on_drag) = &on_drag { - if content.can_be_dragged_at(layout, cursor_position) { - let pane_position = layout.position(); - - let origin = cursor_position - - Vector::new(pane_position.x, pane_position.y); - - *action = state::Action::Dragging { pane, origin }; - - shell.publish(on_drag(DragEvent::Picked { pane })); - } - } + let pane_position = layout.position(); + let origin = + cursor_position - Vector::new(pane_position.x, pane_position.y); + *action = state::Action::Clicking { pane, origin }; } } @@ -749,7 +782,7 @@ pub fn mouse_interaction( spacing: f32, resize_leeway: Option, ) -> Option { - if action.picked_pane().is_some() { + if action.clicked_pane().is_some() || action.picked_pane().is_some() { return Some(mouse::Interaction::Grabbing); } diff --git a/widget/src/pane_grid/state.rs b/widget/src/pane_grid/state.rs index 481cd770..5d1fe254 100644 --- a/widget/src/pane_grid/state.rs +++ b/widget/src/pane_grid/state.rs @@ -403,6 +403,15 @@ pub enum Action { /// /// [`PaneGrid`]: super::PaneGrid Idle, + /// A [`Pane`] in the [`PaneGrid`] is being clicked. + /// + /// [`PaneGrid`]: super::PaneGrid + Clicking { + /// The [`Pane`] being clicked. + pane: Pane, + /// The starting [`Point`] of the click interaction. + origin: Point, + }, /// A [`Pane`] in the [`PaneGrid`] is being dragged. /// /// [`PaneGrid`]: super::PaneGrid @@ -432,6 +441,14 @@ impl Action { } } + /// Returns the current [`Pane`] that is being clicked, if any. + pub fn clicked_pane(&self) -> Option<(Pane, Point)> { + match *self { + Action::Clicking { pane, origin, .. } => Some((pane, origin)), + _ => None, + } + } + /// Returns the current [`Split`] that is being dragged, if any. pub fn picked_split(&self) -> Option<(Split, Axis)> { match *self { -- cgit From e819c2390bad76e811265245bd5fab63fc30a8b2 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 15 Dec 2023 13:15:44 +0100 Subject: Update `winit` to `0.29.4` --- widget/src/canvas/event.rs | 2 +- widget/src/shader/event.rs | 2 +- widget/src/text_editor.rs | 12 ++++++---- widget/src/text_input.rs | 56 ++++++++++++++++++++++------------------------ 4 files changed, 37 insertions(+), 35 deletions(-) (limited to 'widget/src') diff --git a/widget/src/canvas/event.rs b/widget/src/canvas/event.rs index 1288365f..a8eb47f7 100644 --- a/widget/src/canvas/event.rs +++ b/widget/src/canvas/event.rs @@ -8,7 +8,7 @@ pub use crate::core::event::Status; /// A [`Canvas`] event. /// /// [`Canvas`]: crate::Canvas -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub enum Event { /// A mouse event. Mouse(mouse::Event), diff --git a/widget/src/shader/event.rs b/widget/src/shader/event.rs index 1cc484fb..005c8725 100644 --- a/widget/src/shader/event.rs +++ b/widget/src/shader/event.rs @@ -9,7 +9,7 @@ pub use crate::core::event::Status; /// A [`Shader`] event. /// /// [`Shader`]: crate::Shader -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub enum Event { /// A mouse event. Mouse(mouse::Event), diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index a2a186f0..3c0a1806 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -649,6 +649,7 @@ impl Update { keyboard::Event::KeyPressed { key_code, modifiers, + text, } if state.is_focused => { if let Some(motion) = motion(key_code) { let motion = @@ -678,12 +679,15 @@ impl Update { { Some(Self::Paste) } - _ => None, + _ => { + let text = text?; + + edit(Edit::Insert( + text.chars().next().unwrap_or_default(), + )) + } } } - keyboard::Event::CharacterReceived(c) if state.is_focused => { - edit(Edit::Insert(c)) - } _ => None, }, _ => None, diff --git a/widget/src/text_input.rs b/widget/src/text_input.rs index 65d3e1eb..8ba4bd71 100644 --- a/widget/src/text_input.rs +++ b/widget/src/text_input.rs @@ -752,34 +752,9 @@ where return event::Status::Captured; } } - Event::Keyboard(keyboard::Event::CharacterReceived(c)) => { - let state = state(); - - if let Some(focus) = &mut state.is_focused { - let Some(on_input) = on_input else { - return event::Status::Ignored; - }; - - 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); - - let message = (on_input)(editor.contents()); - shell.publish(message); - - focus.updated_at = Instant::now(); - - update_cache(state, value); - - return event::Status::Captured; - } - } - } - Event::Keyboard(keyboard::Event::KeyPressed { key_code, .. }) => { + Event::Keyboard(keyboard::Event::KeyPressed { + key_code, text, .. + }) => { let state = state(); if let Some(focus) = &mut state.is_focused { @@ -971,7 +946,30 @@ where | keyboard::KeyCode::Down => { return event::Status::Ignored; } - _ => {} + _ => { + if let Some(text) = text { + let c = text.chars().next().unwrap_or_default(); + + 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); + + let message = (on_input)(editor.contents()); + shell.publish(message); + + focus.updated_at = Instant::now(); + + update_cache(state, value); + + return event::Status::Captured; + } + } + } } return event::Status::Captured; -- cgit From 9bbf7822e9eae4c7d0b41c2eea14e261119b1d23 Mon Sep 17 00:00:00 2001 From: Giuliano Bellini s294739 Date: Sat, 23 Dec 2023 00:17:10 +0100 Subject: added text::Shaping to Tooltip --- widget/src/tooltip.rs | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'widget/src') diff --git a/widget/src/tooltip.rs b/widget/src/tooltip.rs index 9e102c56..b888980a 100644 --- a/widget/src/tooltip.rs +++ b/widget/src/tooltip.rs @@ -64,6 +64,12 @@ where self } + /// Sets the [`text::Shaping`] strategy of the [`Tooltip`]. + pub fn text_shaping(mut self, shaping: text::Shaping) -> Self { + self.tooltip = self.tooltip.shaping(shaping); + self + } + /// Sets the font of the [`Tooltip`]. /// /// [`Font`]: Renderer::Font -- cgit From 0655a20ad119e2e9790afcc45039fd4ac0e7d432 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 16 Mar 2023 20:23:25 +0100 Subject: Make `Shrink` have priority over `Fill` in layout --- widget/src/button.rs | 13 ++++++--- widget/src/canvas.rs | 3 +-- widget/src/column.rs | 9 +++---- widget/src/container.rs | 33 ++++++++++++----------- widget/src/image.rs | 2 +- widget/src/image/viewer.rs | 9 ++++--- widget/src/keyed/column.rs | 2 ++ widget/src/overlay/menu.rs | 12 ++++----- widget/src/pane_grid.rs | 9 +++---- widget/src/pane_grid/content.rs | 9 ++++--- widget/src/pane_grid/title_bar.rs | 18 ++++++------- widget/src/pick_list.rs | 7 +++-- widget/src/progress_bar.rs | 10 +++---- widget/src/row.rs | 6 ++--- widget/src/rule.rs | 4 +-- widget/src/scrollable.rs | 2 +- widget/src/shader.rs | 2 +- widget/src/slider.rs | 3 +-- widget/src/space.rs | 4 +-- widget/src/svg.rs | 5 +--- widget/src/text_editor.rs | 2 +- widget/src/text_input.rs | 57 +++++++++++++++++++-------------------- widget/src/tooltip.rs | 2 +- widget/src/vertical_slider.rs | 3 +-- 24 files changed, 111 insertions(+), 115 deletions(-) (limited to 'widget/src') diff --git a/widget/src/button.rs b/widget/src/button.rs index 384a3156..ba68caa5 100644 --- a/widget/src/button.rs +++ b/widget/src/button.rs @@ -433,13 +433,18 @@ pub fn layout( ) -> layout::Node { let limits = limits.width(width).height(height); - let mut content = layout_content(&limits.pad(padding)); + let content = layout_content(&limits.shrink(padding)); let padding = padding.fit(content.size(), limits.max()); - let size = limits.pad(padding).resolve(content.size()).pad(padding); - content.move_to(Point::new(padding.left, padding.top)); + let size = limits + .shrink(padding) + .resolve(content.size(), width, height) + .expand(padding); - layout::Node::with_children(size, vec![content]) + layout::Node::with_children( + size, + vec![content.move_to(Point::new(padding.left, padding.top))], + ) } /// Returns the [`mouse::Interaction`] of a [`Button`]. diff --git a/widget/src/canvas.rs b/widget/src/canvas.rs index 390f4d92..9e33c113 100644 --- a/widget/src/canvas.rs +++ b/widget/src/canvas.rs @@ -133,8 +133,7 @@ where _renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - let limits = limits.width(self.width).height(self.height); - let size = limits.resolve(Size::ZERO); + let size = limits.resolve(Size::ZERO, self.width, self.height); layout::Node::new(size) } diff --git a/widget/src/column.rs b/widget/src/column.rs index abb522be..526509bb 100644 --- a/widget/src/column.rs +++ b/widget/src/column.rs @@ -35,7 +35,7 @@ impl<'a, Message, Renderer> Column<'a, Message, Renderer> { Column { spacing: 0.0, padding: Padding::ZERO, - width: Length::Shrink, + width: Length::Fill, height: Length::Shrink, max_width: f32::INFINITY, align_items: Alignment::Start, @@ -126,15 +126,14 @@ where renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - let limits = limits - .max_width(self.max_width) - .width(self.width) - .height(self.height); + let limits = limits.max_width(self.max_width); layout::flex::resolve( layout::flex::Axis::Vertical, renderer, &limits, + self.width, + self.height, self.padding, self.spacing, self.align_items, diff --git a/widget/src/container.rs b/widget/src/container.rs index 5dd7705b..b41a6023 100644 --- a/widget/src/container.rs +++ b/widget/src/container.rs @@ -312,24 +312,27 @@ pub fn layout( layout_content: impl FnOnce(&layout::Limits) -> layout::Node, ) -> layout::Node { let limits = limits - .loose() - .max_width(max_width) - .max_height(max_height) .width(width) - .height(height); + .height(height) + .max_width(max_width) + .max_height(max_height); - let mut content = layout_content(&limits.pad(padding).loose()); + let content = layout_content(&limits.shrink(padding).loose()); let padding = padding.fit(content.size(), limits.max()); - let size = limits.pad(padding).resolve(content.size()); - - content.move_to(Point::new(padding.left, padding.top)); - content.align( - Alignment::from(horizontal_alignment), - Alignment::from(vertical_alignment), - size, - ); - - layout::Node::with_children(size.pad(padding), vec![content]) + let size = limits + .shrink(padding) + .resolve(content.size(), width, height); + + layout::Node::with_children( + size.expand(padding), + vec![content + .move_to(Point::new(padding.left, padding.top)) + .align( + Alignment::from(horizontal_alignment), + Alignment::from(vertical_alignment), + size, + )], + ) } /// Draws the background of a [`Container`] given its [`Appearance`] and its `bounds`. diff --git a/widget/src/image.rs b/widget/src/image.rs index 67699102..b5f1e907 100644 --- a/widget/src/image.rs +++ b/widget/src/image.rs @@ -99,7 +99,7 @@ where }; // The size to be available to the widget prior to `Shrink`ing - let raw_size = limits.width(width).height(height).resolve(image_size); + let raw_size = limits.resolve(image_size, width, height); // The uncropped size of the image when fit to the bounds above let full_size = content_fit.fit(image_size, raw_size); diff --git a/widget/src/image/viewer.rs b/widget/src/image/viewer.rs index 68015ba8..23c4fe86 100644 --- a/widget/src/image/viewer.rs +++ b/widget/src/image/viewer.rs @@ -113,10 +113,11 @@ where ) -> layout::Node { let Size { width, height } = renderer.dimensions(&self.handle); - let mut size = limits - .width(self.width) - .height(self.height) - .resolve(Size::new(width as f32, height as f32)); + let mut size = limits.resolve( + Size::new(width as f32, height as f32), + self.width, + self.height, + ); let expansion_size = if height > width { self.width diff --git a/widget/src/keyed/column.rs b/widget/src/keyed/column.rs index 0ef82407..1b53b43a 100644 --- a/widget/src/keyed/column.rs +++ b/widget/src/keyed/column.rs @@ -196,6 +196,8 @@ where layout::flex::Axis::Vertical, renderer, &limits, + self.width, + self.height, self.padding, self.spacing, self.align_items, diff --git a/widget/src/overlay/menu.rs b/widget/src/overlay/menu.rs index e45b44ae..ef39a952 100644 --- a/widget/src/overlay/menu.rs +++ b/widget/src/overlay/menu.rs @@ -254,15 +254,14 @@ where ) .width(self.width); - let mut node = self.container.layout(self.state, renderer, &limits); + let node = self.container.layout(self.state, renderer, &limits); + let size = node.size(); node.move_to(if space_below > space_above { position + Vector::new(0.0, self.target_height) } else { - position - Vector::new(0.0, node.size().height) - }); - - node + position - Vector::new(0.0, size.height) + }) } fn on_event( @@ -359,7 +358,6 @@ where ) -> layout::Node { use std::f32; - let limits = limits.width(Length::Fill).height(Length::Shrink); let text_size = self.text_size.unwrap_or_else(|| renderer.default_size()); @@ -372,7 +370,7 @@ where * self.options.len() as f32, ); - limits.resolve(intrinsic) + limits.resolve(intrinsic, Length::Fill, Length::Shrink) }; layout::Node::new(size) diff --git a/widget/src/pane_grid.rs b/widget/src/pane_grid.rs index 7057fe59..3d799fd3 100644 --- a/widget/src/pane_grid.rs +++ b/widget/src/pane_grid.rs @@ -490,8 +490,7 @@ pub fn layout( &layout::Limits, ) -> layout::Node, ) -> layout::Node { - let limits = limits.width(width).height(height); - let size = limits.resolve(Size::ZERO); + let size = limits.resolve(Size::ZERO, width, height); let regions = node.pane_regions(spacing, size); let children = contents @@ -500,16 +499,14 @@ pub fn layout( let region = regions.get(&pane)?; let size = Size::new(region.width, region.height); - let mut node = layout_content( + let node = layout_content( content, tree, renderer, &layout::Limits::new(size, size), ); - node.move_to(Point::new(region.x, region.y)); - - Some(node) + Some(node.move_to(Point::new(region.x, region.y))) }) .collect(); diff --git a/widget/src/pane_grid/content.rs b/widget/src/pane_grid/content.rs index 826ea663..ee00f186 100644 --- a/widget/src/pane_grid/content.rs +++ b/widget/src/pane_grid/content.rs @@ -165,7 +165,7 @@ where let title_bar_size = title_bar_layout.size(); - let mut body_layout = self.body.as_widget().layout( + let body_layout = self.body.as_widget().layout( &mut tree.children[0], renderer, &layout::Limits::new( @@ -177,11 +177,12 @@ where ), ); - body_layout.move_to(Point::new(0.0, title_bar_size.height)); - layout::Node::with_children( max_size, - vec![title_bar_layout, body_layout], + vec![ + title_bar_layout, + body_layout.move_to(Point::new(0.0, title_bar_size.height)), + ], ) } else { self.body.as_widget().layout( diff --git a/widget/src/pane_grid/title_bar.rs b/widget/src/pane_grid/title_bar.rs index f4dbb6b1..eb21b743 100644 --- a/widget/src/pane_grid/title_bar.rs +++ b/widget/src/pane_grid/title_bar.rs @@ -217,7 +217,7 @@ where renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - let limits = limits.pad(self.padding); + let limits = limits.shrink(self.padding); let max_size = limits.max(); let title_layout = self.content.as_widget().layout( @@ -228,8 +228,8 @@ where let title_size = title_layout.size(); - let mut node = if let Some(controls) = &self.controls { - let mut controls_layout = controls.as_widget().layout( + let node = if let Some(controls) = &self.controls { + let controls_layout = controls.as_widget().layout( &mut tree.children[1], renderer, &layout::Limits::new(Size::ZERO, max_size), @@ -240,11 +240,13 @@ where let height = title_size.height.max(controls_size.height); - controls_layout.move_to(Point::new(space_before_controls, 0.0)); - layout::Node::with_children( Size::new(max_size.width, height), - vec![title_layout, controls_layout], + vec![ + title_layout, + controls_layout + .move_to(Point::new(space_before_controls, 0.0)), + ], ) } else { layout::Node::with_children( @@ -253,9 +255,7 @@ where ) }; - node.move_to(Point::new(self.padding.left, self.padding.top)); - - layout::Node::with_children(node.size().pad(self.padding), vec![node]) + layout::Node::container(node, self.padding) } pub(crate) fn operate( diff --git a/widget/src/pick_list.rs b/widget/src/pick_list.rs index 022ca8d9..13110725 100644 --- a/widget/src/pick_list.rs +++ b/widget/src/pick_list.rs @@ -393,7 +393,7 @@ where { use std::f32; - let limits = limits.width(width).height(Length::Shrink).pad(padding); + let limits = limits.width(width).height(Length::Shrink); let font = font.unwrap_or_else(|| renderer.default_font()); let text_size = text_size.unwrap_or_else(|| renderer.default_size()); @@ -451,7 +451,10 @@ where f32::from(text_line_height.to_absolute(text_size)), ); - limits.resolve(intrinsic).pad(padding) + limits + .shrink(padding) + .resolve(intrinsic, width, Length::Shrink) + .expand(padding) }; layout::Node::new(size) diff --git a/widget/src/progress_bar.rs b/widget/src/progress_bar.rs index 07de72d5..b84ab2dd 100644 --- a/widget/src/progress_bar.rs +++ b/widget/src/progress_bar.rs @@ -99,11 +99,11 @@ where _renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - let limits = limits - .width(self.width) - .height(self.height.unwrap_or(Length::Fixed(Self::DEFAULT_HEIGHT))); - - let size = limits.resolve(Size::ZERO); + let size = limits.resolve( + Size::ZERO, + self.width, + self.height.unwrap_or(Length::Fixed(Self::DEFAULT_HEIGHT)), + ); layout::Node::new(size) } diff --git a/widget/src/row.rs b/widget/src/row.rs index d52b8c43..650c2c7d 100644 --- a/widget/src/row.rs +++ b/widget/src/row.rs @@ -34,7 +34,7 @@ impl<'a, Message, Renderer> Row<'a, Message, Renderer> { Row { spacing: 0.0, padding: Padding::ZERO, - width: Length::Shrink, + width: Length::Fill, height: Length::Shrink, align_items: Alignment::Start, children, @@ -118,12 +118,12 @@ where renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - let limits = limits.width(self.width).height(self.height); - layout::flex::resolve( layout::flex::Axis::Horizontal, renderer, &limits, + self.width, + self.height, self.padding, self.spacing, self.align_items, diff --git a/widget/src/rule.rs b/widget/src/rule.rs index b5c5fa55..ecaedf60 100644 --- a/widget/src/rule.rs +++ b/widget/src/rule.rs @@ -76,9 +76,7 @@ where _renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - let limits = limits.width(self.width).height(self.height); - - layout::Node::new(limits.resolve(Size::ZERO)) + layout::Node::new(limits.resolve(Size::ZERO, self.width, self.height)) } fn draw( diff --git a/widget/src/scrollable.rs b/widget/src/scrollable.rs index 49aed2f0..525463c4 100644 --- a/widget/src/scrollable.rs +++ b/widget/src/scrollable.rs @@ -489,7 +489,7 @@ pub fn layout( ); let content = layout_content(renderer, &child_limits); - let size = limits.resolve(content.size()); + let size = limits.resolve(content.size(), width, height); layout::Node::with_children(size, vec![content]) } diff --git a/widget/src/shader.rs b/widget/src/shader.rs index 8e334693..5b18ec7d 100644 --- a/widget/src/shader.rs +++ b/widget/src/shader.rs @@ -85,7 +85,7 @@ where limits: &layout::Limits, ) -> layout::Node { let limits = limits.width(self.width).height(self.height); - let size = limits.resolve(Size::ZERO); + let size = limits.resolve(Size::ZERO, self.width, self.height); layout::Node::new(size) } diff --git a/widget/src/slider.rs b/widget/src/slider.rs index ac0982c8..2b600d9d 100644 --- a/widget/src/slider.rs +++ b/widget/src/slider.rs @@ -173,8 +173,7 @@ where _renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - let limits = limits.width(self.width).height(self.height); - let size = limits.resolve(Size::ZERO); + let size = limits.resolve(Size::ZERO, self.width, self.height); layout::Node::new(size) } diff --git a/widget/src/space.rs b/widget/src/space.rs index e5a8f169..afa9a7c8 100644 --- a/widget/src/space.rs +++ b/widget/src/space.rs @@ -59,9 +59,7 @@ where _renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - let limits = limits.width(self.width).height(self.height); - - layout::Node::new(limits.resolve(Size::ZERO)) + layout::Node::new(limits.resolve(Size::ZERO, self.width, self.height)) } fn draw( diff --git a/widget/src/svg.rs b/widget/src/svg.rs index 2d01d1ab..8367ad18 100644 --- a/widget/src/svg.rs +++ b/widget/src/svg.rs @@ -115,10 +115,7 @@ where let image_size = Size::new(width as f32, height as f32); // The size to be available to the widget prior to `Shrink`ing - let raw_size = limits - .width(self.width) - .height(self.height) - .resolve(image_size); + let raw_size = limits.resolve(image_size, self.width, self.height); // The uncropped size of the image when fit to the bounds above let full_size = self.content_fit.fit(image_size, raw_size); diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index a2a186f0..214bce17 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -350,7 +350,7 @@ where } internal.editor.update( - limits.pad(self.padding).max(), + limits.shrink(self.padding).max(), self.font.unwrap_or_else(|| renderer.default_font()), self.text_size.unwrap_or_else(|| renderer.default_size()), self.line_height, diff --git a/widget/src/text_input.rs b/widget/src/text_input.rs index 65d3e1eb..03eb2fd0 100644 --- a/widget/src/text_input.rs +++ b/widget/src/text_input.rs @@ -506,14 +506,11 @@ where { let font = font.unwrap_or_else(|| renderer.default_font()); let text_size = size.unwrap_or_else(|| renderer.default_size()); - let padding = padding.fit(Size::ZERO, limits.max()); - let limits = limits - .width(width) - .pad(padding) - .height(line_height.to_absolute(text_size)); + let height = line_height.to_absolute(text_size); - let text_bounds = limits.resolve(Size::ZERO); + let limits = limits.width(width).shrink(padding).height(height); + let text_bounds = limits.resolve(Size::ZERO, width, height); let placeholder_text = Text { font, @@ -552,41 +549,41 @@ where let icon_width = state.icon.min_width(); - let mut text_node = layout::Node::new( - text_bounds - Size::new(icon_width + icon.spacing, 0.0), - ); - - let mut icon_node = - layout::Node::new(Size::new(icon_width, text_bounds.height)); - - match icon.side { - Side::Left => { - text_node.move_to(Point::new( + let (text_position, icon_position) = match icon.side { + Side::Left => ( + Point::new( padding.left + icon_width + icon.spacing, padding.top, - )); - - icon_node.move_to(Point::new(padding.left, padding.top)); - } - Side::Right => { - text_node.move_to(Point::new(padding.left, padding.top)); - - icon_node.move_to(Point::new( + ), + Point::new(padding.left, padding.top), + ), + Side::Right => ( + Point::new(padding.left, padding.top), + Point::new( padding.left + text_bounds.width - icon_width, padding.top, - )); - } + ), + ), }; + let text_node = layout::Node::new( + text_bounds - Size::new(icon_width + icon.spacing, 0.0), + ) + .move_to(text_position); + + let icon_node = + layout::Node::new(Size::new(icon_width, text_bounds.height)) + .move_to(icon_position); + layout::Node::with_children( - text_bounds.pad(padding), + text_bounds.expand(padding), vec![text_node, icon_node], ) } else { - let mut text = layout::Node::new(text_bounds); - text.move_to(Point::new(padding.left, padding.top)); + let text = layout::Node::new(text_bounds) + .move_to(Point::new(padding.left, padding.top)); - layout::Node::with_children(text_bounds.pad(padding), vec![text]) + layout::Node::with_children(text_bounds.expand(padding), vec![text]) } } diff --git a/widget/src/tooltip.rs b/widget/src/tooltip.rs index b888980a..adef13e4 100644 --- a/widget/src/tooltip.rs +++ b/widget/src/tooltip.rs @@ -353,7 +353,7 @@ where .then(|| viewport.size()) .unwrap_or(Size::INFINITY), ) - .pad(Padding::new(self.padding)), + .shrink(Padding::new(self.padding)), ); let text_bounds = text_layout.bounds(); diff --git a/widget/src/vertical_slider.rs b/widget/src/vertical_slider.rs index 01d3359c..e489104c 100644 --- a/widget/src/vertical_slider.rs +++ b/widget/src/vertical_slider.rs @@ -170,8 +170,7 @@ where _renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - let limits = limits.width(self.width).height(self.height); - let size = limits.resolve(Size::ZERO); + let size = limits.resolve(Size::ZERO, self.width, self.height); layout::Node::new(size) } -- cgit From ed3b3930180f1971da25fdcc66a4130da32400ba Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 16 Mar 2023 20:37:24 +0100 Subject: Fix needless borrow in `row::layout` --- widget/src/row.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'widget/src') diff --git a/widget/src/row.rs b/widget/src/row.rs index 650c2c7d..c4a1db56 100644 --- a/widget/src/row.rs +++ b/widget/src/row.rs @@ -121,7 +121,7 @@ where layout::flex::resolve( layout::flex::Axis::Horizontal, renderer, - &limits, + limits, self.width, self.height, self.padding, -- cgit From 6c9dfbf01ec865f2ccf3b33cc8902d4e7141cd4f Mon Sep 17 00:00:00 2001 From: William Shere <7796394+william-shere@users.noreply.github.com> Date: Fri, 5 Jan 2024 13:50:38 +0000 Subject: Fix doc to include missing feature tags Helper functions behind `lazy` feature were missing the tag in the documentation. --- widget/src/lazy/helpers.rs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'widget/src') diff --git a/widget/src/lazy/helpers.rs b/widget/src/lazy/helpers.rs index 8ca9cb86..5dc60d52 100644 --- a/widget/src/lazy/helpers.rs +++ b/widget/src/lazy/helpers.rs @@ -6,6 +6,7 @@ use std::hash::Hash; /// Creates a new [`Lazy`] widget with the given data `Dependency` and a /// closure that can turn this data into a widget tree. +#[cfg(feature = "lazy")] pub fn lazy<'a, Message, Renderer, Dependency, View>( dependency: Dependency, view: impl Fn(&Dependency) -> View + 'a, @@ -19,6 +20,7 @@ where /// Turns an implementor of [`Component`] into an [`Element`] that can be /// embedded in any application. +#[cfg(feature = "lazy")] pub fn component<'a, C, Message, Renderer>( component: C, ) -> Element<'a, Message, Renderer> @@ -37,6 +39,7 @@ where /// The `view` closure will be provided with the current [`Size`] of /// the [`Responsive`] widget and, therefore, can be used to build the /// contents of the widget in a responsive way. +#[cfg(feature = "lazy")] pub fn responsive<'a, Message, Renderer>( f: impl Fn(Size) -> Element<'a, Message, Renderer> + 'a, ) -> Responsive<'a, Message, Renderer> -- cgit From b083eda663b8939e1c3e86b5ce2cb5fa8fc80ccb Mon Sep 17 00:00:00 2001 From: Héctor Ramón Date: Tue, 9 Jan 2024 02:03:13 +0100 Subject: Fix `Svg` styling --- widget/src/svg.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'widget/src') diff --git a/widget/src/svg.rs b/widget/src/svg.rs index f9b16e1a..05c9265b 100644 --- a/widget/src/svg.rs +++ b/widget/src/svg.rs @@ -168,9 +168,9 @@ where }; let appearance = if is_mouse_over { - theme.appearance(&self.style) - } else { theme.hovered(&self.style) + } else { + theme.appearance(&self.style) }; renderer.draw( -- cgit From 0322e820eb40d36a7425246278b7bcb22b7010aa Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 27 Mar 2023 15:43:52 +0200 Subject: Create `layout` example --- widget/src/column.rs | 2 +- widget/src/row.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'widget/src') diff --git a/widget/src/column.rs b/widget/src/column.rs index 526509bb..80327458 100644 --- a/widget/src/column.rs +++ b/widget/src/column.rs @@ -35,7 +35,7 @@ impl<'a, Message, Renderer> Column<'a, Message, Renderer> { Column { spacing: 0.0, padding: Padding::ZERO, - width: Length::Fill, + width: Length::Shrink, height: Length::Shrink, max_width: f32::INFINITY, align_items: Alignment::Start, diff --git a/widget/src/row.rs b/widget/src/row.rs index c4a1db56..50fc4de0 100644 --- a/widget/src/row.rs +++ b/widget/src/row.rs @@ -34,7 +34,7 @@ impl<'a, Message, Renderer> Row<'a, Message, Renderer> { Row { spacing: 0.0, padding: Padding::ZERO, - width: Length::Fill, + width: Length::Shrink, height: Length::Shrink, align_items: Alignment::Start, children, -- cgit From 22226394f7b1a0e0205b9bb5b3ef9b85a3b406f5 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 5 Jan 2024 17:24:43 +0100 Subject: Introduce `Widget::size_hint` and fix further layout inconsistencies --- widget/src/column.rs | 39 ++++++++++++++++++++++++++++----------- widget/src/container.rs | 15 +++++++++++++-- widget/src/helpers.rs | 22 ++++++++++++++-------- widget/src/lazy.rs | 7 +++++++ widget/src/lazy/component.rs | 7 +++++++ widget/src/row.rs | 41 ++++++++++++++++++++++++++++++----------- 6 files changed, 99 insertions(+), 32 deletions(-) (limited to 'widget/src') diff --git a/widget/src/column.rs b/widget/src/column.rs index 80327458..52cf35ce 100644 --- a/widget/src/column.rs +++ b/widget/src/column.rs @@ -22,16 +22,12 @@ pub struct Column<'a, Message, Renderer = crate::Renderer> { children: Vec>, } -impl<'a, Message, Renderer> Column<'a, Message, Renderer> { +impl<'a, Message, Renderer> Column<'a, Message, Renderer> +where + Renderer: crate::core::Renderer, +{ /// Creates an empty [`Column`]. pub fn new() -> Self { - Self::with_children(Vec::new()) - } - - /// Creates a [`Column`] with the given elements. - pub fn with_children( - children: Vec>, - ) -> Self { Column { spacing: 0.0, padding: Padding::ZERO, @@ -39,10 +35,17 @@ impl<'a, Message, Renderer> Column<'a, Message, Renderer> { height: Length::Shrink, max_width: f32::INFINITY, align_items: Alignment::Start, - children, + children: Vec::new(), } } + /// Creates a [`Column`] with the given elements. + pub fn with_children( + children: impl Iterator>, + ) -> Self { + children.fold(Self::new(), |column, element| column.push(element)) + } + /// Sets the vertical spacing _between_ elements. /// /// Custom margins per element do not exist in iced. You should use this @@ -88,12 +91,26 @@ impl<'a, Message, Renderer> Column<'a, Message, Renderer> { mut self, child: impl Into>, ) -> Self { - self.children.push(child.into()); + let child = child.into(); + let size = child.as_widget().size_hint(); + + if size.width.is_fill() { + self.width = Length::Fill; + } + + if size.height.is_fill() { + self.height = Length::Fill; + } + + self.children.push(child); self } } -impl<'a, Message, Renderer> Default for Column<'a, Message, Renderer> { +impl<'a, Message, Renderer> Default for Column<'a, Message, Renderer> +where + Renderer: crate::core::Renderer, +{ fn default() -> Self { Self::new() } diff --git a/widget/src/container.rs b/widget/src/container.rs index b41a6023..fbc68db7 100644 --- a/widget/src/container.rs +++ b/widget/src/container.rs @@ -46,11 +46,22 @@ where where T: Into>, { + let content = content.into(); + let size = content.as_widget().size_hint(); + Container { id: None, padding: Padding::ZERO, - width: Length::Shrink, - height: Length::Shrink, + width: if size.width.is_fill() { + Length::Fill + } else { + Length::Shrink + }, + height: if size.height.is_fill() { + Length::Fill + } else { + Length::Shrink + }, max_width: f32::INFINITY, max_height: f32::INFINITY, horizontal_alignment: alignment::Horizontal::Left, diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index 115198fb..6eaf3392 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -34,7 +34,7 @@ macro_rules! column { $crate::Column::new() ); ($($x:expr),+ $(,)?) => ( - $crate::Column::with_children(vec![$($crate::core::Element::from($x)),+]) + $crate::Column::with_children([$($crate::core::Element::from($x)),+].into_iter()) ); } @@ -47,7 +47,7 @@ macro_rules! row { $crate::Row::new() ); ($($x:expr),+ $(,)?) => ( - $crate::Row::with_children(vec![$($crate::core::Element::from($x)),+]) + $crate::Row::with_children([$($crate::core::Element::from($x)),+].into_iter()) ); } @@ -65,9 +65,12 @@ where } /// Creates a new [`Column`] with the given children. -pub fn column( - children: Vec>, -) -> Column<'_, Message, Renderer> { +pub fn column<'a, Message, Renderer>( + children: impl Iterator>, +) -> Column<'a, Message, Renderer> +where + Renderer: core::Renderer, +{ Column::with_children(children) } @@ -84,9 +87,12 @@ where /// Creates a new [`Row`] with the given children. /// /// [`Row`]: crate::Row -pub fn row( - children: Vec>, -) -> Row<'_, Message, Renderer> { +pub fn row<'a, Message, Renderer>( + children: impl Iterator>, +) -> Row<'a, Message, Renderer> +where + Renderer: core::Renderer, +{ Row::with_children(children) } diff --git a/widget/src/lazy.rs b/widget/src/lazy.rs index 167a055d..4f6513db 100644 --- a/widget/src/lazy.rs +++ b/widget/src/lazy.rs @@ -150,6 +150,13 @@ where self.with_element(|element| element.as_widget().height()) } + fn size_hint(&self) -> Size { + Size { + width: Length::Shrink, + height: Length::Shrink, + } + } + fn layout( &self, tree: &mut Tree, diff --git a/widget/src/lazy/component.rs b/widget/src/lazy/component.rs index ad0c3823..0aff7485 100644 --- a/widget/src/lazy/component.rs +++ b/widget/src/lazy/component.rs @@ -252,6 +252,13 @@ where self.with_element(|element| element.as_widget().height()) } + fn size_hint(&self) -> Size { + Size { + width: Length::Shrink, + height: Length::Shrink, + } + } + fn layout( &self, tree: &mut Tree, diff --git a/widget/src/row.rs b/widget/src/row.rs index 50fc4de0..ef371ddb 100644 --- a/widget/src/row.rs +++ b/widget/src/row.rs @@ -21,26 +21,31 @@ pub struct Row<'a, Message, Renderer = crate::Renderer> { children: Vec>, } -impl<'a, Message, Renderer> Row<'a, Message, Renderer> { +impl<'a, Message, Renderer> Row<'a, Message, Renderer> +where + Renderer: crate::core::Renderer, +{ /// Creates an empty [`Row`]. pub fn new() -> Self { - Self::with_children(Vec::new()) - } - - /// Creates a [`Row`] with the given elements. - pub fn with_children( - children: Vec>, - ) -> Self { Row { spacing: 0.0, padding: Padding::ZERO, width: Length::Shrink, height: Length::Shrink, align_items: Alignment::Start, - children, + children: Vec::new(), } } + /// Creates a [`Row`] with the given elements. + pub fn with_children( + children: impl Iterator>, + ) -> Self { + children + .into_iter() + .fold(Self::new(), |column, element| column.push(element)) + } + /// Sets the horizontal spacing _between_ elements. /// /// Custom margins per element do not exist in iced. You should use this @@ -80,12 +85,26 @@ impl<'a, Message, Renderer> Row<'a, Message, Renderer> { mut self, child: impl Into>, ) -> Self { - self.children.push(child.into()); + let child = child.into(); + let size = child.as_widget().size_hint(); + + if size.width.is_fill() { + self.width = Length::Fill; + } + + if size.height.is_fill() { + self.height = Length::Fill; + } + + self.children.push(child); self } } -impl<'a, Message, Renderer> Default for Row<'a, Message, Renderer> { +impl<'a, Message, Renderer> Default for Row<'a, Message, Renderer> +where + Renderer: crate::core::Renderer, +{ fn default() -> Self { Self::new() } -- cgit From d278bfd21d0399009e652560afb9a4d185e92637 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 5 Jan 2024 17:46:33 +0100 Subject: Replace `width` and `height` with `Widget::size` --- widget/src/button.rs | 13 ++++++------- widget/src/canvas.rs | 16 ++++++++-------- widget/src/checkbox.rs | 11 +++++------ widget/src/column.rs | 13 ++++++------- widget/src/combo_box.rs | 12 +++++------- widget/src/container.rs | 11 +++++------ widget/src/image.rs | 11 +++++------ widget/src/image/viewer.rs | 11 +++++------ widget/src/keyed/column.rs | 13 ++++++------- widget/src/lazy.rs | 8 ++------ widget/src/lazy/component.rs | 8 ++------ widget/src/lazy/responsive.rs | 11 +++++------ widget/src/mouse_area.rs | 10 +++------- widget/src/overlay/menu.rs | 11 +++++------ widget/src/pane_grid.rs | 11 +++++------ widget/src/pick_list.rs | 11 +++++------ widget/src/progress_bar.rs | 11 +++++------ widget/src/qr_code.rs | 11 +++++------ widget/src/radio.rs | 11 +++++------ widget/src/row.rs | 13 ++++++------- widget/src/rule.rs | 11 +++++------ widget/src/scrollable.rs | 11 +++++------ widget/src/shader.rs | 11 +++++------ widget/src/slider.rs | 11 +++++------ widget/src/space.rs | 11 +++++------ widget/src/svg.rs | 11 +++++------ widget/src/text_editor.rs | 13 ++++++------- widget/src/text_input.rs | 11 +++++------ widget/src/toggler.rs | 11 +++++------ widget/src/tooltip.rs | 8 ++------ widget/src/vertical_slider.rs | 11 +++++------ 31 files changed, 152 insertions(+), 195 deletions(-) (limited to 'widget/src') diff --git a/widget/src/button.rs b/widget/src/button.rs index ba68caa5..1ce4f662 100644 --- a/widget/src/button.rs +++ b/widget/src/button.rs @@ -11,7 +11,7 @@ use crate::core::widget::tree::{self, Tree}; use crate::core::widget::Operation; use crate::core::{ Background, Clipboard, Color, Element, Layout, Length, Padding, Point, - Rectangle, Shell, Vector, Widget, + Rectangle, Shell, Size, Vector, Widget, }; pub use iced_style::button::{Appearance, StyleSheet}; @@ -149,12 +149,11 @@ where tree.diff_children(std::slice::from_ref(&self.content)); } - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - self.height + fn size(&self) -> Size { + Size { + width: self.width, + height: self.height, + } } fn layout( diff --git a/widget/src/canvas.rs b/widget/src/canvas.rs index 9e33c113..2bf09eec 100644 --- a/widget/src/canvas.rs +++ b/widget/src/canvas.rs @@ -14,8 +14,9 @@ use crate::core::layout::{self, Layout}; use crate::core::mouse; use crate::core::renderer; use crate::core::widget::tree::{self, Tree}; -use crate::core::{Clipboard, Element, Shell, Widget}; -use crate::core::{Length, Rectangle, Size, Vector}; +use crate::core::{ + Clipboard, Element, Length, Rectangle, Shell, Size, Vector, Widget, +}; use crate::graphics::geometry; use std::marker::PhantomData; @@ -119,12 +120,11 @@ where tree::State::new(P::State::default()) } - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - self.height + fn size(&self) -> Size { + Size { + width: self.width, + height: self.height, + } } fn layout( diff --git a/widget/src/checkbox.rs b/widget/src/checkbox.rs index a0d9559b..0353b3ad 100644 --- a/widget/src/checkbox.rs +++ b/widget/src/checkbox.rs @@ -174,12 +174,11 @@ where tree::State::new(widget::text::State::::default()) } - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - Length::Shrink + fn size(&self) -> Size { + Size { + width: self.width, + height: Length::Shrink, + } } fn layout( diff --git a/widget/src/column.rs b/widget/src/column.rs index 52cf35ce..9867d97e 100644 --- a/widget/src/column.rs +++ b/widget/src/column.rs @@ -7,7 +7,7 @@ use crate::core::renderer; use crate::core::widget::{Operation, Tree}; use crate::core::{ Alignment, Clipboard, Element, Layout, Length, Padding, Pixels, Rectangle, - Shell, Widget, + Shell, Size, Widget, }; /// A container that distributes its contents vertically. @@ -129,12 +129,11 @@ where tree.diff_children(&self.children); } - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - self.height + fn size(&self) -> Size { + Size { + width: self.width, + height: self.height, + } } fn layout( diff --git a/widget/src/combo_box.rs b/widget/src/combo_box.rs index 31ec27fc..1b2fa947 100644 --- a/widget/src/combo_box.rs +++ b/widget/src/combo_box.rs @@ -8,7 +8,9 @@ use crate::core::renderer; use crate::core::text; use crate::core::time::Instant; use crate::core::widget::{self, Widget}; -use crate::core::{Clipboard, Element, Length, Padding, Rectangle, Shell}; +use crate::core::{ + Clipboard, Element, Length, Padding, Rectangle, Shell, Size, +}; use crate::overlay::menu; use crate::text::LineHeight; use crate::{container, scrollable, text_input, TextInput}; @@ -297,12 +299,8 @@ where + scrollable::StyleSheet + menu::StyleSheet, { - fn width(&self) -> Length { - Widget::::width(&self.text_input) - } - - fn height(&self) -> Length { - Widget::::height(&self.text_input) + fn size(&self) -> Size { + Widget::::size(&self.text_input) } fn layout( diff --git a/widget/src/container.rs b/widget/src/container.rs index fbc68db7..93d8daba 100644 --- a/widget/src/container.rs +++ b/widget/src/container.rs @@ -163,12 +163,11 @@ where self.content.as_widget().diff(tree); } - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - self.height + fn size(&self) -> Size { + Size { + width: self.width, + height: self.height, + } } fn layout( diff --git a/widget/src/image.rs b/widget/src/image.rs index b5f1e907..6750c1b3 100644 --- a/widget/src/image.rs +++ b/widget/src/image.rs @@ -164,12 +164,11 @@ where Renderer: image::Renderer, Handle: Clone + Hash, { - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - self.height + fn size(&self) -> Size { + Size { + width: self.width, + height: self.height, + } } fn layout( diff --git a/widget/src/image/viewer.rs b/widget/src/image/viewer.rs index 23c4fe86..dc910f1f 100644 --- a/widget/src/image/viewer.rs +++ b/widget/src/image/viewer.rs @@ -97,12 +97,11 @@ where tree::State::new(State::new()) } - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - self.height + fn size(&self) -> Size { + Size { + width: self.width, + height: self.height, + } } fn layout( diff --git a/widget/src/keyed/column.rs b/widget/src/keyed/column.rs index 1b53b43a..32320300 100644 --- a/widget/src/keyed/column.rs +++ b/widget/src/keyed/column.rs @@ -8,7 +8,7 @@ use crate::core::widget::tree::{self, Tree}; use crate::core::widget::Operation; use crate::core::{ Alignment, Clipboard, Element, Layout, Length, Padding, Pixels, Rectangle, - Shell, Widget, + Shell, Size, Widget, }; /// A container that distributes its contents vertically. @@ -173,12 +173,11 @@ where } } - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - self.height + fn size(&self) -> Size { + Size { + width: self.width, + height: self.height, + } } fn layout( diff --git a/widget/src/lazy.rs b/widget/src/lazy.rs index 4f6513db..e9edbb4c 100644 --- a/widget/src/lazy.rs +++ b/widget/src/lazy.rs @@ -142,12 +142,8 @@ where } } - fn width(&self) -> Length { - self.with_element(|element| element.as_widget().width()) - } - - fn height(&self) -> Length { - self.with_element(|element| element.as_widget().height()) + fn size(&self) -> Size { + self.with_element(|element| element.as_widget().size()) } fn size_hint(&self) -> Size { diff --git a/widget/src/lazy/component.rs b/widget/src/lazy/component.rs index 0aff7485..3684e0c9 100644 --- a/widget/src/lazy/component.rs +++ b/widget/src/lazy/component.rs @@ -244,12 +244,8 @@ where self.rebuild_element_if_necessary(); } - fn width(&self) -> Length { - self.with_element(|element| element.as_widget().width()) - } - - fn height(&self) -> Length { - self.with_element(|element| element.as_widget().height()) + fn size(&self) -> Size { + self.with_element(|element| element.as_widget().size()) } fn size_hint(&self) -> Size { diff --git a/widget/src/lazy/responsive.rs b/widget/src/lazy/responsive.rs index 86d37b6c..1df0866f 100644 --- a/widget/src/lazy/responsive.rs +++ b/widget/src/lazy/responsive.rs @@ -135,12 +135,11 @@ where }) } - fn width(&self) -> Length { - Length::Fill - } - - fn height(&self) -> Length { - Length::Fill + fn size(&self) -> Size { + Size { + width: Length::Fill, + height: Length::Fill, + } } fn layout( diff --git a/widget/src/mouse_area.rs b/widget/src/mouse_area.rs index 3a5b01a3..87cac3a7 100644 --- a/widget/src/mouse_area.rs +++ b/widget/src/mouse_area.rs @@ -8,7 +8,7 @@ use crate::core::renderer; use crate::core::touch; use crate::core::widget::{tree, Operation, Tree}; use crate::core::{ - Clipboard, Element, Layout, Length, Rectangle, Shell, Widget, + Clipboard, Element, Layout, Length, Rectangle, Shell, Size, Widget, }; /// Emit messages on mouse events. @@ -110,12 +110,8 @@ where tree.diff_children(std::slice::from_ref(&self.content)); } - fn width(&self) -> Length { - self.content.as_widget().width() - } - - fn height(&self) -> Length { - self.content.as_widget().height() + fn size(&self) -> Size { + self.content.as_widget().size() } fn layout( diff --git a/widget/src/overlay/menu.rs b/widget/src/overlay/menu.rs index ef39a952..b9e06de8 100644 --- a/widget/src/overlay/menu.rs +++ b/widget/src/overlay/menu.rs @@ -342,12 +342,11 @@ where Renderer: text::Renderer, Renderer::Theme: StyleSheet, { - fn width(&self) -> Length { - Length::Fill - } - - fn height(&self) -> Length { - Length::Shrink + fn size(&self) -> Size { + Size { + width: Length::Fill, + height: Length::Shrink, + } } fn layout( diff --git a/widget/src/pane_grid.rs b/widget/src/pane_grid.rs index 3d799fd3..36c785b7 100644 --- a/widget/src/pane_grid.rs +++ b/widget/src/pane_grid.rs @@ -265,12 +265,11 @@ where } } - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - self.height + fn size(&self) -> Size { + Size { + width: self.width, + height: self.height, + } } fn layout( diff --git a/widget/src/pick_list.rs b/widget/src/pick_list.rs index 13110725..d83b0624 100644 --- a/widget/src/pick_list.rs +++ b/widget/src/pick_list.rs @@ -164,12 +164,11 @@ where tree::State::new(State::::new()) } - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - Length::Shrink + fn size(&self) -> Size { + Size { + width: self.width, + height: Length::Shrink, + } } fn layout( diff --git a/widget/src/progress_bar.rs b/widget/src/progress_bar.rs index b84ab2dd..a05923a2 100644 --- a/widget/src/progress_bar.rs +++ b/widget/src/progress_bar.rs @@ -85,12 +85,11 @@ where Renderer: crate::core::Renderer, Renderer::Theme: StyleSheet, { - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - self.height.unwrap_or(Length::Fixed(Self::DEFAULT_HEIGHT)) + fn size(&self) -> Size { + Size { + width: self.width, + height: self.height.unwrap_or(Length::Fixed(Self::DEFAULT_HEIGHT)), + } } fn layout( diff --git a/widget/src/qr_code.rs b/widget/src/qr_code.rs index 1dc4da7f..a229eb59 100644 --- a/widget/src/qr_code.rs +++ b/widget/src/qr_code.rs @@ -50,12 +50,11 @@ impl<'a> QRCode<'a> { } impl<'a, Message, Theme> Widget> for QRCode<'a> { - fn width(&self) -> Length { - Length::Shrink - } - - fn height(&self) -> Length { - Length::Shrink + fn size(&self) -> Size { + Size { + width: Length::Shrink, + height: Length::Shrink, + } } fn layout( diff --git a/widget/src/radio.rs b/widget/src/radio.rs index ae2365dd..f91b20b1 100644 --- a/widget/src/radio.rs +++ b/widget/src/radio.rs @@ -201,12 +201,11 @@ where tree::State::new(widget::text::State::::default()) } - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - Length::Shrink + fn size(&self) -> Size { + Size { + width: self.width, + height: Length::Shrink, + } } fn layout( diff --git a/widget/src/row.rs b/widget/src/row.rs index ef371ddb..bcbe9267 100644 --- a/widget/src/row.rs +++ b/widget/src/row.rs @@ -7,7 +7,7 @@ use crate::core::renderer; use crate::core::widget::{Operation, Tree}; use crate::core::{ Alignment, Clipboard, Element, Length, Padding, Pixels, Rectangle, Shell, - Widget, + Size, Widget, }; /// A container that distributes its contents horizontally. @@ -123,12 +123,11 @@ where tree.diff_children(&self.children); } - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - self.height + fn size(&self) -> Size { + Size { + width: self.width, + height: self.height, + } } fn layout( diff --git a/widget/src/rule.rs b/widget/src/rule.rs index ecaedf60..4ab16c40 100644 --- a/widget/src/rule.rs +++ b/widget/src/rule.rs @@ -62,12 +62,11 @@ where Renderer: crate::core::Renderer, Renderer::Theme: StyleSheet, { - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - self.height + fn size(&self) -> Size { + Size { + width: self.width, + height: self.height, + } } fn layout( diff --git a/widget/src/scrollable.rs b/widget/src/scrollable.rs index 525463c4..5197afde 100644 --- a/widget/src/scrollable.rs +++ b/widget/src/scrollable.rs @@ -220,12 +220,11 @@ where tree.diff_children(std::slice::from_ref(&self.content)); } - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - self.height + fn size(&self) -> Size { + Size { + width: self.width, + height: self.height, + } } fn layout( diff --git a/widget/src/shader.rs b/widget/src/shader.rs index 5b18ec7d..82432c6c 100644 --- a/widget/src/shader.rs +++ b/widget/src/shader.rs @@ -70,12 +70,11 @@ where tree::State::new(P::State::default()) } - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - self.height + fn size(&self) -> Size { + Size { + width: self.width, + height: self.height, + } } fn layout( diff --git a/widget/src/slider.rs b/widget/src/slider.rs index 2b600d9d..27588852 100644 --- a/widget/src/slider.rs +++ b/widget/src/slider.rs @@ -159,12 +159,11 @@ where tree::State::new(State::new()) } - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - Length::Shrink + fn size(&self) -> Size { + Size { + width: self.width, + height: Length::Shrink, + } } fn layout( diff --git a/widget/src/space.rs b/widget/src/space.rs index afa9a7c8..9fd4dcb9 100644 --- a/widget/src/space.rs +++ b/widget/src/space.rs @@ -45,12 +45,11 @@ impl Widget for Space where Renderer: core::Renderer, { - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - self.height + fn size(&self) -> Size { + Size { + width: self.width, + height: self.height, + } } fn layout( diff --git a/widget/src/svg.rs b/widget/src/svg.rs index 8367ad18..75ab238a 100644 --- a/widget/src/svg.rs +++ b/widget/src/svg.rs @@ -96,12 +96,11 @@ where Renderer: svg::Renderer, Renderer::Theme: iced_style::svg::StyleSheet, { - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - self.height + fn size(&self) -> Size { + Size { + width: self.width, + height: self.height, + } } fn layout( diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index 214bce17..9118d124 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -9,7 +9,7 @@ use crate::core::text::highlighter::{self, Highlighter}; use crate::core::text::{self, LineHeight}; use crate::core::widget::{self, Widget}; use crate::core::{ - Clipboard, Color, Element, Length, Padding, Pixels, Rectangle, Shell, + Clipboard, Color, Element, Length, Padding, Pixels, Rectangle, Shell, Size, Vector, }; @@ -316,12 +316,11 @@ where }) } - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - self.height + fn size(&self) -> Size { + Size { + width: self.width, + height: self.height, + } } fn layout( diff --git a/widget/src/text_input.rs b/widget/src/text_input.rs index 03eb2fd0..7e91105c 100644 --- a/widget/src/text_input.rs +++ b/widget/src/text_input.rs @@ -283,12 +283,11 @@ where } } - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - Length::Shrink + fn size(&self) -> Size { + Size { + width: self.width, + height: Length::Shrink, + } } fn layout( diff --git a/widget/src/toggler.rs b/widget/src/toggler.rs index d8723080..941159ea 100644 --- a/widget/src/toggler.rs +++ b/widget/src/toggler.rs @@ -168,12 +168,11 @@ where tree::State::new(widget::text::State::::default()) } - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - Length::Shrink + fn size(&self) -> Size { + Size { + width: self.width, + height: Length::Shrink, + } } fn layout( diff --git a/widget/src/tooltip.rs b/widget/src/tooltip.rs index adef13e4..d09a9255 100644 --- a/widget/src/tooltip.rs +++ b/widget/src/tooltip.rs @@ -131,12 +131,8 @@ where widget::tree::Tag::of::() } - fn width(&self) -> Length { - self.content.as_widget().width() - } - - fn height(&self) -> Length { - self.content.as_widget().height() + fn size(&self) -> Size { + self.content.as_widget().size() } fn layout( diff --git a/widget/src/vertical_slider.rs b/widget/src/vertical_slider.rs index e489104c..35bc2fe2 100644 --- a/widget/src/vertical_slider.rs +++ b/widget/src/vertical_slider.rs @@ -156,12 +156,11 @@ where tree::State::new(State::new()) } - fn width(&self) -> Length { - Length::Shrink - } - - fn height(&self) -> Length { - self.height + fn size(&self) -> Size { + Size { + width: Length::Shrink, + height: self.height, + } } fn layout( -- cgit From d62bb8193c1c43f565fcc5c52293d564c91e215d Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 9 Jan 2024 06:35:33 +0100 Subject: Introduce useful helpers in `layout` module --- widget/src/button.rs | 19 +++---------------- widget/src/canvas.rs | 4 +--- widget/src/container.rs | 28 ++++++++++------------------ widget/src/image.rs | 2 +- widget/src/image/viewer.rs | 2 +- widget/src/overlay/menu.rs | 2 +- widget/src/pane_grid.rs | 2 +- widget/src/pick_list.rs | 5 +++-- widget/src/progress_bar.rs | 8 +++----- widget/src/rule.rs | 2 +- widget/src/scrollable.rs | 39 ++++++++++++++++++--------------------- widget/src/shader.rs | 5 +---- widget/src/slider.rs | 4 +--- widget/src/space.rs | 2 +- widget/src/svg.rs | 2 +- widget/src/text_input.rs | 4 ++-- widget/src/vertical_slider.rs | 4 +--- 17 files changed, 50 insertions(+), 84 deletions(-) (limited to 'widget/src') diff --git a/widget/src/button.rs b/widget/src/button.rs index 1ce4f662..86abee77 100644 --- a/widget/src/button.rs +++ b/widget/src/button.rs @@ -10,8 +10,8 @@ use crate::core::touch; use crate::core::widget::tree::{self, Tree}; use crate::core::widget::Operation; use crate::core::{ - Background, Clipboard, Color, Element, Layout, Length, Padding, Point, - Rectangle, Shell, Size, Vector, Widget, + Background, Clipboard, Color, Element, Layout, Length, Padding, Rectangle, + Shell, Size, Vector, Widget, }; pub use iced_style::button::{Appearance, StyleSheet}; @@ -430,20 +430,7 @@ pub fn layout( padding: Padding, layout_content: impl FnOnce(&layout::Limits) -> layout::Node, ) -> layout::Node { - let limits = limits.width(width).height(height); - - let content = layout_content(&limits.shrink(padding)); - let padding = padding.fit(content.size(), limits.max()); - - let size = limits - .shrink(padding) - .resolve(content.size(), width, height) - .expand(padding); - - layout::Node::with_children( - size, - vec![content.move_to(Point::new(padding.left, padding.top))], - ) + layout::padded(limits, width, height, padding, layout_content) } /// Returns the [`mouse::Interaction`] of a [`Button`]. diff --git a/widget/src/canvas.rs b/widget/src/canvas.rs index 2bf09eec..4e42a671 100644 --- a/widget/src/canvas.rs +++ b/widget/src/canvas.rs @@ -133,9 +133,7 @@ where _renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - let size = limits.resolve(Size::ZERO, self.width, self.height); - - layout::Node::new(size) + layout::atomic(limits, self.width, self.height) } fn on_event( diff --git a/widget/src/container.rs b/widget/src/container.rs index 93d8daba..c98de41c 100644 --- a/widget/src/container.rs +++ b/widget/src/container.rs @@ -321,27 +321,19 @@ pub fn layout( vertical_alignment: alignment::Vertical, layout_content: impl FnOnce(&layout::Limits) -> layout::Node, ) -> layout::Node { - let limits = limits - .width(width) - .height(height) - .max_width(max_width) - .max_height(max_height); - - let content = layout_content(&limits.shrink(padding).loose()); - let padding = padding.fit(content.size(), limits.max()); - let size = limits - .shrink(padding) - .resolve(content.size(), width, height); - - layout::Node::with_children( - size.expand(padding), - vec![content - .move_to(Point::new(padding.left, padding.top)) - .align( + layout::positioned( + &limits.max_width(max_width).max_height(max_height), + width, + height, + padding, + |limits| layout_content(&limits.loose()), + |content, size| { + content.align( Alignment::from(horizontal_alignment), Alignment::from(vertical_alignment), size, - )], + ) + }, ) } diff --git a/widget/src/image.rs b/widget/src/image.rs index 6750c1b3..e906ac13 100644 --- a/widget/src/image.rs +++ b/widget/src/image.rs @@ -99,7 +99,7 @@ where }; // The size to be available to the widget prior to `Shrink`ing - let raw_size = limits.resolve(image_size, width, height); + let raw_size = limits.resolve(width, height, image_size); // The uncropped size of the image when fit to the bounds above let full_size = content_fit.fit(image_size, raw_size); diff --git a/widget/src/image/viewer.rs b/widget/src/image/viewer.rs index dc910f1f..98080577 100644 --- a/widget/src/image/viewer.rs +++ b/widget/src/image/viewer.rs @@ -113,9 +113,9 @@ where let Size { width, height } = renderer.dimensions(&self.handle); let mut size = limits.resolve( - Size::new(width as f32, height as f32), self.width, self.height, + Size::new(width as f32, height as f32), ); let expansion_size = if height > width { diff --git a/widget/src/overlay/menu.rs b/widget/src/overlay/menu.rs index b9e06de8..f83eebea 100644 --- a/widget/src/overlay/menu.rs +++ b/widget/src/overlay/menu.rs @@ -369,7 +369,7 @@ where * self.options.len() as f32, ); - limits.resolve(intrinsic, Length::Fill, Length::Shrink) + limits.resolve(Length::Fill, Length::Shrink, intrinsic) }; layout::Node::new(size) diff --git a/widget/src/pane_grid.rs b/widget/src/pane_grid.rs index 36c785b7..cf1f0455 100644 --- a/widget/src/pane_grid.rs +++ b/widget/src/pane_grid.rs @@ -489,7 +489,7 @@ pub fn layout( &layout::Limits, ) -> layout::Node, ) -> layout::Node { - let size = limits.resolve(Size::ZERO, width, height); + let size = limits.resolve(width, height, Size::ZERO); let regions = node.pane_regions(spacing, size); let children = contents diff --git a/widget/src/pick_list.rs b/widget/src/pick_list.rs index d83b0624..2576a1e8 100644 --- a/widget/src/pick_list.rs +++ b/widget/src/pick_list.rs @@ -392,7 +392,6 @@ where { use std::f32; - let limits = limits.width(width).height(Length::Shrink); let font = font.unwrap_or_else(|| renderer.default_font()); let text_size = text_size.unwrap_or_else(|| renderer.default_size()); @@ -451,8 +450,10 @@ where ); limits + .width(width) + .height(Length::Shrink) .shrink(padding) - .resolve(intrinsic, width, Length::Shrink) + .resolve(width, Length::Shrink, intrinsic) .expand(padding) }; diff --git a/widget/src/progress_bar.rs b/widget/src/progress_bar.rs index a05923a2..15f1277b 100644 --- a/widget/src/progress_bar.rs +++ b/widget/src/progress_bar.rs @@ -98,13 +98,11 @@ where _renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - let size = limits.resolve( - Size::ZERO, + layout::atomic( + limits, self.width, self.height.unwrap_or(Length::Fixed(Self::DEFAULT_HEIGHT)), - ); - - layout::Node::new(size) + ) } fn draw( diff --git a/widget/src/rule.rs b/widget/src/rule.rs index 4ab16c40..cded9cb1 100644 --- a/widget/src/rule.rs +++ b/widget/src/rule.rs @@ -75,7 +75,7 @@ where _renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - layout::Node::new(limits.resolve(Size::ZERO, self.width, self.height)) + layout::atomic(limits, self.width, self.height) } fn draw( diff --git a/widget/src/scrollable.rs b/widget/src/scrollable.rs index 5197afde..70db490a 100644 --- a/widget/src/scrollable.rs +++ b/widget/src/scrollable.rs @@ -469,28 +469,25 @@ pub fn layout( direction: &Direction, layout_content: impl FnOnce(&Renderer, &layout::Limits) -> layout::Node, ) -> layout::Node { - let limits = limits.width(width).height(height); - - let child_limits = layout::Limits::new( - Size::new(limits.min().width, limits.min().height), - Size::new( - if direction.horizontal().is_some() { - f32::INFINITY - } else { - limits.max().width - }, - if direction.vertical().is_some() { - f32::MAX - } else { - limits.max().height - }, - ), - ); - - let content = layout_content(renderer, &child_limits); - let size = limits.resolve(content.size(), width, height); + layout::contained(limits, width, height, |limits| { + let child_limits = layout::Limits::new( + Size::new(limits.min().width, limits.min().height), + Size::new( + if direction.horizontal().is_some() { + f32::INFINITY + } else { + limits.max().width + }, + if direction.vertical().is_some() { + f32::MAX + } else { + limits.max().height + }, + ), + ); - layout::Node::with_children(size, vec![content]) + layout_content(renderer, &child_limits) + }) } /// Processes an [`Event`] and updates the [`State`] of a [`Scrollable`] diff --git a/widget/src/shader.rs b/widget/src/shader.rs index 82432c6c..16b68c55 100644 --- a/widget/src/shader.rs +++ b/widget/src/shader.rs @@ -83,10 +83,7 @@ where _renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - let limits = limits.width(self.width).height(self.height); - let size = limits.resolve(Size::ZERO, self.width, self.height); - - layout::Node::new(size) + layout::atomic(limits, self.width, self.height) } fn on_event( diff --git a/widget/src/slider.rs b/widget/src/slider.rs index 27588852..1bc94661 100644 --- a/widget/src/slider.rs +++ b/widget/src/slider.rs @@ -172,9 +172,7 @@ where _renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - let size = limits.resolve(Size::ZERO, self.width, self.height); - - layout::Node::new(size) + layout::atomic(limits, self.width, self.height) } fn on_event( diff --git a/widget/src/space.rs b/widget/src/space.rs index 9fd4dcb9..eef990d1 100644 --- a/widget/src/space.rs +++ b/widget/src/space.rs @@ -58,7 +58,7 @@ where _renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - layout::Node::new(limits.resolve(Size::ZERO, self.width, self.height)) + layout::atomic(limits, self.width, self.height) } fn draw( diff --git a/widget/src/svg.rs b/widget/src/svg.rs index 75ab238a..830abb0f 100644 --- a/widget/src/svg.rs +++ b/widget/src/svg.rs @@ -114,7 +114,7 @@ where let image_size = Size::new(width as f32, height as f32); // The size to be available to the widget prior to `Shrink`ing - let raw_size = limits.resolve(image_size, self.width, self.height); + let raw_size = limits.resolve(self.width, self.height, image_size); // The uncropped size of the image when fit to the bounds above let full_size = self.content_fit.fit(image_size, raw_size); diff --git a/widget/src/text_input.rs b/widget/src/text_input.rs index 7e91105c..d8540658 100644 --- a/widget/src/text_input.rs +++ b/widget/src/text_input.rs @@ -508,8 +508,8 @@ where let padding = padding.fit(Size::ZERO, limits.max()); let height = line_height.to_absolute(text_size); - let limits = limits.width(width).shrink(padding).height(height); - let text_bounds = limits.resolve(Size::ZERO, width, height); + let limits = limits.width(width).shrink(padding); + let text_bounds = limits.resolve(width, height, Size::ZERO); let placeholder_text = Text { font, diff --git a/widget/src/vertical_slider.rs b/widget/src/vertical_slider.rs index 35bc2fe2..a3029d76 100644 --- a/widget/src/vertical_slider.rs +++ b/widget/src/vertical_slider.rs @@ -169,9 +169,7 @@ where _renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - let size = limits.resolve(Size::ZERO, self.width, self.height); - - layout::Node::new(size) + layout::atomic(limits, self.width, self.height) } fn on_event( -- cgit From e710e7694907fe320e0a849e880c51952e6e748f Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 9 Jan 2024 06:44:15 +0100 Subject: Fix `size_hint` for `keyed_column` --- widget/src/helpers.rs | 1 + widget/src/keyed/column.rs | 45 +++++++++++++++++++++++++-------------------- 2 files changed, 26 insertions(+), 20 deletions(-) (limited to 'widget/src') diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index 6eaf3392..75528a0c 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -80,6 +80,7 @@ pub fn keyed_column<'a, Key, Message, Renderer>( ) -> keyed::Column<'a, Key, Message, Renderer> where Key: Copy + PartialEq, + Renderer: core::Renderer, { keyed::Column::with_children(children) } diff --git a/widget/src/keyed/column.rs b/widget/src/keyed/column.rs index 32320300..7f05a81e 100644 --- a/widget/src/keyed/column.rs +++ b/widget/src/keyed/column.rs @@ -30,26 +30,10 @@ where impl<'a, Key, Message, Renderer> Column<'a, Key, Message, Renderer> where Key: Copy + PartialEq, + Renderer: crate::core::Renderer, { /// Creates an empty [`Column`]. pub fn new() -> Self { - Self::with_children(Vec::new()) - } - - /// Creates a [`Column`] with the given elements. - pub fn with_children( - children: impl IntoIterator)>, - ) -> Self { - let (keys, children) = children.into_iter().fold( - (Vec::new(), Vec::new()), - |(mut keys, mut children), (key, child)| { - keys.push(key); - children.push(child); - - (keys, children) - }, - ); - Column { spacing: 0.0, padding: Padding::ZERO, @@ -57,11 +41,20 @@ where height: Length::Shrink, max_width: f32::INFINITY, align_items: Alignment::Start, - keys, - children, + keys: Vec::new(), + children: Vec::new(), } } + /// Creates a [`Column`] with the given elements. + pub fn with_children( + children: impl IntoIterator)>, + ) -> Self { + children + .into_iter() + .fold(Self::new(), |column, (key, child)| column.push(key, child)) + } + /// Sets the vertical spacing _between_ elements. /// /// Custom margins per element do not exist in iced. You should use this @@ -108,8 +101,19 @@ where key: Key, child: impl Into>, ) -> Self { + let child = child.into(); + let size = child.as_widget().size_hint(); + + if size.width.is_fill() { + self.width = Length::Fill; + } + + if size.height.is_fill() { + self.height = Length::Fill; + } + self.keys.push(key); - self.children.push(child.into()); + self.children.push(child); self } } @@ -117,6 +121,7 @@ where impl<'a, Key, Message, Renderer> Default for Column<'a, Key, Message, Renderer> where Key: Copy + PartialEq, + Renderer: crate::core::Renderer, { fn default() -> Self { Self::new() -- cgit From 67277fbf93f4c180eff67bdc4c9dcf84a54d3425 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 9 Jan 2024 06:46:49 +0100 Subject: Make `column` and `row` take an `IntoIterator` --- widget/src/column.rs | 4 ++-- widget/src/helpers.rs | 8 ++++---- widget/src/row.rs | 6 ++---- 3 files changed, 8 insertions(+), 10 deletions(-) (limited to 'widget/src') diff --git a/widget/src/column.rs b/widget/src/column.rs index 9867d97e..d6eea84b 100644 --- a/widget/src/column.rs +++ b/widget/src/column.rs @@ -41,9 +41,9 @@ where /// Creates a [`Column`] with the given elements. pub fn with_children( - children: impl Iterator>, + children: impl IntoIterator>, ) -> Self { - children.fold(Self::new(), |column, element| column.push(element)) + children.into_iter().fold(Self::new(), Self::push) } /// Sets the vertical spacing _between_ elements. diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index 75528a0c..4b988ae3 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -34,7 +34,7 @@ macro_rules! column { $crate::Column::new() ); ($($x:expr),+ $(,)?) => ( - $crate::Column::with_children([$($crate::core::Element::from($x)),+].into_iter()) + $crate::Column::with_children([$($crate::core::Element::from($x)),+]) ); } @@ -47,7 +47,7 @@ macro_rules! row { $crate::Row::new() ); ($($x:expr),+ $(,)?) => ( - $crate::Row::with_children([$($crate::core::Element::from($x)),+].into_iter()) + $crate::Row::with_children([$($crate::core::Element::from($x)),+]) ); } @@ -66,7 +66,7 @@ where /// Creates a new [`Column`] with the given children. pub fn column<'a, Message, Renderer>( - children: impl Iterator>, + children: impl IntoIterator>, ) -> Column<'a, Message, Renderer> where Renderer: core::Renderer, @@ -89,7 +89,7 @@ where /// /// [`Row`]: crate::Row pub fn row<'a, Message, Renderer>( - children: impl Iterator>, + children: impl IntoIterator>, ) -> Row<'a, Message, Renderer> where Renderer: core::Renderer, diff --git a/widget/src/row.rs b/widget/src/row.rs index bcbe9267..90fd2926 100644 --- a/widget/src/row.rs +++ b/widget/src/row.rs @@ -39,11 +39,9 @@ where /// Creates a [`Row`] with the given elements. pub fn with_children( - children: impl Iterator>, + children: impl IntoIterator>, ) -> Self { - children - .into_iter() - .fold(Self::new(), |column, element| column.push(element)) + children.into_iter().fold(Self::new(), Self::push) } /// Sets the horizontal spacing _between_ elements. -- cgit From ecf571dfeb033f3768fccfb06bc9380e59281df3 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 9 Jan 2024 06:47:52 +0100 Subject: Fix unnecessary `into` call in `Container::new` --- widget/src/container.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'widget/src') diff --git a/widget/src/container.rs b/widget/src/container.rs index c98de41c..ecc5c651 100644 --- a/widget/src/container.rs +++ b/widget/src/container.rs @@ -67,7 +67,7 @@ where horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Top, style: Default::default(), - content: content.into(), + content, } } -- cgit From 88f8c343fa7d69203ab98bb7abc85fe002014422 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 9 Jan 2024 07:15:57 +0100 Subject: Fix `cross` calculation in `layout::flex` --- widget/src/pick_list.rs | 1 - 1 file changed, 1 deletion(-) (limited to 'widget/src') diff --git a/widget/src/pick_list.rs b/widget/src/pick_list.rs index 2576a1e8..9f6a371a 100644 --- a/widget/src/pick_list.rs +++ b/widget/src/pick_list.rs @@ -451,7 +451,6 @@ where limits .width(width) - .height(Length::Shrink) .shrink(padding) .resolve(width, Length::Shrink, intrinsic) .expand(padding) -- cgit From 3850a46db6e13f2948f5731f4ceec42764391f5d Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 10 Jan 2024 08:15:05 +0100 Subject: Add `Theme` selector to `layout` example --- widget/src/helpers.rs | 2 +- widget/src/pick_list.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'widget/src') diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index 4b988ae3..498dd76c 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -271,7 +271,7 @@ pub fn pick_list<'a, Message, Renderer, T>( on_selected: impl Fn(T) -> Message + 'a, ) -> PickList<'a, T, Message, Renderer> where - T: ToString + Eq + 'static, + T: ToString + PartialEq + 'static, [T]: ToOwned>, Renderer: core::text::Renderer, Renderer::Theme: pick_list::StyleSheet diff --git a/widget/src/pick_list.rs b/widget/src/pick_list.rs index 9f6a371a..2e3aab6f 100644 --- a/widget/src/pick_list.rs +++ b/widget/src/pick_list.rs @@ -45,7 +45,7 @@ where impl<'a, T: 'a, Message, Renderer> PickList<'a, T, Message, Renderer> where - T: ToString + Eq, + T: ToString + PartialEq, [T]: ToOwned>, Renderer: text::Renderer, Renderer::Theme: StyleSheet @@ -145,7 +145,7 @@ where impl<'a, T: 'a, Message, Renderer> Widget for PickList<'a, T, Message, Renderer> where - T: Clone + ToString + Eq + 'static, + T: Clone + ToString + PartialEq + 'static, [T]: ToOwned>, Message: 'a, Renderer: text::Renderer + 'a, @@ -281,7 +281,7 @@ where impl<'a, T: 'a, Message, Renderer> From> for Element<'a, Message, Renderer> where - T: Clone + ToString + Eq + 'static, + T: Clone + ToString + PartialEq + 'static, [T]: ToOwned>, Message: 'a, Renderer: text::Renderer + 'a, -- cgit From 03c901d49b7cce901cfd76100f08dcff31420af8 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 11 Jan 2024 06:12:19 +0100 Subject: Make `Button` sizing strategy adaptive --- widget/src/button.rs | 9 ++++++--- widget/src/container.rs | 12 ++---------- 2 files changed, 8 insertions(+), 13 deletions(-) (limited to 'widget/src') diff --git a/widget/src/button.rs b/widget/src/button.rs index 86abee77..0ebb8dcc 100644 --- a/widget/src/button.rs +++ b/widget/src/button.rs @@ -71,11 +71,14 @@ where { /// Creates a new [`Button`] with the given content. pub fn new(content: impl Into>) -> Self { + let content = content.into(); + let size = content.as_widget().size_hint(); + Button { - content: content.into(), + content, on_press: None, - width: Length::Shrink, - height: Length::Shrink, + width: size.width.fluid(), + height: size.height.fluid(), padding: Padding::new(5.0), style: ::Style::default(), } diff --git a/widget/src/container.rs b/widget/src/container.rs index ecc5c651..cffb0458 100644 --- a/widget/src/container.rs +++ b/widget/src/container.rs @@ -52,16 +52,8 @@ where Container { id: None, padding: Padding::ZERO, - width: if size.width.is_fill() { - Length::Fill - } else { - Length::Shrink - }, - height: if size.height.is_fill() { - Length::Fill - } else { - Length::Shrink - }, + width: size.width.fluid(), + height: size.height.fluid(), max_width: f32::INFINITY, max_height: f32::INFINITY, horizontal_alignment: alignment::Horizontal::Left, -- cgit From 5315e04a265190e943f42710f0b949e8af7dd37d Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 12 Jan 2024 13:34:14 +0100 Subject: Fix clipping of `TextInput` selection --- widget/src/text_input.rs | 54 +++++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 23 deletions(-) (limited to 'widget/src') diff --git a/widget/src/text_input.rs b/widget/src/text_input.rs index 65d3e1eb..c4c74a67 100644 --- a/widget/src/text_input.rs +++ b/widget/src/text_input.rs @@ -1194,31 +1194,39 @@ pub fn draw( (None, 0.0) }; - if let Some((cursor, color)) = cursor { - renderer.with_translation(Vector::new(-offset, 0.0), |renderer| { - renderer.fill_quad(cursor, color); - }); + let draw = |renderer: &mut Renderer, viewport| { + if let Some((cursor, color)) = cursor { + renderer.with_translation(Vector::new(-offset, 0.0), |renderer| { + renderer.fill_quad(cursor, color); + }); + } else { + renderer.with_translation(Vector::ZERO, |_| {}); + } + + renderer.fill_paragraph( + if text.is_empty() { + &state.placeholder + } else { + &state.value + }, + Point::new(text_bounds.x, text_bounds.center_y()) + - Vector::new(offset, 0.0), + if text.is_empty() { + theme.placeholder_color(style) + } else if is_disabled { + theme.disabled_color(style) + } else { + theme.value_color(style) + }, + viewport, + ); + }; + + if cursor.is_some() { + renderer.with_layer(text_bounds, |renderer| draw(renderer, *viewport)); } else { - renderer.with_translation(Vector::ZERO, |_| {}); + draw(renderer, text_bounds); } - - renderer.fill_paragraph( - if text.is_empty() { - &state.placeholder - } else { - &state.value - }, - Point::new(text_bounds.x, text_bounds.center_y()) - - Vector::new(offset, 0.0), - if text.is_empty() { - theme.placeholder_color(style) - } else if is_disabled { - theme.disabled_color(style) - } else { - theme.value_color(style) - }, - text_bounds, - ); } /// Computes the current [`mouse::Interaction`] of the [`TextInput`]. -- cgit From 64d1ce5532f55d152fa5819532a138da2dca1a39 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 16 Jan 2024 13:28:00 +0100 Subject: Refactor `KeyCode` into `Key` and `Location` --- widget/src/combo_box.rs | 14 +++++----- widget/src/text_editor.rs | 67 +++++++++++++++++++++++++++++------------------ widget/src/text_input.rs | 54 ++++++++++++++++++++------------------ 3 files changed, 76 insertions(+), 59 deletions(-) (limited to 'widget/src') diff --git a/widget/src/combo_box.rs b/widget/src/combo_box.rs index 1b2fa947..73beeac3 100644 --- a/widget/src/combo_box.rs +++ b/widget/src/combo_box.rs @@ -1,6 +1,7 @@ //! Display a dropdown list of searchable and selectable options. use crate::core::event::{self, Event}; use crate::core::keyboard; +use crate::core::keyboard::key; use crate::core::layout::{self, Layout}; use crate::core::mouse; use crate::core::overlay; @@ -436,14 +437,14 @@ where } if let Event::Keyboard(keyboard::Event::KeyPressed { - key_code, + key: keyboard::Key::Named(named_key), modifiers, .. }) = event { let shift_modifer = modifiers.shift(); - match (key_code, shift_modifer) { - (keyboard::KeyCode::Enter, _) => { + match (named_key, shift_modifer) { + (key::Named::Enter, _) => { if let Some(index) = &menu.hovered_option { if let Some(option) = state.filtered_options.options.get(*index) @@ -455,8 +456,7 @@ where event_status = event::Status::Captured; } - (keyboard::KeyCode::Up, _) - | (keyboard::KeyCode::Tab, true) => { + (key::Named::ArrowUp, _) | (key::Named::Tab, true) => { if let Some(index) = &mut menu.hovered_option { if *index == 0 { *index = state @@ -492,8 +492,8 @@ where event_status = event::Status::Captured; } - (keyboard::KeyCode::Down, _) - | (keyboard::KeyCode::Tab, false) + (key::Named::ArrowDown, _) + | (key::Named::Tab, false) if !modifiers.shift() => { if let Some(index) = &mut menu.hovered_option { diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index b95a45e4..09a0cac0 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -1,6 +1,7 @@ //! Display a multi-line text input for text editing. use crate::core::event::{self, Event}; use crate::core::keyboard; +use crate::core::keyboard::key; use crate::core::layout::{self, Layout}; use crate::core::mouse; use crate::core::renderer; @@ -646,34 +647,48 @@ impl Update { }, Event::Keyboard(event) => match event { keyboard::Event::KeyPressed { - key_code, + key, modifiers, text, + .. } if state.is_focused => { - if let Some(motion) = motion(key_code) { - let motion = - if platform::is_jump_modifier_pressed(modifiers) { + if let keyboard::Key::Named(named_key) = key.as_ref() { + if let Some(motion) = motion(named_key) { + let motion = if platform::is_jump_modifier_pressed( + modifiers, + ) { motion.widen() } else { motion }; - return action(if modifiers.shift() { - Action::Select(motion) - } else { - Action::Move(motion) - }); + return action(if modifiers.shift() { + Action::Select(motion) + } else { + Action::Move(motion) + }); + } } - match key_code { - keyboard::KeyCode::Enter => edit(Edit::Enter), - keyboard::KeyCode::Backspace => edit(Edit::Backspace), - keyboard::KeyCode::Delete => edit(Edit::Delete), - keyboard::KeyCode::Escape => Some(Self::Unfocus), - keyboard::KeyCode::C if modifiers.command() => { + match key.as_ref() { + keyboard::Key::Named(key::Named::Enter) => { + edit(Edit::Enter) + } + keyboard::Key::Named(key::Named::Backspace) => { + edit(Edit::Backspace) + } + keyboard::Key::Named(key::Named::Delete) => { + edit(Edit::Delete) + } + keyboard::Key::Named(key::Named::Escape) => { + Some(Self::Unfocus) + } + keyboard::Key::Character("c") + if modifiers.command() => + { Some(Self::Copy) } - keyboard::KeyCode::V + keyboard::Key::Character("v") if modifiers.command() && !modifiers.alt() => { Some(Self::Paste) @@ -694,16 +709,16 @@ impl Update { } } -fn motion(key_code: keyboard::KeyCode) -> Option { - match key_code { - keyboard::KeyCode::Left => Some(Motion::Left), - keyboard::KeyCode::Right => Some(Motion::Right), - keyboard::KeyCode::Up => Some(Motion::Up), - keyboard::KeyCode::Down => Some(Motion::Down), - keyboard::KeyCode::Home => Some(Motion::Home), - keyboard::KeyCode::End => Some(Motion::End), - keyboard::KeyCode::PageUp => Some(Motion::PageUp), - keyboard::KeyCode::PageDown => Some(Motion::PageDown), +fn motion(key: key::Named) -> Option { + match key { + key::Named::ArrowLeft => Some(Motion::Left), + key::Named::ArrowRight => Some(Motion::Right), + key::Named::ArrowUp => Some(Motion::Up), + key::Named::ArrowDown => Some(Motion::Down), + key::Named::Home => Some(Motion::Home), + key::Named::End => Some(Motion::End), + key::Named::PageUp => Some(Motion::PageUp), + key::Named::PageDown => Some(Motion::PageDown), _ => None, } } diff --git a/widget/src/text_input.rs b/widget/src/text_input.rs index 8d28e8ee..c3dce8be 100644 --- a/widget/src/text_input.rs +++ b/widget/src/text_input.rs @@ -14,6 +14,7 @@ use editor::Editor; use crate::core::alignment; use crate::core::event::{self, Event}; use crate::core::keyboard; +use crate::core::keyboard::key; use crate::core::layout; use crate::core::mouse::{self, click}; use crate::core::renderer; @@ -748,9 +749,7 @@ where return event::Status::Captured; } } - Event::Keyboard(keyboard::Event::KeyPressed { - key_code, text, .. - }) => { + Event::Keyboard(keyboard::Event::KeyPressed { key, text, .. }) => { let state = state(); if let Some(focus) = &mut state.is_focused { @@ -761,14 +760,13 @@ where let modifiers = state.keyboard_modifiers; focus.updated_at = Instant::now(); - match key_code { - keyboard::KeyCode::Enter - | keyboard::KeyCode::NumpadEnter => { + match key.as_ref() { + keyboard::Key::Named(key::Named::Enter) => { if let Some(on_submit) = on_submit.clone() { shell.publish(on_submit); } } - keyboard::KeyCode::Backspace => { + keyboard::Key::Named(key::Named::Backspace) => { if platform::is_jump_modifier_pressed(modifiers) && state.cursor.selection(value).is_none() { @@ -788,7 +786,7 @@ where update_cache(state, value); } - keyboard::KeyCode::Delete => { + keyboard::Key::Named(key::Named::Delete) => { if platform::is_jump_modifier_pressed(modifiers) && state.cursor.selection(value).is_none() { @@ -810,7 +808,7 @@ where update_cache(state, value); } - keyboard::KeyCode::Left => { + keyboard::Key::Named(key::Named::ArrowLeft) => { if platform::is_jump_modifier_pressed(modifiers) && !is_secure { @@ -825,7 +823,7 @@ where state.cursor.move_left(value); } } - keyboard::KeyCode::Right => { + keyboard::Key::Named(key::Named::ArrowRight) => { if platform::is_jump_modifier_pressed(modifiers) && !is_secure { @@ -840,7 +838,7 @@ where state.cursor.move_right(value); } } - keyboard::KeyCode::Home => { + keyboard::Key::Named(key::Named::Home) => { if modifiers.shift() { state .cursor @@ -849,7 +847,7 @@ where state.cursor.move_to(0); } } - keyboard::KeyCode::End => { + keyboard::Key::Named(key::Named::End) => { if modifiers.shift() { state.cursor.select_range( state.cursor.start(value), @@ -859,7 +857,7 @@ where state.cursor.move_to(value.len()); } } - keyboard::KeyCode::C + keyboard::Key::Character("c") if state.keyboard_modifiers.command() => { if let Some((start, end)) = @@ -869,7 +867,7 @@ where .write(value.select(start, end).to_string()); } } - keyboard::KeyCode::X + keyboard::Key::Character("x") if state.keyboard_modifiers.command() => { if let Some((start, end)) = @@ -887,7 +885,7 @@ where update_cache(state, value); } - keyboard::KeyCode::V => { + keyboard::Key::Character("v") => { if state.keyboard_modifiers.command() && !state.keyboard_modifiers.alt() { @@ -924,12 +922,12 @@ where state.is_pasting = None; } } - keyboard::KeyCode::A + keyboard::Key::Character("a") if state.keyboard_modifiers.command() => { state.cursor.select_all(value); } - keyboard::KeyCode::Escape => { + keyboard::Key::Named(key::Named::Escape) => { state.is_focused = None; state.is_dragging = false; state.is_pasting = None; @@ -937,9 +935,11 @@ where state.keyboard_modifiers = keyboard::Modifiers::default(); } - keyboard::KeyCode::Tab - | keyboard::KeyCode::Up - | keyboard::KeyCode::Down => { + keyboard::Key::Named( + key::Named::Tab + | key::Named::ArrowUp + | key::Named::ArrowDown, + ) => { return event::Status::Ignored; } _ => { @@ -971,17 +971,19 @@ where return event::Status::Captured; } } - Event::Keyboard(keyboard::Event::KeyReleased { key_code, .. }) => { + Event::Keyboard(keyboard::Event::KeyReleased { key, .. }) => { let state = state(); if state.is_focused.is_some() { - match key_code { - keyboard::KeyCode::V => { + match key.as_ref() { + keyboard::Key::Character("v") => { state.is_pasting = None; } - keyboard::KeyCode::Tab - | keyboard::KeyCode::Up - | keyboard::KeyCode::Down => { + keyboard::Key::Named( + key::Named::Tab + | key::Named::ArrowUp + | key::Named::ArrowDown, + ) => { return event::Status::Ignored; } _ => {} -- cgit