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 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
use std::{path::Path}; use crate::{Error, Context}; use crate::{resource::ResourceSet}; use crate::theme_definition::{ThemeDefinition}; use crate::render::{Renderer, IO}; /// Global options that may be specified when building the Thyme context with /// [`ContextBuilder`](struct.ContextBuilder.html). These options /// cannot be changed afterwards. pub struct BuildOptions { /// Whether to enable background file monitoring for live reload. Note that /// to actually make use of this feature, you will need to call /// [`check_live_reload`](struct.Context.html#method.check_live_reload), typically /// once between each frame. The default value is `true`. pub enable_live_reload: bool, } impl Default for BuildOptions { fn default() -> Self { Self { enable_live_reload: true, } } } /// Structure to register resources and ultimately build the main Thyme [`Context`](struct.Context.html). /// /// You pass resources to it to register them with Thyme. Once this process is complete, call /// [`build`](struct.ContextBuilder.html#method.build) to create your [`Context`](struct.Context.html). pub struct ContextBuilder { resources: ResourceSet, } impl ContextBuilder { /** Creates a new `ContextBuilder`, using the default [`BuildOptions`](struct.BuildOptions.html) # Example ```no_run let mut context_builder = thyme::ContextBuilder::with_defaults(); context_builder.register_theme(theme)?; ... ``` **/ pub fn with_defaults() -> ContextBuilder { ContextBuilder::new(BuildOptions::default()) } /// Creates a new `ContextBuilder`, using the specified [`BuildOptions`](struct.BuildOptions.html) pub fn new(options: BuildOptions) -> ContextBuilder { ContextBuilder { resources: ResourceSet::new(options.enable_live_reload), } } /// Sets the theme for this context. The theme for your UI will be deserialized from /// `theme`. For example, `theme` could be a [`serde_json Value`](https://docs.serde.rs/serde_json/value/enum.Value.html) or /// [`serde_yaml Value`](https://docs.serde.rs/serde_yaml/enum.Value.html). See [`the crate root`](index.html) for a /// discussion of the theme format. If this method is called multiple times, only the last /// theme is used pub fn register_theme<'a, T: serde::Deserializer<'a>>(&mut self, theme: T) -> Result<(), T::Error> { log::debug!("Registering theme"); let theme_def: ThemeDefinition = serde::Deserialize::deserialize(theme)?; self.resources.register_theme(theme_def); Ok(()) } /// Sets the theme for this context by reading from the file at the specified `path`. The files are first /// read to a string and then passed to the function `f`, which returns a serde Deserializable object. That /// object is then deserialized as the theme. See [`register_theme`](#method.register_theme) pub fn register_theme_from_file<T, E, F>( &mut self, path: &Path, f: F, ) -> Result<(), Error> where T: 'static + for<'de> serde::Deserializer<'de>, E: 'static + std::error::Error, F: 'static + Fn(&str) -> Result<T, E> { log::debug!("Reading theme from file: '{:?}'", path); self.resources.register_theme_from_files(&[path], f); Ok(()) } /// Sets the theme for this context by reading from the specified list of files. The files are each read into a /// string and then concatenated together. The string is passed to the function `f` which returns a serde /// Deserializable object, which is finally deserialized into the theme. See /// [`register_theme`](#method.register_theme) pub fn register_theme_from_files<T, E, F>( &mut self, paths: &[&Path], f: F, ) -> Result<(), Error> where T: 'static + for<'de> serde::Deserializer<'de>, E: 'static + std::error::Error, F: 'static + Fn(&str) -> Result<T, E> { log::debug!("Reading theme from files: '{:?}'", paths); self.resources.register_theme_from_files(paths, f); Ok(()) } /// Registers the font data located in the file at the specified `path` with Thyme via the specified `id`. /// See [`register_font`](#method.register_font) pub fn register_font_from_file<T: Into<String>>( &mut self, id: T, path: &Path, ) { let id = id.into(); log::debug!("Reading font source '{}' from file: '{:?}'", id, path); self.resources.register_font_from_file(id, path); } /// Registers the font data for use with Thyme via the specified `id`. The `data` must consist /// of the full binary for a valid TTF or OTF file. /// Once the font has been registered, it can be accessed in your theme file via the font `source`. pub fn register_font<T: Into<String>>( &mut self, id: T, data: Vec<u8> ) { let id = id.into(); log::debug!("Registering font source '{}'", id); self.resources.register_font_from_data(id, data); } /// Reads a texture from the specified image file. See [`register_texture`](#method.register_texture). /// Requires you to enable the `image` feature in `Cargo.toml` to enable the dependancy on the /// [`image`](https://github.com/image-rs/image) crate. #[cfg(feature="image")] pub fn register_texture_from_file<T: Into<String>>( &mut self, id: T, path: &Path, ) { let id = id.into(); log::debug!("Reading texture '{}' from file: '{:?}'", id, path); self.resources.register_image_from_file(id, path); } /// Registers the image data for use with Thyme via the specified `id`. The `data` must consist of /// raw binary image data in RGBA format, with 4 bytes per pixel. The data must start at the /// bottom-left hand corner pixel and progress left-to-right and bottom-to-top. `data.len()` must /// equal `dimensions.0 * dimensions.1 * 4` /// Once the image has been registered, it can be accessed in your theme file via the image `source`. pub fn register_texture<T: Into<String>>( &mut self, id: T, data: Vec<u8>, dimensions: (u32, u32), ) { let id = id.into(); log::debug!("Registering texture '{}'", id); self.resources.register_image_from_data(id, data, dimensions.0, dimensions.1); } /// Consumes this builder and releases the borrows on the [`Renderer`](trait.Renderer.html) and [`IO`](trait.IO.html), /// so they can be used further. Builds a [`Context`](struct.Context.html). pub fn build<R: Renderer, I: IO>(mut self, renderer: &mut R, io: &mut I) -> Result<Context, Error> { log::info!("Building Thyme Context"); let scale_factor = io.scale_factor(); let display_size = io.display_size(); self.resources.cache_data()?; let themes = self.resources.build_assets(renderer, scale_factor)?; Ok(Context::new(self.resources, themes, display_size, scale_factor)) } }