I'm trying to make a GUI for a Rust application, and what looked like it would work well for me is imgui-rs. All I want to be able to do is get some input values from the user, and output a changing png. I had some trouble getting the boilerplate started, but the provided repository has support code that worked well enough for me to get the hello world demo working, but when I tried modifying it to get input and change the display, I ran into ownership issues. What I'm trying to do right now is have the user input an image name, then open that file and display it in the GUI.
Here's what I have right now (excluding the support code linked above):
struct State {
path: ImString,
has_image: bool,
}
impl Default for State {
fn default() -> Self {
Self {
path: ImString::with_capacity(128),
has_image: false,
}
}
}
fn main() {
let mut system = imgui_support::init(file!()); // this is from support code
let mut state = State::default();
system.main_loop(move |_, ui| {
Window::new(im_str!("hello world"))
.size([330.0, 110.0], imgui::Condition::FirstUseEver)
.position([0f32, 0f32], imgui::Condition::FirstUseEver)
.build(ui, || {
ui.input_text(im_str!("File path"), &mut state.path).build();
if ui.small_button(im_str!("Submit file")) {
system.add_img(state.path.to_str()); // try to open file and parse as texture
}
ui.separator();
});
if state.has_image {
Window::new(im_str!("Render:"))
.size([720f32, 720f32], imgui::Condition::FirstUseEver)
.build(ui, || {
imgui::Image::new(TextureId::new(0), [720f32, 720f32]).build(ui);
});
}
});
}
The add_img
function, which I added as a method on the System
struct, is defined as:
pub fn add_img(&mut self, path: &str) -> TextureId {
let textures: &mut Textures<Texture> = &mut self.renderer.textures();
let res_img = image::open(path);
match res_img {
Ok(image) => {
let image = image.to_rgba8();
let dimensions = image.dimensions();
let texture = RawImage2d::from_raw_rgba(image.into_raw(), dimensions);
let gl_texture = Texture2d::new(self.display.get_context(), texture).unwrap();
let texture = imgui_glium_renderer::Texture {
texture: Rc::new(gl_texture),
sampler: SamplerBehavior {
magnify_filter: MagnifySamplerFilter::Linear,
minify_filter: MinifySamplerFilter::Linear,
..Default::default()
},
};
textures.insert(texture)
}
Err(_) => TextureId::new(usize::MAX),
}
}
Right now, I get the error use of moved value system
, and no matter how I try to arrange the data, I can't seem to avoid the use-after-move error.
Is there a better way to organize my code, or perhaps a Rust GUI library that isn't as robust, since I don't need 99% of the imgui features?
The problem is, that the method main_loop
takes ownership of system
. System will never be available for you afterwards.
A quick fix would be to update the method main_loop
to pass renderer
and display
to the closure.
Something like this:
pub fn main_loop<F>(self, mut run_ui: F)
where F: FnMut(&mut bool, &mut Ui, &mut Renderer, &mut glium::Display) + 'static {}
and then when calling run_ui
just add renderer and display.
run_ui(&mut run, &mut ui, &mut renderer, &mut display);
The method add_img
should be made a free-standing function and take renderer and display separately, without depending on System.
pub fn add_img(path: &str, renderer: &mut Renderer, display: &mut glium::Display) -> TextureId {}