Draft Emulator in iced_test

This commit is contained in:
Héctor Ramón Jiménez 2025-05-31 04:34:54 +02:00
parent 921467b5be
commit 16556b51bc
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
12 changed files with 466 additions and 47 deletions

169
test/src/emulator.rs Normal file
View file

@ -0,0 +1,169 @@
use crate::Instruction;
use crate::core;
use crate::core::mouse;
use crate::core::renderer;
use crate::core::window;
use crate::core::{Element, Size};
use crate::program::Program;
use crate::runtime::futures::futures::StreamExt;
use crate::runtime::futures::futures::channel::mpsc;
use crate::runtime::futures::{Executor, Runtime};
use crate::runtime::task;
use crate::runtime::user_interface;
use crate::runtime::{Action, UserInterface};
#[allow(missing_debug_implementations)]
pub struct Emulator<P: Program> {
state: P::State,
runtime: Runtime<P::Executor, mpsc::Sender<Event<P>>, Event<P>>,
renderer: P::Renderer,
size: Size,
window: window::Id,
cursor: mouse::Cursor,
clipboard: Clipboard,
cache: Option<user_interface::Cache>,
}
#[allow(missing_debug_implementations)]
pub enum Event<P: Program> {
Action(Action<P::Message>),
}
impl<P: Program + 'static> Emulator<P> {
pub fn new(
program: &P,
size: Size,
sender: mpsc::Sender<Event<P>>,
) -> Emulator<P> {
use renderer::Headless;
let settings = program.settings();
// TODO: Error handling
let executor = P::Executor::new().expect("Create emulator executor");
let renderer = executor
.block_on(P::Renderer::new(
settings.default_font,
settings.default_text_size,
None,
))
.expect("Create emulator renderer");
let mut runtime = Runtime::new(executor, sender);
let (state, task) = program.boot();
if let Some(stream) = task::into_stream(task) {
runtime.run(stream.map(Event::Action).boxed());
}
Self {
state,
runtime,
renderer,
size,
clipboard: Clipboard { content: None },
cursor: mouse::Cursor::Unavailable,
window: window::Id::unique(),
cache: Some(user_interface::Cache::default()),
}
}
pub fn update(&mut self, program: &P, message: P::Message) {
let task = program.update(&mut self.state, message);
if let Some(stream) = task::into_stream(task) {
self.runtime.run(stream.map(Event::Action).boxed());
}
}
pub fn perform(&mut self, program: &P, action: Action<P::Message>) {
match action {
Action::Output(message) => {
self.update(program, message);
}
Action::LoadFont { .. } => {
// TODO
}
Action::Widget(_operation) => {
// TODO
}
Action::Clipboard(action) => {
// TODO
dbg!(action);
}
Action::Window(_action) => {
// TODO
}
Action::System(action) => {
// TODO
dbg!(action);
}
Action::Exit => {
// TODO
}
}
}
pub fn run(&mut self, program: &P, instruction: Instruction) {
let mut user_interface = UserInterface::build(
program.view(&self.state, self.window),
self.size,
self.cache.take().unwrap(),
&mut self.renderer,
);
let mut messages = Vec::new();
match instruction {
Instruction::Interact(interaction) => {
let events = interaction.events();
for event in &events {
if let core::Event::Mouse(mouse::Event::CursorMoved {
position,
}) = event
{
self.cursor = mouse::Cursor::Available(*position);
}
}
let (_state, _status) = user_interface.update(
&events,
self.cursor,
&mut self.renderer,
&mut self.clipboard,
&mut messages,
);
}
}
self.cache = Some(user_interface.into_cache());
for message in messages {
self.update(program, message);
}
}
pub fn view(
&self,
program: &P,
) -> Element<'_, P::Message, P::Theme, P::Renderer> {
program.view(&self.state, self.window)
}
}
struct Clipboard {
content: Option<String>,
}
impl core::Clipboard for Clipboard {
fn read(&self, _kind: core::clipboard::Kind) -> Option<String> {
self.content.clone()
}
fn write(&mut self, _kind: core::clipboard::Kind, contents: String) {
self.content = Some(contents);
}
}

View file

@ -1,6 +1,7 @@
use crate::core::keyboard;
use crate::core::mouse;
use crate::core::{Event, Point};
use crate::simulator;
use std::fmt;
@ -145,6 +146,62 @@ impl Interaction {
(current, next) => (current, Some(next)),
}
}
pub fn events(&self) -> Vec<Event> {
let mouse_move_ =
|to| Event::Mouse(mouse::Event::CursorMoved { position: to });
let mouse_press =
|button| Event::Mouse(mouse::Event::ButtonPressed(button));
let mouse_release =
|button| Event::Mouse(mouse::Event::ButtonReleased(button));
let key_press = |key| simulator::press_key(key, None);
let key_release = |key| simulator::release_key(key);
match self {
Interaction::Mouse(mouse) => match mouse {
Mouse::Move(to) => vec![mouse_move_(*to)],
Mouse::Press {
button,
at: Some(at),
} => vec![mouse_move_(*at), mouse_press(*button)],
Mouse::Press { button, at: None } => {
vec![mouse_press(*button)]
}
Mouse::Release {
button,
at: Some(at),
} => vec![mouse_move_(*at), mouse_release(*button)],
Mouse::Release { button, at: None } => {
vec![mouse_release(*button)]
}
Mouse::Click {
button,
at: Some(at),
} => {
vec![
mouse_move_(*at),
mouse_press(*button),
mouse_release(*button),
]
}
Mouse::Click { button, at: None } => {
vec![mouse_press(*button), mouse_release(*button)]
}
},
Interaction::Keyboard(keyboard) => match keyboard {
Keyboard::Press(key) => vec![key_press(*key)],
Keyboard::Release(key) => vec![key_release(*key)],
Keyboard::Type(key) => vec![key_press(*key), key_release(*key)],
Keyboard::Typewrite(text) => {
simulator::typewrite(text).collect()
}
},
}
}
}
impl fmt::Display for Interaction {
@ -200,14 +257,6 @@ pub enum Keyboard {
Typewrite(String),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Key {
Enter,
Escape,
Tab,
Backspace,
}
impl fmt::Display for Keyboard {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
@ -227,6 +276,25 @@ impl fmt::Display for Keyboard {
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Key {
Enter,
Escape,
Tab,
Backspace,
}
impl From<Key> for keyboard::Key {
fn from(key: Key) -> Self {
match key {
Key::Enter => Self::Named(keyboard::key::Named::Enter),
Key::Escape => Self::Named(keyboard::key::Named::Escape),
Key::Tab => Self::Named(keyboard::key::Named::Tab),
Key::Backspace => Self::Named(keyboard::key::Named::Backspace),
}
}
}
mod format {
use super::*;

View file

@ -84,10 +84,12 @@
//!
//! [the classical counter interface]: https://book.iced.rs/architecture.html#dissecting-an-interface
#![allow(missing_docs)]
use iced_program as program;
use iced_renderer as renderer;
use iced_runtime as runtime;
use iced_runtime::core;
pub mod emulator;
pub mod instruction;
pub mod selector;
pub mod simulator;
@ -98,8 +100,3 @@ pub use error::Error;
pub use instruction::Instruction;
pub use selector::Selector;
pub use simulator::{Simulator, simulator};
#[derive(Debug, Clone)]
pub struct Test {
instructions: Vec<Instruction>,
}

View file

@ -483,6 +483,38 @@ pub fn click() -> impl Iterator<Item = Event> {
.into_iter()
}
pub fn press_key(
key: impl Into<keyboard::Key>,
text: Option<SmolStr>,
) -> Event {
let key = key.into();
Event::Keyboard(keyboard::Event::KeyPressed {
key: key.clone(),
modified_key: key,
physical_key: keyboard::key::Physical::Unidentified(
keyboard::key::NativeCode::Unidentified,
),
location: keyboard::Location::Standard,
modifiers: keyboard::Modifiers::default(),
text,
})
}
pub fn release_key(key: impl Into<keyboard::Key>) -> Event {
let key = key.into();
Event::Keyboard(keyboard::Event::KeyReleased {
key: key.clone(),
modified_key: key,
physical_key: keyboard::key::Physical::Unidentified(
keyboard::key::NativeCode::Unidentified,
),
location: keyboard::Location::Standard,
modifiers: keyboard::Modifiers::default(),
})
}
/// Returns the sequence of events of a "key tap" (i.e. pressing and releasing a key).
pub fn tap_key(
key: impl Into<keyboard::Key>,
@ -490,28 +522,7 @@ pub fn tap_key(
) -> impl Iterator<Item = Event> {
let key = key.into();
[
Event::Keyboard(keyboard::Event::KeyPressed {
key: key.clone(),
modified_key: key.clone(),
physical_key: keyboard::key::Physical::Unidentified(
keyboard::key::NativeCode::Unidentified,
),
location: keyboard::Location::Standard,
modifiers: keyboard::Modifiers::default(),
text,
}),
Event::Keyboard(keyboard::Event::KeyReleased {
key: key.clone(),
modified_key: key,
physical_key: keyboard::key::Physical::Unidentified(
keyboard::key::NativeCode::Unidentified,
),
location: keyboard::Location::Standard,
modifiers: keyboard::Modifiers::default(),
}),
]
.into_iter()
[press_key(key.clone(), text), release_key(key)].into_iter()
}
/// Returns the sequence of events of typewriting the given text in a keyboard.