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
#![forbid(unsafe_code)]
#![cfg_attr(not(debug_assertions), deny(warnings))] // Forbid warnings in release builds
#![warn(clippy::all, rust_2018_idioms)]

pub mod migrations;

use egui::{FontDefinitions, Style};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;

// This should be incremented anytime there is a new version.
// In addition, a migration feature MUST exist for each version of this in order to preserve compatibility between older version of the
// `EguiTheme` should changes be required.
const CURRENT_VERSION: u32 = 0u32;

const DEFAULT_FONTS: [&str; 4] = [
    "Hack",
    "Ubuntu-Light",
    "NotoEmoji-Regular",
    "emoji-icon-font",
];

/// The EguiTheme is the serializable contents of the relevant font information. This is intended to only be used when reading and writing the `Style` and `FontDefinition` information to/from disk.
///
/// The FontData is stored directly into the theme as base64 encoded Strings.
///
/// # Important
/// This should only be used during the Serialization/Deserialization process. Once the loading has completed, this should be extracted directly to an egui::Context as soon as it has been fully loaded.
#[derive(Serialize, Deserialize)]
pub struct EguiTheme {
    /// The version is used internally to determine how it should represent the data.
    version: u32,
    /// This is the serialized EguiFont style information.
    style: Style,
    /// This is the collection of the font definition information required.
    font_definitions: FontDefinitions,
    /// Font data is used to store the serializable font information that is not otherwise serialized by egui.
    font_data: BTreeMap<String, String>,
}

impl EguiTheme {
    /// Create a new style from the required information.
    /// `style` the egui style information
    /// `font_definitions` the current font definitions.
    pub fn new(style: Style, font_definitions: FontDefinitions) -> Self {
        // TODO: Determine if there is a better way to exclude the defaults.
        let mut font_data = BTreeMap::new();
        for (name, data) in font_definitions.font_data.iter() {
            if !DEFAULT_FONTS.contains(&name.as_str()) {
                font_data.insert(name.clone(), base64::encode(data));
            }
        }
        Self {
            version: CURRENT_VERSION,
            style,
            font_definitions,
            font_data,
        }
    }

    /// Returns the version of this theme.
    pub fn version(&self) -> u32 {
        self.version
    }
    /// Extracts the theme information destructively. This will encode all of the serialized data into an `egui::Context` ready format.
    ///
    /// # Important
    /// The theme will no longer be usable after extraction and will move the `Style` and `FontDefinitions` data for use.
    ///
    /// Style and font data should be managed by your application after extraction.
    pub fn extract(mut self) -> (egui::style::Style, egui::FontDefinitions) {
        // Allows automatic migrations to be performed.
        if cfg!(feature = "migrate_14_to_15") {
            println!("migrate egui 14 -> 15!");
            self = migrations::migration_from_14_to_15(self);
        } else {
            // This is a workaround since the font_data is not automatically serialized.
            // If the keys are not found in the font data, we need to add them before allowing the data to be extracted
            for (key, value) in self.font_data.iter() {
                if !self.font_definitions.font_data.contains_key(key)
                    && !DEFAULT_FONTS.contains(&key.as_str())
                {
                    let data = base64::decode(value).expect("this should work");
                    self.font_definitions
                        .font_data
                        .insert(key.to_owned(), std::borrow::Cow::Owned(data));
                }
            }
        }

        (self.style, self.font_definitions)
    }
}