slightly improved structure and added a few more elements
This commit is contained in:
@@ -0,0 +1,91 @@
|
|||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use crossterm::event::KeyEvent;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
event, event_handler, events::EventManager, extensions::keypress::KeyPressEventHandler, Element,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Input(String, usize, Arc<Mutex<EventManager>>);
|
||||||
|
|
||||||
|
impl Input {
|
||||||
|
pub fn new() -> Input {
|
||||||
|
Input(String::new(), 0, Arc::new(Mutex::new(EventManager::new())))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_event(&self) -> Arc<Mutex<EventManager>> {
|
||||||
|
self.2.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Element for Input {
|
||||||
|
fn init(&mut self, events: &mut crate::events::EventManager) {
|
||||||
|
event_handler!(Self, self, events, on_keypress);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&mut self, scope: &mut crate::render_scope::RenderScope) {
|
||||||
|
let mut s = self.0.clone();
|
||||||
|
|
||||||
|
if self.1 < self.0.len() {
|
||||||
|
s.insert_str(self.1 + 1, "\x1b[0m");
|
||||||
|
s.insert_str(self.1, "\x1b[42m");
|
||||||
|
} else {
|
||||||
|
s += "\x1b[42m \x1b[0m";
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.draw_text(&s);
|
||||||
|
}
|
||||||
|
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KeyPressEventHandler for Input {
|
||||||
|
fn on_keypress(&mut self, event: Box<KeyEvent>) {
|
||||||
|
match event.code {
|
||||||
|
crossterm::event::KeyCode::Char(c) => {
|
||||||
|
self.0.insert(self.1, c);
|
||||||
|
self.1 += 1;
|
||||||
|
self.2
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.dispatch(InputUpdateEvent(self.0.clone()));
|
||||||
|
}
|
||||||
|
crossterm::event::KeyCode::Backspace => {
|
||||||
|
if self.1 > 0 {
|
||||||
|
self.0.remove(self.1 - 1);
|
||||||
|
self.1 -= 1;
|
||||||
|
self.2
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.dispatch(InputUpdateEvent(self.0.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
crossterm::event::KeyCode::Left => {
|
||||||
|
if self.1 > 0 {
|
||||||
|
self.1 -= 1;
|
||||||
|
self.2
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.dispatch(InputUpdateEvent(self.0.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
crossterm::event::KeyCode::Right => {
|
||||||
|
if self.1 < self.0.len() {
|
||||||
|
self.1 += 1;
|
||||||
|
self.2
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.dispatch(InputUpdateEvent(self.0.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
event!(InputUpdateEvent(pub String));
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
pub mod rect;
|
||||||
|
pub mod text;
|
||||||
|
pub mod input;
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
use crate::Element;
|
||||||
|
|
||||||
|
pub struct Rect(pub u32);
|
||||||
|
impl Element for Rect {
|
||||||
|
fn render(&mut self, scope: &mut crate::render_scope::RenderScope) {
|
||||||
|
let (w, h) = scope.get_size();
|
||||||
|
|
||||||
|
if w > 0 && h > 0 {
|
||||||
|
scope.draw_rect(w, h, self.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,11 @@ impl Element for String {
|
|||||||
scope.draw_text(self);
|
scope.draw_text(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> {
|
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||||
Box::new(self)
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,19 @@
|
|||||||
use crossterm::event::{KeyCode, KeyModifiers};
|
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
event,
|
events::{Close, Event, EventManager},
|
||||||
events::{Close, EventManager},
|
|
||||||
extensions::Extension,
|
extensions::Extension,
|
||||||
Screen,
|
Screen,
|
||||||
};
|
};
|
||||||
|
|
||||||
event!(KeyPress(pub crossterm::event::KeyEvent));
|
impl Event for KeyEvent {
|
||||||
|
fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait KeyPressEventHandler {
|
pub trait KeyPressEventHandler {
|
||||||
fn on_keypress(&mut self, event: KeyPress);
|
fn on_keypress(&mut self, event: Box<KeyEvent>);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct KeyPressExtension;
|
pub struct KeyPressExtension;
|
||||||
@@ -36,7 +39,7 @@ impl Extension for KeyPressExtension {
|
|||||||
if e.modifiers.contains(KeyModifiers::CONTROL) && e.code == KeyCode::Char('c') {
|
if e.modifiers.contains(KeyModifiers::CONTROL) && e.code == KeyCode::Char('c') {
|
||||||
events.dispatch(Close);
|
events.dispatch(Close);
|
||||||
} else {
|
} else {
|
||||||
events.dispatch(KeyPress(e));
|
events.dispatch(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|||||||
+8
-2
@@ -10,12 +10,12 @@ use crate::{
|
|||||||
style::Transform,
|
style::Transform,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub mod element;
|
||||||
pub mod events;
|
pub mod events;
|
||||||
pub mod extensions;
|
pub mod extensions;
|
||||||
pub mod macros;
|
pub mod macros;
|
||||||
pub mod render_scope;
|
pub mod render_scope;
|
||||||
pub mod style;
|
pub mod style;
|
||||||
pub mod text;
|
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
pub trait Element {
|
pub trait Element {
|
||||||
@@ -23,7 +23,8 @@ pub trait Element {
|
|||||||
fn render(&mut self, scope: &mut RenderScope) {}
|
fn render(&mut self, scope: &mut RenderScope) {}
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
fn init(&mut self, events: &mut EventManager) {}
|
fn init(&mut self, events: &mut EventManager) {}
|
||||||
fn as_any(self: Box<Self>) -> Box<dyn Any>;
|
fn as_any(&self) -> &dyn Any;
|
||||||
|
fn as_any_mut(&mut self) -> &mut dyn Any;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Component {
|
pub trait Component {
|
||||||
@@ -94,7 +95,12 @@ impl Screen {
|
|||||||
utils::clear().unwrap();
|
utils::clear().unwrap();
|
||||||
for elem in &mut self.elements {
|
for elem in &mut self.elements {
|
||||||
scope.clear();
|
scope.clear();
|
||||||
|
if let Some(t) = elem.get() {
|
||||||
|
scope.set_transform(t);
|
||||||
|
}
|
||||||
|
|
||||||
elem.0.render(&mut scope);
|
elem.0.render(&mut scope);
|
||||||
|
|
||||||
if let Some(t) = elem.get() {
|
if let Some(t) = elem.get() {
|
||||||
scope.set_transform(t);
|
scope.set_transform(t);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,3 +85,40 @@ macro_rules! component {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Macro to create an event handler closure that calls a method on `self`
|
||||||
|
///
|
||||||
|
/// This macro automates the pattern of capturing `self` as a raw pointer,
|
||||||
|
/// then passing an event to a method on `self` inside an unsafe closure.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `$self_ty`: The self type.
|
||||||
|
/// - `$self`: The instance variable (usually `self`) to call the method on.
|
||||||
|
/// - `$events`: The event source which has an `.on` method to register the handler.
|
||||||
|
/// - `$method`: The method name on `self` to call when an event occurs.
|
||||||
|
///
|
||||||
|
/// # Usage
|
||||||
|
/// ```rust
|
||||||
|
/// event_handler!(self, events, on_keypress);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// This expands roughly to:
|
||||||
|
/// ```rust
|
||||||
|
/// let self_ref = self as *mut Self;
|
||||||
|
/// events.on(move |event| unsafe { (*self_ref).on_keypress(event) });
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// This macro uses `unsafe` code because it dereferences a raw pointer inside the closure.
|
||||||
|
/// Ensure that the `self` reference lives at least as long as the closure to avoid undefined behavior.
|
||||||
|
///
|
||||||
|
/// # Why use raw pointers here?
|
||||||
|
/// Often, event handlers require `'static` closures, but `self` is a stack reference.
|
||||||
|
/// Capturing `self` directly is not possible, so this workaround uses a raw pointer.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! event_handler {
|
||||||
|
($self_ty:ty, $self:ident, $events:ident, $method:ident) => {{
|
||||||
|
let self_ref = $self as *mut $self_ty;
|
||||||
|
$events.on(move |e| unsafe { (*self_ref).$method(e) });
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|||||||
+18
-3
@@ -1,4 +1,5 @@
|
|||||||
use osui::{
|
use osui::{
|
||||||
|
element::input::{Input, InputUpdateEvent},
|
||||||
events::EventManager,
|
events::EventManager,
|
||||||
extensions::{keypress::KeyPressExtension, ExtensionManager},
|
extensions::{keypress::KeyPressExtension, ExtensionManager},
|
||||||
style::Transform,
|
style::Transform,
|
||||||
@@ -11,9 +12,23 @@ fn main() {
|
|||||||
let mut events = EventManager::new();
|
let mut events = EventManager::new();
|
||||||
extensions.add(KeyPressExtension);
|
extensions.add(KeyPressExtension);
|
||||||
|
|
||||||
screen
|
let widget = screen.draw(Input::new()).component(Transform::center());
|
||||||
.draw("Hello, World".to_string())
|
|
||||||
.component(Transform::center());
|
let input_event = widget
|
||||||
|
.0
|
||||||
|
.as_any()
|
||||||
|
.downcast_ref::<Input>()
|
||||||
|
.unwrap()
|
||||||
|
.get_event();
|
||||||
|
|
||||||
|
input_event
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.on(|event: Box<InputUpdateEvent>| {
|
||||||
|
if event.0 == "quit" {
|
||||||
|
std::process::exit(0)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
screen.run(&mut events, &mut extensions).unwrap();
|
screen.run(&mut events, &mut extensions).unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-1
@@ -1,10 +1,11 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
style::{RawTransform, Transform},
|
style::{RawTransform, Transform},
|
||||||
utils,
|
utils::{self, hex_ansi_bg},
|
||||||
};
|
};
|
||||||
|
|
||||||
enum RenderMethod {
|
enum RenderMethod {
|
||||||
Text(String),
|
Text(String),
|
||||||
|
Rectangle(u16, u16, u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RenderScope {
|
pub struct RenderScope {
|
||||||
@@ -37,17 +38,45 @@ impl RenderScope {
|
|||||||
self.transform.height = self.transform.height.max(h);
|
self.transform.height = self.transform.height.max(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn draw_rect(&mut self, width: u16, height: u16, color: u32) {
|
||||||
|
self.render_stack
|
||||||
|
.push(RenderMethod::Rectangle(width, height, color));
|
||||||
|
self.transform.width = self.transform.width.max(width);
|
||||||
|
self.transform.height = self.transform.height.max(height);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn draw(&self) {
|
pub fn draw(&self) {
|
||||||
for m in &self.render_stack {
|
for m in &self.render_stack {
|
||||||
match m {
|
match m {
|
||||||
RenderMethod::Text(t) => {
|
RenderMethod::Text(t) => {
|
||||||
println!("\x1b[{};{}H{t}", self.transform.y + 1, self.transform.x + 1)
|
println!("\x1b[{};{}H{t}", self.transform.y + 1, self.transform.x + 1)
|
||||||
}
|
}
|
||||||
|
RenderMethod::Rectangle(width, height, color) => {
|
||||||
|
for i in 0..(*height) {
|
||||||
|
println!(
|
||||||
|
"\x1b[{};{}H{}{}\x1b[0m",
|
||||||
|
self.transform.y + 1 + i,
|
||||||
|
self.transform.x + 1,
|
||||||
|
hex_ansi_bg(*color),
|
||||||
|
" ".repeat(*width as usize)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.render_stack.clear();
|
self.render_stack.clear();
|
||||||
|
self.transform.width = 0;
|
||||||
|
self.transform.height = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_size(&self) -> (u16, u16) {
|
||||||
|
(self.transform.width, self.transform.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_parent_size(&self) -> (u16, u16) {
|
||||||
|
(self.parent_width, self.parent_height)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,12 @@ impl Transform {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn dimensions(&mut self, width: u16, height: u16) -> Self {
|
||||||
|
self.width = Dimension::Const(width);
|
||||||
|
self.height = Dimension::Const(height);
|
||||||
|
self.clone()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn use_dimensions(&self, raw: &mut RawTransform) {
|
pub fn use_dimensions(&self, raw: &mut RawTransform) {
|
||||||
self.width.use_dimension(&mut raw.width);
|
self.width.use_dimension(&mut raw.width);
|
||||||
self.height.use_dimension(&mut raw.height);
|
self.height.use_dimension(&mut raw.height);
|
||||||
|
|||||||
@@ -92,3 +92,17 @@ pub fn str_size(s: &str) -> (u16, u16) {
|
|||||||
|
|
||||||
(max_width, height)
|
(max_width, height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn hex_ansi(hex: u32) -> String {
|
||||||
|
let r = ((hex >> 16) & 0xFF) as u8;
|
||||||
|
let g = ((hex >> 8) & 0xFF) as u8;
|
||||||
|
let b = (hex & 0xFF) as u8;
|
||||||
|
format!("\x1b[38;2;{r};{g};{b}m")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hex_ansi_bg(hex: u32) -> String {
|
||||||
|
let r = ((hex >> 16) & 0xFF) as u8;
|
||||||
|
let g = ((hex >> 8) & 0xFF) as u8;
|
||||||
|
let b = (hex & 0xFF) as u8;
|
||||||
|
format!("\x1b[48;2;{r};{g};{b}m")
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user