Add text::Wrapping support

Co-authored-by: Neeraj Jaiswal <neerajj85@gmail.com>
This commit is contained in:
Héctor Ramón Jiménez 2024-09-04 21:25:59 +02:00
parent 8d826cc662
commit f98328f4f1
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
21 changed files with 160 additions and 30 deletions

View file

@ -161,6 +161,7 @@ impl text::Editor for () {
_new_font: Self::Font, _new_font: Self::Font,
_new_size: Pixels, _new_size: Pixels,
_new_line_height: text::LineHeight, _new_line_height: text::LineHeight,
_new_wrapping: text::Wrapping,
_new_highlighter: &mut impl text::Highlighter, _new_highlighter: &mut impl text::Highlighter,
) { ) {
} }

View file

@ -41,6 +41,9 @@ pub struct Text<Content = String, Font = crate::Font> {
/// The [`Shaping`] strategy of the [`Text`]. /// The [`Shaping`] strategy of the [`Text`].
pub shaping: Shaping, pub shaping: Shaping,
/// The [`Wrapping`] strategy of the [`Text`].
pub wrapping: Wrapping,
} }
/// The shaping strategy of some text. /// The shaping strategy of some text.
@ -67,6 +70,22 @@ pub enum Shaping {
Advanced, Advanced,
} }
/// The wrapping strategy of some text.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum Wrapping {
/// No wrapping.
None,
/// Wraps at the word level.
///
/// This is the default.
#[default]
Word,
/// Wraps at the glyph level.
Glyph,
/// Wraps at the word level, or fallback to glyph level if a word can't fit on a line by itself.
WordOrGlyph,
}
/// The height of a line of text in a paragraph. /// The height of a line of text in a paragraph.
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub enum LineHeight { pub enum LineHeight {

View file

@ -1,6 +1,6 @@
//! Edit text. //! Edit text.
use crate::text::highlighter::{self, Highlighter}; use crate::text::highlighter::{self, Highlighter};
use crate::text::LineHeight; use crate::text::{LineHeight, Wrapping};
use crate::{Pixels, Point, Rectangle, Size}; use crate::{Pixels, Point, Rectangle, Size};
use std::sync::Arc; use std::sync::Arc;
@ -50,6 +50,7 @@ pub trait Editor: Sized + Default {
new_font: Self::Font, new_font: Self::Font,
new_size: Pixels, new_size: Pixels,
new_line_height: LineHeight, new_line_height: LineHeight,
new_wrapping: Wrapping,
new_highlighter: &mut impl Highlighter, new_highlighter: &mut impl Highlighter,
); );

View file

@ -95,6 +95,7 @@ impl<P: Paragraph> Plain<P> {
horizontal_alignment: text.horizontal_alignment, horizontal_alignment: text.horizontal_alignment,
vertical_alignment: text.vertical_alignment, vertical_alignment: text.vertical_alignment,
shaping: text.shaping, shaping: text.shaping,
wrapping: text.wrapping,
}) { }) {
Difference::None => {} Difference::None => {}
Difference::Bounds => { Difference::Bounds => {

View file

@ -11,7 +11,7 @@ use crate::{
Widget, Widget,
}; };
pub use text::{LineHeight, Shaping}; pub use text::{LineHeight, Shaping, Wrapping};
/// A paragraph of text. /// A paragraph of text.
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
@ -29,6 +29,7 @@ where
vertical_alignment: alignment::Vertical, vertical_alignment: alignment::Vertical,
font: Option<Renderer::Font>, font: Option<Renderer::Font>,
shaping: Shaping, shaping: Shaping,
wrapping: Wrapping,
class: Theme::Class<'a>, class: Theme::Class<'a>,
} }
@ -48,7 +49,8 @@ where
height: Length::Shrink, height: Length::Shrink,
horizontal_alignment: alignment::Horizontal::Left, horizontal_alignment: alignment::Horizontal::Left,
vertical_alignment: alignment::Vertical::Top, vertical_alignment: alignment::Vertical::Top,
shaping: Shaping::Basic, shaping: Shaping::default(),
wrapping: Wrapping::default(),
class: Theme::default(), class: Theme::default(),
} }
} }
@ -115,6 +117,12 @@ where
self 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`]. /// Sets the style of the [`Text`].
#[must_use] #[must_use]
pub fn style(mut self, style: impl Fn(&Theme) -> Style + 'a) -> Self pub fn style(mut self, style: impl Fn(&Theme) -> Style + 'a) -> Self
@ -198,6 +206,7 @@ where
self.horizontal_alignment, self.horizontal_alignment,
self.vertical_alignment, self.vertical_alignment,
self.shaping, self.shaping,
self.wrapping,
) )
} }
@ -232,6 +241,7 @@ pub fn layout<Renderer>(
horizontal_alignment: alignment::Horizontal, horizontal_alignment: alignment::Horizontal,
vertical_alignment: alignment::Vertical, vertical_alignment: alignment::Vertical,
shaping: Shaping, shaping: Shaping,
wrapping: Wrapping,
) -> layout::Node ) -> layout::Node
where where
Renderer: text::Renderer, Renderer: text::Renderer,
@ -253,6 +263,7 @@ where
horizontal_alignment, horizontal_alignment,
vertical_alignment, vertical_alignment,
shaping, shaping,
wrapping,
}); });
paragraph.min_bounds() paragraph.min_bounds()

View file

@ -2,7 +2,7 @@ use iced::highlighter;
use iced::keyboard; use iced::keyboard;
use iced::widget::{ use iced::widget::{
self, button, column, container, horizontal_space, pick_list, row, text, self, button, column, container, horizontal_space, pick_list, row, text,
text_editor, tooltip, text_editor, toggler, tooltip,
}; };
use iced::{Center, Element, Fill, Font, Subscription, Task, Theme}; use iced::{Center, Element, Fill, Font, Subscription, Task, Theme};
@ -24,6 +24,7 @@ struct Editor {
file: Option<PathBuf>, file: Option<PathBuf>,
content: text_editor::Content, content: text_editor::Content,
theme: highlighter::Theme, theme: highlighter::Theme,
word_wrap: bool,
is_loading: bool, is_loading: bool,
is_dirty: bool, is_dirty: bool,
} }
@ -32,6 +33,7 @@ struct Editor {
enum Message { enum Message {
ActionPerformed(text_editor::Action), ActionPerformed(text_editor::Action),
ThemeSelected(highlighter::Theme), ThemeSelected(highlighter::Theme),
WordWrapToggled(bool),
NewFile, NewFile,
OpenFile, OpenFile,
FileOpened(Result<(PathBuf, Arc<String>), Error>), FileOpened(Result<(PathBuf, Arc<String>), Error>),
@ -46,6 +48,7 @@ impl Editor {
file: None, file: None,
content: text_editor::Content::new(), content: text_editor::Content::new(),
theme: highlighter::Theme::SolarizedDark, theme: highlighter::Theme::SolarizedDark,
word_wrap: true,
is_loading: true, is_loading: true,
is_dirty: false, is_dirty: false,
}, },
@ -76,6 +79,11 @@ impl Editor {
Task::none() Task::none()
} }
Message::WordWrapToggled(word_wrap) => {
self.word_wrap = word_wrap;
Task::none()
}
Message::NewFile => { Message::NewFile => {
if !self.is_loading { if !self.is_loading {
self.file = None; self.file = None;
@ -152,6 +160,11 @@ impl Editor {
self.is_dirty.then_some(Message::SaveFile) self.is_dirty.then_some(Message::SaveFile)
), ),
horizontal_space(), horizontal_space(),
toggler(
Some("Word Wrap"),
self.word_wrap,
Message::WordWrapToggled
),
pick_list( pick_list(
highlighter::Theme::ALL, highlighter::Theme::ALL,
Some(self.theme), Some(self.theme),
@ -189,6 +202,11 @@ impl Editor {
text_editor(&self.content) text_editor(&self.content)
.height(Fill) .height(Fill)
.on_action(Message::ActionPerformed) .on_action(Message::ActionPerformed)
.wrapping(if self.word_wrap {
text::Wrapping::Word
} else {
text::Wrapping::None
})
.highlight( .highlight(
self.file self.file
.as_deref() .as_deref()

View file

@ -78,7 +78,7 @@ impl Styling {
.on_toggle(Message::CheckboxToggled); .on_toggle(Message::CheckboxToggled);
let toggler = toggler( let toggler = toggler(
String::from("Toggle me!"), Some("Toggle me!"),
self.toggler_value, self.toggler_value,
Message::TogglerToggled, Message::TogglerToggled,
) )

View file

@ -358,7 +358,7 @@ impl Tour {
.push("A toggler is mostly used to enable or disable something.") .push("A toggler is mostly used to enable or disable something.")
.push( .push(
Container::new(toggler( Container::new(toggler(
"Toggle me to continue...".to_owned(), Some("Toggle me to continue..."),
self.toggler, self.toggler,
Message::TogglerChanged, Message::TogglerChanged,
)) ))

View file

@ -11,7 +11,7 @@ pub use cosmic_text;
use crate::core::alignment; use crate::core::alignment;
use crate::core::font::{self, Font}; use crate::core::font::{self, Font};
use crate::core::text::Shaping; use crate::core::text::{Shaping, Wrapping};
use crate::core::{Color, Pixels, Point, Rectangle, Size, Transformation}; use crate::core::{Color, Pixels, Point, Rectangle, Size, Transformation};
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
@ -306,6 +306,16 @@ pub fn to_shaping(shaping: Shaping) -> cosmic_text::Shaping {
} }
} }
/// Converts some [`Wrapping`] strategy to a [`cosmic_text::Wrap`] strategy.
pub 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,
Wrapping::WordOrGlyph => cosmic_text::Wrap::WordOrGlyph,
}
}
/// Converts some [`Color`] to a [`cosmic_text::Color`]. /// Converts some [`Color`] to a [`cosmic_text::Color`].
pub fn to_color(color: Color) -> cosmic_text::Color { pub fn to_color(color: Color) -> cosmic_text::Color {
let [r, g, b, a] = color.into_rgba8(); let [r, g, b, a] = color.into_rgba8();

View file

@ -3,7 +3,7 @@ use crate::core::text::editor::{
self, Action, Cursor, Direction, Edit, Motion, self, Action, Cursor, Direction, Edit, Motion,
}; };
use crate::core::text::highlighter::{self, Highlighter}; use crate::core::text::highlighter::{self, Highlighter};
use crate::core::text::LineHeight; use crate::core::text::{LineHeight, Wrapping};
use crate::core::{Font, Pixels, Point, Rectangle, Size}; use crate::core::{Font, Pixels, Point, Rectangle, Size};
use crate::text; use crate::text;
@ -437,6 +437,7 @@ impl editor::Editor for Editor {
new_font: Font, new_font: Font,
new_size: Pixels, new_size: Pixels,
new_line_height: LineHeight, new_line_height: LineHeight,
new_wrapping: Wrapping,
new_highlighter: &mut impl Highlighter, new_highlighter: &mut impl Highlighter,
) { ) {
let editor = let editor =
@ -448,13 +449,12 @@ impl editor::Editor for Editor {
let mut font_system = let mut font_system =
text::font_system().write().expect("Write font system"); text::font_system().write().expect("Write font system");
let buffer = buffer_mut_from_editor(&mut internal.editor);
if font_system.version() != internal.version { if font_system.version() != internal.version {
log::trace!("Updating `FontSystem` of `Editor`..."); log::trace!("Updating `FontSystem` of `Editor`...");
for line in buffer_mut_from_editor(&mut internal.editor) for line in buffer.lines.iter_mut() {
.lines
.iter_mut()
{
line.reset(); line.reset();
} }
@ -465,10 +465,7 @@ impl editor::Editor for Editor {
if new_font != internal.font { if new_font != internal.font {
log::trace!("Updating font of `Editor`..."); log::trace!("Updating font of `Editor`...");
for line in buffer_mut_from_editor(&mut internal.editor) for line in buffer.lines.iter_mut() {
.lines
.iter_mut()
{
let _ = line.set_attrs_list(cosmic_text::AttrsList::new( let _ = line.set_attrs_list(cosmic_text::AttrsList::new(
text::to_attributes(new_font), text::to_attributes(new_font),
)); ));
@ -478,7 +475,7 @@ impl editor::Editor for Editor {
internal.topmost_line_changed = Some(0); internal.topmost_line_changed = Some(0);
} }
let metrics = buffer_from_editor(&internal.editor).metrics(); let metrics = buffer.metrics();
let new_line_height = new_line_height.to_absolute(new_size); let new_line_height = new_line_height.to_absolute(new_size);
if new_size.0 != metrics.font_size if new_size.0 != metrics.font_size
@ -486,12 +483,20 @@ impl editor::Editor for Editor {
{ {
log::trace!("Updating `Metrics` of `Editor`..."); log::trace!("Updating `Metrics` of `Editor`...");
buffer_mut_from_editor(&mut internal.editor).set_metrics( buffer.set_metrics(
font_system.raw(), font_system.raw(),
cosmic_text::Metrics::new(new_size.0, new_line_height.0), cosmic_text::Metrics::new(new_size.0, new_line_height.0),
); );
} }
let new_wrap = text::to_wrap(new_wrapping);
if new_wrap != buffer.wrap() {
log::trace!("Updating `Wrap` strategy of `Editor`...");
buffer.set_wrap(font_system.raw(), new_wrap);
}
if new_bounds != internal.bounds { if new_bounds != internal.bounds {
log::trace!("Updating size of `Editor`..."); log::trace!("Updating size of `Editor`...");

View file

@ -1,7 +1,7 @@
//! Draw paragraphs. //! Draw paragraphs.
use crate::core; use crate::core;
use crate::core::alignment; use crate::core::alignment;
use crate::core::text::{Hit, Shaping, Span, Text}; use crate::core::text::{Hit, Shaping, Span, Text, Wrapping};
use crate::core::{Font, Point, Rectangle, Size}; use crate::core::{Font, Point, Rectangle, Size};
use crate::text; use crate::text;
@ -17,6 +17,7 @@ struct Internal {
buffer: cosmic_text::Buffer, buffer: cosmic_text::Buffer,
font: Font, font: Font,
shaping: Shaping, shaping: Shaping,
wrapping: Wrapping,
horizontal_alignment: alignment::Horizontal, horizontal_alignment: alignment::Horizontal,
vertical_alignment: alignment::Vertical, vertical_alignment: alignment::Vertical,
bounds: Size, bounds: Size,
@ -94,6 +95,7 @@ impl core::text::Paragraph for Paragraph {
horizontal_alignment: text.horizontal_alignment, horizontal_alignment: text.horizontal_alignment,
vertical_alignment: text.vertical_alignment, vertical_alignment: text.vertical_alignment,
shaping: text.shaping, shaping: text.shaping,
wrapping: text.wrapping,
bounds: text.bounds, bounds: text.bounds,
min_bounds, min_bounds,
version: font_system.version(), version: font_system.version(),
@ -160,6 +162,7 @@ impl core::text::Paragraph for Paragraph {
horizontal_alignment: text.horizontal_alignment, horizontal_alignment: text.horizontal_alignment,
vertical_alignment: text.vertical_alignment, vertical_alignment: text.vertical_alignment,
shaping: text.shaping, shaping: text.shaping,
wrapping: text.wrapping,
bounds: text.bounds, bounds: text.bounds,
min_bounds, min_bounds,
version: font_system.version(), version: font_system.version(),
@ -192,6 +195,7 @@ impl core::text::Paragraph for Paragraph {
|| metrics.line_height != text.line_height.to_absolute(text.size).0 || metrics.line_height != text.line_height.to_absolute(text.size).0
|| paragraph.font != text.font || paragraph.font != text.font
|| paragraph.shaping != text.shaping || paragraph.shaping != text.shaping
|| paragraph.wrapping != text.wrapping
|| paragraph.horizontal_alignment != text.horizontal_alignment || paragraph.horizontal_alignment != text.horizontal_alignment
|| paragraph.vertical_alignment != text.vertical_alignment || paragraph.vertical_alignment != text.vertical_alignment
{ {
@ -387,6 +391,7 @@ impl Default for Internal {
}), }),
font: Font::default(), font: Font::default(),
shaping: Shaping::default(), shaping: Shaping::default(),
wrapping: Wrapping::default(),
horizontal_alignment: alignment::Horizontal::Left, horizontal_alignment: alignment::Horizontal::Left,
vertical_alignment: alignment::Vertical::Top, vertical_alignment: alignment::Vertical::Top,
bounds: Size::ZERO, bounds: Size::ZERO,

View file

@ -408,6 +408,7 @@ impl Renderer {
horizontal_alignment: alignment::Horizontal::Left, horizontal_alignment: alignment::Horizontal::Left,
vertical_alignment: alignment::Vertical::Top, vertical_alignment: alignment::Vertical::Top,
shaping: core::text::Shaping::Basic, shaping: core::text::Shaping::Basic,
wrapping: core::text::Wrapping::Word,
}; };
renderer.fill_text( renderer.fill_text(

View file

@ -50,6 +50,7 @@ pub struct Checkbox<
text_size: Option<Pixels>, text_size: Option<Pixels>,
text_line_height: text::LineHeight, text_line_height: text::LineHeight,
text_shaping: text::Shaping, text_shaping: text::Shaping,
text_wrapping: text::Wrapping,
font: Option<Renderer::Font>, font: Option<Renderer::Font>,
icon: Icon<Renderer::Font>, icon: Icon<Renderer::Font>,
class: Theme::Class<'a>, class: Theme::Class<'a>,
@ -81,7 +82,8 @@ where
spacing: Self::DEFAULT_SPACING, spacing: Self::DEFAULT_SPACING,
text_size: None, text_size: None,
text_line_height: text::LineHeight::default(), text_line_height: text::LineHeight::default(),
text_shaping: text::Shaping::Basic, text_shaping: text::Shaping::default(),
text_wrapping: text::Wrapping::default(),
font: None, font: None,
icon: Icon { icon: Icon {
font: Renderer::ICON_FONT, font: Renderer::ICON_FONT,
@ -158,6 +160,12 @@ where
self self
} }
/// Sets the [`text::Wrapping`] strategy of the [`Checkbox`].
pub fn text_wrapping(mut self, wrapping: text::Wrapping) -> Self {
self.text_wrapping = wrapping;
self
}
/// Sets the [`Renderer::Font`] of the text of the [`Checkbox`]. /// Sets the [`Renderer::Font`] of the text of the [`Checkbox`].
/// ///
/// [`Renderer::Font`]: crate::core::text::Renderer /// [`Renderer::Font`]: crate::core::text::Renderer
@ -240,6 +248,7 @@ where
alignment::Horizontal::Left, alignment::Horizontal::Left,
alignment::Vertical::Top, alignment::Vertical::Top,
self.text_shaping, self.text_shaping,
self.text_wrapping,
) )
}, },
) )
@ -348,6 +357,7 @@ where
horizontal_alignment: alignment::Horizontal::Center, horizontal_alignment: alignment::Horizontal::Center,
vertical_alignment: alignment::Vertical::Center, vertical_alignment: alignment::Vertical::Center,
shaping: *shaping, shaping: *shaping,
wrapping: text::Wrapping::default(),
}, },
bounds.center(), bounds.center(),
style.icon_color, style.icon_color,

View file

@ -767,7 +767,7 @@ where
/// ///
/// [`Toggler`]: crate::Toggler /// [`Toggler`]: crate::Toggler
pub fn toggler<'a, Message, Theme, Renderer>( pub fn toggler<'a, Message, Theme, Renderer>(
label: impl Into<Option<String>>, label: Option<impl text::IntoFragment<'a>>,
is_checked: bool, is_checked: bool,
f: impl Fn(bool) -> Message + 'a, f: impl Fn(bool) -> Message + 'a,
) -> Toggler<'a, Message, Theme, Renderer> ) -> Toggler<'a, Message, Theme, Renderer>

View file

@ -532,6 +532,7 @@ where
horizontal_alignment: alignment::Horizontal::Left, horizontal_alignment: alignment::Horizontal::Left,
vertical_alignment: alignment::Vertical::Center, vertical_alignment: alignment::Vertical::Center,
shaping: self.text_shaping, shaping: self.text_shaping,
wrapping: text::Wrapping::default(),
}, },
Point::new(bounds.x + self.padding.left, bounds.center_y()), Point::new(bounds.x + self.padding.left, bounds.center_y()),
if is_selected { if is_selected {

View file

@ -81,7 +81,7 @@ where
padding: crate::button::DEFAULT_PADDING, padding: crate::button::DEFAULT_PADDING,
text_size: None, text_size: None,
text_line_height: text::LineHeight::default(), text_line_height: text::LineHeight::default(),
text_shaping: text::Shaping::Basic, text_shaping: text::Shaping::default(),
font: None, font: None,
handle: Handle::default(), handle: Handle::default(),
class: <Theme as Catalog>::default(), class: <Theme as Catalog>::default(),
@ -250,6 +250,7 @@ where
horizontal_alignment: alignment::Horizontal::Left, horizontal_alignment: alignment::Horizontal::Left,
vertical_alignment: alignment::Vertical::Center, vertical_alignment: alignment::Vertical::Center,
shaping: self.text_shaping, shaping: self.text_shaping,
wrapping: text::Wrapping::default(),
}; };
for (option, paragraph) in options.iter().zip(state.options.iter_mut()) for (option, paragraph) in options.iter().zip(state.options.iter_mut())
@ -515,6 +516,7 @@ where
horizontal_alignment: alignment::Horizontal::Right, horizontal_alignment: alignment::Horizontal::Right,
vertical_alignment: alignment::Vertical::Center, vertical_alignment: alignment::Vertical::Center,
shaping, shaping,
wrapping: text::Wrapping::default(),
}, },
Point::new( Point::new(
bounds.x + bounds.width - self.padding.right, bounds.x + bounds.width - self.padding.right,
@ -544,6 +546,7 @@ where
horizontal_alignment: alignment::Horizontal::Left, horizontal_alignment: alignment::Horizontal::Left,
vertical_alignment: alignment::Vertical::Center, vertical_alignment: alignment::Vertical::Center,
shaping: self.text_shaping, shaping: self.text_shaping,
wrapping: text::Wrapping::default(),
}, },
Point::new(bounds.x + self.padding.left, bounds.center_y()), Point::new(bounds.x + self.padding.left, bounds.center_y()),
if is_selected { if is_selected {

View file

@ -82,6 +82,7 @@ where
text_size: Option<Pixels>, text_size: Option<Pixels>,
text_line_height: text::LineHeight, text_line_height: text::LineHeight,
text_shaping: text::Shaping, text_shaping: text::Shaping,
text_wrapping: text::Wrapping,
font: Option<Renderer::Font>, font: Option<Renderer::Font>,
class: Theme::Class<'a>, class: Theme::Class<'a>,
} }
@ -122,10 +123,11 @@ where
label: label.into(), label: label.into(),
width: Length::Shrink, width: Length::Shrink,
size: Self::DEFAULT_SIZE, size: Self::DEFAULT_SIZE,
spacing: Self::DEFAULT_SPACING, //15 spacing: Self::DEFAULT_SPACING,
text_size: None, text_size: None,
text_line_height: text::LineHeight::default(), text_line_height: text::LineHeight::default(),
text_shaping: text::Shaping::Basic, text_shaping: text::Shaping::default(),
text_wrapping: text::Wrapping::default(),
font: None, font: None,
class: Theme::default(), class: Theme::default(),
} }
@ -170,6 +172,12 @@ where
self self
} }
/// Sets the [`text::Wrapping`] strategy of the [`Radio`] button.
pub fn text_wrapping(mut self, wrapping: text::Wrapping) -> Self {
self.text_wrapping = wrapping;
self
}
/// Sets the text font of the [`Radio`] button. /// Sets the text font of the [`Radio`] button.
pub fn font(mut self, font: impl Into<Renderer::Font>) -> Self { pub fn font(mut self, font: impl Into<Renderer::Font>) -> Self {
self.font = Some(font.into()); self.font = Some(font.into());
@ -245,6 +253,7 @@ where
alignment::Horizontal::Left, alignment::Horizontal::Left,
alignment::Vertical::Top, alignment::Vertical::Top,
self.text_shaping, self.text_shaping,
self.text_wrapping,
) )
}, },
) )

View file

@ -5,7 +5,7 @@ use crate::core::mouse;
use crate::core::renderer; use crate::core::renderer;
use crate::core::text::{Paragraph, Span}; use crate::core::text::{Paragraph, Span};
use crate::core::widget::text::{ use crate::core::widget::text::{
self, Catalog, LineHeight, Shaping, Style, StyleFn, self, Catalog, LineHeight, Shaping, Style, StyleFn, Wrapping,
}; };
use crate::core::widget::tree::{self, Tree}; use crate::core::widget::tree::{self, Tree};
use crate::core::{ use crate::core::{
@ -29,6 +29,7 @@ where
font: Option<Renderer::Font>, font: Option<Renderer::Font>,
align_x: alignment::Horizontal, align_x: alignment::Horizontal,
align_y: alignment::Vertical, align_y: alignment::Vertical,
wrapping: Wrapping,
class: Theme::Class<'a>, class: Theme::Class<'a>,
} }
@ -50,6 +51,7 @@ where
font: None, font: None,
align_x: alignment::Horizontal::Left, align_x: alignment::Horizontal::Left,
align_y: alignment::Vertical::Top, align_y: alignment::Vertical::Top,
wrapping: Wrapping::default(),
class: Theme::default(), class: Theme::default(),
} }
} }
@ -118,6 +120,12 @@ where
self self
} }
/// Sets the [`Wrapping`] strategy of the [`Rich`] text.
pub fn wrapping(mut self, wrapping: Wrapping) -> Self {
self.wrapping = wrapping;
self
}
/// Sets the default style of the [`Rich`] text. /// Sets the default style of the [`Rich`] text.
#[must_use] #[must_use]
pub fn style(mut self, style: impl Fn(&Theme) -> Style + 'a) -> Self pub fn style(mut self, style: impl Fn(&Theme) -> Style + 'a) -> Self
@ -218,6 +226,7 @@ where
self.font, self.font,
self.align_x, self.align_x,
self.align_y, self.align_y,
self.wrapping,
) )
} }
@ -444,6 +453,7 @@ fn layout<Link, Renderer>(
font: Option<Renderer::Font>, font: Option<Renderer::Font>,
horizontal_alignment: alignment::Horizontal, horizontal_alignment: alignment::Horizontal,
vertical_alignment: alignment::Vertical, vertical_alignment: alignment::Vertical,
wrapping: Wrapping,
) -> layout::Node ) -> layout::Node
where where
Link: Clone, Link: Clone,
@ -464,6 +474,7 @@ where
horizontal_alignment, horizontal_alignment,
vertical_alignment, vertical_alignment,
shaping: Shaping::Advanced, shaping: Shaping::Advanced,
wrapping,
}; };
if state.spans != spans { if state.spans != spans {
@ -480,6 +491,7 @@ where
horizontal_alignment, horizontal_alignment,
vertical_alignment, vertical_alignment,
shaping: Shaping::Advanced, shaping: Shaping::Advanced,
wrapping,
}) { }) {
core::text::Difference::None => {} core::text::Difference::None => {}
core::text::Difference::Bounds => { core::text::Difference::Bounds => {

View file

@ -9,7 +9,7 @@ use crate::core::mouse;
use crate::core::renderer; use crate::core::renderer;
use crate::core::text::editor::{Cursor, Editor as _}; use crate::core::text::editor::{Cursor, Editor as _};
use crate::core::text::highlighter::{self, Highlighter}; use crate::core::text::highlighter::{self, Highlighter};
use crate::core::text::{self, LineHeight, Text}; use crate::core::text::{self, LineHeight, Text, Wrapping};
use crate::core::time::{Duration, Instant}; use crate::core::time::{Duration, Instant};
use crate::core::widget::operation; use crate::core::widget::operation;
use crate::core::widget::{self, Widget}; use crate::core::widget::{self, Widget};
@ -47,6 +47,7 @@ pub struct TextEditor<
width: Length, width: Length,
height: Length, height: Length,
padding: Padding, padding: Padding,
wrapping: Wrapping,
class: Theme::Class<'a>, class: Theme::Class<'a>,
key_binding: Option<Box<dyn Fn(KeyPress) -> Option<Binding<Message>> + 'a>>, key_binding: Option<Box<dyn Fn(KeyPress) -> Option<Binding<Message>> + 'a>>,
on_edit: Option<Box<dyn Fn(Action) -> Message + 'a>>, on_edit: Option<Box<dyn Fn(Action) -> Message + 'a>>,
@ -74,6 +75,7 @@ where
width: Length::Fill, width: Length::Fill,
height: Length::Shrink, height: Length::Shrink,
padding: Padding::new(5.0), padding: Padding::new(5.0),
wrapping: Wrapping::default(),
class: Theme::default(), class: Theme::default(),
key_binding: None, key_binding: None,
on_edit: None, on_edit: None,
@ -148,6 +150,12 @@ where
self self
} }
/// Sets the [`Wrapping`] strategy of the [`TextEditor`].
pub fn wrapping(mut self, wrapping: Wrapping) -> Self {
self.wrapping = wrapping;
self
}
/// Highlights the [`TextEditor`] using the given syntax and theme. /// Highlights the [`TextEditor`] using the given syntax and theme.
#[cfg(feature = "highlighter")] #[cfg(feature = "highlighter")]
pub fn highlight( pub fn highlight(
@ -186,6 +194,7 @@ where
width: self.width, width: self.width,
height: self.height, height: self.height,
padding: self.padding, padding: self.padding,
wrapping: self.wrapping,
class: self.class, class: self.class,
key_binding: self.key_binding, key_binding: self.key_binding,
on_edit: self.on_edit, on_edit: self.on_edit,
@ -496,6 +505,7 @@ where
self.font.unwrap_or_else(|| renderer.default_font()), self.font.unwrap_or_else(|| renderer.default_font()),
self.text_size.unwrap_or_else(|| renderer.default_size()), self.text_size.unwrap_or_else(|| renderer.default_size()),
self.line_height, self.line_height,
self.wrapping,
state.highlighter.borrow_mut().deref_mut(), state.highlighter.borrow_mut().deref_mut(),
); );
@ -784,6 +794,7 @@ where
horizontal_alignment: alignment::Horizontal::Left, horizontal_alignment: alignment::Horizontal::Left,
vertical_alignment: alignment::Vertical::Top, vertical_alignment: alignment::Vertical::Top,
shaping: text::Shaping::Advanced, shaping: text::Shaping::Advanced,
wrapping: self.wrapping,
}, },
text_bounds.position(), text_bounds.position(),
style.placeholder, style.placeholder,

View file

@ -251,6 +251,7 @@ where
horizontal_alignment: alignment::Horizontal::Left, horizontal_alignment: alignment::Horizontal::Left,
vertical_alignment: alignment::Vertical::Center, vertical_alignment: alignment::Vertical::Center,
shaping: text::Shaping::Advanced, shaping: text::Shaping::Advanced,
wrapping: text::Wrapping::default(),
}; };
state.placeholder.update(placeholder_text); state.placeholder.update(placeholder_text);
@ -275,6 +276,7 @@ where
horizontal_alignment: alignment::Horizontal::Center, horizontal_alignment: alignment::Horizontal::Center,
vertical_alignment: alignment::Vertical::Center, vertical_alignment: alignment::Vertical::Center,
shaping: text::Shaping::Advanced, shaping: text::Shaping::Advanced,
wrapping: text::Wrapping::default(),
}; };
state.icon.update(icon_text); state.icon.update(icon_text);
@ -1432,6 +1434,7 @@ fn replace_paragraph<Renderer>(
horizontal_alignment: alignment::Horizontal::Left, horizontal_alignment: alignment::Horizontal::Left,
vertical_alignment: alignment::Vertical::Top, vertical_alignment: alignment::Vertical::Top,
shaping: text::Shaping::Advanced, shaping: text::Shaping::Advanced,
wrapping: text::Wrapping::default(),
}); });
} }

View file

@ -40,13 +40,14 @@ pub struct Toggler<
{ {
is_toggled: bool, is_toggled: bool,
on_toggle: Box<dyn Fn(bool) -> Message + 'a>, on_toggle: Box<dyn Fn(bool) -> Message + 'a>,
label: Option<String>, label: Option<text::Fragment<'a>>,
width: Length, width: Length,
size: f32, size: f32,
text_size: Option<Pixels>, text_size: Option<Pixels>,
text_line_height: text::LineHeight, text_line_height: text::LineHeight,
text_alignment: alignment::Horizontal, text_alignment: alignment::Horizontal,
text_shaping: text::Shaping, text_shaping: text::Shaping,
text_wrapping: text::Wrapping,
spacing: f32, spacing: f32,
font: Option<Renderer::Font>, font: Option<Renderer::Font>,
class: Theme::Class<'a>, class: Theme::Class<'a>,
@ -69,7 +70,7 @@ where
/// will receive the new state of the [`Toggler`] and must produce a /// will receive the new state of the [`Toggler`] and must produce a
/// `Message`. /// `Message`.
pub fn new<F>( pub fn new<F>(
label: impl Into<Option<String>>, label: Option<impl text::IntoFragment<'a>>,
is_toggled: bool, is_toggled: bool,
f: F, f: F,
) -> Self ) -> Self
@ -79,13 +80,14 @@ where
Toggler { Toggler {
is_toggled, is_toggled,
on_toggle: Box::new(f), on_toggle: Box::new(f),
label: label.into(), label: label.map(text::IntoFragment::into_fragment),
width: Length::Shrink, width: Length::Shrink,
size: Self::DEFAULT_SIZE, size: Self::DEFAULT_SIZE,
text_size: None, text_size: None,
text_line_height: text::LineHeight::default(), text_line_height: text::LineHeight::default(),
text_alignment: alignment::Horizontal::Left, text_alignment: alignment::Horizontal::Left,
text_shaping: text::Shaping::Basic, text_shaping: text::Shaping::default(),
text_wrapping: text::Wrapping::default(),
spacing: Self::DEFAULT_SIZE / 2.0, spacing: Self::DEFAULT_SIZE / 2.0,
font: None, font: None,
class: Theme::default(), class: Theme::default(),
@ -131,6 +133,12 @@ where
self self
} }
/// Sets the [`text::Wrapping`] strategy of the [`Toggler`].
pub fn text_wrapping(mut self, wrapping: text::Wrapping) -> Self {
self.text_wrapping = wrapping;
self
}
/// Sets the spacing between the [`Toggler`] and the text. /// Sets the spacing between the [`Toggler`] and the text.
pub fn spacing(mut self, spacing: impl Into<Pixels>) -> Self { pub fn spacing(mut self, spacing: impl Into<Pixels>) -> Self {
self.spacing = spacing.into().0; self.spacing = spacing.into().0;
@ -216,6 +224,7 @@ where
self.text_alignment, self.text_alignment,
alignment::Vertical::Top, alignment::Vertical::Top,
self.text_shaping, self.text_shaping,
self.text_wrapping,
) )
} else { } else {
layout::Node::new(Size::ZERO) layout::Node::new(Size::ZERO)