diff options
| author | 2023-01-18 15:01:17 -0800 | |
|---|---|---|
| committer | 2023-01-18 15:01:17 -0800 | |
| commit | 70d487ba20a50c06c73f0ffcd8198f1a7eac7f37 (patch) | |
| tree | afb8e161b18236d4440cba8bb0e0ce896858d653 /native/src/widget | |
| parent | 790fa3e7a01a790aa3f07083fe9abf6b68fa7ba1 (diff) | |
| parent | 5ef0648bf447aaca8b96782643401e54a2bf7759 (diff) | |
| download | iced-70d487ba20a50c06c73f0ffcd8198f1a7eac7f37.tar.gz iced-70d487ba20a50c06c73f0ffcd8198f1a7eac7f37.tar.bz2 iced-70d487ba20a50c06c73f0ffcd8198f1a7eac7f37.zip | |
Merge remote-tracking branch 'origin/master' into feat/multi-window-support
# Conflicts:
#	examples/events/src/main.rs
#	glutin/src/application.rs
#	native/src/window.rs
#	winit/src/window.rs
Diffstat (limited to '')
| -rw-r--r-- | native/src/widget.rs | 10 | ||||
| -rw-r--r-- | native/src/widget/action.rs | 9 | ||||
| -rw-r--r-- | native/src/widget/operation.rs | 4 | ||||
| -rw-r--r-- | native/src/widget/pane_grid.rs | 2 | ||||
| -rw-r--r-- | native/src/widget/text_input.rs | 103 | 
5 files changed, 98 insertions, 30 deletions
| diff --git a/native/src/widget.rs b/native/src/widget.rs index f714e28a..fb759ec8 100644 --- a/native/src/widget.rs +++ b/native/src/widget.rs @@ -110,12 +110,12 @@ use crate::{Clipboard, Layout, Length, Point, Rectangle, Shell};  /// - [`geometry`], a custom widget showcasing how to draw geometry with the  /// `Mesh2D` primitive in [`iced_wgpu`].  /// -/// [examples]: https://github.com/iced-rs/iced/tree/0.6/examples -/// [`bezier_tool`]: https://github.com/iced-rs/iced/tree/0.6/examples/bezier_tool -/// [`custom_widget`]: https://github.com/iced-rs/iced/tree/0.6/examples/custom_widget -/// [`geometry`]: https://github.com/iced-rs/iced/tree/0.6/examples/geometry +/// [examples]: https://github.com/iced-rs/iced/tree/0.7/examples +/// [`bezier_tool`]: https://github.com/iced-rs/iced/tree/0.7/examples/bezier_tool +/// [`custom_widget`]: https://github.com/iced-rs/iced/tree/0.7/examples/custom_widget +/// [`geometry`]: https://github.com/iced-rs/iced/tree/0.7/examples/geometry  /// [`lyon`]: https://github.com/nical/lyon -/// [`iced_wgpu`]: https://github.com/iced-rs/iced/tree/0.6/wgpu +/// [`iced_wgpu`]: https://github.com/iced-rs/iced/tree/0.7/wgpu  pub trait Widget<Message, Renderer>  where      Renderer: crate::Renderer, diff --git a/native/src/widget/action.rs b/native/src/widget/action.rs index 9aa79dec..1e21ff38 100644 --- a/native/src/widget/action.rs +++ b/native/src/widget/action.rs @@ -3,6 +3,7 @@ use crate::widget::Id;  use iced_futures::MaybeSend; +use std::any::Any;  use std::rc::Rc;  /// An operation to be performed on the widget tree. @@ -84,6 +85,10 @@ where              ) {                  self.operation.focusable(state, id);              } + +            fn custom(&mut self, state: &mut dyn Any, id: Option<&Id>) { +                self.operation.custom(state, id); +            }          }          let Self { operation, .. } = self; @@ -118,6 +123,10 @@ where          self.operation.text_input(state, id);      } +    fn custom(&mut self, state: &mut dyn Any, id: Option<&Id>) { +        self.operation.custom(state, id); +    } +      fn finish(&self) -> operation::Outcome<B> {          match self.operation.finish() {              operation::Outcome::None => operation::Outcome::None, diff --git a/native/src/widget/operation.rs b/native/src/widget/operation.rs index a0aa4117..73e6a6b0 100644 --- a/native/src/widget/operation.rs +++ b/native/src/widget/operation.rs @@ -9,6 +9,7 @@ pub use text_input::TextInput;  use crate::widget::Id; +use std::any::Any;  use std::fmt;  /// A piece of logic that can traverse the widget tree of an application in @@ -33,6 +34,9 @@ pub trait Operation<T> {      /// Operates on a widget that has text input.      fn text_input(&mut self, _state: &mut dyn TextInput, _id: Option<&Id>) {} +    /// Operates on a custom widget with some state. +    fn custom(&mut self, _state: &mut dyn Any, _id: Option<&Id>) {} +      /// Finishes the [`Operation`] and returns its [`Outcome`].      fn finish(&self) -> Outcome<T> {          Outcome::None diff --git a/native/src/widget/pane_grid.rs b/native/src/widget/pane_grid.rs index f8dbab74..8dbd1825 100644 --- a/native/src/widget/pane_grid.rs +++ b/native/src/widget/pane_grid.rs @@ -6,7 +6,7 @@  //! The [`pane_grid` example] showcases how to use a [`PaneGrid`] with resizing,  //! drag and drop, and hotkey support.  //! -//! [`pane_grid` example]: https://github.com/iced-rs/iced/tree/0.6/examples/pane_grid +//! [`pane_grid` example]: https://github.com/iced-rs/iced/tree/0.7/examples/pane_grid  mod axis;  mod configuration;  mod content; diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 8b4514e3..8755b85d 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -18,10 +18,12 @@ use crate::layout;  use crate::mouse::{self, click};  use crate::renderer;  use crate::text::{self, Text}; +use crate::time::{Duration, Instant};  use crate::touch;  use crate::widget;  use crate::widget::operation::{self, Operation};  use crate::widget::tree::{self, Tree}; +use crate::window;  use crate::{      Clipboard, Color, Command, Element, Layout, Length, Padding, Point,      Rectangle, Shell, Size, Vector, Widget, @@ -425,7 +427,18 @@ where              let state = state();              let is_clicked = layout.bounds().contains(cursor_position); -            state.is_focused = is_clicked; +            state.is_focused = if is_clicked { +                state.is_focused.or_else(|| { +                    let now = Instant::now(); + +                    Some(Focus { +                        updated_at: now, +                        now, +                    }) +                }) +            } else { +                None +            };              if is_clicked {                  let text_layout = layout.children().next().unwrap(); @@ -541,26 +554,30 @@ where          Event::Keyboard(keyboard::Event::CharacterReceived(c)) => {              let state = state(); -            if state.is_focused -                && state.is_pasting.is_none() -                && !state.keyboard_modifiers.command() -                && !c.is_control() -            { -                let mut editor = Editor::new(value, &mut state.cursor); +            if let Some(focus) = &mut state.is_focused { +                if state.is_pasting.is_none() +                    && !state.keyboard_modifiers.command() +                    && !c.is_control() +                { +                    let mut editor = Editor::new(value, &mut state.cursor); -                editor.insert(c); +                    editor.insert(c); -                let message = (on_change)(editor.contents()); -                shell.publish(message); +                    let message = (on_change)(editor.contents()); +                    shell.publish(message); -                return event::Status::Captured; +                    focus.updated_at = Instant::now(); + +                    return event::Status::Captured; +                }              }          }          Event::Keyboard(keyboard::Event::KeyPressed { key_code, .. }) => {              let state = state(); -            if state.is_focused { +            if let Some(focus) = &mut state.is_focused {                  let modifiers = state.keyboard_modifiers; +                focus.updated_at = Instant::now();                  match key_code {                      keyboard::KeyCode::Enter @@ -721,7 +738,7 @@ where                          state.cursor.select_all(value);                      }                      keyboard::KeyCode::Escape => { -                        state.is_focused = false; +                        state.is_focused = None;                          state.is_dragging = false;                          state.is_pasting = None; @@ -742,7 +759,7 @@ where          Event::Keyboard(keyboard::Event::KeyReleased { key_code, .. }) => {              let state = state(); -            if state.is_focused { +            if state.is_focused.is_some() {                  match key_code {                      keyboard::KeyCode::V => {                          state.is_pasting = None; @@ -765,6 +782,21 @@ where              state.keyboard_modifiers = modifiers;          } +        Event::Window(window::Event::RedrawRequested(now)) => { +            let state = state(); + +            if let Some(focus) = &mut state.is_focused { +                focus.now = now; + +                let millis_until_redraw = CURSOR_BLINK_INTERVAL_MILLIS +                    - (now - focus.updated_at).as_millis() +                        % CURSOR_BLINK_INTERVAL_MILLIS; + +                shell.request_redraw(window::RedrawRequest::At( +                    now + Duration::from_millis(millis_until_redraw as u64), +                )); +            } +        }          _ => {}      } @@ -820,7 +852,7 @@ pub fn draw<Renderer>(      let text = value.to_string();      let size = size.unwrap_or_else(|| renderer.default_size()); -    let (cursor, offset) = if state.is_focused() { +    let (cursor, offset) = if let Some(focus) = &state.is_focused {          match state.cursor.state(value) {              cursor::State::Index(position) => {                  let (text_value_width, offset) = @@ -833,7 +865,13 @@ pub fn draw<Renderer>(                          font.clone(),                      ); -                ( +                let is_cursor_visible = ((focus.now - focus.updated_at) +                    .as_millis() +                    / CURSOR_BLINK_INTERVAL_MILLIS) +                    % 2 +                    == 0; + +                let cursor = if is_cursor_visible {                      Some((                          renderer::Quad {                              bounds: Rectangle { @@ -847,9 +885,12 @@ pub fn draw<Renderer>(                              border_color: Color::TRANSPARENT,                          },                          theme.value_color(style), -                    )), -                    offset, -                ) +                    )) +                } else { +                    None +                }; + +                (cursor, offset)              }              cursor::State::Selection { start, end } => {                  let left = start.min(end); @@ -958,7 +999,7 @@ pub fn mouse_interaction(  /// The state of a [`TextInput`].  #[derive(Debug, Default, Clone)]  pub struct State { -    is_focused: bool, +    is_focused: Option<Focus>,      is_dragging: bool,      is_pasting: Option<Value>,      last_click: Option<mouse::Click>, @@ -967,6 +1008,12 @@ pub struct State {      // TODO: Add stateful horizontal scrolling offset  } +#[derive(Debug, Clone, Copy)] +struct Focus { +    updated_at: Instant, +    now: Instant, +} +  impl State {      /// Creates a new [`State`], representing an unfocused [`TextInput`].      pub fn new() -> Self { @@ -976,7 +1023,7 @@ impl State {      /// Creates a new [`State`], representing a focused [`TextInput`].      pub fn focused() -> Self {          Self { -            is_focused: true, +            is_focused: None,              is_dragging: false,              is_pasting: None,              last_click: None, @@ -987,7 +1034,7 @@ impl State {      /// Returns whether the [`TextInput`] is currently focused or not.      pub fn is_focused(&self) -> bool { -        self.is_focused +        self.is_focused.is_some()      }      /// Returns the [`Cursor`] of the [`TextInput`]. @@ -997,13 +1044,19 @@ impl State {      /// Focuses the [`TextInput`].      pub fn focus(&mut self) { -        self.is_focused = true; +        let now = Instant::now(); + +        self.is_focused = Some(Focus { +            updated_at: now, +            now, +        }); +          self.move_cursor_to_end();      }      /// Unfocuses the [`TextInput`].      pub fn unfocus(&mut self) { -        self.is_focused = false; +        self.is_focused = None;      }      /// Moves the [`Cursor`] of the [`TextInput`] to the front of the input text. @@ -1156,3 +1209,5 @@ where          )          .map(text::Hit::cursor)  } + +const CURSOR_BLINK_INTERVAL_MILLIS: u128 = 500; | 
