From 4cb52996e2100c1b7e025c43d3d6d82f54d24e1e Mon Sep 17 00:00:00 2001 From: electria Date: Tue, 4 Aug 2026 14:34:46 -0700 Subject: [PATCH] feat: lazy load images this keeps the images loaded, eventually causing an OOM weird that dropping the handles doesn't clear memory --- src/app/mod.rs | 62 +++++++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/src/app/mod.rs b/src/app/mod.rs index 323b8d9..d55b9cc 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -1,18 +1,16 @@ -use std::{ - borrow::Cow, - collections::HashMap, - env, - path::{Path, PathBuf}, -}; +use std::{borrow::Cow, collections::HashMap, env, path::PathBuf}; -use iced::{Color, Element, Length, Renderer, Task, Theme, application, theme, widget}; +use iced::{Color, Element, Renderer, Task, Theme, application, theme, widget}; use ignore::{WalkBuilder, types::TypesBuilder}; -use image::{DynamicImage, EncodableLayout, RgbaImage}; use rayon::iter::{ParallelBridge, ParallelIterator}; use crate::SUPPORTED_TYPES; -enum Message {} +#[derive(Clone, Debug)] +enum Message { + LoadImage(PathBuf), + UnloadImage(PathBuf), +} #[derive(Default)] enum Mode { @@ -23,7 +21,7 @@ enum Mode { #[derive(Default)] struct State { mode: Mode, - images: HashMap>, + images: HashMap>, } impl State { @@ -54,32 +52,40 @@ impl State { .map(|entry| entry.into_path()) }) .filter(|path| path.is_file()) - .map(|path| { - let image = image::open(&path) - .inspect_err(|e| eprintln!("{e}")) - .ok() - .map(DynamicImage::into_rgba8); - let handle = image.as_ref().map(|image| { - widget::image::Handle::from_rgba(image.width(), image.height(), unsafe { - std::mem::transmute::<&[u8], &'static [u8]>(image.as_bytes()) - }) - }); - (path, image.map(|image| (image, handle.unwrap()))) - }) + .map(|path| (path, None)) .collect(), } } fn update(&mut self, message: Message) -> Task { + match message { + Message::LoadImage(path) => { + self.images + .entry(path.clone()) + .and_modify(|e| *e = Some(widget::image::Handle::from_path(path))); + } + Message::UnloadImage(path) => { + self.images.entry(path).and_modify(|e| *e = None); + } + } + Task::none() } fn view(&self) -> Element<'_, Message, Theme, Renderer> { widget::scrollable( - widget::grid( - self.images - .iter() - .filter_map(|(_k, v)| v.as_ref()) - .map(|(_image, handle)| widget::image(handle).into()), - ) + widget::grid(self.images.iter().map(|(path, opt_handle)| { + opt_handle.as_ref().map_or_else( + || { + widget::sensor(widget::space()) + .on_show(|_size| Message::LoadImage(path.clone())) + .into() + }, + |handle| { + widget::sensor(widget::image(handle)) + .on_hide(Message::UnloadImage(path.clone())) + .into() + }, + ) + })) .fluid(500) .spacing(0), )