use super::super::render;
use super::fixture::World;
use crate::cli_outbound::Cli;
pub(super) struct Screen {
ctx: egui::Context,
lernie: Cli,
bl: Cli,
bz: Cli,
}
impl Screen {
pub(super) fn new() -> Self {
Self {
ctx: egui::Context::default(),
lernie: Cli::new("yog-absent-lernie"),
bl: Cli::new("yog-absent-bl"),
bz: Cli::new("yog-absent-bz"),
}
}
pub(super) fn with_lernie(lernie: Cli) -> Self {
Self {
lernie,
..Self::new()
}
}
pub(super) fn frame(&self, world: &mut World, events: Vec<egui::Event>) -> bool {
let _ = self.run(world, events);
self.ctx.wants_keyboard_input()
}
pub(super) fn text(&self, world: &mut World) -> String {
let out = self.run(world, Vec::new());
crate::paint_probe::text_of(&out)
}
pub(super) fn shapes(
&self,
world: &mut World,
events: Vec<egui::Event>,
) -> Vec<egui::epaint::ClippedShape> {
self.output(world, events).shapes
}
pub(super) fn output(&self, world: &mut World, events: Vec<egui::Event>) -> egui::FullOutput {
self.run(world, events)
}
fn run(&self, world: &mut World, events: Vec<egui::Event>) -> egui::FullOutput {
world.substrate(&self.lernie, &self.bl);
let mut out = self.paint(world, events);
world.drain(&mut |world| out = self.paint(world, Vec::new()));
out
}
fn paint(&self, world: &mut World, events: Vec<egui::Event>) -> egui::FullOutput {
let input = egui::RawInput {
modifiers: modifiers_of(&events),
events,
..super::input()
};
self.ctx.run(input, |ctx| {
render(
ctx,
&mut world.model,
&mut world.state,
&self.lernie,
&self.bl,
&self.bz,
);
})
}
pub(super) fn column(&self) -> f32 {
egui::containers::panel::PanelState::load(&self.ctx, egui::Id::new("conversations"))
.expect("the conversation panel stores its rect")
.rect
.right()
}
pub(super) fn focused(&self) -> Option<egui::Id> {
self.ctx.memory(egui::Memory::focused)
}
pub(super) fn reveal(&self) {
self.ctx.memory_mut(|m| m.set_everything_is_visible(true));
}
pub(super) fn response(&self, id: egui::Id) -> Option<egui::Response> {
self.ctx.read_response(id)
}
pub(super) fn tooltipped(&self, id: egui::Id) -> bool {
egui::popup::was_tooltip_open_last_frame(&self.ctx, id)
}
pub(super) fn idle(&self, world: &mut World) -> bool {
self.frame(world, Vec::new())
}
pub(super) fn release(&self, world: &mut World) {
assert!(
!self.frame(world, vec![press(egui::Key::Escape, egui::Modifiers::NONE)]),
"Escape is the release gesture: the box must let go"
);
}
}
fn modifiers_of(events: &[egui::Event]) -> egui::Modifiers {
events
.iter()
.find_map(|e| match e {
egui::Event::Key { modifiers, .. } => Some(*modifiers),
_ => None,
})
.unwrap_or(egui::Modifiers::NONE)
}
pub(super) fn command_shift() -> egui::Modifiers {
egui::Modifiers {
shift: true,
..egui::Modifiers::COMMAND
}
}
pub(super) fn click(screen: &Screen, world: &mut World, pos: egui::Pos2) {
screen.frame(world, vec![egui::Event::PointerMoved(pos)]);
for pressed in [true, false] {
screen.frame(
world,
vec![egui::Event::PointerButton {
pos,
button: egui::PointerButton::Primary,
pressed,
modifiers: egui::Modifiers::NONE,
}],
);
}
}
pub(super) fn rect_of(shapes: &[egui::epaint::ClippedShape], text: &str) -> Option<egui::Rect> {
shapes.iter().find_map(|clipped| find(&clipped.shape, text))
}
pub(super) fn locate(shapes: &[egui::epaint::ClippedShape], text: &str) -> Option<egui::Pos2> {
rect_of(shapes, text).map(|r| r.center())
}
fn find(shape: &egui::Shape, text: &str) -> Option<egui::Rect> {
let mut painted = Vec::new();
crate::paint_probe::collect(shape, &mut painted);
painted
.into_iter()
.find(|(seen, _)| seen == text)
.map(|(_, rect)| rect)
}
pub(super) fn press(key: egui::Key, modifiers: egui::Modifiers) -> egui::Event {
egui::Event::Key {
key,
physical_key: None,
pressed: true,
repeat: false,
modifiers,
}
}