diff --git a/assets/default_config.toml b/assets/default_config.toml index 24b0967..3839c8a 100644 --- a/assets/default_config.toml +++ b/assets/default_config.toml @@ -6,6 +6,9 @@ prompt = '' # space between window border and the content in pixel padding = 100 +# horizontal alignment of text content: "left" or "center" +alignment = "left" + fonts = [ 'Noto Sans Mono', ] # list of otf or ttf fonts. later elements work as fallback diff --git a/src/app.rs b/src/app.rs index 0da6cca..c4b7d96 100644 --- a/src/app.rs +++ b/src/app.rs @@ -173,19 +173,42 @@ impl App { Some(prompt) => prompt, None => &self.config.prompt, }; + + // Calculate horizontal and vertical offsets based on alignment mode + let (base_x_offset, vertical_padding) = if self.config.alignment == "center" { + // For center alignment: + // - Use padding only for vertical spacing (top margin) + // - Calculate horizontal offset to center the text block + let estimated_text_width = width / 3; // Rough estimate for text block width + let horizontal_offset = if width > estimated_text_width { + (width - estimated_text_width) / 2 + } else { + width / 4 // Fallback to quarter screen width + }; + (horizontal_offset, padding) + } else { + // Default to left alignment: use padding for both horizontal and vertical spacing + (padding, padding) + }; + let prompt_width = if prompt.is_empty() { 0 } else { - let (width, _) = self.font.render( + let (pwidth, _) = self.font.measure_text(prompt); + pwidth + (font_size * 0.2) as u32 + }; + + // Now render at the consistent position + if !prompt.is_empty() { + self.font.render( prompt, &self.config.colors.prompt, &mut img, - padding, - padding, + base_x_offset, + vertical_padding, None, ); - width + (font_size * 0.2) as u32 - }; + } if !self.query.is_empty() { let color = if self.select_input { @@ -197,14 +220,14 @@ impl App { &self.query, color, &mut img, - padding + prompt_width, - padding, + base_x_offset + prompt_width, + vertical_padding, None, ); } let spacer = (1.5 * font_size) as u32; - let max_entries = ((height.saturating_sub(2 * padding).saturating_sub(spacer)) as f32 + let max_entries = ((height.saturating_sub(2 * vertical_padding).saturating_sub(spacer)) as f32 / (font_size * 1.2)) as usize; let offset = if self.select_index > (max_entries / 2) { self.select_index - max_entries / 2 @@ -223,13 +246,15 @@ impl App { } else { &self.config.colors.text }; + + // Use the same base_x_offset for all search results to keep them aligned self.font.render( &matched.name, color, &mut img, - padding, - padding + spacer + (i - offset) as u32 * (font_size * 1.2) as u32, - Some((width - (padding * 2)) as usize), + base_x_offset, + vertical_padding + spacer + (i - offset) as u32 * (font_size * 1.2) as u32, + Some((width - (base_x_offset * 2)) as usize), ); } diff --git a/src/config.rs b/src/config.rs index df0d4fd..491d8a4 100644 --- a/src/config.rs +++ b/src/config.rs @@ -51,6 +51,7 @@ pub struct SearchConfig { pub struct Config { pub prompt: String, pub padding: u32, + pub alignment: String, pub font: Option, pub fonts: Vec, pub font_size: f32, @@ -135,6 +136,7 @@ impl Default for Config { Self { prompt: String::new(), padding: 100, + alignment: "left".to_string(), font: None, fonts: vec![], font_size: 32., diff --git a/src/font.rs b/src/font.rs index 6f2dab5..a8f09b2 100644 --- a/src/font.rs +++ b/src/font.rs @@ -97,6 +97,32 @@ impl Font { res } + pub fn measure_text(&self, text: &str) -> (u32, u32) { + let mut width = 0; + let mut layout = self.layout.borrow_mut(); + layout.reset(&LayoutSettings::default()); + + for c in Self::replace_tabs(text, self.tab_width).chars() { + let mut font_index = 0; + for (i, font) in self.fonts.iter().enumerate() { + if font.lookup_glyph_index(c) != 0 { + font_index = i; + break; + } + } + layout.append( + &self.fonts, + &TextStyle::new(&c.to_string(), self.size * self.scale as f32, font_index), + ); + } + + if let Some(glyph) = layout.glyphs().last() { + width = glyph.x as usize + glyph.width; + } + + (width as u32, layout.height() as u32) + } + pub fn render( &self, text: &str,