Use f32 instead of i32 for lengths

This allows users to use logical coordinates instead of physical ones.
This commit is contained in:
Héctor Ramón Jiménez 2023-02-04 11:13:53 +01:00
parent f08bea22ed
commit 4320ae6329
No known key found for this signature in database
GPG key ID: 140CC052C94F138E
15 changed files with 203 additions and 155 deletions

View file

@ -107,7 +107,7 @@ pub struct LayoutRun<'a> {
/// The array of layout glyphs to draw
pub glyphs: &'a [LayoutGlyph],
/// Y offset of line
pub line_y: i32,
pub line_y: f32,
/// width of line
pub line_w: f32,
}
@ -174,7 +174,7 @@ pub struct LayoutRunIter<'a, 'b> {
line_i: usize,
layout_i: usize,
remaining_len: usize,
line_y: i32,
line_y: f32,
total_layout: i32,
}
@ -192,16 +192,14 @@ impl<'a, 'b> LayoutRunIter<'a, 'b> {
.sum();
let top_cropped_layout_lines =
total_layout_lines.saturating_sub(buffer.scroll.try_into().unwrap_or_default());
let maximum_lines = buffer
.height
.checked_div(buffer.metrics.line_height)
.unwrap_or_default();
let maximum_lines = (buffer.height / buffer.metrics.line_height) as i32;
let bottom_cropped_layout_lines =
if top_cropped_layout_lines > maximum_lines.try_into().unwrap_or_default() {
maximum_lines.try_into().unwrap_or_default()
} else {
top_cropped_layout_lines
};
Self {
buffer,
line_i: 0,
@ -259,30 +257,30 @@ impl<'a, 'b> Iterator for LayoutRunIter<'a, 'b> {
impl<'a, 'b> ExactSizeIterator for LayoutRunIter<'a, 'b> {}
/// Metrics of text
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Metrics {
/// Font size in pixels
pub font_size: i32,
pub font_size: f32,
/// Line height in pixels
pub line_height: i32,
pub line_height: f32,
}
impl Metrics {
pub const fn new(font_size: i32, line_height: i32) -> Self {
pub const fn new(font_size: f32, line_height: f32) -> Self {
Self {
font_size,
line_height,
}
}
pub const fn scale(self, scale: i32) -> Self {
pub fn scale(self, scale: f32) -> Self {
Self {
font_size: self.font_size * scale,
line_height: self.line_height * scale,
}
}
fn y_offset(&self) -> i32 {
fn y_offset(&self) -> f32 {
self.font_size - self.line_height
}
}
@ -299,8 +297,8 @@ pub struct Buffer<'a> {
/// [BufferLine]s (or paragraphs) of text in the buffer
pub lines: Vec<BufferLine>,
metrics: Metrics,
width: i32,
height: i32,
width: f32,
height: f32,
scroll: i32,
/// True if a redraw is requires. Set to false after processing
redraw: bool,
@ -310,14 +308,14 @@ pub struct Buffer<'a> {
impl<'a> Buffer<'a> {
/// Create a new [`Buffer`] with the provided [`FontSystem`] and [`Metrics`]
pub fn new(font_system: &'a FontSystem, metrics: Metrics) -> Self {
assert_ne!(metrics.line_height, 0, "line height cannot be 0");
assert_ne!(metrics.line_height, 0.0, "line height cannot be 0");
let mut buffer = Self {
font_system,
lines: Vec::new(),
metrics,
width: 0,
height: 0,
width: 0.0,
height: 0.0,
scroll: 0,
redraw: false,
wrap: Wrap::Word,
@ -497,7 +495,7 @@ impl<'a> Buffer<'a> {
/// Set the current [`Metrics`]
pub fn set_metrics(&mut self, metrics: Metrics) {
if metrics != self.metrics {
assert_ne!(metrics.font_size, 0, "font size cannot be 0");
assert_ne!(metrics.font_size, 0.0, "font size cannot be 0");
self.metrics = metrics;
self.relayout();
self.shape_until_scroll();
@ -519,14 +517,14 @@ impl<'a> Buffer<'a> {
}
/// Get the current buffer dimensions (width, height)
pub fn size(&self) -> (i32, i32) {
pub fn size(&self) -> (f32, f32) {
(self.width, self.height)
}
/// Set the current buffer dimensions
pub fn set_size(&mut self, width: i32, height: i32) {
let clamped_width = width.max(0);
let clamped_height = height.max(0);
pub fn set_size(&mut self, width: f32, height: f32) {
let clamped_width = width.max(0.0);
let clamped_height = height.max(0.0);
if clamped_width != self.width || clamped_height != self.height {
self.width = clamped_width;
@ -551,7 +549,7 @@ impl<'a> Buffer<'a> {
/// Get the number of lines that can be viewed in the buffer
pub fn visible_lines(&self) -> i32 {
self.height / self.metrics.line_height
(self.height / self.metrics.line_height) as i32
}
/// Set text of buffer, using provided attributes for each line by default
@ -588,7 +586,7 @@ impl<'a> Buffer<'a> {
}
/// Convert x, y position to Cursor (hit detection)
pub fn hit(&self, x: i32, y: i32) -> Option<Cursor> {
pub fn hit(&self, x: f32, y: f32) -> Option<Cursor> {
#[cfg(all(feature = "std", not(target_arch = "wasm32")))]
let instant = std::time::Instant::now();
@ -616,12 +614,12 @@ impl<'a> Buffer<'a> {
'hit: for (glyph_i, glyph) in run.glyphs.iter().enumerate() {
if first_glyph {
first_glyph = false;
if (run.rtl && x > glyph.x as i32) || (!run.rtl && x < 0) {
if (run.rtl && x > glyph.x) || (!run.rtl && x < 0.0) {
new_cursor_glyph = 0;
new_cursor_char = 0;
}
}
if x >= glyph.x as i32 && x <= (glyph.x + glyph.w) as i32 {
if x >= glyph.x && x <= glyph.x + glyph.w {
new_cursor_glyph = glyph_i;
let cluster = &run.text[glyph.start..glyph.end];
@ -629,10 +627,10 @@ impl<'a> Buffer<'a> {
let mut egc_x = glyph.x;
let egc_w = glyph.w / (total as f32);
for (egc_i, egc) in cluster.grapheme_indices(true) {
if x >= egc_x as i32 && x <= (egc_x + egc_w) as i32 {
if x >= egc_x && x <= egc_x + egc_w {
new_cursor_char = egc_i;
let right_half = x >= (egc_x + egc_w / 2.0) as i32;
let right_half = x >= egc_x + egc_w / 2.0;
if right_half != glyph.level.is_rtl() {
// If clicking on last half of glyph, move cursor past glyph
new_cursor_char += egc.len();
@ -643,7 +641,7 @@ impl<'a> Buffer<'a> {
egc_x += egc_w;
}
let right_half = x >= (glyph.x + glyph.w / 2.0) as i32;
let right_half = x >= glyph.x + glyph.w / 2.0;
if right_half != glyph.level.is_rtl() {
// If clicking on last half of glyph, move cursor past glyph
new_cursor_char = cluster.len();
@ -704,7 +702,7 @@ impl<'a> Buffer<'a> {
};
cache.with_pixels(cache_key, glyph_color, |x, y, color| {
f(x_int + x, run.line_y + y_int + y, 1, 1, color);
f(x_int + x, run.line_y as i32 + y_int + y, 1, 1, color);
});
}
}

View file

@ -184,8 +184,8 @@ impl BufferLine {
pub fn layout(
&mut self,
font_system: &FontSystem,
font_size: i32,
width: i32,
font_size: f32,
width: f32,
wrap: Wrap,
) -> &[LayoutLine] {
if self.layout_opt.is_none() {

View file

@ -7,8 +7,8 @@ pub struct CacheKey {
pub font_id: fontdb::ID,
/// Glyph ID
pub glyph_id: u16,
/// Font size in pixels
pub font_size: i32,
/// `f32` bits of font size
pub font_size_bits: u32,
/// Binning of fractional X offset
pub x_bin: SubpixelBin,
/// Binning of fractional Y offset
@ -19,7 +19,7 @@ impl CacheKey {
pub fn new(
font_id: fontdb::ID,
glyph_id: u16,
font_size: i32,
font_size: f32,
pos: (f32, f32),
) -> (Self, i32, i32) {
let (x, x_bin) = SubpixelBin::new(pos.0);
@ -28,7 +28,7 @@ impl CacheKey {
Self {
font_id,
glyph_id,
font_size,
font_size_bits: font_size.to_bits(),
x_bin,
y_bin,
},

View file

@ -424,14 +424,14 @@ impl<'a> Edit<'a> for Editor<'a> {
self.buffer.set_redraw(true);
}
Action::PageUp => {
self.action(Action::Vertical(-self.buffer.size().1));
self.action(Action::Vertical(-self.buffer.size().1 as i32));
}
Action::PageDown => {
self.action(Action::Vertical(self.buffer.size().1));
self.action(Action::Vertical(self.buffer.size().1 as i32));
}
Action::Vertical(px) => {
// TODO more efficient
let lines = px / self.buffer.metrics().line_height;
let lines = px / self.buffer.metrics().line_height as i32;
if lines < 0 {
for _ in 0..-lines {
self.action(Action::Up);
@ -541,7 +541,7 @@ impl<'a> Edit<'a> for Editor<'a> {
Action::Click { x, y } => {
self.select_opt = None;
if let Some(new_cursor) = self.buffer.hit(x, y) {
if let Some(new_cursor) = self.buffer.hit(x as f32, y as f32) {
if new_cursor != self.cursor {
self.cursor = new_cursor;
self.buffer.set_redraw(true);
@ -554,7 +554,7 @@ impl<'a> Edit<'a> for Editor<'a> {
self.buffer.set_redraw(true);
}
if let Some(new_cursor) = self.buffer.hit(x, y) {
if let Some(new_cursor) = self.buffer.hit(x as f32, y as f32) {
if new_cursor != self.cursor {
self.cursor = new_cursor;
self.buffer.set_redraw(true);
@ -753,7 +753,7 @@ impl<'a> Edit<'a> for Editor<'a> {
} else if let Some((min, max)) = range_opt.take() {
f(
min,
line_y - font_size,
(line_y - font_size) as i32,
cmp::max(0, max - min) as u32,
line_height as u32,
Color::rgba(color.r(), color.g(), color.b(), 0x33),
@ -765,7 +765,7 @@ impl<'a> Edit<'a> for Editor<'a> {
if run.glyphs.is_empty() && end.line > line_i {
// Highlight all of internal empty lines
range_opt = Some((0, self.buffer.size().0));
range_opt = Some((0, self.buffer.size().0 as i32));
}
if let Some((mut min, mut max)) = range_opt.take() {
@ -774,12 +774,12 @@ impl<'a> Edit<'a> for Editor<'a> {
if run.rtl {
min = 0;
} else {
max = self.buffer.size().0;
max = self.buffer.size().0 as i32;
}
}
f(
min,
line_y - font_size,
(line_y - font_size) as i32,
cmp::max(0, max - min) as u32,
line_height as u32,
Color::rgba(color.r(), color.g(), color.b(), 0x33),
@ -815,7 +815,7 @@ impl<'a> Edit<'a> for Editor<'a> {
},
};
f(x, line_y - font_size, 1, line_height as u32, color);
f(x, (line_y - font_size) as i32, 1, line_height as u32, color);
}
for glyph in run.glyphs.iter() {
@ -827,7 +827,7 @@ impl<'a> Edit<'a> for Editor<'a> {
};
cache.with_pixels(cache_key, glyph_color, |x, y, color| {
f(x_int + x, line_y + y_int + y, 1, 1, color);
f(x_int + x, line_y as i32 + y_int + y, 1, 1, color);
});
}
}

View file

@ -237,7 +237,7 @@ impl<'a> Edit<'a> for ViEditor<'a> {
let cursor_glyph_opt = |cursor: &Cursor| -> Option<(usize, f32, f32)> {
//TODO: better calculation of width
let default_width = (font_size as f32) / 2.0;
let default_width = font_size / 2.0;
if cursor.line == line_i {
for (glyph_i, glyph) in run.glyphs.iter().enumerate() {
if cursor.index >= glyph.start && cursor.index < glyph.end {
@ -312,7 +312,7 @@ impl<'a> Edit<'a> for ViEditor<'a> {
} else if let Some((min, max)) = range_opt.take() {
f(
min,
line_y - font_size,
(line_y - font_size) as i32,
cmp::max(0, max - min) as u32,
line_height as u32,
Color::rgba(color.r(), color.g(), color.b(), 0x33),
@ -324,7 +324,7 @@ impl<'a> Edit<'a> for ViEditor<'a> {
if run.glyphs.is_empty() && end.line > line_i {
// Highlight all of internal empty lines
range_opt = Some((0, self.buffer().size().0));
range_opt = Some((0, self.buffer().size().0 as i32));
}
if let Some((mut min, mut max)) = range_opt.take() {
@ -333,12 +333,12 @@ impl<'a> Edit<'a> for ViEditor<'a> {
if run.rtl {
min = 0;
} else {
max = self.buffer().size().0;
max = self.buffer().size().0 as i32;
}
}
f(
min,
line_y - font_size,
(line_y - font_size) as i32,
cmp::max(0, max - min) as u32,
line_height as u32,
Color::rgba(color.r(), color.g(), color.b(), 0x33),
@ -397,13 +397,19 @@ impl<'a> Edit<'a> for ViEditor<'a> {
let right_x = cmp::max(start_x, end_x);
f(
left_x,
line_y - font_size,
(line_y - font_size) as i32,
(right_x - left_x) as u32,
line_height as u32,
Color::rgba(color.r(), color.g(), color.b(), 0x33),
);
} else {
f(start_x, line_y - font_size, 1, line_height as u32, color);
f(
start_x,
(line_y - font_size) as i32,
1,
line_height as u32,
color,
);
}
}
@ -416,7 +422,7 @@ impl<'a> Edit<'a> for ViEditor<'a> {
};
cache.with_pixels(cache_key, glyph_color, |x, y, color| {
f(x_int + x, line_y + y_int + y, 1, 1, color)
f(x_int + x, line_y as i32 + y_int + y, 1, 1, color);
});
}
}

View file

@ -21,13 +21,13 @@
//! let mut swash_cache = SwashCache::new(&font_system);
//!
//! // Text metrics indicate the font size and line height of a buffer
//! let metrics = Metrics::new(14, 20);
//! let metrics = Metrics::new(14.0, 20.0);
//!
//! // A Buffer provides shaping and layout for a UTF-8 string, create one per text widget
//! let mut buffer = Buffer::new(&font_system, metrics);
//!
//! // Set a size for the text buffer, in pixels
//! buffer.set_size(80, 25);
//! buffer.set_size(80.0, 25.0);
//!
//! // Attributes indicate what font to choose
//! let attrs = Attrs::new();

View file

@ -237,14 +237,14 @@ pub struct ShapeGlyph {
impl ShapeGlyph {
fn layout(
&self,
font_size: i32,
font_size: f32,
x: f32,
y: f32,
w: f32,
level: unicode_bidi::Level,
) -> LayoutGlyph {
let x_offset = font_size as f32 * self.x_offset;
let y_offset = font_size as f32 * self.y_offset;
let x_offset = font_size * self.x_offset;
let y_offset = font_size * self.y_offset;
let (cache_key, x_int, y_int) = CacheKey::new(
self.font_id,
@ -608,8 +608,8 @@ impl ShapeLine {
pub fn layout(
&self,
font_size: i32,
line_width: i32,
font_size: f32,
line_width: f32,
wrap: Wrap,
align: Option<Align>,
) -> Vec<LayoutLine> {
@ -637,8 +637,8 @@ impl ShapeLine {
// 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 };
let start_x = if self.rtl { line_width } else { 0.0 };
let end_x = if self.rtl { 0.0 } else { line_width };
let mut x = start_x;
let mut y;
@ -655,7 +655,7 @@ impl ShapeLine {
.push((span_index, (0, 0), (span.words.len(), 0)));
}
} else {
let mut fit_x = line_width as f32;
let mut fit_x = line_width;
for (span_index, span) in self.spans.iter().enumerate() {
let mut word_ranges = Vec::new();
let mut word_range_width = 0.;
@ -666,7 +666,7 @@ impl ShapeLine {
// incongruent directions
let mut fitting_start = (span.words.len(), 0);
for (i, word) in span.words.iter().enumerate().rev() {
let word_size = font_size as f32 * word.x_advance;
let word_size = font_size * word.x_advance;
if fit_x - word_size >= 0. {
// fits
fit_x -= word_size;
@ -677,7 +677,7 @@ impl ShapeLine {
continue;
} else if wrap == Wrap::Glyph {
for (glyph_i, glyph) in word.glyphs.iter().enumerate().rev() {
let glyph_size = font_size as f32 * glyph.x_advance;
let glyph_size = font_size * glyph.x_advance;
if fit_x - glyph_size >= 0. {
fit_x -= glyph_size;
word_range_width += glyph_size;
@ -690,7 +690,7 @@ impl ShapeLine {
number_of_blanks,
));
number_of_blanks = 0;
fit_x = line_width as f32 - glyph_size;
fit_x = line_width - glyph_size;
word_range_width = glyph_size;
fitting_start = (i, glyph_i + 1);
}
@ -706,8 +706,7 @@ impl ShapeLine {
// previous word if it's a whitespace
if previous_word.blank {
number_of_blanks -= 1;
prev_word_width =
Some(previous_word.x_advance * font_size as f32);
prev_word_width = Some(previous_word.x_advance * font_size);
}
}
if let Some(width) = prev_word_width {
@ -727,11 +726,11 @@ impl ShapeLine {
}
number_of_blanks = 0;
if word.blank {
fit_x = line_width as f32;
fit_x = line_width;
word_range_width = 0.;
fitting_start = (i + 1, 0);
} else {
fit_x = line_width as f32 - word_size;
fit_x = line_width - word_size;
word_range_width = word_size;
fitting_start = (i + 1, 0);
}
@ -742,7 +741,7 @@ impl ShapeLine {
// congruent direction
let mut fitting_start = (0, 0);
for (i, word) in span.words.iter().enumerate() {
let word_size = font_size as f32 * word.x_advance;
let word_size = font_size * word.x_advance;
if fit_x - word_size >= 0. {
// fits
fit_x -= word_size;
@ -753,7 +752,7 @@ impl ShapeLine {
continue;
} else if wrap == Wrap::Glyph {
for (glyph_i, glyph) in word.glyphs.iter().enumerate() {
let glyph_size = font_size as f32 * glyph.x_advance;
let glyph_size = font_size * glyph.x_advance;
if fit_x - glyph_size >= 0. {
fit_x -= glyph_size;
word_range_width += glyph_size;
@ -766,7 +765,7 @@ impl ShapeLine {
number_of_blanks,
));
number_of_blanks = 0;
fit_x = line_width as f32 - glyph_size;
fit_x = line_width - glyph_size;
word_range_width = glyph_size;
fitting_start = (i, glyph_i);
}
@ -782,8 +781,7 @@ impl ShapeLine {
// previous word if it's a whitespace
if previous_word.blank {
number_of_blanks -= 1;
prev_word_width =
Some(previous_word.x_advance * font_size as f32);
prev_word_width = Some(previous_word.x_advance * font_size);
}
}
if let Some(width) = prev_word_width {
@ -804,11 +802,11 @@ impl ShapeLine {
number_of_blanks = 0;
if word.blank {
fit_x = line_width as f32;
fit_x = line_width;
word_range_width = 0.;
fitting_start = (i + 1, 0);
} else {
fit_x = line_width as f32 - word_size;
fit_x = line_width - word_size;
word_range_width = word_size;
fitting_start = (i, 0);
}
@ -872,7 +870,7 @@ impl ShapeLine {
} else {
x += word_range_width;
}
if word_range_width > line_width as f32 {
if word_range_width > line_width {
// single word is bigger than line_width
vl_range_of_spans.push(current_visual_line);
current_visual_line = VisualLine::default();
@ -895,15 +893,15 @@ impl ShapeLine {
x = start_x;
y = 0.;
let alignment_correction = match (align, self.rtl) {
(Align::Left, true) => line_width as f32 - visual_line.w,
(Align::Left, true) => line_width - 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::Right, false) => line_width - visual_line.w,
(Align::Center, _) => (line_width - visual_line.w) / 2.0,
(Align::Justified, _) => {
// Don't justify the last line in a paragraph.
if visual_line.spaces > 0 && index != number_of_visual_lines - 1 {
(line_width as f32 - visual_line.w) / visual_line.spaces as f32
(line_width - visual_line.w) / visual_line.spaces as f32
} else {
0.
}
@ -927,8 +925,8 @@ impl ShapeLine {
[*starting_glyph..*ending_glyph]
.iter()
{
let x_advance = font_size as f32 * glyph.x_advance;
let y_advance = font_size as f32 * glyph.y_advance;
let x_advance = font_size * glyph.x_advance;
let y_advance = font_size * glyph.y_advance;
x -= x_advance;
if word_blank && align == Align::Justified {
x -= alignment_correction;
@ -958,8 +956,8 @@ impl ShapeLine {
let word_blank = word.blank;
for glyph in &word.glyphs[g1..g2] {
let x_advance = font_size as f32 * glyph.x_advance;
let y_advance = font_size as f32 * glyph.y_advance;
let x_advance = font_size * glyph.x_advance;
let y_advance = font_size * glyph.y_advance;
x -= x_advance;
if word_blank && align == Align::Justified {
x -= alignment_correction;
@ -1002,8 +1000,8 @@ impl ShapeLine {
[*starting_glyph..*ending_glyph]
.iter()
{
let x_advance = font_size as f32 * glyph.x_advance;
let y_advance = font_size as f32 * glyph.y_advance;
let x_advance = font_size * glyph.x_advance;
let y_advance = font_size * glyph.y_advance;
if word_blank && align == Align::Justified {
glyphs.push(glyph.layout(
font_size,
@ -1033,8 +1031,8 @@ impl ShapeLine {
let word_blank = word.blank;
for glyph in &word.glyphs[g1..g2] {
let x_advance = font_size as f32 * glyph.x_advance;
let y_advance = font_size as f32 * glyph.y_advance;
let x_advance = font_size * glyph.x_advance;
let y_advance = font_size * glyph.y_advance;
if word_blank && align == Align::Justified {
glyphs.push(glyph.layout(
font_size,

View file

@ -31,7 +31,7 @@ fn swash_image(
// Build the scaler
let mut scaler = context
.builder(font.as_swash())
.size(cache_key.font_size as f32)
.size(f32::from_bits(cache_key.font_size_bits))
.hint(true)
.build();
@ -74,7 +74,7 @@ fn swash_outline_commands(
// Build the scaler
let mut scaler = context
.builder(font.as_swash())
.size(cache_key.font_size as f32)
.size(f32::from_bits(cache_key.font_size_bits))
.build();
// Scale the outline