1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#![deny(missing_docs)]
/*!
See [README.md](https://crates.io/crates/sylasteven-system-pns)
**/

use lazy_static::lazy_static;

use sylasteven as engine;

use glutin::{GlContext, GlWindow};
use nanovg::{Font, Frame, Image};

pub use nanovg;

use std::collections::HashMap as Map;

use std::path::PathBuf;

struct Context(nanovg::Context);

unsafe impl Sync for Context {}

lazy_static! {
    static ref NANOVG: Context = Context(
        nanovg::ContextBuilder::new()
            .stencil_strokes()
            .build()
            .expect("Initialization of NanoVG failed!")
    );
}

/// The user interface render event.
pub struct RenderEvent {
    /// The frame used for UI rendering.
    pub frame: Frame<'static>,
    /// The default font for UI rendering.
    pub default_font: Font<'static>,
}

/// The user interface render system.
pub struct Render {
    default_font: Font<'static>,
    /// All loaded images mapped by their file path.
    pub images: Map<std::path::PathBuf, Image<'static>>,
    gl_window: GlWindow,
}

impl Render {
    /// Creates the render system by taking a GlWindow from glutin?
    pub fn new(gl_window: GlWindow) -> Render {
        let default_font = Font::from_file(
            &NANOVG.0,
            "Default",
            concat!(env!("CARGO_MANIFEST_DIR"), "/resources/FreeMono.ttf"),
        )
        .expect("Failed to load font 'FreeMono.ttf'");
        Self {
            default_font,
            gl_window,
            images: Map::new(),
        }
    }

    /// Adds new images and maps them to their file path.
    /// When adding the same file multiple times, the image is not loaded again.
    pub fn add_image(&mut self, path: &PathBuf) -> Option<PathBuf> {
        let context = &NANOVG.0;
        if self.images.get(path).is_some() {
            Some(path.clone())
        } else if let Some(image) = Image::new(context).build_from_file(&path).ok() {
            let result = Some(path.clone());
            self.images.insert(path.to_path_buf(), image);
            result
        } else {
            None
        }
    }
}

use crate::engine::{Handler, System};

impl<H: Handler> System<H> for Render
where
    H::Event: From<RenderEvent>,
{
    fn process(&mut self, handler: &mut H) {
        let context = &NANOVG.0;
        let Self {
            gl_window,
            default_font,
            ..
        } = self;
        let (width, height) = gl_window.get_inner_size().expect("Oh no");
        let (width, height) = (width as i32, height as i32);

        unsafe {
            gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT | gl::STENCIL_BUFFER_BIT);
            gl::Viewport(0, 0, width, height);
        }

        let (width, height) = (width as f32, height as f32);

        context.frame((width, height), gl_window.hidpi_factor(), |frame| {
            handler.handle(
                RenderEvent {
                    frame,
                    default_font: *default_font,
                }
                .into(),
            )
        });
        gl_window.swap_buffers().unwrap();
    }
}