Draft Emulator in iced_test
This commit is contained in:
parent
921467b5be
commit
16556b51bc
12 changed files with 466 additions and 47 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -2654,6 +2654,7 @@ dependencies = [
|
||||||
name = "iced_test"
|
name = "iced_test"
|
||||||
version = "0.14.0-dev"
|
version = "0.14.0-dev"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"iced_program",
|
||||||
"iced_renderer",
|
"iced_renderer",
|
||||||
"iced_runtime",
|
"iced_runtime",
|
||||||
"nom 8.0.0",
|
"nom 8.0.0",
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,10 @@ where
|
||||||
P::name()
|
P::name()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn settings(&self) -> core::Settings {
|
||||||
|
self.program.settings()
|
||||||
|
}
|
||||||
|
|
||||||
fn boot(&self) -> (Self::State, Task<Self::Message>) {
|
fn boot(&self) -> (Self::State, Task<Self::Message>) {
|
||||||
let (state, boot) = self.program.boot();
|
let (state, boot) = self.program.boot();
|
||||||
let (devtools, task) = DevTools::new(state);
|
let (devtools, task) = DevTools::new(state);
|
||||||
|
|
|
||||||
|
|
@ -68,8 +68,7 @@ where
|
||||||
mouse::Event::ButtonPressed(_)
|
mouse::Event::ButtonPressed(_)
|
||||||
| mouse::Event::ButtonReleased(_)
|
| mouse::Event::ButtonReleased(_)
|
||||||
| mouse::Event::WheelScrolled { .. } => {
|
| mouse::Event::WheelScrolled { .. } => {
|
||||||
shell
|
shell.publish(on_event(Event::Mouse(*event)));
|
||||||
.publish(on_event(Event::Mouse(event.clone())));
|
|
||||||
}
|
}
|
||||||
mouse::Event::CursorMoved { position } => {
|
mouse::Event::CursorMoved { position } => {
|
||||||
shell.publish(on_event(Event::Mouse(
|
shell.publish(on_event(Event::Mouse(
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,11 @@ pub use iced_runtime as runtime;
|
||||||
pub use iced_runtime::core;
|
pub use iced_runtime::core;
|
||||||
pub use iced_runtime::futures;
|
pub use iced_runtime::futures;
|
||||||
|
|
||||||
|
use crate::core::renderer;
|
||||||
use crate::core::text;
|
use crate::core::text;
|
||||||
use crate::core::theme;
|
use crate::core::theme;
|
||||||
use crate::core::window;
|
use crate::core::window;
|
||||||
use crate::core::{Element, Font};
|
use crate::core::{Element, Font, Settings};
|
||||||
use crate::futures::{Executor, Subscription};
|
use crate::futures::{Executor, Subscription};
|
||||||
use crate::graphics::compositor;
|
use crate::graphics::compositor;
|
||||||
use crate::runtime::Task;
|
use crate::runtime::Task;
|
||||||
|
|
@ -36,6 +37,8 @@ pub trait Program: Sized {
|
||||||
/// Returns the unique name of the [`Program`].
|
/// Returns the unique name of the [`Program`].
|
||||||
fn name() -> &'static str;
|
fn name() -> &'static str;
|
||||||
|
|
||||||
|
fn settings(&self) -> Settings;
|
||||||
|
|
||||||
fn boot(&self) -> (Self::State, Task<Self::Message>);
|
fn boot(&self) -> (Self::State, Task<Self::Message>);
|
||||||
|
|
||||||
fn update(
|
fn update(
|
||||||
|
|
@ -128,6 +131,10 @@ pub fn with_title<P: Program>(
|
||||||
P::name()
|
P::name()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn settings(&self) -> Settings {
|
||||||
|
self.program.settings()
|
||||||
|
}
|
||||||
|
|
||||||
fn boot(&self) -> (Self::State, Task<Self::Message>) {
|
fn boot(&self) -> (Self::State, Task<Self::Message>) {
|
||||||
self.program.boot()
|
self.program.boot()
|
||||||
}
|
}
|
||||||
|
|
@ -210,6 +217,10 @@ pub fn with_subscription<P: Program>(
|
||||||
P::name()
|
P::name()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn settings(&self) -> Settings {
|
||||||
|
self.program.settings()
|
||||||
|
}
|
||||||
|
|
||||||
fn boot(&self) -> (Self::State, Task<Self::Message>) {
|
fn boot(&self) -> (Self::State, Task<Self::Message>) {
|
||||||
self.program.boot()
|
self.program.boot()
|
||||||
}
|
}
|
||||||
|
|
@ -293,6 +304,10 @@ pub fn with_theme<P: Program>(
|
||||||
P::name()
|
P::name()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn settings(&self) -> Settings {
|
||||||
|
self.program.settings()
|
||||||
|
}
|
||||||
|
|
||||||
fn boot(&self) -> (Self::State, Task<Self::Message>) {
|
fn boot(&self) -> (Self::State, Task<Self::Message>) {
|
||||||
self.program.boot()
|
self.program.boot()
|
||||||
}
|
}
|
||||||
|
|
@ -372,6 +387,10 @@ pub fn with_style<P: Program>(
|
||||||
P::name()
|
P::name()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn settings(&self) -> Settings {
|
||||||
|
self.program.settings()
|
||||||
|
}
|
||||||
|
|
||||||
fn boot(&self) -> (Self::State, Task<Self::Message>) {
|
fn boot(&self) -> (Self::State, Task<Self::Message>) {
|
||||||
self.program.boot()
|
self.program.boot()
|
||||||
}
|
}
|
||||||
|
|
@ -447,6 +466,10 @@ pub fn with_scale_factor<P: Program>(
|
||||||
P::name()
|
P::name()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn settings(&self) -> Settings {
|
||||||
|
self.program.settings()
|
||||||
|
}
|
||||||
|
|
||||||
fn boot(&self) -> (Self::State, Task<Self::Message>) {
|
fn boot(&self) -> (Self::State, Task<Self::Message>) {
|
||||||
self.program.boot()
|
self.program.boot()
|
||||||
}
|
}
|
||||||
|
|
@ -530,6 +553,10 @@ pub fn with_executor<P: Program, E: Executor>(
|
||||||
P::name()
|
P::name()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn settings(&self) -> Settings {
|
||||||
|
self.program.settings()
|
||||||
|
}
|
||||||
|
|
||||||
fn boot(&self) -> (Self::State, Task<Self::Message>) {
|
fn boot(&self) -> (Self::State, Task<Self::Message>) {
|
||||||
self.program.boot()
|
self.program.boot()
|
||||||
}
|
}
|
||||||
|
|
@ -585,10 +612,15 @@ pub fn with_executor<P: Program, E: Executor>(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The renderer of some [`Program`].
|
/// The renderer of some [`Program`].
|
||||||
pub trait Renderer: text::Renderer<Font = Font> + compositor::Default {}
|
pub trait Renderer:
|
||||||
|
text::Renderer<Font = Font> + compositor::Default + renderer::Headless
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> Renderer for T where T: text::Renderer<Font = Font> + compositor::Default
|
impl<T> Renderer for T where
|
||||||
{}
|
T: text::Renderer<Font = Font> + compositor::Default + renderer::Headless
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// A particular instance of a running [`Program`].
|
/// A particular instance of a running [`Program`].
|
||||||
#[allow(missing_debug_implementations)]
|
#[allow(missing_debug_implementations)]
|
||||||
|
|
|
||||||
|
|
@ -136,6 +136,10 @@ where
|
||||||
) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> {
|
) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> {
|
||||||
self.view.view(state)
|
self.view.view(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn settings(&self) -> Settings {
|
||||||
|
Settings::default()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Application {
|
Application {
|
||||||
|
|
@ -173,6 +177,9 @@ impl<P: Program> Application<P> {
|
||||||
where
|
where
|
||||||
Self: 'static,
|
Self: 'static,
|
||||||
{
|
{
|
||||||
|
let settings = self.settings.clone();
|
||||||
|
let window = self.window.clone();
|
||||||
|
|
||||||
#[cfg(all(feature = "debug", not(target_arch = "wasm32")))]
|
#[cfg(all(feature = "debug", not(target_arch = "wasm32")))]
|
||||||
let program = {
|
let program = {
|
||||||
iced_debug::init(iced_debug::Metadata {
|
iced_debug::init(iced_debug::Metadata {
|
||||||
|
|
@ -181,13 +188,13 @@ impl<P: Program> Application<P> {
|
||||||
can_time_travel: cfg!(feature = "time-travel"),
|
can_time_travel: cfg!(feature = "time-travel"),
|
||||||
});
|
});
|
||||||
|
|
||||||
iced_devtools::attach(self.raw)
|
iced_devtools::attach(self)
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(any(not(feature = "debug"), target_arch = "wasm32"))]
|
#[cfg(any(not(feature = "debug"), target_arch = "wasm32"))]
|
||||||
let program = self.raw;
|
let program = self;
|
||||||
|
|
||||||
Ok(shell::run(program, self.settings, Some(self.window))?)
|
Ok(shell::run(program, settings, Some(window))?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the [`Settings`] that will be used to run the [`Application`].
|
/// Sets the [`Settings`] that will be used to run the [`Application`].
|
||||||
|
|
@ -409,6 +416,66 @@ impl<P: Program> Application<P> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<P: Program> Program for Application<P> {
|
||||||
|
type State = P::State;
|
||||||
|
type Message = P::Message;
|
||||||
|
type Theme = P::Theme;
|
||||||
|
type Renderer = P::Renderer;
|
||||||
|
type Executor = P::Executor;
|
||||||
|
|
||||||
|
fn name() -> &'static str {
|
||||||
|
P::name()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn settings(&self) -> Settings {
|
||||||
|
self.settings.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn boot(&self) -> (Self::State, Task<Self::Message>) {
|
||||||
|
self.raw.boot()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(
|
||||||
|
&self,
|
||||||
|
state: &mut Self::State,
|
||||||
|
message: Self::Message,
|
||||||
|
) -> Task<Self::Message> {
|
||||||
|
self.raw.update(state, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn view<'a>(
|
||||||
|
&self,
|
||||||
|
state: &'a Self::State,
|
||||||
|
window: window::Id,
|
||||||
|
) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> {
|
||||||
|
self.raw.view(state, window)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn title(&self, state: &Self::State, window: window::Id) -> String {
|
||||||
|
self.raw.title(state, window)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn subscription(&self, state: &Self::State) -> Subscription<Self::Message> {
|
||||||
|
self.raw.subscription(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn theme(
|
||||||
|
&self,
|
||||||
|
state: &Self::State,
|
||||||
|
window: iced_core::window::Id,
|
||||||
|
) -> Self::Theme {
|
||||||
|
self.raw.theme(state, window)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn style(&self, state: &Self::State, theme: &Self::Theme) -> theme::Style {
|
||||||
|
self.raw.style(state, theme)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scale_factor(&self, state: &Self::State, window: window::Id) -> f64 {
|
||||||
|
self.raw.scale_factor(state, window)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The logic to initialize the `State` of some [`Application`].
|
/// The logic to initialize the `State` of some [`Application`].
|
||||||
///
|
///
|
||||||
/// This trait is implemented for both `Fn() -> State` and
|
/// This trait is implemented for both `Fn() -> State` and
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,10 @@ where
|
||||||
name.split("::").next().unwrap_or("a_cool_application")
|
name.split("::").next().unwrap_or("a_cool_application")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn settings(&self) -> Settings {
|
||||||
|
Settings::default()
|
||||||
|
}
|
||||||
|
|
||||||
fn boot(&self) -> (State, Task<Self::Message>) {
|
fn boot(&self) -> (State, Task<Self::Message>) {
|
||||||
let (state, task) = self.boot.boot();
|
let (state, task) = self.boot.boot();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,10 @@ where
|
||||||
name.split("::").next().unwrap_or("a_cool_daemon")
|
name.split("::").next().unwrap_or("a_cool_daemon")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn settings(&self) -> Settings {
|
||||||
|
Settings::default()
|
||||||
|
}
|
||||||
|
|
||||||
fn boot(&self) -> (Self::State, Task<Self::Message>) {
|
fn boot(&self) -> (Self::State, Task<Self::Message>) {
|
||||||
self.boot.boot()
|
self.boot.boot()
|
||||||
}
|
}
|
||||||
|
|
@ -117,6 +121,8 @@ impl<P: Program> Daemon<P> {
|
||||||
where
|
where
|
||||||
Self: 'static,
|
Self: 'static,
|
||||||
{
|
{
|
||||||
|
let settings = self.settings.clone();
|
||||||
|
|
||||||
#[cfg(all(feature = "debug", not(target_arch = "wasm32")))]
|
#[cfg(all(feature = "debug", not(target_arch = "wasm32")))]
|
||||||
let program = {
|
let program = {
|
||||||
iced_debug::init(iced_debug::Metadata {
|
iced_debug::init(iced_debug::Metadata {
|
||||||
|
|
@ -125,13 +131,13 @@ impl<P: Program> Daemon<P> {
|
||||||
can_time_travel: cfg!(feature = "time-travel"),
|
can_time_travel: cfg!(feature = "time-travel"),
|
||||||
});
|
});
|
||||||
|
|
||||||
iced_devtools::attach(self.raw)
|
iced_devtools::attach(self)
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(any(not(feature = "debug"), target_arch = "wasm32"))]
|
#[cfg(any(not(feature = "debug"), target_arch = "wasm32"))]
|
||||||
let program = self.raw;
|
let program = self;
|
||||||
|
|
||||||
Ok(shell::run(program, self.settings, None)?)
|
Ok(shell::run(program, settings, None)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the [`Settings`] that will be used to run the [`Daemon`].
|
/// Sets the [`Settings`] that will be used to run the [`Daemon`].
|
||||||
|
|
@ -250,6 +256,66 @@ impl<P: Program> Daemon<P> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<P: Program> Program for Daemon<P> {
|
||||||
|
type State = P::State;
|
||||||
|
type Message = P::Message;
|
||||||
|
type Theme = P::Theme;
|
||||||
|
type Renderer = P::Renderer;
|
||||||
|
type Executor = P::Executor;
|
||||||
|
|
||||||
|
fn name() -> &'static str {
|
||||||
|
P::name()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn settings(&self) -> Settings {
|
||||||
|
self.settings.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn boot(&self) -> (Self::State, Task<Self::Message>) {
|
||||||
|
self.raw.boot()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(
|
||||||
|
&self,
|
||||||
|
state: &mut Self::State,
|
||||||
|
message: Self::Message,
|
||||||
|
) -> Task<Self::Message> {
|
||||||
|
self.raw.update(state, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn view<'a>(
|
||||||
|
&self,
|
||||||
|
state: &'a Self::State,
|
||||||
|
window: window::Id,
|
||||||
|
) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> {
|
||||||
|
self.raw.view(state, window)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn title(&self, state: &Self::State, window: window::Id) -> String {
|
||||||
|
self.raw.title(state, window)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn subscription(&self, state: &Self::State) -> Subscription<Self::Message> {
|
||||||
|
self.raw.subscription(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn theme(
|
||||||
|
&self,
|
||||||
|
state: &Self::State,
|
||||||
|
window: iced_core::window::Id,
|
||||||
|
) -> Self::Theme {
|
||||||
|
self.raw.theme(state, window)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn style(&self, state: &Self::State, theme: &Self::Theme) -> theme::Style {
|
||||||
|
self.raw.style(state, theme)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scale_factor(&self, state: &Self::State, window: window::Id) -> f64 {
|
||||||
|
self.raw.scale_factor(state, window)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The title logic of some [`Daemon`].
|
/// The title logic of some [`Daemon`].
|
||||||
///
|
///
|
||||||
/// This trait is implemented both for `&static str` and
|
/// This trait is implemented both for `&static str` and
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
iced_runtime.workspace = true
|
iced_runtime.workspace = true
|
||||||
|
iced_program.workspace = true
|
||||||
|
|
||||||
iced_renderer.workspace = true
|
iced_renderer.workspace = true
|
||||||
iced_renderer.features = ["fira-sans"]
|
iced_renderer.features = ["fira-sans"]
|
||||||
|
|
|
||||||
169
test/src/emulator.rs
Normal file
169
test/src/emulator.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::core::keyboard;
|
use crate::core::keyboard;
|
||||||
use crate::core::mouse;
|
use crate::core::mouse;
|
||||||
use crate::core::{Event, Point};
|
use crate::core::{Event, Point};
|
||||||
|
use crate::simulator;
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
|
@ -145,6 +146,62 @@ impl Interaction {
|
||||||
(current, next) => (current, Some(next)),
|
(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 {
|
impl fmt::Display for Interaction {
|
||||||
|
|
@ -200,14 +257,6 @@ pub enum Keyboard {
|
||||||
Typewrite(String),
|
Typewrite(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub enum Key {
|
|
||||||
Enter,
|
|
||||||
Escape,
|
|
||||||
Tab,
|
|
||||||
Backspace,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Keyboard {
|
impl fmt::Display for Keyboard {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
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 {
|
mod format {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -84,10 +84,12 @@
|
||||||
//!
|
//!
|
||||||
//! [the classical counter interface]: https://book.iced.rs/architecture.html#dissecting-an-interface
|
//! [the classical counter interface]: https://book.iced.rs/architecture.html#dissecting-an-interface
|
||||||
#![allow(missing_docs)]
|
#![allow(missing_docs)]
|
||||||
|
use iced_program as program;
|
||||||
use iced_renderer as renderer;
|
use iced_renderer as renderer;
|
||||||
use iced_runtime as runtime;
|
use iced_runtime as runtime;
|
||||||
use iced_runtime::core;
|
use iced_runtime::core;
|
||||||
|
|
||||||
|
pub mod emulator;
|
||||||
pub mod instruction;
|
pub mod instruction;
|
||||||
pub mod selector;
|
pub mod selector;
|
||||||
pub mod simulator;
|
pub mod simulator;
|
||||||
|
|
@ -98,8 +100,3 @@ pub use error::Error;
|
||||||
pub use instruction::Instruction;
|
pub use instruction::Instruction;
|
||||||
pub use selector::Selector;
|
pub use selector::Selector;
|
||||||
pub use simulator::{Simulator, simulator};
|
pub use simulator::{Simulator, simulator};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Test {
|
|
||||||
instructions: Vec<Instruction>,
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -483,6 +483,38 @@ pub fn click() -> impl Iterator<Item = Event> {
|
||||||
.into_iter()
|
.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).
|
/// Returns the sequence of events of a "key tap" (i.e. pressing and releasing a key).
|
||||||
pub fn tap_key(
|
pub fn tap_key(
|
||||||
key: impl Into<keyboard::Key>,
|
key: impl Into<keyboard::Key>,
|
||||||
|
|
@ -490,28 +522,7 @@ pub fn tap_key(
|
||||||
) -> impl Iterator<Item = Event> {
|
) -> impl Iterator<Item = Event> {
|
||||||
let key = key.into();
|
let key = key.into();
|
||||||
|
|
||||||
[
|
[press_key(key.clone(), text), release_key(key)].into_iter()
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the sequence of events of typewriting the given text in a keyboard.
|
/// Returns the sequence of events of typewriting the given text in a keyboard.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue