finally working event system
This commit is contained in:
@@ -0,0 +1,57 @@
|
|||||||
|
use std::{
|
||||||
|
any::{Any, TypeId},
|
||||||
|
collections::HashMap,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::Close;
|
||||||
|
|
||||||
|
pub trait Event {
|
||||||
|
fn as_any(self: Box<Self>) -> Box<dyn Any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EventHandlerFn(pub Box<dyn FnMut(Box<dyn Event>)>);
|
||||||
|
|
||||||
|
impl std::fmt::Debug for EventHandlerFn {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "FnMut()")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct EventManager {
|
||||||
|
handlers: HashMap<TypeId, Vec<EventHandlerFn>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EventManager {
|
||||||
|
pub fn new() -> EventManager {
|
||||||
|
EventManager {
|
||||||
|
handlers: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on<E: Event + 'static, F: FnMut(Box<E>) + 'static>(&mut self, mut f: F) {
|
||||||
|
let wrapper = Box::new(move |evt: Box<dyn Event>| {
|
||||||
|
if let Ok(concrete) = evt.as_any().downcast::<E>() {
|
||||||
|
f(concrete);
|
||||||
|
}
|
||||||
|
}) as Box<dyn FnMut(Box<dyn Event>)>;
|
||||||
|
|
||||||
|
self.handlers
|
||||||
|
.entry(TypeId::of::<E>())
|
||||||
|
.or_insert_with(Vec::new)
|
||||||
|
.push(EventHandlerFn(wrapper));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dispatch<E: Event + 'static + Clone>(&mut self, evt: E) {
|
||||||
|
let tid = evt.type_id();
|
||||||
|
if let Some(handlers) = self.handlers.get_mut(&tid) {
|
||||||
|
for h in handlers {
|
||||||
|
(h.0)(Box::new(evt.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if tid == TypeId::of::<Close>() {
|
||||||
|
std::process::exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
use crossterm::event::{KeyCode, KeyModifiers};
|
||||||
|
|
||||||
|
use crate::{event_manager::EventManager, Close, Event, Extension, Screen};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct KeyPress(pub crossterm::event::KeyEvent);
|
||||||
|
impl Event for KeyPress {
|
||||||
|
fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait KeyPressEventHandler {
|
||||||
|
fn on_keypress(&mut self, event: KeyPress);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct KeyPressExtension;
|
||||||
|
|
||||||
|
impl Extension for KeyPressExtension {
|
||||||
|
fn init(&mut self, _: &mut Screen, events: &mut EventManager) {
|
||||||
|
crossterm::terminal::enable_raw_mode().unwrap();
|
||||||
|
crate::utils::clear().unwrap();
|
||||||
|
crate::utils::hide_cursor().unwrap();
|
||||||
|
|
||||||
|
events.on(|_: Box<Close>| {
|
||||||
|
crossterm::terminal::disable_raw_mode().unwrap();
|
||||||
|
crate::utils::show_cursor().unwrap();
|
||||||
|
crate::utils::clear().unwrap();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tick_start(&mut self, _: &mut Screen, _: &mut EventManager) {}
|
||||||
|
|
||||||
|
fn tick_end(&mut self, _: &mut Screen, events: &mut EventManager) {
|
||||||
|
match crossterm::event::read().unwrap() {
|
||||||
|
crossterm::event::Event::Key(e) => {
|
||||||
|
if e.modifiers.contains(KeyModifiers::CONTROL) && e.code == KeyCode::Char('c') {
|
||||||
|
events.dispatch(Close);
|
||||||
|
} else {
|
||||||
|
events.dispatch(KeyPress(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub mod keypress;
|
||||||
+101
-10
@@ -1,23 +1,114 @@
|
|||||||
use std::{
|
use std::any::Any;
|
||||||
any::{Any, TypeId},
|
|
||||||
collections::HashMap,
|
use crate::{
|
||||||
|
event_manager::{Event, EventManager},
|
||||||
|
render_scope::RenderScope,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub mod event_manager;
|
||||||
|
pub mod extensions;
|
||||||
|
pub mod render_scope;
|
||||||
pub mod text;
|
pub mod text;
|
||||||
|
pub mod utils;
|
||||||
|
|
||||||
pub trait Event {}
|
pub trait Element {
|
||||||
|
#[allow(unused)]
|
||||||
|
fn render(&mut self, scope: &mut RenderScope) {}
|
||||||
|
#[allow(unused)]
|
||||||
|
fn init(&mut self, events: &mut EventManager) {}
|
||||||
|
fn as_any(self: Box<Self>) -> Box<dyn Any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Extension {
|
||||||
|
fn init(&mut self, screen: &mut Screen, events: &mut EventManager);
|
||||||
|
fn tick_start(&mut self, screen: &mut Screen, events: &mut EventManager);
|
||||||
|
fn tick_end(&mut self, screen: &mut Screen, events: &mut EventManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ExtensionManager(Vec<Box<dyn Extension>>);
|
||||||
|
|
||||||
|
impl Extension for ExtensionManager {
|
||||||
|
fn init(&mut self, screen: &mut Screen, events: &mut EventManager) {
|
||||||
|
for ext in &mut self.0 {
|
||||||
|
ext.init(screen, events);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tick_start(&mut self, screen: &mut Screen, events: &mut EventManager) {
|
||||||
|
for ext in &mut self.0 {
|
||||||
|
ext.tick_start(screen, events);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tick_end(&mut self, screen: &mut Screen, events: &mut EventManager) {
|
||||||
|
for ext in &mut self.0 {
|
||||||
|
ext.tick_end(screen, events);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExtensionManager {
|
||||||
|
pub fn new() -> ExtensionManager {
|
||||||
|
ExtensionManager(Vec::new())
|
||||||
|
}
|
||||||
|
pub fn add<E: Extension + 'static>(&mut self, extension: E) {
|
||||||
|
self.0.push(Box::new(extension));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EventHandlerFn(pub Box<dyn FnMut(Box<dyn Event>)>);
|
||||||
|
|
||||||
|
impl std::fmt::Debug for EventHandlerFn {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "FnMut()")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Close;
|
||||||
|
|
||||||
|
impl Event for Close {
|
||||||
|
fn as_any(self: Box<Self>) -> Box<dyn Any> {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Screen {
|
pub struct Screen {
|
||||||
element: Box<dyn Element>,
|
pub elements: Vec<Box<dyn Element>>,
|
||||||
event_handlers: HashMap<TypeId, Box<dyn FnMut(Box<dyn Event>)>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Screen {
|
impl Screen {
|
||||||
pub fn on_event<E: Event, F: FnMut(E)>(&mut self, f: F) {
|
pub fn new() -> Screen {
|
||||||
|
Screen {
|
||||||
|
elements: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Element {
|
pub fn draw<E: Element + 'static>(&mut self, element: E) {
|
||||||
fn init(&mut self, screen: &mut Screen);
|
self.elements.push(Box::new(element));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(
|
||||||
|
&mut self,
|
||||||
|
events: &mut EventManager,
|
||||||
|
extension_manager: &mut ExtensionManager,
|
||||||
|
) -> std::io::Result<()> {
|
||||||
|
let mut scope = RenderScope {};
|
||||||
|
|
||||||
|
for elem in &mut self.elements {
|
||||||
|
elem.init(events);
|
||||||
|
}
|
||||||
|
|
||||||
|
extension_manager.init(self, events);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
extension_manager.tick_start(self, events);
|
||||||
|
|
||||||
|
for elem in &mut self.elements {
|
||||||
|
elem.render(&mut scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
extension_manager.tick_end(self, events);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-1
@@ -1,3 +1,15 @@
|
|||||||
fn main() {
|
use osui::{
|
||||||
|
event_manager::EventManager, extensions::keypress::KeyPressExtension, ExtensionManager, Screen,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut screen = Screen::new();
|
||||||
|
let mut extensions = ExtensionManager::new();
|
||||||
|
let mut events = EventManager::new();
|
||||||
|
|
||||||
|
extensions.add(KeyPressExtension);
|
||||||
|
|
||||||
|
screen.draw("Hello, World".to_string());
|
||||||
|
|
||||||
|
screen.run(&mut events, &mut extensions).unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
pub struct RenderScope {}
|
||||||
|
|
||||||
|
impl RenderScope {
|
||||||
|
pub fn draw_text(&mut self, text: &str) {
|
||||||
|
println!("{text}");
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
use crate::Element;
|
||||||
|
|
||||||
|
impl Element for String {
|
||||||
|
fn render(&mut self, scope: &mut crate::render_scope::RenderScope) {
|
||||||
|
scope.draw_text(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> {
|
||||||
|
Box::new(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
//! The `utils` module provides utility functions for terminal manipulation
|
||||||
|
//! and string operations, designed to enhance terminal-based applications.
|
||||||
|
|
||||||
|
use std::io::{self, Write};
|
||||||
|
|
||||||
|
/// Clears the terminal screen and moves the cursor to the top-left corner.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// A `io::Result<()>` indicating whether the operation succeeded.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// utils::clear().unwrap();
|
||||||
|
/// ```
|
||||||
|
pub fn clear() -> io::Result<()> {
|
||||||
|
print!("\x1B[2J\x1B[H");
|
||||||
|
std::io::stdout().flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hides the terminal cursor.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// A `io::Result<()>` indicating whether the operation succeeded.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// utils::hide_cursor().unwrap();
|
||||||
|
/// ```
|
||||||
|
pub fn hide_cursor() -> io::Result<()> {
|
||||||
|
print!("\x1b[?25l");
|
||||||
|
std::io::stdout().flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shows the terminal cursor.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// A `io::Result<()>` indicating whether the operation succeeded.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// utils::show_cursor().unwrap();
|
||||||
|
/// ```
|
||||||
|
pub fn show_cursor() -> io::Result<()> {
|
||||||
|
print!("\x1B[?25h");
|
||||||
|
std::io::stdout().flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Flushes the terminal's stdout buffer.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// A `io::Result<()>` indicating whether the operation succeeded.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// utils::flush().unwrap();
|
||||||
|
/// ```
|
||||||
|
pub fn flush() -> io::Result<()> {
|
||||||
|
std::io::stdout().flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculates the width and height of a string when rendered in a terminal.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// - `s`: The input string.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// A tuple `(u16, u16)` where:
|
||||||
|
/// - The first value is the maximum width of the string in characters.
|
||||||
|
/// - The second value is the height of the string in lines.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// let (width, height) = utils::str_size("Hello\nWorld!");
|
||||||
|
/// assert_eq!((5, 2), (width, height));
|
||||||
|
/// ```
|
||||||
|
pub fn str_size(s: &str) -> (u16, u16) {
|
||||||
|
let mut height = 1;
|
||||||
|
let mut max_width = 0;
|
||||||
|
let mut current_width = 0;
|
||||||
|
|
||||||
|
for b in s.bytes() {
|
||||||
|
if b == b'\n' {
|
||||||
|
height += 1;
|
||||||
|
max_width = max_width.max(current_width);
|
||||||
|
current_width = 0;
|
||||||
|
} else {
|
||||||
|
current_width += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
max_width = max_width.max(current_width);
|
||||||
|
|
||||||
|
(max_width, height)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user