use std::sync::Arc;
use crate::prelude::*;
pub use ::egui;
use serde::Deserialize;
pub mod widgets;
pub fn ui_plugin(_session: &mut Session) {
}
#[derive(HasSchema, Clone, Debug, Default, Deref, DerefMut)]
pub struct EguiCtx(pub egui::Context);
#[derive(HasSchema, Clone, Deref, DerefMut)]
#[schema(no_default)]
pub struct EguiInputHook(pub Arc<dyn Fn(&mut Game, &mut egui::RawInput) + Sync + Send + 'static>);
impl EguiInputHook {
pub fn new<F: Fn(&mut Game, &mut egui::RawInput) + Sync + Send + 'static>(hook: F) -> Self {
Self(Arc::new(hook))
}
}
#[derive(HasSchema, Clone, Debug, Default, Deref, DerefMut)]
pub struct EguiTextures(pub HashMap<Handle<Image>, egui::TextureId>);
impl EguiTextures {
#[track_caller]
pub fn get(&self, handle: Handle<Image>) -> egui::TextureId {
*self.0.get(&handle).unwrap()
}
}
#[derive(HasSchema, Clone)]
#[schema(no_default)]
#[type_data(asset_loader(["ttf", "otf"], FontLoader))]
pub struct Font {
pub family_name: Arc<str>,
pub data: egui::FontData,
pub monospace: bool,
}
#[derive(HasSchema, Debug, serde::Deserialize, Clone)]
#[derive_type_data(SchemaDeserialize)]
pub struct FontMeta {
#[serde(deserialize_with = "deserialize_arc_str")]
pub family: Arc<str>,
pub size: f32,
pub color: Color,
}
impl Default for FontMeta {
fn default() -> Self {
Self {
family: "".into(),
size: Default::default(),
color: Default::default(),
}
}
}
impl From<FontMeta> for egui::FontSelection {
fn from(val: FontMeta) -> Self {
egui::FontSelection::FontId(val.id())
}
}
impl FontMeta {
pub fn id(&self) -> egui::FontId {
egui::FontId::new(self.size, egui::FontFamily::Name(self.family.clone()))
}
pub fn rich(&self, t: impl Into<String>) -> egui::RichText {
egui::RichText::new(t).color(self.color).font(self.id())
}
pub fn with_color(&self, color: Color) -> Self {
Self {
family: self.family.clone(),
size: self.size,
color,
}
}
}
fn deserialize_arc_str<'de, D: serde::Deserializer<'de>>(d: D) -> Result<Arc<str>, D::Error> {
String::deserialize(d).map(|x| x.into())
}
pub struct FontLoader;
impl AssetLoader for FontLoader {
fn load(&self, _ctx: AssetLoadCtx, bytes: &[u8]) -> BoxedFuture<anyhow::Result<SchemaBox>> {
let bytes = bytes.to_vec();
Box::pin(async move {
let (family_name, monospace) = {
let face = ttf_parser::Face::parse(&bytes, 0)?;
(
face.names()
.into_iter()
.filter(|x| x.name_id == ttf_parser::name_id::FAMILY)
.find_map(|x| x.to_string())
.ok_or_else(|| {
anyhow::format_err!("Could not read font family from font file")
})?
.into(),
face.is_monospaced(),
)
};
let data = egui::FontData::from_owned(bytes.to_vec());
Ok(SchemaBox::new(Font {
family_name,
data,
monospace,
}))
})
}
}
#[derive(HasSchema, Clone, Debug)]
#[repr(C)]
pub struct EguiSettings {
pub scale: f64,
}
impl Default for EguiSettings {
fn default() -> Self {
Self { scale: 1.0 }
}
}
pub trait EguiContextExt {
fn clear_focus(self);
fn get_state<T: Clone + Default + Sync + Send + 'static>(self) -> T;
fn set_state<T: Clone + Default + Sync + Send + 'static>(self, value: T);
}
impl EguiContextExt for &egui::Context {
fn clear_focus(self) {
self.memory_mut(|r| r.request_focus(egui::Id::null()));
}
fn get_state<T: Clone + Default + Sync + Send + 'static>(self) -> T {
self.data_mut(|data| data.get_temp_mut_or_default::<T>(egui::Id::null()).clone())
}
fn set_state<T: Clone + Default + Sync + Send + 'static>(self, value: T) {
self.data_mut(|data| *data.get_temp_mut_or_default::<T>(egui::Id::null()) = value);
}
}
pub trait EguiResponseExt {
fn focus_by_default(self, ui: &mut egui::Ui) -> egui::Response;
}
impl EguiResponseExt for egui::Response {
fn focus_by_default(self, ui: &mut egui::Ui) -> egui::Response {
if ui.ctx().memory(|r| r.focus().is_none()) {
ui.ctx().memory_mut(|r| r.request_focus(self.id));
self
} else {
self
}
}
}