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(renderer::Settings::from(&settings), None))
94 .expect("Create emulator renderer");
95
96 let runtime = Runtime::new(executor, sender);
97
98 let (state, task) = runtime.enter(|| {
99 if let Some(preset) = preset {
100 preset.boot()
101 } else {
102 program.boot()
103 }
104 });
105
106 let mut emulator = Self {
107 state,
108 runtime,
109 renderer,
110 mode,
111 size,
112 cursor: mouse::Cursor::Unavailable,
113 window: core::window::Id::unique(),
114 cache: Some(user_interface::Cache::default()),
115 pending_tasks: 0,
116 };
117
118 emulator.resubscribe(program);
119 emulator.wait_for(task);
120
121 emulator
122 }
123
124 pub fn update(&mut self, program: &P, message: P::Message) {
130 let task = self
131 .runtime
132 .enter(|| program.update(&mut self.state, message));
133
134 self.resubscribe(program);
135
136 match self.mode {
137 Mode::Zen if self.pending_tasks > 0 => self.wait_for(task),
138 _ => {
139 if let Some(stream) = task::into_stream(task) {
140 self.runtime.run(
141 stream
142 .map(Action_::Runtime)
143 .map(Action)
144 .map(Event::Action)
145 .boxed(),
146 );
147 }
148 }
149 }
150 }
151
152 pub fn perform(&mut self, program: &P, action: Action<P>) {
157 match action.0 {
158 Action_::CountDown => {
159 if self.pending_tasks > 0 {
160 self.pending_tasks -= 1;
161
162 if self.pending_tasks == 0 {
163 self.runtime.send(Event::Ready);
164 }
165 }
166 }
167 Action_::Runtime(action) => match action {
168 runtime::Action::Output(message) => {
169 self.update(program, message);
170 }
171 runtime::Action::Widget(operation) => {
172 let mut user_interface = UserInterface::build(
173 program.view(&self.state, self.window),
174 self.size,
175 self.cache.take().unwrap(),
176 &mut self.renderer,
177 );
178
179 let mut operation = Some(operation);
180
181 while let Some(mut current) = operation.take() {
182 user_interface.operate(&self.renderer, &mut current);
183
184 match current.finish() {
185 widget::operation::Outcome::None => {}
186 widget::operation::Outcome::Some(()) => {}
187 widget::operation::Outcome::Chain(next) => {
188 operation = Some(next);
189 }
190 }
191 }
192
193 self.cache = Some(user_interface.into_cache());
194 }
195 runtime::Action::Clipboard(action) => {
196 dbg!(action);
198 }
199 runtime::Action::Window(action) => {
200 use crate::runtime::window;
201
202 match action {
203 window::Action::Open(id, _settings, sender) => {
204 self.window = id;
205
206 let _ = sender.send(self.window);
207 }
208 window::Action::GetOldest(sender) | window::Action::GetLatest(sender) => {
209 let _ = sender.send(Some(self.window));
210 }
211 window::Action::GetSize(id, sender) => {
212 if id == self.window {
213 let _ = sender.send(self.size);
214 }
215 }
216 window::Action::GetMaximized(id, sender) => {
217 if id == self.window {
218 let _ = sender.send(false);
219 }
220 }
221 window::Action::GetMinimized(id, sender) => {
222 if id == self.window {
223 let _ = sender.send(None);
224 }
225 }
226 window::Action::GetPosition(id, sender) => {
227 if id == self.window {
228 let _ = sender.send(Some(Point::ORIGIN));
229 }
230 }
231 window::Action::GetScaleFactor(id, sender) => {
232 if id == self.window {
233 let _ = sender.send(1.0);
234 }
235 }
236 window::Action::GetMode(id, sender) => {
237 if id == self.window {
238 let _ = sender.send(core::window::Mode::Windowed);
239 }
240 }
241 _ => {
242 }
244 }
245 }
246 runtime::Action::System(action) => {
247 dbg!(action);
249 }
250 runtime::Action::Font(action) => {
251 dbg!(action);
253 }
254 runtime::Action::Image(action) => {
255 dbg!(action);
257 }
258 iced_runtime::Action::Event { window, event } => {
259 dbg!(window, event);
261 }
262 runtime::Action::Tick => {
263 }
265 runtime::Action::Exit => {
266 }
268 runtime::Action::Reload => {
269 }
271 runtime::Action::Announce(_) => {}
272 },
273 }
274 }
275
276 pub fn run(&mut self, program: &P, instruction: &Instruction) {
283 let mut user_interface = UserInterface::build(
284 program.view(&self.state, self.window),
285 self.size,
286 self.cache.take().unwrap(),
287 &mut self.renderer,
288 );
289
290 let mut messages = Vec::new();
291
292 match instruction {
293 Instruction::Interact(interaction) => {
294 let Some(events) = interaction.events(|target| match target {
295 instruction::Target::Id(id) => {
296 use widget::Operation;
297
298 let mut operation = Selector::find(widget::Id::from(id.to_owned()));
299
300 user_interface.operate(
301 &self.renderer,
302 &mut widget::operation::black_box(&mut operation),
303 );
304
305 match operation.finish() {
306 widget::operation::Outcome::Some(widget) => {
307 Some(widget?.visible_bounds()?.center())
308 }
309 _ => None,
310 }
311 }
312 instruction::Target::Text(text) => {
313 use widget::Operation;
314
315 let mut operation = Selector::find(text.as_str());
316
317 user_interface.operate(
318 &self.renderer,
319 &mut widget::operation::black_box(&mut operation),
320 );
321
322 match operation.finish() {
323 widget::operation::Outcome::Some(text) => {
324 Some(text?.visible_bounds()?.center())
325 }
326 _ => None,
327 }
328 }
329 instruction::Target::Point(position) => Some(*position),
330 }) else {
331 self.runtime.send(Event::Failed(instruction.clone()));
332 self.cache = Some(user_interface.into_cache());
333 return;
334 };
335
336 for event in &events {
337 if let core::Event::Mouse(mouse::Event::CursorMoved { position }) = event {
338 self.cursor = mouse::Cursor::Available(*position);
339 }
340 }
341
342 let (_state, statuses) =
343 user_interface.update(&events, self.cursor, &mut self.renderer, &mut messages);
344
345 for (event, status) in events.iter().zip(statuses.iter().copied()) {
346 let _ = runtime::keyboard::handle_ctrl_tab(
347 event,
348 &mut user_interface,
349 &self.renderer,
350 ) || runtime::keyboard::handle_tab(
351 event,
352 status,
353 &mut user_interface,
354 &self.renderer,
355 );
356 }
357
358 self.cache = Some(user_interface.into_cache());
359
360 let task = self.runtime.enter(|| {
361 Task::batch(
362 messages
363 .into_iter()
364 .map(|message| program.update(&mut self.state, message)),
365 )
366 });
367
368 self.resubscribe(program);
369 self.wait_for(task);
370 }
371 Instruction::Expect(expectation) => match expectation {
372 instruction::Expectation::Text(text) => {
373 use widget::Operation;
374
375 let mut operation = Selector::find(text.as_str());
376
377 user_interface.operate(
378 &self.renderer,
379 &mut widget::operation::black_box(&mut operation),
380 );
381
382 match operation.finish() {
383 widget::operation::Outcome::Some(Some(_text)) => {
384 self.runtime.send(Event::Ready);
385 }
386 _ => {
387 self.runtime.send(Event::Failed(instruction.clone()));
388 }
389 }
390
391 self.cache = Some(user_interface.into_cache());
392 }
393 instruction::Expectation::Focused(target) => {
394 use crate::selector;
395 use widget::Operation;
396
397 let mut operation = selector::is_focused().find();
398
399 user_interface.operate(
400 &self.renderer,
401 &mut widget::operation::black_box(&mut operation),
402 );
403
404 let focused_id = match operation.finish() {
405 widget::operation::Outcome::Some(Some(focused)) => match focused {
406 selector::Target::Focusable { id, .. }
407 | selector::Target::TextInput { id, .. }
408 | selector::Target::Container { id, .. }
409 | selector::Target::Scrollable { id, .. }
410 | selector::Target::Text { id, .. }
411 | selector::Target::Custom { id, .. }
412 | selector::Target::Accessible { id, .. } => id,
413 },
414 _ => None,
415 };
416
417 let found = match &target {
418 instruction::Target::Id(id) => focused_id
419 .as_ref()
420 .is_some_and(|fid| *fid == widget::Id::from(id.to_owned())),
421 instruction::Target::Text(_) => {
422 false
425 }
426 instruction::Target::Point(_) => false,
427 };
428
429 if found {
430 self.runtime.send(Event::Ready);
431 } else {
432 self.runtime.send(Event::Failed(instruction.clone()));
433 }
434
435 self.cache = Some(user_interface.into_cache());
436 }
437 },
438 }
439 }
440
441 fn wait_for(&mut self, task: Task<P::Message>) {
442 if let Some(stream) = task::into_stream(task) {
443 match self.mode {
444 Mode::Zen => {
445 self.pending_tasks += 1;
446
447 self.runtime.run(
448 stream
449 .map(Action_::Runtime)
450 .map(Action)
451 .map(Event::Action)
452 .chain(stream::once(async {
453 Event::Action(Action(Action_::CountDown))
454 }))
455 .boxed(),
456 );
457 }
458 Mode::Patient => {
459 self.runtime.run(
460 stream
461 .map(Action_::Runtime)
462 .map(Action)
463 .map(Event::Action)
464 .chain(stream::once(async { Event::Ready }))
465 .boxed(),
466 );
467 }
468 Mode::Immediate => {
469 self.runtime.run(
470 stream
471 .map(Action_::Runtime)
472 .map(Action)
473 .map(Event::Action)
474 .boxed(),
475 );
476 self.runtime.send(Event::Ready);
477 }
478 }
479 } else if self.pending_tasks == 0 {
480 self.runtime.send(Event::Ready);
481 }
482 }
483
484 fn resubscribe(&mut self, program: &P) {
485 self.runtime
486 .track(subscription::into_recipes(self.runtime.enter(|| {
487 program.subscription(&self.state).map(|message| {
488 Event::Action(Action(Action_::Runtime(runtime::Action::Output(message))))
489 })
490 })));
491 }
492
493 pub fn view(&self, program: &P) -> Element<'_, P::Message, P::Theme, P::Renderer> {
495 program.view(&self.state, self.window)
496 }
497
498 pub fn theme(&self, program: &P) -> Option<P::Theme> {
500 program.theme(&self.state, self.window)
501 }
502
503 pub fn screenshot(
505 &mut self,
506 program: &P,
507 theme: &P::Theme,
508 scale_factor: f32,
509 ) -> window::Screenshot {
510 use core::renderer::Headless;
511
512 let style = program.style(&self.state, theme);
513
514 let mut user_interface = UserInterface::build(
515 program.view(&self.state, self.window),
516 self.size,
517 self.cache.take().unwrap(),
518 &mut self.renderer,
519 );
520
521 let _ = user_interface.update(
523 &[core::Event::Window(window::Event::RedrawRequested(
524 Instant::now(),
525 ))],
526 mouse::Cursor::Unavailable,
527 &mut self.renderer,
528 &mut Vec::new(),
529 );
530
531 user_interface.draw(
532 &mut self.renderer,
533 theme,
534 &renderer::Style {
535 text_color: style.text_color,
536 },
537 mouse::Cursor::Unavailable,
538 );
539
540 let physical_size = Size::new(
541 (self.size.width * scale_factor).round() as u32,
542 (self.size.height * scale_factor).round() as u32,
543 );
544
545 let rgba = self
546 .renderer
547 .screenshot(physical_size, scale_factor, style.background_color);
548
549 window::Screenshot {
550 rgba: Bytes::from(rgba),
551 size: physical_size,
552 scale_factor,
553 }
554 }
555
556 pub fn into_state(self) -> (P::State, core::window::Id) {
558 (self.state, self.window)
559 }
560}
561
562#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
567pub enum Mode {
568 #[default]
573 Zen,
574 Patient,
576 Immediate,
578}
579
580impl Mode {
581 pub const ALL: &[Self] = &[Self::Zen, Self::Patient, Self::Immediate];
583}
584
585impl fmt::Display for Mode {
586 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
587 f.write_str(match self {
588 Self::Zen => "Zen",
589 Self::Patient => "Patient",
590 Self::Immediate => "Immediate",
591 })
592 }
593}