Align Left, Right, and Center works

This commit is contained in:
Hojjat 2023-02-22 18:31:49 -07:00
parent 9a4d067f9d
commit 00ff5b72f3
5 changed files with 106 additions and 31 deletions

View file

@ -100,6 +100,7 @@ where
self.metrics.font_size, self.metrics.font_size,
limits.max().width as i32, limits.max().width as i32,
self.line.wrap(), self.line.wrap(),
self.line.align(),
); );
let mut width = 0; let mut width = 0;
@ -160,7 +161,12 @@ where
let shape = self.line.shape_opt().as_ref().unwrap(); let shape = self.line.shape_opt().as_ref().unwrap();
//TODO: can we cache this? //TODO: can we cache this?
let layout_lines = shape.layout(self.metrics.font_size, layout_w, self.line.wrap()); let layout_lines = shape.layout(
self.metrics.font_size,
layout_w,
self.line.wrap(),
self.line.align(),
);
let mut cache = state.cache.lock().unwrap(); let mut cache = state.cache.lock().unwrap();

View file

@ -10,7 +10,9 @@ use unicode_segmentation::UnicodeSegmentation;
#[cfg(feature = "swash")] #[cfg(feature = "swash")]
use crate::Color; use crate::Color;
use crate::{Attrs, AttrsList, BufferLine, FontSystem, LayoutGlyph, LayoutLine, ShapeLine, Wrap}; use crate::{
Align, Attrs, AttrsList, BufferLine, FontSystem, LayoutGlyph, LayoutLine, ShapeLine, Wrap,
};
/// Current cursor location /// Current cursor location
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd)] #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd)]
@ -301,6 +303,7 @@ pub struct Buffer<'a> {
/// True if a redraw is requires. Set to false after processing /// True if a redraw is requires. Set to false after processing
redraw: bool, redraw: bool,
wrap: Wrap, wrap: Wrap,
align: Align,
} }
impl<'a> Buffer<'a> { impl<'a> Buffer<'a> {
@ -317,6 +320,7 @@ impl<'a> Buffer<'a> {
scroll: 0, scroll: 0,
redraw: false, redraw: false,
wrap: Wrap::Word, wrap: Wrap::Word,
align: Align::Center,
}; };
buffer.set_text("", Attrs::new()); buffer.set_text("", Attrs::new());
buffer buffer
@ -334,6 +338,7 @@ impl<'a> Buffer<'a> {
self.metrics.font_size, self.metrics.font_size,
self.width, self.width,
self.wrap, self.wrap,
self.align,
); );
} }
} }
@ -364,6 +369,7 @@ impl<'a> Buffer<'a> {
self.metrics.font_size, self.metrics.font_size,
self.width, self.width,
self.wrap, self.wrap,
self.align,
); );
total_layout += layout.len() as i32; total_layout += layout.len() as i32;
} }
@ -397,6 +403,7 @@ impl<'a> Buffer<'a> {
self.metrics.font_size, self.metrics.font_size,
self.width, self.width,
self.wrap, self.wrap,
self.align,
); );
if line_i == cursor.line { if line_i == cursor.line {
let layout_cursor = self.layout_cursor(&cursor); let layout_cursor = self.layout_cursor(&cursor);
@ -482,6 +489,7 @@ impl<'a> Buffer<'a> {
self.metrics.font_size, self.metrics.font_size,
self.width, self.width,
self.wrap, self.wrap,
self.align,
)) ))
} }

View file

@ -1,7 +1,7 @@
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
use alloc::{string::String, vec::Vec}; use alloc::{string::String, vec::Vec};
use crate::{AttrsList, FontSystem, LayoutLine, ShapeLine, Wrap}; use crate::{Align, AttrsList, FontSystem, LayoutLine, ShapeLine, Wrap};
/// A line (or paragraph) of text that is shaped and laid out /// A line (or paragraph) of text that is shaped and laid out
pub struct BufferLine { pub struct BufferLine {
@ -9,6 +9,7 @@ pub struct BufferLine {
text: String, text: String,
attrs_list: AttrsList, attrs_list: AttrsList,
wrap: Wrap, wrap: Wrap,
align: Align,
shape_opt: Option<ShapeLine>, shape_opt: Option<ShapeLine>,
layout_opt: Option<Vec<LayoutLine>>, layout_opt: Option<Vec<LayoutLine>>,
} }
@ -22,6 +23,7 @@ impl BufferLine {
text: text.into(), text: text.into(),
attrs_list, attrs_list,
wrap: Wrap::Word, wrap: Wrap::Word,
align: Align::Left,
shape_opt: None, shape_opt: None,
layout_opt: None, layout_opt: None,
} }
@ -94,6 +96,25 @@ impl BufferLine {
} }
} }
/// Get the Text alignment
pub fn align(&self) -> Align {
self.align
}
/// Set the text alignment
///
/// Will reset shape and layout if it differs from current alignment.
/// Returns true if the line was reset
pub fn set_align(&mut self, align: Align) -> bool {
if align != self.align {
self.align = align;
self.reset();
true
} else {
false
}
}
/// Append line at end of this line /// Append line at end of this line
/// ///
/// The wrap setting of the appended line will be lost /// The wrap setting of the appended line will be lost
@ -165,11 +186,13 @@ impl BufferLine {
font_size: i32, font_size: i32,
width: i32, width: i32,
wrap: Wrap, wrap: Wrap,
align: Align,
) -> &[LayoutLine] { ) -> &[LayoutLine] {
if self.layout_opt.is_none() { if self.layout_opt.is_none() {
self.wrap = wrap; self.wrap = wrap;
self.align = align;
let shape = self.shape(font_system); let shape = self.shape(font_system);
let layout = shape.layout(font_size, width, wrap); let layout = shape.layout(font_size, width, wrap, align);
self.layout_opt = Some(layout); self.layout_opt = Some(layout);
} }
self.layout_opt.as_ref().expect("layout not found") self.layout_opt.as_ref().expect("layout not found")

View file

@ -80,3 +80,23 @@ impl Display for Wrap {
} }
} }
} }
/// Align or justify
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum Align {
Left,
Right,
Center,
Justified,
}
impl Display for Align {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Left => write!(f, "Left"),
Self::Right => write!(f, "Right"),
Self::Center => write!(f, "Center"),
Self::Justified => write!(f, "Justified"),
}
}
}

View file

@ -9,7 +9,7 @@ use unicode_script::{Script, UnicodeScript};
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
use crate::fallback::FontFallbackIter; use crate::fallback::FontFallbackIter;
use crate::{AttrsList, CacheKey, Color, Font, FontSystem, LayoutGlyph, LayoutLine, Wrap}; use crate::{Align, AttrsList, CacheKey, Color, Font, FontSystem, LayoutGlyph, LayoutLine, Wrap};
fn shape_fallback( fn shape_fallback(
font: &Font, font: &Font,
@ -600,15 +600,28 @@ impl ShapeLine {
runs runs
} }
pub fn layout(&self, font_size: i32, line_width: i32, wrap: Wrap) -> Vec<LayoutLine> { pub fn layout(
&self,
font_size: i32,
line_width: i32,
wrap: Wrap,
align: Align,
) -> Vec<LayoutLine> {
let mut layout_lines = Vec::with_capacity(1); let mut layout_lines = Vec::with_capacity(1);
// This is used to create a visual line for empty lines (e.g. lines with only a <CR>) // This is used to create a visual line for empty lines (e.g. lines with only a <CR>)
let mut push_line = true; let mut push_line = true;
#[derive(Default)]
struct VisualLine {
ranges: Vec<VlRange>,
spaces: u32,
w: f32,
}
// For each visual line a list of (span index, and range of words in that span) // For each visual line a list of (span index, and range of words in that span)
// Note that a BiDi visual line could have multiple spans or parts of them // Note that a BiDi visual line could have multiple spans or parts of them
let mut vl_range_of_spans = Vec::with_capacity(1); // let mut vl_range_of_spans = Vec::with_capacity(1);
let mut vl_range_of_spans: Vec<VisualLine> = Vec::with_capacity(1);
let start_x = if self.rtl { line_width as f32 } else { 0.0 }; let start_x = if self.rtl { line_width as f32 } else { 0.0 };
let end_x = if self.rtl { 0.0 } else { line_width as f32 }; let end_x = if self.rtl { 0.0 } else { line_width as f32 };
@ -618,11 +631,14 @@ impl ShapeLine {
// This would keep the maximum number of spans that would fit on a visual line // This would keep the maximum number of spans that would fit on a visual line
// If one span is too large, this variable will hold the range of words inside that span // If one span is too large, this variable will hold the range of words inside that span
// that fits on a line. // that fits on a line.
let mut current_visual_line: Vec<VlRange> = Vec::with_capacity(1); // let mut current_visual_line: Vec<VlRange> = Vec::with_capacity(1);
let mut current_visual_line = VisualLine::default();
if wrap == Wrap::None { if wrap == Wrap::None {
for (span_index, span) in self.spans.iter().enumerate() { for (span_index, span) in self.spans.iter().enumerate() {
current_visual_line.push((span_index, (0, 0), (span.words.len(), 0))); current_visual_line
.ranges
.push((span_index, (0, 0), (span.words.len(), 0)));
} }
} else { } else {
let mut fit_x = line_width as f32; let mut fit_x = line_width as f32;
@ -740,27 +756,29 @@ impl ShapeLine {
}; };
if fits { if fits {
current_visual_line.push(( current_visual_line.ranges.push((
span_index, span_index,
(starting_word, starting_glyph), (starting_word, starting_glyph),
(ending_word, ending_glyph), (ending_word, ending_glyph),
)); ));
current_visual_line.w += word_range_width;
if self.rtl { if self.rtl {
x -= word_range_width; x -= word_range_width;
} else { } else {
x += word_range_width; x += word_range_width;
} }
} else { } else {
if !current_visual_line.is_empty() { if !current_visual_line.ranges.is_empty() {
vl_range_of_spans.push(current_visual_line); vl_range_of_spans.push(current_visual_line);
current_visual_line = Vec::with_capacity(1); current_visual_line = VisualLine::default();
x = start_x; x = start_x;
} }
current_visual_line.push(( current_visual_line.ranges.push((
span_index, span_index,
(starting_word, starting_glyph), (starting_word, starting_glyph),
(ending_word, ending_glyph), (ending_word, ending_glyph),
)); ));
current_visual_line.w += word_range_width;
if self.rtl { if self.rtl {
x -= word_range_width; x -= word_range_width;
} else { } else {
@ -769,7 +787,7 @@ impl ShapeLine {
if word_range_width > line_width as f32 { if word_range_width > line_width as f32 {
// single word is bigger than line_width // single word is bigger than line_width
vl_range_of_spans.push(current_visual_line); vl_range_of_spans.push(current_visual_line);
current_visual_line = Vec::with_capacity(1); current_visual_line = VisualLine::default();
x = start_x; x = start_x;
} }
} }
@ -777,23 +795,32 @@ impl ShapeLine {
} }
} }
if !current_visual_line.is_empty() { if !current_visual_line.ranges.is_empty() {
vl_range_of_spans.push(current_visual_line); vl_range_of_spans.push(current_visual_line);
} }
// Create the LayoutLines using the ranges inside visual lines // Create the LayoutLines using the ranges inside visual lines
for visual_line in &vl_range_of_spans { for visual_line in vl_range_of_spans {
let new_order = self.reorder(visual_line); let new_order = self.reorder(&visual_line.ranges);
let mut glyphs = Vec::with_capacity(1); let mut glyphs = Vec::with_capacity(1);
x = start_x; x = start_x;
y = 0.; y = 0.;
let alignment_correction = match (align, self.rtl) {
(Align::Left, true) => line_width as f32 - visual_line.w,
(Align::Left, false) => 0.,
(Align::Right, true) => 0.,
(Align::Right, false) => line_width as f32 - visual_line.w,
(Align::Center, _) => (line_width as f32 - visual_line.w) / 2.0,
(Align::Justified, _) => unimplemented!("I need to work on the number of spaces"),
};
if self.rtl { if self.rtl {
x -= alignment_correction;
for range in new_order.iter().rev() { for range in new_order.iter().rev() {
for ( for (
span_index, span_index,
(starting_word, starting_glyph), (starting_word, starting_glyph),
(ending_word, ending_glyph), (ending_word, ending_glyph),
) in visual_line[range.clone()].iter() ) in visual_line.ranges[range.clone()].iter()
{ {
let span = &self.spans[*span_index]; let span = &self.spans[*span_index];
if starting_word == ending_word { if starting_word == ending_word {
@ -803,13 +830,8 @@ impl ShapeLine {
{ {
let x_advance = font_size as f32 * glyph.x_advance; let x_advance = font_size as f32 * glyph.x_advance;
let y_advance = font_size as f32 * glyph.y_advance; let y_advance = font_size as f32 * glyph.y_advance;
if self.rtl { x -= x_advance;
x -= x_advance;
}
glyphs.push(glyph.layout(font_size, x, y, span.level)); glyphs.push(glyph.layout(font_size, x, y, span.level));
if !self.rtl {
x += x_advance;
}
y += y_advance; y += y_advance;
} }
} else { } else {
@ -841,12 +863,13 @@ impl ShapeLine {
} }
} }
} else { } else {
x += alignment_correction;
for range in new_order { for range in new_order {
for ( for (
span_index, span_index,
(starting_word, starting_glyph), (starting_word, starting_glyph),
(ending_word, ending_glyph), (ending_word, ending_glyph),
) in visual_line[range.clone()].iter() ) in visual_line.ranges[range.clone()].iter()
{ {
let span = &self.spans[*span_index]; let span = &self.spans[*span_index];
if starting_word == ending_word { if starting_word == ending_word {
@ -856,13 +879,8 @@ impl ShapeLine {
{ {
let x_advance = font_size as f32 * glyph.x_advance; let x_advance = font_size as f32 * glyph.x_advance;
let y_advance = font_size as f32 * glyph.y_advance; let y_advance = font_size as f32 * glyph.y_advance;
if self.rtl {
x -= x_advance;
}
glyphs.push(glyph.layout(font_size, x, y, span.level)); glyphs.push(glyph.layout(font_size, x, y, span.level));
if !self.rtl { x += x_advance;
x += x_advance;
}
y += y_advance; y += y_advance;
} }
} else { } else {