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,
limits.max().width as i32,
self.line.wrap(),
self.line.align(),
);
let mut width = 0;
@ -160,7 +161,12 @@ where
let shape = self.line.shape_opt().as_ref().unwrap();
//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();

View file

@ -10,7 +10,9 @@ use unicode_segmentation::UnicodeSegmentation;
#[cfg(feature = "swash")]
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
#[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
redraw: bool,
wrap: Wrap,
align: Align,
}
impl<'a> Buffer<'a> {
@ -317,6 +320,7 @@ impl<'a> Buffer<'a> {
scroll: 0,
redraw: false,
wrap: Wrap::Word,
align: Align::Center,
};
buffer.set_text("", Attrs::new());
buffer
@ -334,6 +338,7 @@ impl<'a> Buffer<'a> {
self.metrics.font_size,
self.width,
self.wrap,
self.align,
);
}
}
@ -364,6 +369,7 @@ impl<'a> Buffer<'a> {
self.metrics.font_size,
self.width,
self.wrap,
self.align,
);
total_layout += layout.len() as i32;
}
@ -397,6 +403,7 @@ impl<'a> Buffer<'a> {
self.metrics.font_size,
self.width,
self.wrap,
self.align,
);
if line_i == cursor.line {
let layout_cursor = self.layout_cursor(&cursor);
@ -482,6 +489,7 @@ impl<'a> Buffer<'a> {
self.metrics.font_size,
self.width,
self.wrap,
self.align,
))
}

View file

@ -1,7 +1,7 @@
#[cfg(not(feature = "std"))]
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
pub struct BufferLine {
@ -9,6 +9,7 @@ pub struct BufferLine {
text: String,
attrs_list: AttrsList,
wrap: Wrap,
align: Align,
shape_opt: Option<ShapeLine>,
layout_opt: Option<Vec<LayoutLine>>,
}
@ -22,6 +23,7 @@ impl BufferLine {
text: text.into(),
attrs_list,
wrap: Wrap::Word,
align: Align::Left,
shape_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
///
/// The wrap setting of the appended line will be lost
@ -165,11 +186,13 @@ impl BufferLine {
font_size: i32,
width: i32,
wrap: Wrap,
align: Align,
) -> &[LayoutLine] {
if self.layout_opt.is_none() {
self.wrap = wrap;
self.align = align;
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.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 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(
font: &Font,
@ -600,15 +600,28 @@ impl ShapeLine {
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);
// This is used to create a visual line for empty lines (e.g. lines with only a <CR>)
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)
// 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 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
// If one span is too large, this variable will hold the range of words inside that span
// 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 {
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 {
let mut fit_x = line_width as f32;
@ -740,27 +756,29 @@ impl ShapeLine {
};
if fits {
current_visual_line.push((
current_visual_line.ranges.push((
span_index,
(starting_word, starting_glyph),
(ending_word, ending_glyph),
));
current_visual_line.w += word_range_width;
if self.rtl {
x -= word_range_width;
} else {
x += word_range_width;
}
} else {
if !current_visual_line.is_empty() {
if !current_visual_line.ranges.is_empty() {
vl_range_of_spans.push(current_visual_line);
current_visual_line = Vec::with_capacity(1);
current_visual_line = VisualLine::default();
x = start_x;
}
current_visual_line.push((
current_visual_line.ranges.push((
span_index,
(starting_word, starting_glyph),
(ending_word, ending_glyph),
));
current_visual_line.w += word_range_width;
if self.rtl {
x -= word_range_width;
} else {
@ -769,7 +787,7 @@ impl ShapeLine {
if word_range_width > line_width as f32 {
// single word is bigger than line_width
vl_range_of_spans.push(current_visual_line);
current_visual_line = Vec::with_capacity(1);
current_visual_line = VisualLine::default();
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);
}
// Create the LayoutLines using the ranges inside visual lines
for visual_line in &vl_range_of_spans {
let new_order = self.reorder(visual_line);
for visual_line in vl_range_of_spans {
let new_order = self.reorder(&visual_line.ranges);
let mut glyphs = Vec::with_capacity(1);
x = start_x;
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 {
x -= alignment_correction;
for range in new_order.iter().rev() {
for (
span_index,
(starting_word, starting_glyph),
(ending_word, ending_glyph),
) in visual_line[range.clone()].iter()
) in visual_line.ranges[range.clone()].iter()
{
let span = &self.spans[*span_index];
if starting_word == ending_word {
@ -803,13 +830,8 @@ impl ShapeLine {
{
let x_advance = font_size as f32 * glyph.x_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));
if !self.rtl {
x += x_advance;
}
y += y_advance;
}
} else {
@ -841,12 +863,13 @@ impl ShapeLine {
}
}
} else {
x += alignment_correction;
for range in new_order {
for (
span_index,
(starting_word, starting_glyph),
(ending_word, ending_glyph),
) in visual_line[range.clone()].iter()
) in visual_line.ranges[range.clone()].iter()
{
let span = &self.spans[*span_index];
if starting_word == ending_word {
@ -856,13 +879,8 @@ impl ShapeLine {
{
let x_advance = font_size as f32 * glyph.x_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));
if !self.rtl {
x += x_advance;
}
x += x_advance;
y += y_advance;
}
} else {