1use crate::core;
3use crate::core::mouse;
4use crate::core::renderer;
5use crate::core::time::Instant;
6use crate::core::widget;
7use crate::core::window;
8use crate::core::{Bytes, Element, Point, Size};
9use crate::instruction;
10use crate::program;
11use crate::program::Program;
12use crate::runtime;
13use crate::runtime::futures::futures::StreamExt;
14use crate::runtime::futures::futures::channel::mpsc;
15use crate::runtime::futures::futures::stream;
16use crate::runtime::futures::subscription;
17use crate::runtime::futures::{Executor, Runtime};
18use crate::runtime::task;
19use crate::runtime::user_interface;
20use crate::runtime::{Task, UserInterface};
21use crate::{Instruction, Selector};
22
23use std::fmt;
24
25pub struct Emulator<P: Program> {
35 state: P::State,
36 runtime: Runtime<P::Executor, mpsc::Sender<Event<P>>, Event<P>>,
37 renderer: P::Renderer,
38 mode: Mode,
39 size: Size,
40 window: core::window::Id,
41 cursor: mouse::Cursor,
42 cache: Option<user_interface::Cache>,
43 pending_tasks: usize,
44}
45
46pub enum Event<P: Program> {
48 Action(Action<P>),
50 Failed(Instruction),
52 Ready,
54}
55
56pub struct Action<P: Program>(Action_<P>);
58
59enum Action_<P: Program> {
60 Runtime(runtime::Action<P::Message>),
61 CountDown,
62}
63
64impl<P: Program + 'static> Emulator<P> {
65 pub fn new(sender: mpsc::Sender<Event<P>>, program: &P, mode: Mode, size: Size) -> Emulator<P> {
71 Self::with_preset(sender, program, mode, size, None)
72 }
73
74 pub fn with_preset(
79 sender: mpsc::Sender<Event<P>>,
80 program: &P,
81 mode: Mode,
82 size: Size,
83 preset: Option<&program::Preset<P::State, P::Message>>,
84 ) -> Emulator<P> {
85 use renderer::Headless;
86
87 let settings = program.settings();
88
89 let executor = P::Executor::new().expect("Create emulator executor");
91
92 let renderer = executor
93 .block_on(P::Renderer::new(
94 settings.default_font,
95 settings.default_text_size,
96 None,
97 ))
98 .expect("Create emulator renderer");
99
100 let runtime = Runtime::new(executor, sender);
101
102 let (state, task) = runtime.enter(|| {
103 if let Some(preset) = preset {
104 preset.boot()
105 } else {
106 program.boot()
107 }
108 });
109
110 let mut emulator = Self {
111 state,
112 runtime,
113 renderer,
114 mode,
115 size,
116 cursor: mouse::Cursor::Unavailable,
117 window: core::window::Id::unique(),
118 cache: Some(user_interface::Cache::default()),
119 pending_tasks: 0,
120 };
121
122 emulator.resubscribe(program);
123 emulator.wait_for(task);
124
125 emulator
126 }
127
128 pub fn update(&mut self, program: &P, message: P::Message) {
134 let task = self
135 .runtime
136 .enter(|| program.update(&mut self.state, message));
137
138 self.resubscribe(program);
139
140 match self.mode {
141 Mode::Zen if self.pending_tasks > 0 => self.wait_for(task),
142 _ => {
143 if let Some(stream) = task::into_stream(task) {
144 self.runtime.run(
145 stream
146 .map(Action_::Runtime)
147 .map(Action)
148 .map(Event::Action)
149 .boxed(),
150 );
151 }
152 }
153 }
154 }
155
156 pub fn perform(&mut self, program: &P, action: Action<P>) {
161 match action.0 {
162 Action_::CountDown => {
163 if self.pending_tasks > 0 {
164 self.pending_tasks -= 1;
165
166 if self.pending_tasks == 0 {
167 self.runtime.send(Event::Ready);
168 }
169 }
170 }
171 Action_::Runtime(action) => match action {
172 runtime::Action::Output(message) => {
173 self.update(program, message);
174 }
175 runtime::Action::LoadFont { .. } => {
176 }
178 runtime::Action::Widget(operation) => {
179 let mut user_interface = UserInterface::build(
180 program.view(&self.state, self.window),
181 self.size,
182 self.cache.take().unwrap(),
183 &mut self.renderer,
184 );
185
186 let mut operation = Some(operation);
187
188 while let Some(mut current) = operation.take() {
189 user_interface.operate(&self.renderer, &mut current);
190
191 match current.finish() {
192 widget::operation::Outcome::None => {}
193 widget::operation::Outcome::Some(()) => {}
194 widget::operation::Outcome::Chain(next) => {
195 operation = Some(next);
196 }
197 }
198 }
199
200 self.cache = Some(user_interface.into_cache());
201 }
202 runtime::Action::Clipboard(action) => {
203 dbg!(action);
205 }
206 runtime::Action::Window(action) => {
207 use crate::runtime::window;
208
209 match action {
210 window::Action::Open(id, _settings, sender) => {
211 self.window = id;
212
213 let _ = sender.send(self.window);
214 }
215 window::Action::GetOldest(sender) | window::Action::GetLatest(sender) => {
216 let _ = sender.send(Some(self.window));
217 }
218 window::Action::GetSize(id, sender) => {
219 if id == self.window {
220 let _ = sender.send(self.size);
221 }
222 }
223 window::Action::GetMaximized(id, sender) => {
224 if id == self.window {
225 let _ = sender.send(false);
226 }
227 }
228 window::Action::GetMinimized(id, sender) => {
229 if id == self.window {
230 let _ = sender.send(None);
231 }
232 }
233 window::Action::GetPosition(id, sender) => {
234 if id == self.window {
235 let _ = sender.send(Some(Point::ORIGIN));
236 }
237 }
238 window::Action::GetScaleFactor(id, sender) => {
239 if id == self.window {
240 let _ = sender.send(1.0);
241 }
242 }
243 window::Action::GetMode(id, sender) => {
244 if id == self.window {
245 let _ = sender.send(core::window::Mode::Windowed);
246 }
247 }
248 _ => {
249 }
251 }
252 }
253 runtime::Action::System(action) => {
254 dbg!(action);
256 }
257 runtime::Action::Image(action) => {
258 dbg!(action);
260 }
261 iced_runtime::Action::Event { window, event } => {
262 dbg!(window, event);
264 }
265 runtime::Action::Tick => {
266 }
268 runtime::Action::Exit => {
269 }
271 runtime::Action::Reload => {
272 }
274 },
275 }
276 }
277
278 pub fn run(&mut self, program: &P, instruction: &Instruction) {
285 let mut user_interface = UserInterface::build(
286 program.view(&self.state, self.window),
287 self.size,
288 self.cache.take().unwrap(),
289 &mut self.renderer,
290 );
291
292 let mut messages = Vec::new();
293
294 match instruction {
295 Instruction::Interact(interaction) => {
296 let Some(events) = interaction.events(|target| match target {
297 instruction::Target::Id(id) => {
298 use widget::Operation;
299
300 let mut operation = Selector::find(widget::Id::from(id.to_owned()));
301
302 user_interface.operate(
303 &self.renderer,
304 &mut widget::operation::black_box(&mut operation),
305 );
306
307 match operation.finish() {
308 widget::operation::Outcome::Some(widget) => {
309 Some(widget?.visible_bounds()?.center())
310 }
311 _ => None,
312 }
313 }
314 instruction::Target::Text(text) => {
315 use widget::Operation;
316
317 let mut operation = Selector::find(text.as_str());
318
319 user_interface.operate(
320 &self.renderer,
321 &mut widget::operation::black_box(&mut operation),
322 );
323
324 match operation.finish() {
325 widget::operation::Outcome::Some(text) => {
326 Some(text?.visible_bounds()?.center())
327 }
328 _ => None,
329 }
330 }
331 instruction::Target::Point(position) => Some(*position),
332 }) else {
333 self.runtime.send(Event::Failed(instruction.clone()));
334 self.cache = Some(user_interface.into_cache());
335 return;
336 };
337
338 for event in &events {
339 if let core::Event::Mouse(mouse::Event::CursorMoved { position }) = event {
340 self.cursor = mouse::Cursor::Available(*position);
341 }
342 }
343
344 let (_state, _status) =
345 user_interface.update(&events, self.cursor, &mut self.renderer, &mut messages);
346
347 self.cache = Some(user_interface.into_cache());
348
349 let task = self.runtime.enter(|| {
350 Task::batch(
351 messages
352 .into_iter()
353 .map(|message| program.update(&mut self.state, message)),
354 )
355 });
356
357 self.resubscribe(program);
358 self.wait_for(task);
359 }
360 Instruction::Expect(expectation) => match expectation {
361 instruction::Expectation::Text(text) => {
362 use widget::Operation;
363
364 let mut operation = Selector::find(text.as_str());
365
366 user_interface.operate(
367 &self.renderer,
368 &mut widget::operation::black_box(&mut operation),
369 );
370
371 match operation.finish() {
372 widget::operation::Outcome::Some(Some(_text)) => {
373 self.runtime.send(Event::Ready);
374 }
375 _ => {
376 self.runtime.send(Event::Failed(instruction.clone()));
377 }
378 }
379
380 self.cache = Some(user_interface.into_cache());
381 }
382 },
383 }
384 }
385
386 fn wait_for(&mut self, task: Task<P::Message>) {
387 if let Some(stream) = task::into_stream(task) {
388 match self.mode {
389 Mode::Zen => {
390 self.pending_tasks += 1;
391
392 self.runtime.run(
393 stream
394 .map(Action_::Runtime)
395 .map(Action)
396 .map(Event::Action)
397 .chain(stream::once(async {
398 Event::Action(Action(Action_::CountDown))
399 }))
400 .boxed(),
401 );
402 }
403 Mode::Patient => {
404 self.runtime.run(
405 stream
406 .map(Action_::Runtime)
407 .map(Action)
408 .map(Event::Action)
409 .chain(stream::once(async { Event::Ready }))
410 .boxed(),
411 );
412 }
413 Mode::Immediate => {
414 self.runtime.run(
415 stream
416 .map(Action_::Runtime)
417 .map(Action)
418 .map(Event::Action)
419 .boxed(),
420 );
421 self.runtime.send(Event::Ready);
422 }
423 }
424 } else if self.pending_tasks == 0 {
425 self.runtime.send(Event::Ready);
426 }
427 }
428
429 fn resubscribe(&mut self, program: &P) {
430 self.runtime
431 .track(subscription::into_recipes(self.runtime.enter(|| {
432 program.subscription(&self.state).map(|message| {
433 Event::Action(Action(Action_::Runtime(runtime::Action::Output(message))))
434 })
435 })));
436 }
437
438 pub fn view(&self, program: &P) -> Element<'_, P::Message, P::Theme, P::Renderer> {
440 program.view(&self.state, self.window)
441 }
442
443 pub fn theme(&self, program: &P) -> Option<P::Theme> {
445 program.theme(&self.state, self.window)
446 }
447
448 pub fn screenshot(
450 &mut self,
451 program: &P,
452 theme: &P::Theme,
453 scale_factor: f32,
454 ) -> window::Screenshot {
455 use core::renderer::Headless;
456
457 let style = program.style(&self.state, theme);
458
459 let mut user_interface = UserInterface::build(
460 program.view(&self.state, self.window),
461 self.size,
462 self.cache.take().unwrap(),
463 &mut self.renderer,
464 );
465
466 let _ = user_interface.update(
468 &[core::Event::Window(window::Event::RedrawRequested(
469 Instant::now(),
470 ))],
471 mouse::Cursor::Unavailable,
472 &mut self.renderer,
473 &mut Vec::new(),
474 );
475
476 user_interface.draw(
477 &mut self.renderer,
478 theme,
479 &renderer::Style {
480 text_color: style.text_color,
481 },
482 mouse::Cursor::Unavailable,
483 );
484
485 let physical_size = Size::new(
486 (self.size.width * scale_factor).round() as u32,
487 (self.size.height * scale_factor).round() as u32,
488 );
489
490 let rgba = self
491 .renderer
492 .screenshot(physical_size, scale_factor, style.background_color);
493
494 window::Screenshot {
495 rgba: Bytes::from(rgba),
496 size: physical_size,
497 scale_factor,
498 }
499 }
500
501 pub fn into_state(self) -> (P::State, core::window::Id) {
503 (self.state, self.window)
504 }
505}
506
507#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
512pub enum Mode {
513 #[default]
518 Zen,
519 Patient,
521 Immediate,
523}
524
525impl Mode {
526 pub const ALL: &[Self] = &[Self::Zen, Self::Patient, Self::Immediate];
528}
529
530impl fmt::Display for Mode {
531 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
532 f.write_str(match self {
533 Self::Zen => "Zen",
534 Self::Patient => "Patient",
535 Self::Immediate => "Immediate",
536 })
537 }
538}