Repository: j0ru/kickoff (language: rust)

Issue:
 'Image index, out of bounds" When text goes offscreen
Hello!
Recently found your project and liked it much better than bemenu and started using it. Its great how it will show up over full-screen apps unlike bemenu.

Whenever the menu options are longer than the screen I receive the following error:
`thread 'main' panicked at 'Image index (1128, 41) out of bounds (1128, 728)', /home/user/.cargo/registry/src/github.com-1ecc6299db9ec823/image-0.24.5/./src/buffer.rs:946:21`


For example if I run:

`echo https://somereallylong.url.com/needtomakeuprandomstufftomaketheurlorentrylongeraaaaaaaaaaaaabbbbbbbbbbbbbbbcccccccccccccccdddddddddddddddeeeeeeeeeeeeeeeeeessss | kickoff --from-stdin --stdout`


I have a file with "bookmarks" with either long text or url's that I use (bemenu) now kickoff to run. 

Also, I swapped out bemenu for kickoff when using [Firecat53's demnu based bitwaden app](https://github.com/firecat53/bitwarden-menu).
This works perfectly, but if the saved entries are longer than the screen, the same issue occurs. 






Here's that snippet of code from the buffer.rs file:

```
    /// Gets a reference to the mutable pixel at location `(x, y)`
    ///
    /// # Panics
    ///
    /// Panics if `(x, y)` is out of the bounds `(width, height)`.
    #[inline]
    pub fn get_pixel_mut(&mut self, x: u32, y: u32) -> &mut P {
        match self.pixel_indices(x, y) {
            None => panic!(
                "Image index {:?} out of bounds {:?}",
                (x, y),
                (self.width, self.height)
            ),
            Some(pixel_indices) => <P as Pixel>::from_slice_mut(&mut self.data[pixel_indices]),
        }
    }
```




Discussion / hints:
Hey, thanks for taking the time!

I think I had this case handled before, but probably forgot to add the checks when rewriting the font rendering.
It's fixed in main, but I'll probably want to improve the visuals when Entries are longer than the display, so I'm leaving this Issue open

@j0ru That was quick! thank you, works perfectly. 

Fix patch:
diff --git a/src/font.rs b/src/font.rs
index eb17f17..3b66355 100644
--- a/src/font.rs
+++ b/src/font.rs
@@ -82,10 +82,13 @@ impl Font {
         image: &mut RgbaImage,
         x_offset: u32,
         y_offset: u32,
+        max_width: Option<usize>,
     ) -> (u32, u32) {
         let mut width = 0;
+        let mut current_width = 0.;
         let mut layout = self.layout.borrow_mut();
         layout.reset(&LayoutSettings::default());
+
         for c in text.chars() {
             let mut font_index = 0;
             for (i, font) in self.fonts.iter().enumerate() {
@@ -101,7 +104,13 @@ impl Font {
         }
 
         for glyph in layout.glyphs() {
-            let (_, bitmap) = self.render_glyph(glyph.key);
+            if let Some(max_width) = max_width {
+                if current_width as usize + glyph.width > max_width {
+                    break;
+                }
+            }
+            let (metrics, bitmap) = self.render_glyph(glyph.key);
+            current_width += metrics.advance_width;
             for (i, alpha) in bitmap.iter().enumerate() {
                 if alpha != &0 {
                     let x = glyph.x + x_offset as f32 + (i % glyph.width) as f32;
diff --git a/src/main.rs b/src/main.rs
index 641603d..6b16596 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -317,8 +317,14 @@ async fn run() -> Result<Option<JoinHandle<()>>, Box<dyn Error>> {
                 ImageBuffer::from_pixel(width, height, config.colors.background.to_rgba());
             let prompt = args.prompt.as_ref().unwrap_or(&config.prompt);
             let prompt_width = if !prompt.is_empty() {
-                let (width, _) =
-                    font.render(prompt, &config.colors.prompt, &mut img, padding, padding);
+                let (width, _) = font.render(
+                    prompt,
+                    &config.colors.prompt,
+                    &mut img,
+                    padding,
+                    padding,
+                    None,
+                );
                 width
             } else {
                 0
@@ -330,7 +336,14 @@ async fn run() -> Result<Option<JoinHandle<()>>, Box<dyn Error>> {
                 } else {
                     &config.colors.text_query
                 };
-                font.render(query, color, &mut img, padding + prompt_width, padding);
+                font.render(
+                    query,
+                    color,
+                    &mut img,
+                    padding + prompt_width,
+                    padding,
+                    None,
+                );
             }
 
             let spacer = (1.5 * font_size) as u32;
@@ -358,6 +371,7 @@ async fn run() -> Result<Option<JoinHandle<()>>, Box<dyn Error>> {
                     &mut img,
                     padding,
                     padding + spacer + (i - offset) as u32 * (font_size * 1.2) as u32,
+                    Some((width - (padding * 2)) as usize),
                 );
             }
 


Test patch: