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