From 03b34931383e701c39c653a7662a616fe21a0947 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 14 Oct 2021 16:07:22 +0700 Subject: Remove trait-specific draw logic in `iced_native` --- native/src/widget/text_input.rs | 89 ++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 55 deletions(-) (limited to 'native/src/widget/text_input.rs') diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 84d171be..fe95eb95 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -170,38 +170,39 @@ where layout: Layout<'_>, cursor_position: Point, value: Option<&Value>, - ) -> Renderer::Output { - let value = value.unwrap_or(&self.value); - let bounds = layout.bounds(); - let text_bounds = layout.children().next().unwrap().bounds(); - - if self.is_secure { - self::Renderer::draw( - renderer, - bounds, - text_bounds, - cursor_position, - self.font, - self.size.unwrap_or(renderer.default_size()), - &self.placeholder, - &value.secure(), - &self.state, - &self.style, - ) - } else { - self::Renderer::draw( - renderer, - bounds, - text_bounds, - cursor_position, - self.font, - self.size.unwrap_or(renderer.default_size()), - &self.placeholder, - value, - &self.state, - &self.style, - ) - } + ) { + // TODO + // let value = value.unwrap_or(&self.value); + // let bounds = layout.bounds(); + // let text_bounds = layout.children().next().unwrap().bounds(); + + // if self.is_secure { + // self::Renderer::draw( + // renderer, + // bounds, + // text_bounds, + // cursor_position, + // self.font, + // self.size.unwrap_or(renderer.default_size()), + // &self.placeholder, + // &value.secure(), + // &self.state, + // &self.style, + // ) + // } else { + // self::Renderer::draw( + // renderer, + // bounds, + // text_bounds, + // cursor_position, + // self.font, + // self.size.unwrap_or(renderer.default_size()), + // &self.placeholder, + // value, + // &self.state, + // &self.style, + // ) + // } } } @@ -630,7 +631,7 @@ where layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, - ) -> Renderer::Output { + ) { self.draw(renderer, layout, cursor_position, None) } @@ -673,28 +674,6 @@ pub trait Renderer: text::Renderer + Sized { state: &State, ) -> f32; - /// Draws a [`TextInput`]. - /// - /// It receives: - /// - the bounds of the [`TextInput`] - /// - the bounds of the text (i.e. the current value) - /// - the cursor position - /// - the placeholder to show when the value is empty - /// - the current [`Value`] - /// - the current [`State`] - fn draw( - &mut self, - bounds: Rectangle, - text_bounds: Rectangle, - cursor_position: Point, - font: Self::Font, - size: u16, - placeholder: &str, - value: &Value, - state: &State, - style: &Self::Style, - ) -> Self::Output; - /// Computes the position of the text cursor at the given X coordinate of /// a [`TextInput`]. fn find_cursor_position( -- cgit From edea093350e1b576e2b7db50c525e7fa5c3bea9f Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 18 Oct 2021 15:19:04 +0700 Subject: Move `Defaults` from `iced_graphics` to `iced_native` --- native/src/widget/text_input.rs | 85 +++++++++++++++++------------------------ 1 file changed, 35 insertions(+), 50 deletions(-) (limited to 'native/src/widget/text_input.rs') diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index fe95eb95..93af04d1 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -15,6 +15,7 @@ use crate::event::{self, Event}; use crate::keyboard; use crate::layout; use crate::mouse::{self, click}; +use crate::renderer; use crate::text; use crate::touch; use crate::{ @@ -158,54 +159,6 @@ where } } -impl<'a, Message, Renderer> TextInput<'a, Message, Renderer> -where - Renderer: self::Renderer, -{ - /// Draws the [`TextInput`] with the given [`Renderer`], overriding its - /// [`Value`] if provided. - pub fn draw( - &self, - renderer: &mut Renderer, - layout: Layout<'_>, - cursor_position: Point, - value: Option<&Value>, - ) { - // TODO - // let value = value.unwrap_or(&self.value); - // let bounds = layout.bounds(); - // let text_bounds = layout.children().next().unwrap().bounds(); - - // if self.is_secure { - // self::Renderer::draw( - // renderer, - // bounds, - // text_bounds, - // cursor_position, - // self.font, - // self.size.unwrap_or(renderer.default_size()), - // &self.placeholder, - // &value.secure(), - // &self.state, - // &self.style, - // ) - // } else { - // self::Renderer::draw( - // renderer, - // bounds, - // text_bounds, - // cursor_position, - // self.font, - // self.size.unwrap_or(renderer.default_size()), - // &self.placeholder, - // value, - // &self.state, - // &self.style, - // ) - // } - } -} - impl<'a, Message, Renderer> Widget for TextInput<'a, Message, Renderer> where @@ -627,12 +580,44 @@ where fn draw( &self, renderer: &mut Renderer, - _defaults: &Renderer::Defaults, + _style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, ) { - self.draw(renderer, layout, cursor_position, None) + + // TODO + // let value = value.unwrap_or(&self.value); + // let bounds = layout.bounds(); + // let text_bounds = layout.children().next().unwrap().bounds(); + + // if self.is_secure { + // self::Renderer::draw( + // renderer, + // bounds, + // text_bounds, + // cursor_position, + // self.font, + // self.size.unwrap_or(renderer.default_size()), + // &self.placeholder, + // &value.secure(), + // &self.state, + // &self.style, + // ) + // } else { + // self::Renderer::draw( + // renderer, + // bounds, + // text_bounds, + // cursor_position, + // self.font, + // self.size.unwrap_or(renderer.default_size()), + // &self.placeholder, + // value, + // &self.state, + // &self.style, + // ) + // } } fn hash_layout(&self, state: &mut Hasher) { -- cgit From e914888f57394e4b67b40e42f1ad9df4ae8147e6 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 20 Oct 2021 18:40:39 +0700 Subject: Implement `Widget::draw` for `TextInput` --- native/src/widget/text_input.rs | 395 ++++++++++++++++++++++++++++------------ 1 file changed, 277 insertions(+), 118 deletions(-) (limited to 'native/src/widget/text_input.rs') diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 93af04d1..22cb9092 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -11,20 +11,22 @@ pub use value::Value; use editor::Editor; +use crate::alignment; use crate::event::{self, Event}; use crate::keyboard; use crate::layout; use crate::mouse::{self, click}; use crate::renderer; -use crate::text; use crate::touch; use crate::{ - Clipboard, Element, Hasher, Layout, Length, Padding, Point, Rectangle, - Size, Widget, + Background, Clipboard, Color, Element, Hasher, Layout, Length, Padding, + Point, Rectangle, Size, Vector, Widget, }; use std::u32; +pub use iced_style::text_input::{Style, StyleSheet}; + /// A field that can be filled with text. /// /// # Example @@ -50,7 +52,7 @@ use std::u32; /// ``` /// ![Text input drawn by `iced_wgpu`](https://github.com/hecrj/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/text_input.png?raw=true) #[allow(missing_debug_implementations)] -pub struct TextInput<'a, Message, Renderer: self::Renderer> { +pub struct TextInput<'a, Message, Renderer: renderer::Text> { state: &'a mut State, placeholder: String, value: Value, @@ -62,13 +64,13 @@ pub struct TextInput<'a, Message, Renderer: self::Renderer> { size: Option, on_change: Box Message>, on_submit: Option, - style: Renderer::Style, + style_sheet: &'a dyn StyleSheet, } impl<'a, Message, Renderer> TextInput<'a, Message, Renderer> where Message: Clone, - Renderer: self::Renderer, + Renderer: renderer::Text, { /// Creates a new [`TextInput`]. /// @@ -98,7 +100,7 @@ where size: None, on_change: Box::new(on_change), on_submit: None, - style: Renderer::Style::default(), + style_sheet: Default::default(), } } @@ -148,8 +150,8 @@ where } /// Sets the style of the [`TextInput`]. - pub fn style(mut self, style: impl Into) -> Self { - self.style = style.into(); + pub fn style(mut self, style_sheet: &'a dyn StyleSheet) -> Self { + self.style_sheet = style_sheet.into(); self } @@ -163,7 +165,7 @@ impl<'a, Message, Renderer> Widget for TextInput<'a, Message, Renderer> where Message: Clone, - Renderer: self::Renderer, + Renderer: renderer::Text, { fn width(&self) -> Length { self.width @@ -229,7 +231,8 @@ where self.value.clone() }; - renderer.find_cursor_position( + find_cursor_position( + renderer, text_layout.bounds(), self.font, self.size, @@ -248,16 +251,16 @@ where if self.is_secure { self.state.cursor.select_all(&self.value); } else { - let position = renderer - .find_cursor_position( - text_layout.bounds(), - self.font, - self.size, - &self.value, - &self.state, - target, - ) - .unwrap_or(0); + let position = find_cursor_position( + renderer, + text_layout.bounds(), + self.font, + self.size, + &self.value, + &self.state, + target, + ) + .unwrap_or(0); self.state.cursor.select_range( self.value.previous_start_of_word(position), @@ -296,16 +299,16 @@ where self.value.clone() }; - let position = renderer - .find_cursor_position( - text_layout.bounds(), - self.font, - self.size, - &value, - &self.state, - target, - ) - .unwrap_or(0); + let position = find_cursor_position( + renderer, + text_layout.bounds(), + self.font, + self.size, + &value, + &self.state, + target, + ) + .unwrap_or(0); self.state.cursor.select_range( self.state.cursor.start(&value), @@ -585,39 +588,164 @@ where cursor_position: Point, _viewport: &Rectangle, ) { + let secure_value = self.is_secure.then(|| self.value.secure()); + let value = secure_value.as_ref().unwrap_or(&self.value); + + let bounds = layout.bounds(); + let text_bounds = layout.children().next().unwrap().bounds(); - // TODO - // let value = value.unwrap_or(&self.value); - // let bounds = layout.bounds(); - // let text_bounds = layout.children().next().unwrap().bounds(); - - // if self.is_secure { - // self::Renderer::draw( - // renderer, - // bounds, - // text_bounds, - // cursor_position, - // self.font, - // self.size.unwrap_or(renderer.default_size()), - // &self.placeholder, - // &value.secure(), - // &self.state, - // &self.style, - // ) - // } else { - // self::Renderer::draw( - // renderer, - // bounds, - // text_bounds, - // cursor_position, - // self.font, - // self.size.unwrap_or(renderer.default_size()), - // &self.placeholder, - // value, - // &self.state, - // &self.style, - // ) - // } + let is_mouse_over = bounds.contains(cursor_position); + + let style = if self.state.is_focused() { + self.style_sheet.focused() + } else if is_mouse_over { + self.style_sheet.hovered() + } else { + self.style_sheet.active() + }; + + renderer.fill_rectangle(renderer::Quad { + bounds, + background: style.background, + border_radius: style.border_radius, + border_width: style.border_width, + border_color: style.border_color, + }); + + let text = value.to_string(); + let size = self.size.unwrap_or(renderer.default_size()); + + let (cursor, offset) = if self.state.is_focused() { + match self.state.cursor.state(&value) { + cursor::State::Index(position) => { + let (text_value_width, offset) = + measure_cursor_and_scroll_offset( + renderer, + text_bounds, + &value, + size, + position, + self.font, + ); + + ( + Some(renderer::Quad { + bounds: Rectangle { + x: text_bounds.x + text_value_width, + y: text_bounds.y, + width: 1.0, + height: text_bounds.height, + }, + background: Background::Color( + self.style_sheet.value_color(), + ), + border_radius: 0.0, + border_width: 0.0, + border_color: Color::TRANSPARENT, + }), + offset, + ) + } + cursor::State::Selection { start, end } => { + let left = start.min(end); + let right = end.max(start); + + let (left_position, left_offset) = + measure_cursor_and_scroll_offset( + renderer, + text_bounds, + &value, + size, + left, + self.font, + ); + + let (right_position, right_offset) = + measure_cursor_and_scroll_offset( + renderer, + text_bounds, + &value, + size, + right, + self.font, + ); + + let width = right_position - left_position; + + ( + Some(renderer::Quad { + bounds: Rectangle { + x: text_bounds.x + left_position, + y: text_bounds.y, + width, + height: text_bounds.height, + }, + background: Background::Color( + self.style_sheet.selection_color(), + ), + border_radius: 0.0, + border_width: 0.0, + border_color: Color::TRANSPARENT, + }), + if end == right { + right_offset + } else { + left_offset + }, + ) + } + } + } else { + (None, 0.0) + }; + + let text_width = renderer.measure_width( + if text.is_empty() { + &self.placeholder + } else { + &text + }, + size, + self.font, + ); + + let render = |renderer: &mut Renderer| { + if let Some(cursor) = cursor { + renderer.fill_rectangle(cursor); + } + + renderer.fill_text(renderer::text::Section { + content: if text.is_empty() { + &self.placeholder + } else { + &text + }, + color: if text.is_empty() { + self.style_sheet.placeholder_color() + } else { + self.style_sheet.value_color() + }, + font: self.font, + bounds: Rectangle { + y: text_bounds.center_y(), + width: f32::INFINITY, + ..text_bounds + }, + size: f32::from(size), + horizontal_alignment: alignment::Horizontal::Left, + vertical_alignment: alignment::Vertical::Center, + }); + }; + + if text_width > text_bounds.width { + renderer.with_layer( + text_bounds, + Vector::new(offset as u32, 0), + render, + ); + } else { + render(renderer); + } } fn hash_layout(&self, state: &mut Hasher) { @@ -632,65 +760,11 @@ where } } -/// The renderer of a [`TextInput`]. -/// -/// Your [renderer] will need to implement this trait before being -/// able to use a [`TextInput`] in your user interface. -/// -/// [renderer]: crate::renderer -pub trait Renderer: text::Renderer + Sized { - /// The style supported by this renderer. - type Style: Default; - - /// Returns the width of the value of the [`TextInput`]. - fn measure_value(&self, value: &str, size: u16, font: Self::Font) -> f32; - - /// Returns the current horizontal offset of the value of the - /// [`TextInput`]. - /// - /// This is the amount of horizontal scrolling applied when the [`Value`] - /// does not fit the [`TextInput`]. - fn offset( - &self, - text_bounds: Rectangle, - font: Self::Font, - size: u16, - value: &Value, - state: &State, - ) -> f32; - - /// Computes the position of the text cursor at the given X coordinate of - /// a [`TextInput`]. - fn find_cursor_position( - &self, - text_bounds: Rectangle, - font: Self::Font, - size: Option, - value: &Value, - state: &State, - x: f32, - ) -> Option { - let size = size.unwrap_or(self.default_size()); - - let offset = self.offset(text_bounds, font, size, &value, &state); - - self.hit_test( - &value.to_string(), - size.into(), - font, - Size::INFINITY, - Point::new(x + offset, text_bounds.height / 2.0), - true, - ) - .map(text::Hit::cursor) - } -} - impl<'a, Message, Renderer> From> for Element<'a, Message, Renderer> where Message: 'a + Clone, - Renderer: 'a + self::Renderer, + Renderer: 'a + renderer::Text, { fn from( text_input: TextInput<'a, Message, Renderer>, @@ -781,3 +855,88 @@ mod platform { } } } + +fn offset( + renderer: &Renderer, + text_bounds: Rectangle, + font: Renderer::Font, + size: u16, + value: &Value, + state: &State, +) -> f32 +where + Renderer: renderer::Text, +{ + if state.is_focused() { + let cursor = state.cursor(); + + let focus_position = match cursor.state(value) { + cursor::State::Index(i) => i, + cursor::State::Selection { end, .. } => end, + }; + + let (_, offset) = measure_cursor_and_scroll_offset( + renderer, + text_bounds, + value, + size, + focus_position, + font, + ); + + offset + } else { + 0.0 + } +} + +fn measure_cursor_and_scroll_offset( + renderer: &Renderer, + text_bounds: Rectangle, + value: &Value, + size: u16, + cursor_index: usize, + font: Renderer::Font, +) -> (f32, f32) +where + Renderer: renderer::Text, +{ + let text_before_cursor = value.until(cursor_index).to_string(); + + let text_value_width = + renderer.measure_width(&text_before_cursor, size, font); + + let offset = ((text_value_width + 5.0) - text_bounds.width).max(0.0); + + (text_value_width, offset) +} + +/// Computes the position of the text cursor at the given X coordinate of +/// a [`TextInput`]. +fn find_cursor_position( + renderer: &Renderer, + text_bounds: Rectangle, + font: Renderer::Font, + size: Option, + value: &Value, + state: &State, + x: f32, +) -> Option +where + Renderer: renderer::Text, +{ + let size = size.unwrap_or(renderer.default_size()); + + let offset = offset(renderer, text_bounds, font, size, &value, &state); + + renderer + .hit_test( + &value.to_string(), + size.into(), + font, + Size::INFINITY, + Point::new(x + offset, text_bounds.height / 2.0), + true, + ) + .map(renderer::text::Hit::cursor) +} -- cgit From cc560aca181202e827bfdc9afb6fc9fbe42f54d6 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 20 Oct 2021 18:45:45 +0700 Subject: Implement `Widget::mouse_interaction` for `TextInput` --- native/src/widget/text_input.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'native/src/widget/text_input.rs') diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 22cb9092..0a2b19a3 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -580,6 +580,19 @@ where event::Status::Ignored } + fn mouse_interaction( + &self, + layout: Layout<'_>, + _viewport: &Rectangle, + cursor_position: Point, + ) -> mouse::Interaction { + if layout.bounds().contains(cursor_position) { + mouse::Interaction::Text + } else { + mouse::Interaction::default() + } + } + fn draw( &self, renderer: &mut Renderer, -- cgit From 4a11cbd99445338619dfaf1f327dbc25b2983cb7 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 25 Oct 2021 16:16:35 +0700 Subject: Implement `Widget::mouse_interaction` for `PaneGrid` ... and fix rendering of drag interaction in `PaneGrid` by introducing an explicit `with_translation` method to `Renderer` and simplifying the `with_layer` and `Clip` primitive. --- native/src/widget/text_input.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'native/src/widget/text_input.rs') diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 0a2b19a3..c2950db9 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -751,11 +751,9 @@ where }; if text_width > text_bounds.width { - renderer.with_layer( - text_bounds, - Vector::new(offset as u32, 0), - render, - ); + renderer.with_layer(text_bounds, |renderer| { + renderer.with_translation(Vector::new(-offset, 0.0), render) + }); } else { render(renderer); } -- cgit From 0aafcde0ef1533c9eeba0379de8c0082e30c7504 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 31 Oct 2021 15:35:12 +0700 Subject: Remove `widget` module re-exports in `iced_native` --- native/src/widget/text_input.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'native/src/widget/text_input.rs') diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index c2950db9..5d67ddfe 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -31,9 +31,10 @@ pub use iced_style::text_input::{Style, StyleSheet}; /// /// # Example /// ``` -/// # use iced_native::{text_input, renderer::Null}; +/// # use iced_native::renderer::Null; +/// # use iced_native::widget::text_input; /// # -/// # pub type TextInput<'a, Message> = iced_native::TextInput<'a, Message, Null>; +/// # pub type TextInput<'a, Message> = iced_native::widget::TextInput<'a, Message, Null>; /// #[derive(Debug, Clone)] /// enum Message { /// TextInputChanged(String), -- cgit From b3a01973c6c726e6539be959659f4306ef3234c6 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 31 Oct 2021 16:13:03 +0700 Subject: Introduce first-class `text` module in `iced_native` --- native/src/widget/text_input.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'native/src/widget/text_input.rs') diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 5d67ddfe..a3641330 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -17,6 +17,7 @@ use crate::keyboard; use crate::layout; use crate::mouse::{self, click}; use crate::renderer; +use crate::text::{self, Text}; use crate::touch; use crate::{ Background, Clipboard, Color, Element, Hasher, Layout, Length, Padding, @@ -53,7 +54,7 @@ pub use iced_style::text_input::{Style, StyleSheet}; /// ``` /// ![Text input drawn by `iced_wgpu`](https://github.com/hecrj/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/text_input.png?raw=true) #[allow(missing_debug_implementations)] -pub struct TextInput<'a, Message, Renderer: renderer::Text> { +pub struct TextInput<'a, Message, Renderer: text::Renderer> { state: &'a mut State, placeholder: String, value: Value, @@ -71,7 +72,7 @@ pub struct TextInput<'a, Message, Renderer: renderer::Text> { impl<'a, Message, Renderer> TextInput<'a, Message, Renderer> where Message: Clone, - Renderer: renderer::Text, + Renderer: text::Renderer, { /// Creates a new [`TextInput`]. /// @@ -166,7 +167,7 @@ impl<'a, Message, Renderer> Widget for TextInput<'a, Message, Renderer> where Message: Clone, - Renderer: renderer::Text, + Renderer: text::Renderer, { fn width(&self) -> Length { self.width @@ -728,7 +729,7 @@ where renderer.fill_rectangle(cursor); } - renderer.fill_text(renderer::text::Section { + renderer.fill_text(Text { content: if text.is_empty() { &self.placeholder } else { @@ -776,7 +777,7 @@ impl<'a, Message, Renderer> From> for Element<'a, Message, Renderer> where Message: 'a + Clone, - Renderer: 'a + renderer::Text, + Renderer: 'a + text::Renderer, { fn from( text_input: TextInput<'a, Message, Renderer>, @@ -877,7 +878,7 @@ fn offset( state: &State, ) -> f32 where - Renderer: renderer::Text, + Renderer: text::Renderer, { if state.is_focused() { let cursor = state.cursor(); @@ -911,7 +912,7 @@ fn measure_cursor_and_scroll_offset( font: Renderer::Font, ) -> (f32, f32) where - Renderer: renderer::Text, + Renderer: text::Renderer, { let text_before_cursor = value.until(cursor_index).to_string(); @@ -935,7 +936,7 @@ fn find_cursor_position( x: f32, ) -> Option where - Renderer: renderer::Text, + Renderer: text::Renderer, { let size = size.unwrap_or(renderer.default_size()); @@ -950,5 +951,5 @@ where Point::new(x + offset, text_bounds.height / 2.0), true, ) - .map(renderer::text::Hit::cursor) + .map(text::Hit::cursor) } -- cgit From 0d3c9ef7bd3d0a0abecdf8e8b5391e32773ca20e Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 31 Oct 2021 17:45:57 +0700 Subject: Reintroduce `Box` for `style_sheet` in `TextInput` --- native/src/widget/text_input.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'native/src/widget/text_input.rs') diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index a3641330..5be1d3d6 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -66,7 +66,7 @@ pub struct TextInput<'a, Message, Renderer: text::Renderer> { size: Option, on_change: Box Message>, on_submit: Option, - style_sheet: &'a dyn StyleSheet, + style_sheet: Box, } impl<'a, Message, Renderer> TextInput<'a, Message, Renderer> @@ -152,7 +152,10 @@ where } /// Sets the style of the [`TextInput`]. - pub fn style(mut self, style_sheet: &'a dyn StyleSheet) -> Self { + pub fn style( + mut self, + style_sheet: impl Into>, + ) -> Self { self.style_sheet = style_sheet.into(); self } -- cgit From a1db8d62c00dc355e677b98fb620cba3b478ae53 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 1 Nov 2021 16:29:28 +0700 Subject: Reintroduce `draw` method in `TextInput` ... which allows overriding the rendered value. --- native/src/widget/text_input.rs | 330 +++++++++++++++++++++------------------- 1 file changed, 174 insertions(+), 156 deletions(-) (limited to 'native/src/widget/text_input.rs') diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 2d5f6815..5dd68f81 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -166,6 +166,179 @@ where } } +impl<'a, Message, Renderer> TextInput<'a, Message, Renderer> +where + Renderer: text::Renderer, +{ + /// Draws the [`TextInput`] with the given [`Renderer`], overriding its + /// [`Value`] if provided. + pub fn draw( + &self, + renderer: &mut Renderer, + layout: Layout<'_>, + cursor_position: Point, + value: Option<&Value>, + ) { + let value = value.unwrap_or(&self.value); + let secure_value = self.is_secure.then(|| value.secure()); + let value = secure_value.as_ref().unwrap_or(&value); + + let bounds = layout.bounds(); + let text_bounds = layout.children().next().unwrap().bounds(); + + let is_mouse_over = bounds.contains(cursor_position); + + let style = if self.state.is_focused() { + self.style_sheet.focused() + } else if is_mouse_over { + self.style_sheet.hovered() + } else { + self.style_sheet.active() + }; + + renderer.fill_rectangle(renderer::Quad { + bounds, + background: style.background, + border_radius: style.border_radius, + border_width: style.border_width, + border_color: style.border_color, + }); + + let text = value.to_string(); + let size = self.size.unwrap_or(renderer.default_size()); + + let (cursor, offset) = if self.state.is_focused() { + match self.state.cursor.state(&value) { + cursor::State::Index(position) => { + let (text_value_width, offset) = + measure_cursor_and_scroll_offset( + renderer, + text_bounds, + &value, + size, + position, + self.font, + ); + + ( + Some(renderer::Quad { + bounds: Rectangle { + x: text_bounds.x + text_value_width, + y: text_bounds.y, + width: 1.0, + height: text_bounds.height, + }, + background: Background::Color( + self.style_sheet.value_color(), + ), + border_radius: 0.0, + border_width: 0.0, + border_color: Color::TRANSPARENT, + }), + offset, + ) + } + cursor::State::Selection { start, end } => { + let left = start.min(end); + let right = end.max(start); + + let (left_position, left_offset) = + measure_cursor_and_scroll_offset( + renderer, + text_bounds, + &value, + size, + left, + self.font, + ); + + let (right_position, right_offset) = + measure_cursor_and_scroll_offset( + renderer, + text_bounds, + &value, + size, + right, + self.font, + ); + + let width = right_position - left_position; + + ( + Some(renderer::Quad { + bounds: Rectangle { + x: text_bounds.x + left_position, + y: text_bounds.y, + width, + height: text_bounds.height, + }, + background: Background::Color( + self.style_sheet.selection_color(), + ), + border_radius: 0.0, + border_width: 0.0, + border_color: Color::TRANSPARENT, + }), + if end == right { + right_offset + } else { + left_offset + }, + ) + } + } + } else { + (None, 0.0) + }; + + let text_width = renderer.measure_width( + if text.is_empty() { + &self.placeholder + } else { + &text + }, + size, + self.font, + ); + + let render = |renderer: &mut Renderer| { + if let Some(cursor) = cursor { + renderer.fill_rectangle(cursor); + } + + renderer.fill_text(Text { + content: if text.is_empty() { + &self.placeholder + } else { + &text + }, + color: if text.is_empty() { + self.style_sheet.placeholder_color() + } else { + self.style_sheet.value_color() + }, + font: self.font, + bounds: Rectangle { + y: text_bounds.center_y(), + width: f32::INFINITY, + ..text_bounds + }, + size: f32::from(size), + horizontal_alignment: alignment::Horizontal::Left, + vertical_alignment: alignment::Vertical::Center, + }); + }; + + if text_width > text_bounds.width { + renderer.with_layer(text_bounds, |renderer| { + renderer.with_translation(Vector::new(-offset, 0.0), render) + }); + } else { + render(renderer); + } + } +} + impl<'a, Message, Renderer> Widget for TextInput<'a, Message, Renderer> where @@ -604,162 +777,7 @@ where cursor_position: Point, _viewport: &Rectangle, ) { - let secure_value = self.is_secure.then(|| self.value.secure()); - let value = secure_value.as_ref().unwrap_or(&self.value); - - let bounds = layout.bounds(); - let text_bounds = layout.children().next().unwrap().bounds(); - - let is_mouse_over = bounds.contains(cursor_position); - - let style = if self.state.is_focused() { - self.style_sheet.focused() - } else if is_mouse_over { - self.style_sheet.hovered() - } else { - self.style_sheet.active() - }; - - renderer.fill_rectangle(renderer::Quad { - bounds, - background: style.background, - border_radius: style.border_radius, - border_width: style.border_width, - border_color: style.border_color, - }); - - let text = value.to_string(); - let size = self.size.unwrap_or(renderer.default_size()); - - let (cursor, offset) = if self.state.is_focused() { - match self.state.cursor.state(&value) { - cursor::State::Index(position) => { - let (text_value_width, offset) = - measure_cursor_and_scroll_offset( - renderer, - text_bounds, - &value, - size, - position, - self.font, - ); - - ( - Some(renderer::Quad { - bounds: Rectangle { - x: text_bounds.x + text_value_width, - y: text_bounds.y, - width: 1.0, - height: text_bounds.height, - }, - background: Background::Color( - self.style_sheet.value_color(), - ), - border_radius: 0.0, - border_width: 0.0, - border_color: Color::TRANSPARENT, - }), - offset, - ) - } - cursor::State::Selection { start, end } => { - let left = start.min(end); - let right = end.max(start); - - let (left_position, left_offset) = - measure_cursor_and_scroll_offset( - renderer, - text_bounds, - &value, - size, - left, - self.font, - ); - - let (right_position, right_offset) = - measure_cursor_and_scroll_offset( - renderer, - text_bounds, - &value, - size, - right, - self.font, - ); - - let width = right_position - left_position; - - ( - Some(renderer::Quad { - bounds: Rectangle { - x: text_bounds.x + left_position, - y: text_bounds.y, - width, - height: text_bounds.height, - }, - background: Background::Color( - self.style_sheet.selection_color(), - ), - border_radius: 0.0, - border_width: 0.0, - border_color: Color::TRANSPARENT, - }), - if end == right { - right_offset - } else { - left_offset - }, - ) - } - } - } else { - (None, 0.0) - }; - - let text_width = renderer.measure_width( - if text.is_empty() { - &self.placeholder - } else { - &text - }, - size, - self.font, - ); - - let render = |renderer: &mut Renderer| { - if let Some(cursor) = cursor { - renderer.fill_rectangle(cursor); - } - - renderer.fill_text(Text { - content: if text.is_empty() { - &self.placeholder - } else { - &text - }, - color: if text.is_empty() { - self.style_sheet.placeholder_color() - } else { - self.style_sheet.value_color() - }, - font: self.font, - bounds: Rectangle { - y: text_bounds.center_y(), - width: f32::INFINITY, - ..text_bounds - }, - size: f32::from(size), - horizontal_alignment: alignment::Horizontal::Left, - vertical_alignment: alignment::Vertical::Center, - }); - }; - - if text_width > text_bounds.width { - renderer.with_layer(text_bounds, |renderer| { - renderer.with_translation(Vector::new(-offset, 0.0), render) - }); - } else { - render(renderer); - } + self.draw(renderer, layout, cursor_position, None) } fn hash_layout(&self, state: &mut Hasher) { -- cgit From 631e95ee0be01dc7f5e5183e1429972aee37787f Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 2 Nov 2021 15:03:29 +0700 Subject: Move `viewport` argument to last position in `mouse_interaction` methods This keeps the order of the arguments consistent with `draw`. --- native/src/widget/text_input.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'native/src/widget/text_input.rs') diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 5dd68f81..db60b553 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -759,8 +759,8 @@ where fn mouse_interaction( &self, layout: Layout<'_>, - _viewport: &Rectangle, cursor_position: Point, + _viewport: &Rectangle, ) -> mouse::Interaction { if layout.bounds().contains(cursor_position) { mouse::Interaction::Text -- cgit From 023aded2772f0cd6abd716fe5c8624d5d22e21fa Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 4 Nov 2021 19:22:29 +0700 Subject: Rename `fill_rectangle` to `fill_quad` in `Renderer` --- native/src/widget/text_input.rs | 76 +++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 37 deletions(-) (limited to 'native/src/widget/text_input.rs') diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index db60b553..40c6c573 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -20,8 +20,8 @@ use crate::renderer; use crate::text::{self, Text}; use crate::touch; use crate::{ - Background, Clipboard, Color, Element, Hasher, Layout, Length, Padding, - Point, Rectangle, Size, Vector, Widget, + Clipboard, Color, Element, Hasher, Layout, Length, Padding, Point, + Rectangle, Size, Vector, Widget, }; use std::u32; @@ -196,13 +196,15 @@ where self.style_sheet.active() }; - renderer.fill_rectangle(renderer::Quad { - bounds, - background: style.background, - border_radius: style.border_radius, - border_width: style.border_width, - border_color: style.border_color, - }); + renderer.fill_quad( + renderer::Quad { + bounds, + border_radius: style.border_radius, + border_width: style.border_width, + border_color: style.border_color, + }, + style.background, + ); let text = value.to_string(); let size = self.size.unwrap_or(renderer.default_size()); @@ -221,20 +223,20 @@ where ); ( - Some(renderer::Quad { - bounds: Rectangle { - x: text_bounds.x + text_value_width, - y: text_bounds.y, - width: 1.0, - height: text_bounds.height, + Some(( + renderer::Quad { + bounds: Rectangle { + x: text_bounds.x + text_value_width, + y: text_bounds.y, + width: 1.0, + height: text_bounds.height, + }, + border_radius: 0.0, + border_width: 0.0, + border_color: Color::TRANSPARENT, }, - background: Background::Color( - self.style_sheet.value_color(), - ), - border_radius: 0.0, - border_width: 0.0, - border_color: Color::TRANSPARENT, - }), + self.style_sheet.value_color(), + )), offset, ) } @@ -265,20 +267,20 @@ where let width = right_position - left_position; ( - Some(renderer::Quad { - bounds: Rectangle { - x: text_bounds.x + left_position, - y: text_bounds.y, - width, - height: text_bounds.height, + Some(( + renderer::Quad { + bounds: Rectangle { + x: text_bounds.x + left_position, + y: text_bounds.y, + width, + height: text_bounds.height, + }, + border_radius: 0.0, + border_width: 0.0, + border_color: Color::TRANSPARENT, }, - background: Background::Color( - self.style_sheet.selection_color(), - ), - border_radius: 0.0, - border_width: 0.0, - border_color: Color::TRANSPARENT, - }), + self.style_sheet.selection_color(), + )), if end == right { right_offset } else { @@ -302,8 +304,8 @@ where ); let render = |renderer: &mut Renderer| { - if let Some(cursor) = cursor { - renderer.fill_rectangle(cursor); + if let Some((cursor, color)) = cursor { + renderer.fill_quad(cursor, color); } renderer.fill_text(Text { -- cgit