Rust-SDL2 / rust-sdl2

SDL2 bindings for Rust
MIT License
2.77k stars 474 forks source link

Struct owning ttf fonts #1239

Open Maccraft123 opened 2 years ago

Maccraft123 commented 2 years ago

Hello,

I'm trying to rewrite a SDL2-based GUI library from C to Rust. That library uses a 'context' struct for its data, like linked lists of buttons and tabs, sdl2 contexts etc.

I'm trying to recreate this in rust-sdl2, however it looks like I'm trying to use rust-sdl2 wrong.

The issue that I'm having is making that context struct to own TTF context, SDL context, font and texture creator, but on new Rust compiler errors out, I assume it's due to it thinking that context struct doesn't live long enough?

I've been stuck at this problem for almost a week

Can I get help with that on here? Maybe some pointers onto getting it to compile and work? Or idea that would work better with Rust's ownership system?

I want all buttons to own their textures, and context struct to own all tabs that own all buttons

Code of library is on https://github.com/R-ARM/rtoybox-rs

Example program using that library:

use ragnarok_tk::Toolkit;

fn main() -> Result<(), String> {
    let mut tk = Toolkit::new()?;

    tk.add_tab("A tab")?;
    tk.add_btn( "A button")?;
    println!("{:#?}", tk);
    //tk.set_alpha(0);
    while tk.tick()? {
        //for ev in tk.poll_events() {
            // button events
            // etc
        //}
    }

    Ok(())
}
Cobrand commented 2 years ago

You technically can't "own" a texture, they depend directly on your graphics card. This means that if you destroy the window and your graphics backend, your Texture becomes a reference to nothing. In Rust, the solution to prevent that is with lifetimes.

There is a feature called unsafe-textures where the textures don't have lifetimes, but it's only safe if all the textures are destroyed at the very end. If you are loading / unloading multiple textures during your program, I don't recommend it.

Maccraft123 commented 2 years ago

Thanks for the response!

Since the texture in the library is always attached to a button(and everything that vaguely resembles a button) or a tab, implementing Drop for both of them and destroying texture when a given button/tab is destroyed should be safe Turns out i understood it wrong. I don't expect users to create too many textures so that won't be a problem Figured it out

//#[derive(Debug)]
struct Button {
    name: &'static str,
    x: i32, // prolly should be Option<i32>
    y: i32,
    w: u32,
    h: u32,
    typ: ButtonType,
    text: Option<Texture> // always there
}

impl Button {
    fn name(&self) -> &'static str { self.name }
    fn x(&self) -> i32 { self.x }
    fn y(&self) -> i32 { self.y }
    fn w(&self) -> u32 { self.w }
    fn h(&self) -> u32 { self.h }
    fn typ(&self) -> ButtonType { self.typ }

    fn new(tk: &Toolkit, name: &'static str, x: i32, y: i32) -> Result<Button, ToolkitError> {
        let texture = Some(tk.render_text(name)?);
        let attr = texture.as_ref().unwrap().query();
        Ok(Button {
            name: name,
            x: x,
            y: y,
            w: attr.width,
            h: attr.height,
            typ: ButtonType::Normal,
            text: texture,
        })
    }
}
impl Drop for Button {
    fn drop(&mut self) {
        if let Some(texture) = self.text.take() {
            unsafe {
                texture.destroy();
            }
        }   
    }   
}   

I'm now having a problem with font being in the main struct of the UI library. I'm working around it by hacking up font loading in a function used to handle font rendering in UI library. And it worked... but it's an ugly hack and probably a lot slower than having font live inside main Toolkit struct. obraz

error[E0505]: cannot move out of `ttf` because it is borrowed
   --> ragnarok_tk/src/lib.rs:313:18
    |
287 |     pub fn new() -> Result<Toolkit, ToolkitError> {
    |                -- lifetime `'a` defined here
...
295 |         let font = ttf.load_font("/usr/share/fonts/liberation/LiberationSans.ttf", 28)?;
    |                    ------------------------------------------------------------------- borrow of `ttf` occurs here
...
313 |             ttf: ttf,
    |                  ^^^ move out of `ttf` occurs here
...
317 |         Ok(tmp)
    |         ------- returning this value requires that `ttf` is borrowed for `'a`

error[E0515]: cannot return value referencing local variable `ttf`
   --> ragnarok_tk/src/lib.rs:317:9
    |
295 |         let font = ttf.load_font("/usr/share/fonts/liberation/LiberationSans.ttf", 28)?;
    |                    ------------------------------------------------------------------- `ttf` is borrowed here
...
317 |         Ok(tmp)
    |         ^^^^^^^ returns a value referencing data owned by the current function

Some errors have detailed explanations: E0505, E0515.
For more information about an error, try `rustc --explain E0505`.