diff --git a/core/src/renderer/null.rs b/core/src/renderer/null.rs index e8709dbc93..633d2ad79b 100644 --- a/core/src/renderer/null.rs +++ b/core/src/renderer/null.rs @@ -2,7 +2,7 @@ use crate::alignment; use crate::image; use crate::renderer::{self, Renderer}; use crate::svg; -use crate::text::{self, Text}; +use crate::text::{self, Text, Wrapping}; use crate::{ Background, Color, Font, Pixels, Point, Radians, Rectangle, Size, Transformation, @@ -143,6 +143,7 @@ impl text::Editor for () { &mut self, _new_bounds: Size, _new_font: Self::Font, + _new_wrapping: Wrapping, _new_size: Pixels, _new_line_height: text::LineHeight, _new_highlighter: &mut impl text::Highlighter, diff --git a/core/src/text.rs b/core/src/text.rs index b30feae05d..9ef34ce536 100644 --- a/core/src/text.rs +++ b/core/src/text.rs @@ -39,6 +39,23 @@ pub struct Text { /// The [`Shaping`] strategy of the [`Text`]. pub shaping: Shaping, + + /// The [`Wrapping`] strategy of the [`Text`]. + pub wrapping: Wrapping, +} + +/// The wrapping strategy of some text. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] +pub enum Wrapping { + /// No wrapping. + None, + /// Word wrapping. + /// + /// This is the default. + #[default] + Word, + /// Glyph wrapping. + Glyph, } /// The shaping strategy of some text. diff --git a/core/src/text/editor.rs b/core/src/text/editor.rs index fbf6069607..06ee8244b3 100644 --- a/core/src/text/editor.rs +++ b/core/src/text/editor.rs @@ -1,6 +1,6 @@ //! Edit text. use crate::text::highlighter::{self, Highlighter}; -use crate::text::LineHeight; +use crate::text::{LineHeight, Wrapping}; use crate::{Pixels, Point, Rectangle, Size}; use std::sync::Arc; @@ -45,6 +45,7 @@ pub trait Editor: Sized + Default { &mut self, new_bounds: Size, new_font: Self::Font, + new_wrapping: Wrapping, new_size: Pixels, new_line_height: LineHeight, new_highlighter: &mut impl Highlighter, diff --git a/core/src/widget/text.rs b/core/src/widget/text.rs index f1f0b34586..5bc8003c4e 100644 --- a/core/src/widget/text.rs +++ b/core/src/widget/text.rs @@ -12,7 +12,7 @@ use crate::{ use std::borrow::Cow; -pub use text::{LineHeight, Shaping}; +pub use text::{LineHeight, Shaping, Wrapping}; /// A paragraph of text. #[allow(missing_debug_implementations)] @@ -31,6 +31,7 @@ where font: Option, shaping: Shaping, class: Theme::Class<'a>, + wrapping: Wrapping, } impl<'a, Theme, Renderer> Text<'a, Theme, Renderer> @@ -51,6 +52,7 @@ where vertical_alignment: alignment::Vertical::Top, shaping: Shaping::Basic, class: Theme::default(), + wrapping: Default::default(), } } @@ -110,6 +112,12 @@ where self } + /// Sets the [`Wrapping`] strategy of the [`Text`]. + pub fn wrapping(mut self, wrapping: Wrapping) -> Self { + self.wrapping = wrapping; + self + } + /// Sets the style of the [`Text`]. #[must_use] pub fn style(mut self, style: impl Fn(&Theme) -> Style + 'a) -> Self @@ -191,6 +199,7 @@ where self.horizontal_alignment, self.vertical_alignment, self.shaping, + self.wrapping, ) } @@ -225,6 +234,7 @@ pub fn layout( horizontal_alignment: alignment::Horizontal, vertical_alignment: alignment::Vertical, shaping: Shaping, + wrapping: Wrapping, ) -> layout::Node where Renderer: text::Renderer, @@ -246,6 +256,7 @@ where horizontal_alignment, vertical_alignment, shaping, + wrapping, }); paragraph.min_bounds() diff --git a/examples/editor/src/main.rs b/examples/editor/src/main.rs index c20a7d67e5..ba032e9baa 100644 --- a/examples/editor/src/main.rs +++ b/examples/editor/src/main.rs @@ -2,10 +2,11 @@ use iced::highlighter::{self, Highlighter}; use iced::keyboard; use iced::widget::{ button, column, container, horizontal_space, pick_list, row, text, - text_editor, tooltip, + text_editor, toggler, tooltip, }; use iced::{Alignment, Command, Element, Font, Length, Subscription, Theme}; +use iced::widget::text::Wrapping; use std::ffi; use std::io; use std::path::{Path, PathBuf}; @@ -27,6 +28,7 @@ struct Editor { theme: highlighter::Theme, is_loading: bool, is_dirty: bool, + wrap: bool, } #[derive(Debug, Clone)] @@ -38,6 +40,7 @@ enum Message { FileOpened(Result<(PathBuf, Arc), Error>), SaveFile, FileSaved(Result), + ToggleWrap(bool), } impl Editor { @@ -48,6 +51,7 @@ impl Editor { theme: highlighter::Theme::SolarizedDark, is_loading: true, is_dirty: false, + wrap: true, } } @@ -122,6 +126,10 @@ impl Editor { Command::none() } + Message::ToggleWrap(enabled) => { + self.wrap = enabled; + Command::none() + } } } @@ -148,13 +156,16 @@ impl Editor { self.is_dirty.then_some(Message::SaveFile) ), horizontal_space(), + toggler("Word wrap".to_string(), self.wrap, Message::ToggleWrap) + .text_size(14) + .width(Length::Shrink), pick_list( highlighter::Theme::ALL, Some(self.theme), Message::ThemeSelected ) .text_size(14) - .padding([5, 10]) + .padding([5, 10]), ] .spacing(10) .align_items(Alignment::Center); @@ -184,6 +195,11 @@ impl Editor { controls, text_editor(&self.content) .height(Length::Fill) + .wrapping(if self.wrap { + Wrapping::Word + } else { + Wrapping::None + }) .on_action(Message::ActionPerformed) .highlight::( highlighter::Settings { diff --git a/graphics/src/text.rs b/graphics/src/text.rs index 30269e69eb..2ce64d2e5e 100644 --- a/graphics/src/text.rs +++ b/graphics/src/text.rs @@ -14,6 +14,7 @@ use crate::core::font::{self, Font}; use crate::core::text::Shaping; use crate::core::{Color, Pixels, Point, Rectangle, Size, Transformation}; +use iced_core::text::Wrapping; use once_cell::sync::OnceCell; use std::borrow::Cow; use std::sync::{Arc, RwLock, Weak}; @@ -250,6 +251,14 @@ pub fn to_attributes(font: Font) -> cosmic_text::Attrs<'static> { .style(to_style(font.style)) } +fn to_wrap(wrapping: Wrapping) -> cosmic_text::Wrap { + match wrapping { + Wrapping::None => cosmic_text::Wrap::None, + Wrapping::Word => cosmic_text::Wrap::Word, + Wrapping::Glyph => cosmic_text::Wrap::Glyph, + } +} + fn to_family(family: font::Family) -> cosmic_text::Family<'static> { match family { font::Family::Name(name) => cosmic_text::Family::Name(name), diff --git a/graphics/src/text/editor.rs b/graphics/src/text/editor.rs index 4b8f0f2ade..e72ad7ddc9 100644 --- a/graphics/src/text/editor.rs +++ b/graphics/src/text/editor.rs @@ -9,6 +9,7 @@ use crate::text; use cosmic_text::Edit as _; +use crate::text::to_wrap; use std::fmt; use std::sync::{self, Arc}; @@ -484,6 +485,7 @@ impl editor::Editor for Editor { &mut self, new_bounds: Size, new_font: Font, + new_wrapping: text::Wrapping, new_size: Pixels, new_line_height: LineHeight, new_highlighter: &mut impl Highlighter, @@ -535,6 +537,15 @@ impl editor::Editor for Editor { ); } + let new_wrap = to_wrap(new_wrapping); + if new_wrap != internal.editor.buffer().wrap() { + log::trace!("Updating wrapping strategy of `Editor`..."); + internal + .editor + .buffer_mut() + .set_wrap(font_system.raw(), new_wrap); + } + if new_bounds != internal.bounds { log::trace!("Updating size of `Editor`..."); diff --git a/graphics/src/text/paragraph.rs b/graphics/src/text/paragraph.rs index 31a323ac90..28057814d6 100644 --- a/graphics/src/text/paragraph.rs +++ b/graphics/src/text/paragraph.rs @@ -5,6 +5,7 @@ use crate::core::text::{Hit, LineHeight, Shaping, Text}; use crate::core::{Font, Pixels, Point, Size}; use crate::text; +use iced_core::text::Wrapping; use std::fmt; use std::sync::{self, Arc}; @@ -22,6 +23,7 @@ struct Internal { bounds: Size, min_bounds: Size, version: text::Version, + wrapping: Wrapping, } impl Paragraph { @@ -88,6 +90,8 @@ impl core::text::Paragraph for Paragraph { text::to_shaping(text.shaping), ); + buffer.set_wrap(font_system.raw(), text::to_wrap(text.wrapping)); + let min_bounds = text::measure(&buffer); Self(Some(Arc::new(Internal { @@ -100,6 +104,7 @@ impl core::text::Paragraph for Paragraph { bounds: text.bounds, min_bounds, version: font_system.version(), + wrapping: text.wrapping, }))) } @@ -141,6 +146,7 @@ impl core::text::Paragraph for Paragraph { horizontal_alignment: internal.horizontal_alignment, vertical_alignment: internal.vertical_alignment, shaping: internal.shaping, + wrapping: internal.wrapping, }); } } @@ -159,6 +165,7 @@ impl core::text::Paragraph for Paragraph { || paragraph.shaping != text.shaping || paragraph.horizontal_alignment != text.horizontal_alignment || paragraph.vertical_alignment != text.vertical_alignment + || paragraph.wrapping != text.wrapping { core::text::Difference::Shape } else if paragraph.bounds != text.bounds { @@ -247,6 +254,7 @@ impl fmt::Debug for Paragraph { .field("vertical_alignment", ¶graph.vertical_alignment) .field("bounds", ¶graph.bounds) .field("min_bounds", ¶graph.min_bounds) + .field("wrapping", ¶graph.wrapping) .finish() } } @@ -261,6 +269,7 @@ impl PartialEq for Internal { && self.bounds == other.bounds && self.min_bounds == other.min_bounds && self.buffer.metrics() == other.buffer.metrics() + && self.wrapping == other.wrapping } } @@ -279,6 +288,7 @@ impl Default for Internal { bounds: Size::ZERO, min_bounds: Size::ZERO, version: text::Version::default(), + wrapping: Wrapping::default(), } } } diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index ad88ce3e4d..f4a6c1ce57 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -408,6 +408,7 @@ impl Renderer { horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Top, shaping: core::text::Shaping::Basic, + wrapping: core::text::Wrapping::default(), }; renderer.fill_text( diff --git a/widget/src/checkbox.rs b/widget/src/checkbox.rs index 225c316d48..7c690b4911 100644 --- a/widget/src/checkbox.rs +++ b/widget/src/checkbox.rs @@ -5,6 +5,7 @@ use crate::core::layout; use crate::core::mouse; use crate::core::renderer; use crate::core::text; +use crate::core::text::Wrapping; use crate::core::theme::palette; use crate::core::touch; use crate::core::widget; @@ -240,6 +241,7 @@ where alignment::Horizontal::Left, alignment::Vertical::Top, self.text_shaping, + Wrapping::default(), ) }, ) @@ -348,6 +350,7 @@ where horizontal_alignment: alignment::Horizontal::Center, vertical_alignment: alignment::Vertical::Center, shaping: *shaping, + wrapping: Wrapping::default(), }, bounds.center(), style.icon_color, diff --git a/widget/src/overlay/menu.rs b/widget/src/overlay/menu.rs index 98efe30523..cad3ca0d6f 100644 --- a/widget/src/overlay/menu.rs +++ b/widget/src/overlay/menu.rs @@ -534,6 +534,7 @@ where horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Center, shaping: self.text_shaping, + wrapping: text::Wrapping::default(), }, Point::new(bounds.x + self.padding.left, bounds.center_y()), if is_selected { diff --git a/widget/src/pick_list.rs b/widget/src/pick_list.rs index edccfdaaa6..e45d7af639 100644 --- a/widget/src/pick_list.rs +++ b/widget/src/pick_list.rs @@ -225,6 +225,7 @@ where horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Center, shaping: self.text_shaping, + wrapping: text::Wrapping::default(), }; for (option, paragraph) in options.iter().zip(state.options.iter_mut()) @@ -490,6 +491,7 @@ where horizontal_alignment: alignment::Horizontal::Right, vertical_alignment: alignment::Vertical::Center, shaping, + wrapping: text::Wrapping::default(), }, Point::new( bounds.x + bounds.width - self.padding.right, @@ -519,6 +521,7 @@ where horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Center, shaping: self.text_shaping, + wrapping: text::Wrapping::default(), }, Point::new(bounds.x + self.padding.left, bounds.center_y()), if is_selected { diff --git a/widget/src/radio.rs b/widget/src/radio.rs index 6b22961db4..de92ab27b1 100644 --- a/widget/src/radio.rs +++ b/widget/src/radio.rs @@ -244,6 +244,7 @@ where alignment::Horizontal::Left, alignment::Vertical::Top, self.text_shaping, + text::Wrapping::default(), ) }, ) diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index 7c0b98ea6d..30b0566812 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -20,6 +20,7 @@ use std::fmt; use std::ops::DerefMut; use std::sync::Arc; +use crate::text::Wrapping; pub use text::editor::{Action, Edit, Motion}; /// A multi-line text input. @@ -43,6 +44,7 @@ pub struct TextEditor< height: Length, padding: Padding, class: Theme::Class<'a>, + wrapping: Wrapping, on_edit: Option Message + 'a>>, highlighter_settings: Highlighter::Settings, highlighter_format: fn( @@ -68,6 +70,7 @@ where height: Length::Shrink, padding: Padding::new(5.0), class: Theme::default(), + wrapping: Wrapping::default(), on_edit: None, highlighter_settings: (), highlighter_format: |_highlight, _theme| { @@ -131,6 +134,12 @@ where self } + /// Sets the [`Wrapping`] strategy of the [`TextEditor`]. + pub fn wrapping(mut self, wrapping: Wrapping) -> Self { + self.wrapping = wrapping; + self + } + /// Highlights the [`TextEditor`] with the given [`Highlighter`] and /// a strategy to turn its highlights into some text format. pub fn highlight( @@ -150,6 +159,7 @@ where height: self.height, padding: self.padding, class: self.class, + wrapping: self.wrapping, on_edit: self.on_edit, highlighter_settings: settings, highlighter_format: to_format, @@ -400,6 +410,7 @@ where internal.editor.update( limits.shrink(self.padding).max(), self.font.unwrap_or_else(|| renderer.default_font()), + self.wrapping, self.text_size.unwrap_or_else(|| renderer.default_size()), self.line_height, state.highlighter.borrow_mut().deref_mut(), diff --git a/widget/src/text_input.rs b/widget/src/text_input.rs index e9f0783876..a2bd5dd049 100644 --- a/widget/src/text_input.rs +++ b/widget/src/text_input.rs @@ -238,6 +238,7 @@ where horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Center, shaping: text::Shaping::Advanced, + wrapping: text::Wrapping::default(), }; state.placeholder.update(placeholder_text); @@ -262,6 +263,7 @@ where horizontal_alignment: alignment::Horizontal::Center, vertical_alignment: alignment::Vertical::Center, shaping: text::Shaping::Advanced, + wrapping: text::Wrapping::default(), }; state.icon.update(icon_text); @@ -1383,6 +1385,7 @@ fn replace_paragraph( horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Top, shaping: text::Shaping::Advanced, + wrapping: text::Wrapping::default(), }); } diff --git a/widget/src/toggler.rs b/widget/src/toggler.rs index ca6e37b0c0..ae6df3fb2a 100644 --- a/widget/src/toggler.rs +++ b/widget/src/toggler.rs @@ -216,6 +216,7 @@ where self.text_alignment, alignment::Vertical::Top, self.text_shaping, + text::Wrapping::default(), ) } else { layout::Node::new(Size::ZERO)