#![deny(warnings)]
#![warn(clippy::missing_docs_in_private_items)]
pub use crate::snapshot::ContextSnapshotDeltas;
use crate::snapshot::*;
pub use egui;
use egui::*;
use serde::*;
use std::ops::*;
use std::sync::*;
use wings::*;
mod private_hack;
mod snapshot;
static CONTEXT: OnceLock<Context> = OnceLock::new();
#[system_trait(host)]
pub trait Egui: 'static {
#[doc(hidden)]
fn begin_context_edit(&self, deltas: ContextSnapshotDeltas) -> CreateContextSnapshot;
#[doc(hidden)]
fn end_context_edit(&self, state: CreateContextSnapshot);
}
impl dyn Egui {
pub fn context(&'_ self) -> EguiHandle<'_> {
let mut initialized = false;
let context = CONTEXT.get_or_init(|| {
let result = Context::default();
result.begin_pass(RawInput::default());
initialized = true;
result
});
let deltas = if initialized {
ContextSnapshotDeltas::default()
} else {
ContextSnapshotDeltas::from_context(context)
};
self.begin_context_edit(deltas).apply(context);
let initial_deltas = ContextSnapshotDeltas::from_context(context);
EguiHandle {
ctx: self,
initial_deltas,
}
}
}
pub struct EguiHandle<'a> {
ctx: &'a dyn Egui,
initial_deltas: ContextSnapshotDeltas,
}
impl<'a> Deref for EguiHandle<'a> {
type Target = Context;
fn deref(&self) -> &Self::Target {
CONTEXT.get().expect("Failed to get egui context.")
}
}
impl<'a> Drop for EguiHandle<'a> {
fn drop(&mut self) {
self.ctx
.end_context_edit(CreateContextSnapshot::FromContext(
self.clone(),
self.initial_deltas,
));
}
}
#[doc(hidden)]
pub enum CreateContextSnapshot {
Created(ContextSnapshot),
FromContext(Context, ContextSnapshotDeltas),
}
impl CreateContextSnapshot {
pub fn apply(self, context: &Context) {
let Self::Created(value) = self else {
panic!("Snapshot was not `Created` variant.")
};
let exposed = private_hack::Context::from_context(context);
let mut ctx = exposed.0.write();
let frame_nr = ctx
.viewports
.get(&ctx.last_viewport)
.map(|x| x.repaint.cumulative_frame_nr)
.unwrap_or(u64::MAX);
let new_frame = frame_nr != value.deltas.frame_count;
if let Some(style) = value.style {
match ctx.memory.options.theme() {
private_hack::Theme::Dark => {
ctx.memory.options.dark_style = style;
}
private_hack::Theme::Light => {
ctx.memory.options.light_style = style;
}
}
}
Self::apply_memory_snapshot(&mut ctx, value.memory);
Self::apply_options_snapshot(&mut ctx, &value.options);
ctx.new_zoom_factor = value.new_zoom_factor;
ctx.last_viewport = value.last_viewport;
Self::apply_viewport_snapshots(&mut ctx, &value.deltas, value.viewports);
ctx.memory.data.insert_temp(Id::NULL, value.deltas);
let last_style = LastStyle(ctx.memory.options.style().clone());
ctx.memory.data.insert_temp(Id::NULL, last_style);
if let Some(font_definitions) = value.font_definitions {
let to_insert =
std::mem::replace(&mut ctx.memory.new_font_definitions, Some(font_definitions));
Self::update_fonts_mut(&mut ctx);
ctx.memory.new_font_definitions = to_insert;
} else if new_frame {
let to_insert = std::mem::take(&mut ctx.memory.new_font_definitions);
Self::update_fonts_mut(&mut ctx);
ctx.memory.new_font_definitions = to_insert;
}
}
fn apply_memory_snapshot(ctx: &mut private_hack::ContextImpl, snapshot: MemorySnapshot) {
ctx.memory.data.insert_temp(
Id::new(ViewportId::ROOT),
egui::text_selection::LabelSelectionState::from(snapshot.label_selection_state),
);
ctx.memory.new_font_definitions = snapshot.new_font_definitions;
ctx.memory.add_fonts = snapshot.add_fonts;
ctx.memory.viewport_id = snapshot.viewport_id;
ctx.memory.popups = snapshot.popups;
ctx.memory.everything_is_visible = snapshot.everything_is_visible;
ctx.memory.to_global = snapshot.to_global;
ctx.memory.areas = snapshot.areas;
ctx.memory.interactions = snapshot.interactions;
ctx.memory.focus = snapshot.focus;
}
fn apply_options_snapshot(ctx: &mut private_hack::ContextImpl, snapshot: &OptionsSnapshot) {
ctx.memory.options.theme_preference = snapshot.theme_preference;
ctx.memory.options.fallback_theme = snapshot.fallback_theme;
ctx.memory.options.zoom_factor = snapshot.zoom_factor;
ctx.memory.options.zoom_with_keyboard = snapshot.zoom_with_keyboard;
ctx.memory.options.tessellation_options = snapshot.tessellation_options;
ctx.memory.options.repaint_on_widget_change = snapshot.repaint_on_widget_change;
ctx.memory.options.max_passes = snapshot.max_passes;
ctx.memory.options.screen_reader = snapshot.screen_reader;
ctx.memory.options.warn_on_id_clash = snapshot.warn_on_id_clash;
ctx.memory.options.input_options = snapshot.input_options;
ctx.memory.options.reduce_texture_memory = snapshot.reduce_texture_memory;
}
fn apply_viewport_snapshots(
ctx: &mut private_hack::ContextImpl,
deltas: &ContextSnapshotDeltas,
snapshots: ViewportIdMap<ViewportStateSnapshot>,
) {
ctx.viewports.retain(|x, _| snapshots.contains_key(x));
for (id, snapshot) in snapshots {
let viewport = ctx.viewports.get_mut(&id).expect("Failed to get viewport.");
viewport.class = snapshot.class;
viewport.builder = snapshot.builder;
viewport.input = snapshot.input;
viewport.this_pass = snapshot.this_pass;
viewport.prev_pass = snapshot.prev_pass;
viewport.used = snapshot.used;
viewport.hits = snapshot.hits;
viewport.interact_widgets = snapshot.interact_widgets;
viewport.repaint.cumulative_pass_nr = deltas.pass_count;
viewport.repaint.cumulative_frame_nr = deltas.frame_count;
viewport.graphics = snapshot.graphics;
viewport.output = snapshot.output;
viewport.commands = snapshot.commands;
viewport.num_multipass_in_row = snapshot.num_multipass_in_row;
}
Self::reinitialize_galleys(ctx)
}
fn reinitialize_galleys(ctx: &mut private_hack::ContextImpl) {
let pixels_per_point = ctx
.viewports
.get(&ctx.last_viewport)
.map(|x| x.input.pixels_per_point)
.unwrap_or(1.0);
if let Some(ref mut fonts) = &mut ctx.fonts {
for viewport in ctx.viewports.values_mut() {
for paint_lists in viewport.graphics.as_inner_mut() {
for paint_list in paint_lists.values_mut() {
for clipped_shape in paint_list.as_inner_mut() {
Self::reinitialize_galleys_for_shape(&mut clipped_shape.shape, fonts, pixels_per_point);
}
}
}
}
}
}
fn reinitialize_galleys_for_shape(shape: &mut Shape, fonts: &mut egui::epaint::Fonts, pixels_per_point: f32) {
match shape {
Shape::Vec(x) => {
for shape in x {
Self::reinitialize_galleys_for_shape(shape, fonts, pixels_per_point);
}
}
Shape::Text(x) =>{
x.galley = fonts.with_pixels_per_point(pixels_per_point).layout_job((*x.galley.job).clone());
},
_ => {}
}
}
fn update_fonts_mut(ctx: &mut private_hack::ContextImpl) {
if let Some(viewport) = ctx.viewports.get(&ctx.last_viewport) {
let input = &viewport.input;
let max_texture_side = input.max_texture_side;
if let Some(font_definitions) = ctx.memory.new_font_definitions.take() {
ctx.fonts = None;
ctx.font_definitions = font_definitions;
}
let text_alpha_from_coverage = ctx.memory.options.style().visuals.text_alpha_from_coverage;
let fonts = ctx.fonts.get_or_insert_with(|| {
text::Fonts::new(
max_texture_side,
text_alpha_from_coverage,
ctx.font_definitions.clone(),
)
});
{
fonts.begin_pass(max_texture_side, text_alpha_from_coverage);
}
}
}
}
impl Serialize for CreateContextSnapshot {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self {
CreateContextSnapshot::FromContext(context, deltas) => {
let current_deltas = ContextSnapshotDeltas::from_context(context);
let exposed = private_hack::Context::from_context(context);
let ctx = exposed.0.read();
let style = (deltas.style_count != current_deltas.style_count)
.then(|| ctx.memory.options.style().clone());
let font_definitions = (deltas.font_definitions_count
!= current_deltas.font_definitions_count)
.then_some(&ctx.font_definitions);
let borrow = ContextShapshotBorrow {
deltas: ¤t_deltas,
font_definitions,
memory: &ctx.memory,
style,
new_zoom_factor: &ctx.new_zoom_factor,
last_viewport: &ctx.last_viewport,
viewports: &ctx.viewports,
};
<ContextShapshotBorrow as Serialize>::serialize(&borrow, serializer)
}
CreateContextSnapshot::Created(_) => Err(serde::ser::Error::custom(
"Cannot serialize created snapshot",
)),
}
}
}
impl<'de> Deserialize<'de> for CreateContextSnapshot {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
Ok(Self::Created(
<ContextSnapshot as Deserialize>::deserialize(deserializer)?,
))
}
}
#[derive(Clone)]
struct LastStyle(Arc<private_hack::Style>);