diff --git a/Cargo.lock b/Cargo.lock index 6fbb998..a1ffbcb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -604,9 +604,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ "autocfg", ] diff --git a/src/draw/input_text.rs b/src/draw/input_text.rs index b67ee96..f33554f 100644 --- a/src/draw/input_text.rs +++ b/src/draw/input_text.rs @@ -63,6 +63,10 @@ impl<'a> Drawable for InputText<'a> { .min(self.params.radius.top_right); let pos = Point::new(rect_point.x + padding.left, rect_point.y + padding.top); + let end_pos = Point::new( + dt.width() as f32 - self.params.padding.right - self.params.margin.right, + pos.y, + ); let password_text = if self.params.password { Some("*".repeat(self.text.chars().count())) @@ -89,6 +93,7 @@ impl<'a> Drawable for InputText<'a> { text, font_size, pos, + end_pos, FontColor::Single(color.as_source()), &DrawOptions::new(), ); diff --git a/src/draw/list_view.rs b/src/draw/list_view.rs index dd10b8a..855f693 100644 --- a/src/draw/list_view.rs +++ b/src/draw/list_view.rs @@ -134,6 +134,8 @@ where } let pos = Point::new(x_offset + icon_size_f32 + icon_spacing, y_offset); + let end_pos = Point::new(dt.width() as f32 - self.params.margin.right, y_offset); + let color = if i == selected_item { self.params.selected_font_color } else { @@ -176,7 +178,8 @@ where }; let font = &self.params.font; - font.draw(dt, item.name, font_size, pos, color, &draw_opts); + font.draw(dt, item.name, font_size, pos, end_pos, color, &draw_opts); + if i == selected_item && has_subname { if let Some(subname) = item.subname { font.draw( @@ -187,6 +190,7 @@ where pos.x + self.params.action_left_margin, pos.y + entry_height + item_spacing, ), + Point::new(end_pos.x, pos.y + entry_height + item_spacing), FontColor::Single(self.params.font_color.as_source()), &draw_opts, ); diff --git a/src/font.rs b/src/font.rs index e1e3bd3..68322d8 100644 --- a/src/font.rs +++ b/src/font.rs @@ -25,12 +25,14 @@ pub trait FontBackend: Sized { fn font_by_path(path: &Path) -> Result; + #[allow(clippy::too_many_arguments)] fn draw( &self, dt: &mut DrawTarget, text: &str, font_size: f32, start_pos: Point, + end_pos: Point, color: FontColor, opts: &DrawOptions, ); diff --git a/src/font/fdue.rs b/src/font/fdue.rs index 3e2c57d..f27329f 100644 --- a/src/font/fdue.rs +++ b/src/font/fdue.rs @@ -4,7 +4,9 @@ use std::path::{Path, PathBuf}; use anyhow::Context; use fontconfig::{Fontconfig, Pattern}; -use fontdue::layout::{CoordinateSystem, Layout, LayoutSettings, TextStyle, VerticalAlign}; +use fontdue::layout::{ + CoordinateSystem, Layout, LayoutSettings, TextStyle, VerticalAlign, WrapStyle, +}; use levenshtein::levenshtein; use once_cell::sync::Lazy; use raqote::{DrawOptions, Point, SolidSource}; @@ -109,7 +111,7 @@ impl FontBackend for Font { let pat = Pattern::new(&FONTCONFIG_CACHE); fontconfig::list_fonts(&pat, None) .iter() - .filter_map(|pat| { + .find_map(|pat| { let path = std::path::Path::new(pat.filename()?); if !path.exists() { return None; @@ -117,7 +119,6 @@ impl FontBackend for Font { let index = pat.face_index().and_then(index_to_u32); Some(Font::from_path(path, index)) }) - .next() .expect("cannot find any font") .expect("cannot load default font") } @@ -172,9 +173,10 @@ impl FontBackend for Font { fn draw( &self, dt: &mut DrawTarget, - text: &str, + mut text: &str, font_size: f32, start_pos: Point, + end_pos: Point, color: FontColor, opts: &DrawOptions, ) { @@ -185,13 +187,47 @@ impl FontBackend for Font { x: start_pos.x, y: start_pos.y, max_height: Some(font_size), + max_width: Some(end_pos.x - start_pos.x), vertical_align: VerticalAlign::Middle, + wrap_style: WrapStyle::Letter, ..LayoutSettings::default() }); layout.append(&[&self.inner], &TextStyle::new(text, font_size, 0)); - for (n, g) in layout.glyphs().iter().enumerate() { + let take_glyphs = match layout.lines() { + Some(vec) => { + // If layout return miltiple lines then we have text overflow, cut the text + // and layout again + match vec.get(1) { + Some(second_line) => second_line.glyph_start, + None => layout.glyphs().len(), + } + } + None => layout.glyphs().len(), + }; + + if take_glyphs != layout.glyphs().len() { + let overflow_text = "..."; + + // Try place ... in end of cutted text. Check strange case if width of window is too small + // even for overflow_text. No panic at all + let glyph_offset = if take_glyphs > overflow_text.len() { + take_glyphs - overflow_text.len() + } else { + take_glyphs + }; + + text = &text[0..layout.glyphs().get(glyph_offset).unwrap().byte_offset]; + layout.clear(); + layout.append(&[&self.inner], &TextStyle::new(text, font_size, 0)); + + if glyph_offset != take_glyphs { + layout.append(&[&self.inner], &TextStyle::new(overflow_text, font_size, 0)); + } + } + + for (n, g) in layout.glyphs().iter().take(take_glyphs).enumerate() { let (_, b) = self.inner.rasterize_config(g.key); assert!(g.width * g.height <= BUF_SIZE);