+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "osui"
|
name = "osui"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
description = "A TUI library for advanced uis"
|
description = "A TUI library for advanced uis"
|
||||||
|
|||||||
+8
-6
@@ -23,6 +23,7 @@ pub fn app(screen: Arc<Screen>) -> Rsx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}});
|
}});
|
||||||
|
@AlwaysFocused;
|
||||||
Paginator {
|
Paginator {
|
||||||
FlexRow {
|
FlexRow {
|
||||||
Heading, smooth: false, { "OSUI" }
|
Heading, smooth: false, { "OSUI" }
|
||||||
@@ -46,16 +47,17 @@ pub fn app(screen: Arc<Screen>) -> Rsx {
|
|||||||
// TODO: div with width full
|
// TODO: div with width full
|
||||||
}
|
}
|
||||||
|
|
||||||
FlexCol, gap: 2, {
|
FlexRow, gap: 1, {
|
||||||
@transform!{ y: Center };
|
%count
|
||||||
static Div { // static only affects the element, not children
|
"This will increment every second: {count}"
|
||||||
%count
|
|
||||||
"This will increment every second: {count}"
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transform::new().padding(1, 1).dimensions(40, 1);
|
@Transform::new().padding(1, 1).dimensions(40, 1);
|
||||||
@Style { foreground: Some(0xffffff), background: Background::RoundedOutline(0xff0000) };
|
@Style { foreground: Some(0xffffff), background: Background::RoundedOutline(0xff0000) };
|
||||||
Input { }
|
Input { }
|
||||||
|
|
||||||
|
@Transform::new().padding(1, 1).dimensions(40, 1);
|
||||||
|
@Style { foreground: Some(0xffffff), background: Background::RoundedOutline(0xffff00) };
|
||||||
|
Input { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-4
@@ -20,12 +20,20 @@ impl Div {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Element for Div {
|
impl Element for Div {
|
||||||
fn render(&mut self, scope: &mut crate::render_scope::RenderScope) {
|
fn render(
|
||||||
|
&mut self,
|
||||||
|
scope: &mut crate::render_scope::RenderScope,
|
||||||
|
_: &crate::extensions::Context,
|
||||||
|
) {
|
||||||
let (width, height) = scope.get_size_or(self.size.0, self.size.1);
|
let (width, height) = scope.get_size_or(self.size.0, self.size.1);
|
||||||
scope.use_area(width, height);
|
scope.use_area(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_render(&mut self, scope: &mut crate::render_scope::RenderScope) {
|
fn after_render(
|
||||||
|
&mut self,
|
||||||
|
scope: &mut crate::render_scope::RenderScope,
|
||||||
|
ctx: &crate::extensions::Context,
|
||||||
|
) {
|
||||||
let mut transform = scope.get_transform().clone();
|
let mut transform = scope.get_transform().clone();
|
||||||
let (w, h) = scope.get_parent_size();
|
let (w, h) = scope.get_parent_size();
|
||||||
scope.set_parent_size(transform.width, transform.height);
|
scope.set_parent_size(transform.width, transform.height);
|
||||||
@@ -43,7 +51,8 @@ impl Element for Div {
|
|||||||
if let Some(t) = elem.get() {
|
if let Some(t) = elem.get() {
|
||||||
scope.set_transform(&t);
|
scope.set_transform(&t);
|
||||||
}
|
}
|
||||||
elem.get_elem().render(scope);
|
elem.get_elem().render(scope, ctx);
|
||||||
|
ctx.render(elem, scope);
|
||||||
if let Some(t) = elem.get() {
|
if let Some(t) = elem.get() {
|
||||||
scope.set_transform(&t);
|
scope.set_transform(&t);
|
||||||
}
|
}
|
||||||
@@ -54,7 +63,8 @@ impl Element for Div {
|
|||||||
t.y += transform.y + transform.py;
|
t.y += transform.y + transform.py;
|
||||||
|
|
||||||
scope.draw();
|
scope.draw();
|
||||||
elem.get_elem().after_render(scope);
|
elem.get_elem().after_render(scope, ctx);
|
||||||
|
ctx.after_render(elem, scope);
|
||||||
}
|
}
|
||||||
scope.set_parent_size(w, h);
|
scope.set_parent_size(w, h);
|
||||||
self.size = (transform.width, transform.height);
|
self.size = (transform.width, transform.height);
|
||||||
|
|||||||
+32
-8
@@ -38,13 +38,22 @@ impl FlexCol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Element for FlexRow {
|
impl Element for FlexRow {
|
||||||
fn render(&mut self, scope: &mut crate::render_scope::RenderScope) {
|
fn render(
|
||||||
|
&mut self,
|
||||||
|
scope: &mut crate::render_scope::RenderScope,
|
||||||
|
_: &crate::extensions::Context,
|
||||||
|
) {
|
||||||
let (width, height) = scope.get_size_or(self.size.0, self.size.1);
|
let (width, height) = scope.get_size_or(self.size.0, self.size.1);
|
||||||
scope.use_area(width, height);
|
scope.use_area(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_render(&mut self, scope: &mut crate::render_scope::RenderScope) {
|
fn after_render(
|
||||||
|
&mut self,
|
||||||
|
scope: &mut crate::render_scope::RenderScope,
|
||||||
|
ctx: &crate::extensions::Context,
|
||||||
|
) {
|
||||||
let mut transform = scope.get_transform().clone();
|
let mut transform = scope.get_transform().clone();
|
||||||
|
let transform_before = transform.clone();
|
||||||
let (w, h) = scope.get_parent_size();
|
let (w, h) = scope.get_parent_size();
|
||||||
scope.set_parent_size(transform.width, transform.height);
|
scope.set_parent_size(transform.width, transform.height);
|
||||||
|
|
||||||
@@ -62,7 +71,8 @@ impl Element for FlexRow {
|
|||||||
if let Some(t) = elem.get() {
|
if let Some(t) = elem.get() {
|
||||||
scope.set_transform(&t);
|
scope.set_transform(&t);
|
||||||
}
|
}
|
||||||
elem.get_elem().render(scope);
|
elem.get_elem().render(scope, ctx);
|
||||||
|
ctx.render(elem, scope);
|
||||||
if let Some(t) = elem.get() {
|
if let Some(t) = elem.get() {
|
||||||
scope.set_transform(&t);
|
scope.set_transform(&t);
|
||||||
}
|
}
|
||||||
@@ -77,10 +87,12 @@ impl Element for FlexRow {
|
|||||||
|
|
||||||
scope.draw();
|
scope.draw();
|
||||||
|
|
||||||
elem.get_elem().after_render(scope);
|
elem.get_elem().after_render(scope, ctx);
|
||||||
|
ctx.after_render(elem, scope);
|
||||||
}
|
}
|
||||||
scope.set_parent_size(w, h);
|
scope.set_parent_size(w, h);
|
||||||
self.size = (transform.width, transform.height);
|
self.size = (transform.width, transform.height);
|
||||||
|
scope.set_transform_raw(transform_before);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_child(&mut self, element: &Arc<Widget>) {
|
fn draw_child(&mut self, element: &Arc<Widget>) {
|
||||||
@@ -98,13 +110,22 @@ impl Element for FlexRow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Element for FlexCol {
|
impl Element for FlexCol {
|
||||||
fn render(&mut self, scope: &mut crate::render_scope::RenderScope) {
|
fn render(
|
||||||
|
&mut self,
|
||||||
|
scope: &mut crate::render_scope::RenderScope,
|
||||||
|
_: &crate::extensions::Context,
|
||||||
|
) {
|
||||||
let (width, height) = scope.get_size_or(self.size.0, self.size.1);
|
let (width, height) = scope.get_size_or(self.size.0, self.size.1);
|
||||||
scope.use_area(width, height);
|
scope.use_area(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_render(&mut self, scope: &mut crate::render_scope::RenderScope) {
|
fn after_render(
|
||||||
|
&mut self,
|
||||||
|
scope: &mut crate::render_scope::RenderScope,
|
||||||
|
ctx: &crate::extensions::Context,
|
||||||
|
) {
|
||||||
let mut transform = scope.get_transform().clone();
|
let mut transform = scope.get_transform().clone();
|
||||||
|
let transform_before = transform.clone();
|
||||||
let (w, h) = scope.get_parent_size();
|
let (w, h) = scope.get_parent_size();
|
||||||
scope.set_parent_size(transform.width, transform.height);
|
scope.set_parent_size(transform.width, transform.height);
|
||||||
|
|
||||||
@@ -122,7 +143,8 @@ impl Element for FlexCol {
|
|||||||
if let Some(t) = elem.get() {
|
if let Some(t) = elem.get() {
|
||||||
scope.set_transform(&t);
|
scope.set_transform(&t);
|
||||||
}
|
}
|
||||||
elem.get_elem().render(scope);
|
elem.get_elem().render(scope, ctx);
|
||||||
|
ctx.render(elem, scope);
|
||||||
if let Some(t) = elem.get() {
|
if let Some(t) = elem.get() {
|
||||||
scope.set_transform(&t);
|
scope.set_transform(&t);
|
||||||
}
|
}
|
||||||
@@ -137,9 +159,11 @@ impl Element for FlexCol {
|
|||||||
|
|
||||||
scope.draw();
|
scope.draw();
|
||||||
|
|
||||||
elem.get_elem().after_render(scope);
|
elem.get_elem().after_render(scope, ctx);
|
||||||
|
ctx.after_render(elem, scope);
|
||||||
}
|
}
|
||||||
scope.set_parent_size(w, h);
|
scope.set_parent_size(w, h);
|
||||||
|
scope.set_transform_raw(transform_before);
|
||||||
self.size = (transform.width, transform.height);
|
self.size = (transform.width, transform.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,11 @@ impl Heading {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Element for Heading {
|
impl Element for Heading {
|
||||||
fn render(&mut self, scope: &mut crate::prelude::RenderScope) {
|
fn render(
|
||||||
|
&mut self,
|
||||||
|
scope: &mut crate::prelude::RenderScope,
|
||||||
|
_: &crate::extensions::Context,
|
||||||
|
) {
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
for element in &self.children {
|
for element in &self.children {
|
||||||
if element.get::<NoRender>().is_some() {
|
if element.get::<NoRender>().is_some() {
|
||||||
|
|||||||
@@ -11,7 +11,11 @@ pub struct Input {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Element for Input {
|
impl Element for Input {
|
||||||
fn render(&mut self, scope: &mut crate::prelude::RenderScope) {
|
fn render(
|
||||||
|
&mut self,
|
||||||
|
scope: &mut crate::prelude::RenderScope,
|
||||||
|
_: &crate::extensions::Context,
|
||||||
|
) {
|
||||||
let s = self.state.get();
|
let s = self.state.get();
|
||||||
scope.draw_text(0, 0, &s);
|
scope.draw_text(0, 0, &s);
|
||||||
if let Some(c) = s.chars().nth(self.cursor) {
|
if let Some(c) = s.chars().nth(self.cursor) {
|
||||||
|
|||||||
+5
-15
@@ -13,7 +13,11 @@ pub use paginator::*;
|
|||||||
use crate::Element;
|
use crate::Element;
|
||||||
|
|
||||||
impl Element for String {
|
impl Element for String {
|
||||||
fn render(&mut self, scope: &mut crate::render_scope::RenderScope) {
|
fn render(
|
||||||
|
&mut self,
|
||||||
|
scope: &mut crate::render_scope::RenderScope,
|
||||||
|
_: &crate::extensions::Context,
|
||||||
|
) {
|
||||||
scope.draw_text(0, 0, self);
|
scope.draw_text(0, 0, self);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,17 +29,3 @@ impl Element for String {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for (String, u32) {
|
|
||||||
fn render(&mut self, scope: &mut crate::render_scope::RenderScope) {
|
|
||||||
scope.draw_text_colored(0, 0, &self.0, self.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_any(&self) -> &dyn std::any::Any {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -24,12 +24,20 @@ impl Paginator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Element for Paginator {
|
impl Element for Paginator {
|
||||||
fn render(&mut self, scope: &mut crate::render_scope::RenderScope) {
|
fn render(
|
||||||
|
&mut self,
|
||||||
|
scope: &mut crate::render_scope::RenderScope,
|
||||||
|
_: &crate::extensions::Context,
|
||||||
|
) {
|
||||||
let (width, height) = scope.get_size_or(self.size.0, self.size.1);
|
let (width, height) = scope.get_size_or(self.size.0, self.size.1);
|
||||||
scope.use_area(width, height);
|
scope.use_area(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_render(&mut self, scope: &mut crate::render_scope::RenderScope) {
|
fn after_render(
|
||||||
|
&mut self,
|
||||||
|
scope: &mut crate::render_scope::RenderScope,
|
||||||
|
ctx: &crate::extensions::Context,
|
||||||
|
) {
|
||||||
if let Some(elem) = self.children.get(self.index) {
|
if let Some(elem) = self.children.get(self.index) {
|
||||||
if elem.get::<NoRender>().is_some() {
|
if elem.get::<NoRender>().is_some() {
|
||||||
return;
|
return;
|
||||||
@@ -46,7 +54,7 @@ impl Element for Paginator {
|
|||||||
if let Some(t) = elem.get() {
|
if let Some(t) = elem.get() {
|
||||||
scope.set_transform(&t);
|
scope.set_transform(&t);
|
||||||
}
|
}
|
||||||
elem.get_elem().render(scope);
|
elem.get_elem().render(scope, ctx);
|
||||||
if let Some(t) = elem.get() {
|
if let Some(t) = elem.get() {
|
||||||
scope.set_transform(&t);
|
scope.set_transform(&t);
|
||||||
}
|
}
|
||||||
@@ -57,7 +65,8 @@ impl Element for Paginator {
|
|||||||
t.y += transform.y + transform.py;
|
t.y += transform.y + transform.py;
|
||||||
|
|
||||||
scope.draw();
|
scope.draw();
|
||||||
elem.get_elem().after_render(scope);
|
elem.get_elem().after_render(scope, ctx);
|
||||||
|
ctx.after_render(elem, scope);
|
||||||
|
|
||||||
scope.set_parent_size(w, h);
|
scope.set_parent_size(w, h);
|
||||||
self.size = (transform.width, transform.height);
|
self.size = (transform.width, transform.height);
|
||||||
|
|||||||
@@ -0,0 +1,271 @@
|
|||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use crate::component;
|
||||||
|
|
||||||
|
use super::Extension;
|
||||||
|
|
||||||
|
use crossterm::event::{Event as CrosstermEvent, KeyCode, KeyModifiers};
|
||||||
|
|
||||||
|
component!(AlwaysFocused);
|
||||||
|
|
||||||
|
enum Direction {
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RelativeFocusExtension {
|
||||||
|
cursor: usize,
|
||||||
|
rendered: Arc<Mutex<Vec<(usize, u16, u16)>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Extension for RelativeFocusExtension {
|
||||||
|
fn event(&mut self, ctx: &super::Context, event: &dyn super::Event) {
|
||||||
|
if let Some(e) = event.get::<CrosstermEvent>() {
|
||||||
|
match e {
|
||||||
|
CrosstermEvent::Key(k) => {
|
||||||
|
if k.modifiers.contains(KeyModifiers::SHIFT) {
|
||||||
|
match k.code {
|
||||||
|
KeyCode::Right => {
|
||||||
|
let rendered = self.rendered.lock().unwrap();
|
||||||
|
let (_, current_x, current_y) = rendered
|
||||||
|
.iter()
|
||||||
|
.find(|r| r.0 == self.cursor)
|
||||||
|
.unwrap_or(&(0, 0, 0));
|
||||||
|
|
||||||
|
if let Some(index) = find_closest_in_direction(
|
||||||
|
Direction::Right,
|
||||||
|
*current_x,
|
||||||
|
*current_y,
|
||||||
|
&rendered,
|
||||||
|
self.cursor,
|
||||||
|
) {
|
||||||
|
self.cursor = index;
|
||||||
|
for (i, w) in ctx.get_widgets().iter().enumerate() {
|
||||||
|
w.set_focused(i == self.cursor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyCode::Left => {
|
||||||
|
let rendered = self.rendered.lock().unwrap();
|
||||||
|
let (_, current_x, current_y) = rendered
|
||||||
|
.iter()
|
||||||
|
.find(|r| r.0 == self.cursor)
|
||||||
|
.unwrap_or(&(0, 0, 0));
|
||||||
|
|
||||||
|
if let Some(index) = find_closest_in_direction(
|
||||||
|
Direction::Left,
|
||||||
|
*current_x,
|
||||||
|
*current_y,
|
||||||
|
&rendered,
|
||||||
|
self.cursor,
|
||||||
|
) {
|
||||||
|
self.cursor = index;
|
||||||
|
for (i, w) in ctx.get_widgets().iter().enumerate() {
|
||||||
|
w.set_focused(i == self.cursor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyCode::Up => {
|
||||||
|
let rendered = self.rendered.lock().unwrap();
|
||||||
|
let (_, current_x, current_y) = rendered
|
||||||
|
.iter()
|
||||||
|
.find(|r| r.0 == self.cursor)
|
||||||
|
.unwrap_or(&(0, 0, 0));
|
||||||
|
|
||||||
|
if let Some(index) = find_closest_in_direction(
|
||||||
|
Direction::Up,
|
||||||
|
*current_x,
|
||||||
|
*current_y,
|
||||||
|
&rendered,
|
||||||
|
self.cursor,
|
||||||
|
) {
|
||||||
|
self.cursor = index;
|
||||||
|
for (i, w) in ctx.get_widgets().iter().enumerate() {
|
||||||
|
w.set_focused(i == self.cursor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyCode::Down => {
|
||||||
|
let rendered = self.rendered.lock().unwrap();
|
||||||
|
let (_, current_x, current_y) = *rendered
|
||||||
|
.iter()
|
||||||
|
.find(|r| r.0 == self.cursor)
|
||||||
|
.unwrap_or(&(0, 0, 0));
|
||||||
|
|
||||||
|
if let Some(index) = find_closest_in_direction(
|
||||||
|
Direction::Down,
|
||||||
|
current_x,
|
||||||
|
current_y,
|
||||||
|
&rendered,
|
||||||
|
self.cursor,
|
||||||
|
) {
|
||||||
|
self.cursor = index;
|
||||||
|
|
||||||
|
for (i, w) in ctx.get_widgets().iter().enumerate() {
|
||||||
|
if let Some(AlwaysFocused) = w.get() {
|
||||||
|
w.set_focused(true);
|
||||||
|
} else {
|
||||||
|
w.set_focused(i == self.cursor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&mut self, _ctx: &super::Context, _scope: &mut crate::prelude::RenderScope) {
|
||||||
|
self.rendered.lock().unwrap().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn after_render_widget(
|
||||||
|
&mut self,
|
||||||
|
ctx: &super::Context,
|
||||||
|
scope: &mut crate::prelude::RenderScope,
|
||||||
|
widget: &std::sync::Arc<crate::prelude::Widget>,
|
||||||
|
) {
|
||||||
|
let t = scope.get_transform().clone();
|
||||||
|
let ctx = ctx.clone();
|
||||||
|
let widget = widget.clone();
|
||||||
|
|
||||||
|
let rendered = self.rendered.clone();
|
||||||
|
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
if let Some(p) = ctx
|
||||||
|
.get_widgets()
|
||||||
|
.iter()
|
||||||
|
.position(|w| Arc::ptr_eq(w, &widget))
|
||||||
|
{
|
||||||
|
rendered.lock().unwrap().push((p, t.x, t.y));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RelativeFocusExtension {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
cursor: 0,
|
||||||
|
rendered: Arc::new(Mutex::new(Vec::new())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_closest_in_direction(
|
||||||
|
direction: Direction,
|
||||||
|
current_x: u16,
|
||||||
|
current_y: u16,
|
||||||
|
rendered: &[(usize, u16, u16)],
|
||||||
|
cursor: usize,
|
||||||
|
) -> Option<usize> {
|
||||||
|
let mut closest_index: Option<usize> = None;
|
||||||
|
let mut closest_distance = u16::MAX;
|
||||||
|
let mut same_line_or_col_found = false;
|
||||||
|
|
||||||
|
for &(i, x, y) in rendered {
|
||||||
|
if i == cursor {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
match direction {
|
||||||
|
Direction::Right if x > current_x => {
|
||||||
|
if y == current_y {
|
||||||
|
let dx = x - current_x;
|
||||||
|
if !same_line_or_col_found || dx < closest_distance {
|
||||||
|
closest_index = Some(i);
|
||||||
|
closest_distance = dx;
|
||||||
|
same_line_or_col_found = true;
|
||||||
|
}
|
||||||
|
} else if !same_line_or_col_found {
|
||||||
|
let dx = x - current_x;
|
||||||
|
let dy = (y as isize - current_y as isize).abs() as u16;
|
||||||
|
let dist = dx * dx + dy * dy;
|
||||||
|
if dist < closest_distance
|
||||||
|
|| (dist == closest_distance && i < closest_index.unwrap_or(usize::MAX))
|
||||||
|
{
|
||||||
|
closest_index = Some(i);
|
||||||
|
closest_distance = dist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Direction::Left if x < current_x => {
|
||||||
|
if y == current_y {
|
||||||
|
let dx = current_x - x;
|
||||||
|
if !same_line_or_col_found || dx < closest_distance {
|
||||||
|
closest_index = Some(i);
|
||||||
|
closest_distance = dx;
|
||||||
|
same_line_or_col_found = true;
|
||||||
|
}
|
||||||
|
} else if !same_line_or_col_found {
|
||||||
|
let dx = current_x - x;
|
||||||
|
let dy = (y as isize - current_y as isize).abs() as u16;
|
||||||
|
let dist = dx * dx + dy * dy;
|
||||||
|
if dist < closest_distance
|
||||||
|
|| (dist == closest_distance && i < closest_index.unwrap_or(usize::MAX))
|
||||||
|
{
|
||||||
|
closest_index = Some(i);
|
||||||
|
closest_distance = dist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Direction::Down if y > current_y => {
|
||||||
|
if x == current_x {
|
||||||
|
let dy = y - current_y;
|
||||||
|
if !same_line_or_col_found || dy < closest_distance {
|
||||||
|
closest_index = Some(i);
|
||||||
|
closest_distance = dy;
|
||||||
|
same_line_or_col_found = true;
|
||||||
|
}
|
||||||
|
} else if !same_line_or_col_found {
|
||||||
|
let dx = (x as isize - current_x as isize).abs() as u16;
|
||||||
|
let dy = y - current_y;
|
||||||
|
let dist = dx * dx + dy * dy;
|
||||||
|
if dist < closest_distance
|
||||||
|
|| (dist == closest_distance && i < closest_index.unwrap_or(usize::MAX))
|
||||||
|
{
|
||||||
|
closest_index = Some(i);
|
||||||
|
closest_distance = dist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Direction::Up if y < current_y => {
|
||||||
|
if x == current_x {
|
||||||
|
let dy = current_y - y;
|
||||||
|
if !same_line_or_col_found || dy < closest_distance {
|
||||||
|
closest_index = Some(i);
|
||||||
|
closest_distance = dy;
|
||||||
|
same_line_or_col_found = true;
|
||||||
|
}
|
||||||
|
} else if !same_line_or_col_found {
|
||||||
|
let dx = (x as isize - current_x as isize).abs() as u16;
|
||||||
|
let dy = current_y - y;
|
||||||
|
let dist = dx * dx + dy * dy;
|
||||||
|
if dist < closest_distance
|
||||||
|
|| (dist == closest_distance && i < closest_index.unwrap_or(usize::MAX))
|
||||||
|
{
|
||||||
|
closest_index = Some(i);
|
||||||
|
closest_distance = dist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closest_index
|
||||||
|
}
|
||||||
@@ -1,20 +1,19 @@
|
|||||||
use crate::extensions::Extension;
|
use crate::extensions::{Extension, Context};
|
||||||
|
|
||||||
pub struct InputExtension;
|
pub struct InputExtension;
|
||||||
|
|
||||||
impl Extension for InputExtension {
|
impl Extension for InputExtension {
|
||||||
fn init(&mut self, screen: std::sync::Arc<crate::Screen>) {
|
fn init(&mut self, ctx: &Context) {
|
||||||
|
let ctx = ctx.clone();
|
||||||
crossterm::terminal::enable_raw_mode().unwrap();
|
crossterm::terminal::enable_raw_mode().unwrap();
|
||||||
std::thread::spawn(move || loop {
|
std::thread::spawn(move || loop {
|
||||||
if let Ok(e) = crossterm::event::read() {
|
if let Ok(e) = crossterm::event::read() {
|
||||||
for widget in screen.widgets.lock().unwrap().iter() {
|
ctx.event(&e);
|
||||||
widget.event(&e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_close(&mut self, _screen: std::sync::Arc<crate::Screen>) {
|
fn on_close(&mut self) {
|
||||||
crossterm::terminal::disable_raw_mode().unwrap();
|
crossterm::terminal::disable_raw_mode().unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+71
-4
@@ -1,8 +1,10 @@
|
|||||||
|
pub mod focus;
|
||||||
pub mod id;
|
pub mod id;
|
||||||
pub mod input_handling;
|
pub mod input_handling;
|
||||||
pub mod tick;
|
pub mod tick;
|
||||||
pub mod velocity;
|
pub mod velocity;
|
||||||
|
|
||||||
|
pub use focus::*;
|
||||||
pub use id::*;
|
pub use id::*;
|
||||||
pub use input_handling::*;
|
pub use input_handling::*;
|
||||||
pub use tick::*;
|
pub use tick::*;
|
||||||
@@ -11,7 +13,7 @@ pub use velocity::*;
|
|||||||
use std::{
|
use std::{
|
||||||
any::{type_name, Any},
|
any::{type_name, Any},
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex, MutexGuard},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -21,15 +23,29 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub trait Extension {
|
pub trait Extension {
|
||||||
fn init(&mut self, _screen: Arc<Screen>) {}
|
fn init(&mut self, _ctx: &Context) {}
|
||||||
fn on_close(&mut self, _screen: Arc<Screen>) {}
|
fn event(&mut self, _ctx: &Context, _event: &dyn Event) {}
|
||||||
fn render_widget(&mut self, _scope: &mut RenderScope, _widget: &Arc<Widget>) {}
|
fn on_close(&mut self) {}
|
||||||
|
fn render(&mut self, _ctx: &Context, _scope: &mut RenderScope) {}
|
||||||
|
fn render_widget(&mut self, _ctx: &Context, _scope: &mut RenderScope, _widget: &Arc<Widget>) {}
|
||||||
|
fn after_render_widget(
|
||||||
|
&mut self,
|
||||||
|
_ctx: &Context,
|
||||||
|
_scope: &mut RenderScope,
|
||||||
|
_widget: &Arc<Widget>,
|
||||||
|
) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Event: Send + Sync {
|
pub trait Event: Send + Sync {
|
||||||
fn as_any(&self) -> &dyn Any;
|
fn as_any(&self) -> &dyn Any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Context {
|
||||||
|
screen: Arc<Screen>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Handler<E: Event>(Arc<Mutex<dyn FnMut(&Arc<Widget>, &E) + Send + Sync>>);
|
pub struct Handler<E: Event>(Arc<Mutex<dyn FnMut(&Arc<Widget>, &E) + Send + Sync>>);
|
||||||
|
|
||||||
@@ -64,3 +80,54 @@ impl<'a> dyn Event + 'a {
|
|||||||
self.as_any().downcast_ref()
|
self.as_any().downcast_ref()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Context {
|
||||||
|
pub fn new(screen: Arc<Screen>) -> Self {
|
||||||
|
Self { screen }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn event<E: Event + Clone + 'static>(&self, e: &E) {
|
||||||
|
for widget in self.screen.widgets.lock().unwrap().iter() {
|
||||||
|
widget.event(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
for ext in self.screen.extensions.lock().unwrap().iter() {
|
||||||
|
ext.lock().unwrap().event(self, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_widgets(&self) -> MutexGuard<Vec<Arc<Widget>>> {
|
||||||
|
self.screen.widgets.lock().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter_components<C: Component + 'static + Clone, F: FnMut(&Arc<Widget>, Option<C>)>(
|
||||||
|
&self,
|
||||||
|
mut iterator: F,
|
||||||
|
) {
|
||||||
|
for widget in self.screen.widgets.lock().unwrap().iter() {
|
||||||
|
iterator(widget, widget.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_components<C: Component + 'static + Clone>(&self) -> Vec<C> {
|
||||||
|
let mut components = Vec::new();
|
||||||
|
for widget in self.screen.widgets.lock().unwrap().iter() {
|
||||||
|
if let Some(c) = widget.get() {
|
||||||
|
components.push(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
components
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(&self, w: &Arc<Widget>, scope: &mut RenderScope) {
|
||||||
|
for ext in self.screen.extensions.lock().unwrap().iter() {
|
||||||
|
ext.lock().unwrap().render_widget(self, scope, w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn after_render(&self, w: &Arc<Widget>, scope: &mut RenderScope) {
|
||||||
|
for ext in self.screen.extensions.lock().unwrap().iter() {
|
||||||
|
ext.lock().unwrap().after_render_widget(self, scope, w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,22 +1,21 @@
|
|||||||
use std::{fmt::Debug, sync::Arc};
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use crate::extensions::Extension;
|
use crate::event;
|
||||||
use crate::{event, Screen};
|
use crate::extensions::{Context, Extension};
|
||||||
|
|
||||||
pub struct TickExtension(pub u16);
|
pub struct TickExtension(pub u16);
|
||||||
|
|
||||||
event!(TickEvent(pub u32));
|
event!(TickEvent(pub u32));
|
||||||
|
|
||||||
impl Extension for TickExtension {
|
impl Extension for TickExtension {
|
||||||
fn init(&mut self, screen: Arc<Screen>) {
|
fn init(&mut self, ctx: &Context) {
|
||||||
|
let ctx = ctx.clone();
|
||||||
let rate_dur = 1000 / self.0 as u64;
|
let rate_dur = 1000 / self.0 as u64;
|
||||||
std::thread::spawn({
|
std::thread::spawn({
|
||||||
move || {
|
move || {
|
||||||
let mut tick = 0;
|
let mut tick = 0;
|
||||||
loop {
|
loop {
|
||||||
for w in screen.widgets.lock().unwrap().iter() {
|
ctx.event(&TickEvent(tick));
|
||||||
w.event(&TickEvent(tick));
|
|
||||||
}
|
|
||||||
tick += 1;
|
tick += 1;
|
||||||
std::thread::sleep(std::time::Duration::from_millis(rate_dur));
|
std::thread::sleep(std::time::Duration::from_millis(rate_dur));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ use crate::{
|
|||||||
extensions::Extension,
|
extensions::Extension,
|
||||||
style::{Position, Transform},
|
style::{Position, Transform},
|
||||||
widget::Widget,
|
widget::Widget,
|
||||||
Screen,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct VelocityExtension;
|
pub struct VelocityExtension;
|
||||||
@@ -43,12 +42,13 @@ impl VelocityExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Extension for VelocityExtension {
|
impl Extension for VelocityExtension {
|
||||||
fn init(&mut self, screen: Arc<Screen>) {
|
fn init(&mut self, ctx: &super::Context) {
|
||||||
|
let ctx = ctx.clone();
|
||||||
std::thread::spawn({
|
std::thread::spawn({
|
||||||
move || {
|
move || {
|
||||||
let mut tick = 0;
|
let mut tick = 0;
|
||||||
loop {
|
loop {
|
||||||
for widget in screen.widgets.lock().unwrap().iter() {
|
for widget in ctx.get_widgets().iter() {
|
||||||
Self::apply_velocity_xy(tick, widget);
|
Self::apply_velocity_xy(tick, widget);
|
||||||
}
|
}
|
||||||
if tick > 1000 {
|
if tick > 1000 {
|
||||||
|
|||||||
+28
-23
@@ -33,6 +33,7 @@ use std::sync::{Arc, Mutex};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
extensions::{Extension, Handler},
|
extensions::{Extension, Handler},
|
||||||
|
prelude::Context,
|
||||||
render_scope::RenderScope,
|
render_scope::RenderScope,
|
||||||
widget::{BoxedElement, DynWidget, Element, StaticWidget, Widget, WidgetLoad},
|
widget::{BoxedElement, DynWidget, Element, StaticWidget, Widget, WidgetLoad},
|
||||||
};
|
};
|
||||||
@@ -108,31 +109,22 @@ impl Screen {
|
|||||||
|
|
||||||
/// Draws a static element and returns its widget handle.
|
/// Draws a static element and returns its widget handle.
|
||||||
pub fn draw<E: Element + 'static + Send + Sync>(self: &Arc<Self>, element: E) -> Arc<Widget> {
|
pub fn draw<E: Element + 'static + Send + Sync>(self: &Arc<Self>, element: E) -> Arc<Widget> {
|
||||||
let w = Arc::new(Widget::Static(StaticWidget::new(Box::new(element))));
|
self.draw_box(Box::new(element))
|
||||||
self.widgets.lock().unwrap().push(w.clone());
|
|
||||||
w
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draws a boxed element and returns its widget handle.
|
/// Draws a boxed element and returns its widget handle.
|
||||||
pub fn draw_box(self: &Arc<Self>, element: BoxedElement) -> Arc<Widget> {
|
pub fn draw_box(self: &Arc<Self>, element: BoxedElement) -> Arc<Widget> {
|
||||||
let w = Arc::new(Widget::Static(StaticWidget::new(element)));
|
let w = Arc::new(Widget::Static(StaticWidget::new(element)));
|
||||||
self.widgets.lock().unwrap().push(w.clone());
|
self.draw_widget(w.clone());
|
||||||
w
|
w
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds an existing widget to the screen.
|
|
||||||
pub fn draw_widget(self: &Arc<Self>, widget: Arc<Widget>) {
|
|
||||||
self.widgets.lock().unwrap().push(widget);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Draws a dynamic element using a closure and returns its widget handle.
|
/// Draws a dynamic element using a closure and returns its widget handle.
|
||||||
pub fn draw_dyn<F: FnMut() -> WidgetLoad + 'static + Send + Sync>(
|
pub fn draw_dyn<F: FnMut() -> WidgetLoad + 'static + Send + Sync>(
|
||||||
self: &Arc<Self>,
|
self: &Arc<Self>,
|
||||||
element: F,
|
element: F,
|
||||||
) -> Arc<Widget> {
|
) -> Arc<Widget> {
|
||||||
let w = Arc::new(Widget::Dynamic(DynWidget::new(element)));
|
self.draw_box_dyn(Box::new(element))
|
||||||
self.widgets.lock().unwrap().push(w.clone());
|
|
||||||
w
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draws a dynamic element from a boxed closure and returns its widget handle.
|
/// Draws a dynamic element from a boxed closure and returns its widget handle.
|
||||||
@@ -141,10 +133,20 @@ impl Screen {
|
|||||||
element: Box<dyn FnMut() -> WidgetLoad + Send + Sync>,
|
element: Box<dyn FnMut() -> WidgetLoad + Send + Sync>,
|
||||||
) -> Arc<Widget> {
|
) -> Arc<Widget> {
|
||||||
let w = Arc::new(Widget::Dynamic(DynWidget::new(element)));
|
let w = Arc::new(Widget::Dynamic(DynWidget::new(element)));
|
||||||
self.widgets.lock().unwrap().push(w.clone());
|
self.draw_widget(w.clone());
|
||||||
w
|
w
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds an existing widget to the screen.
|
||||||
|
pub fn draw_widget(self: &Arc<Self>, widget: Arc<Widget>) {
|
||||||
|
if self.widgets.lock().unwrap().len() == 0 {
|
||||||
|
widget.set_focused(true); // first widget is focused
|
||||||
|
self.widgets.lock().unwrap().push(widget);
|
||||||
|
} else {
|
||||||
|
self.widgets.lock().unwrap().push(widget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Registers an extension with the screen.
|
/// Registers an extension with the screen.
|
||||||
pub fn extension<E: Extension + Send + Sync + 'static>(self: &Arc<Self>, ext: E) {
|
pub fn extension<E: Extension + Send + Sync + 'static>(self: &Arc<Self>, ext: E) {
|
||||||
self.extensions
|
self.extensions
|
||||||
@@ -157,14 +159,16 @@ impl Screen {
|
|||||||
///
|
///
|
||||||
/// This method blocks and repeatedly renders the screen at a fixed interval.
|
/// This method blocks and repeatedly renders the screen at a fixed interval.
|
||||||
pub fn run(self: &Arc<Self>) -> std::io::Result<()> {
|
pub fn run(self: &Arc<Self>) -> std::io::Result<()> {
|
||||||
|
let ctx = Context::new(self.clone());
|
||||||
|
|
||||||
for ext in self.extensions.lock().unwrap().iter() {
|
for ext in self.extensions.lock().unwrap().iter() {
|
||||||
ext.lock().unwrap().init(self.clone());
|
ext.lock().unwrap().init(&ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
utils::hide_cursor()?;
|
utils::hide_cursor()?;
|
||||||
|
|
||||||
while *self.running.lock().unwrap() {
|
while *self.running.lock().unwrap() {
|
||||||
self.render()?;
|
self.render(&ctx)?;
|
||||||
std::thread::sleep(std::time::Duration::from_millis(28));
|
std::thread::sleep(std::time::Duration::from_millis(28));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,17 +178,18 @@ impl Screen {
|
|||||||
/// Renders all widgets and applies extensions.
|
/// Renders all widgets and applies extensions.
|
||||||
///
|
///
|
||||||
/// This method is called internally by `run`.
|
/// This method is called internally by `run`.
|
||||||
pub fn render(self: &Arc<Self>) -> std::io::Result<()> {
|
pub fn render(self: &Arc<Self>, ctx: &Context) -> std::io::Result<()> {
|
||||||
let mut scope = RenderScope::new();
|
let mut scope = RenderScope::new();
|
||||||
let (w, h) = crossterm::terminal::size().unwrap();
|
let (w, h) = crossterm::terminal::size().unwrap();
|
||||||
scope.set_parent_size(w, h);
|
scope.set_parent_size(w, h);
|
||||||
|
|
||||||
|
for ext in self.extensions.lock().unwrap().iter() {
|
||||||
|
ext.lock().unwrap().render(ctx, &mut scope);
|
||||||
|
}
|
||||||
|
|
||||||
utils::clear()?;
|
utils::clear()?;
|
||||||
for elem in self.widgets.lock().unwrap().iter() {
|
for elem in self.widgets.lock().unwrap().iter() {
|
||||||
if elem.get::<NoRender>().is_some() || elem.get::<NoRenderRoot>().is_some() {
|
if elem.get::<NoRender>().is_some() || elem.get::<NoRenderRoot>().is_some() {
|
||||||
for ext in self.extensions.lock().unwrap().iter() {
|
|
||||||
ext.lock().unwrap().render_widget(&mut scope, elem);
|
|
||||||
}
|
|
||||||
elem.auto_refresh();
|
elem.auto_refresh();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -202,17 +207,17 @@ impl Screen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for ext in self.extensions.lock().unwrap().iter() {
|
for ext in self.extensions.lock().unwrap().iter() {
|
||||||
ext.lock().unwrap().render_widget(&mut scope, elem);
|
ext.lock().unwrap().render_widget(ctx, &mut scope, elem);
|
||||||
}
|
}
|
||||||
|
|
||||||
elem.get_elem().render(&mut scope);
|
elem.get_elem().render(&mut scope, ctx);
|
||||||
|
|
||||||
if let Some(t) = elem.get() {
|
if let Some(t) = elem.get() {
|
||||||
scope.set_transform(&t);
|
scope.set_transform(&t);
|
||||||
}
|
}
|
||||||
scope.draw();
|
scope.draw();
|
||||||
|
|
||||||
elem.get_elem().after_render(&mut scope);
|
elem.get_elem().after_render(&mut scope, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
elem.auto_refresh();
|
elem.auto_refresh();
|
||||||
@@ -229,7 +234,7 @@ impl Screen {
|
|||||||
utils::clear().unwrap();
|
utils::clear().unwrap();
|
||||||
|
|
||||||
for ext in self.extensions.lock().unwrap().iter() {
|
for ext in self.extensions.lock().unwrap().iter() {
|
||||||
ext.lock().unwrap().on_close(self.clone());
|
ext.lock().unwrap().on_close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use osui::prelude::*;
|
|||||||
fn main() -> std::io::Result<()> {
|
fn main() -> std::io::Result<()> {
|
||||||
let screen = Screen::new();
|
let screen = Screen::new();
|
||||||
screen.extension(InputExtension);
|
screen.extension(InputExtension);
|
||||||
|
screen.extension(RelativeFocusExtension::new());
|
||||||
|
|
||||||
demos::app(screen.clone()).draw(&screen);
|
demos::app(screen.clone()).draw(&screen);
|
||||||
|
|
||||||
|
|||||||
+76
-51
@@ -31,11 +31,11 @@ pub type BoxedComponent = Box<dyn Component + Send + Sync>;
|
|||||||
pub trait Element: Send + Sync {
|
pub trait Element: Send + Sync {
|
||||||
/// Called to perform rendering for the element.
|
/// Called to perform rendering for the element.
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
fn render(&mut self, scope: &mut RenderScope) {}
|
fn render(&mut self, scope: &mut RenderScope, ctx: &crate::extensions::Context) {}
|
||||||
|
|
||||||
/// Called after rendering, for follow-up logic or cleanup.
|
/// Called after rendering, for follow-up logic or cleanup.
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
fn after_render(&mut self, scope: &mut RenderScope) {}
|
fn after_render(&mut self, scope: &mut RenderScope, ctx: &crate::extensions::Context) {}
|
||||||
|
|
||||||
/// Called to draw child widgets, if any.
|
/// Called to draw child widgets, if any.
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
@@ -66,19 +66,24 @@ pub trait Component: Send + Sync {
|
|||||||
pub struct WidgetLoad(BoxedElement, HashMap<TypeId, BoxedComponent>);
|
pub struct WidgetLoad(BoxedElement, HashMap<TypeId, BoxedComponent>);
|
||||||
|
|
||||||
/// A widget with fixed content and no dynamic behavior.
|
/// A widget with fixed content and no dynamic behavior.
|
||||||
pub struct StaticWidget(Mutex<BoxedElement>, Mutex<HashMap<TypeId, BoxedComponent>>);
|
pub struct StaticWidget {
|
||||||
|
element: Mutex<BoxedElement>,
|
||||||
|
components: Mutex<HashMap<TypeId, BoxedComponent>>,
|
||||||
|
focused: Mutex<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
/// A widget with dynamic content and dependency tracking.
|
/// A widget with dynamic content and dependency tracking.
|
||||||
///
|
///
|
||||||
/// This widget supports reactive updates and can be rebuilt using
|
/// This widget supports reactive updates and can be rebuilt using
|
||||||
/// a provided `FnMut()` function when dependencies change.
|
/// a provided `FnMut()` function when dependencies change.
|
||||||
pub struct DynWidget(
|
pub struct DynWidget {
|
||||||
Mutex<BoxedElement>,
|
element: Mutex<BoxedElement>,
|
||||||
Mutex<HashMap<TypeId, BoxedComponent>>,
|
components: Mutex<HashMap<TypeId, BoxedComponent>>,
|
||||||
Mutex<Box<dyn FnMut() -> WidgetLoad + Send + Sync>>,
|
load: Mutex<Box<dyn FnMut() -> WidgetLoad + Send + Sync>>,
|
||||||
Mutex<Vec<Box<dyn DependencyHandler>>>,
|
dependencies: Mutex<Vec<Box<dyn DependencyHandler>>>,
|
||||||
Mutex<Option<Box<dyn FnMut(WidgetLoad) -> WidgetLoad + Send + Sync>>>,
|
injection: Mutex<Option<Box<dyn FnMut(WidgetLoad) -> WidgetLoad + Send + Sync>>>,
|
||||||
);
|
focused: Mutex<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
/// A reference-counted wrapper around either a static or dynamic widget.
|
/// A reference-counted wrapper around either a static or dynamic widget.
|
||||||
///
|
///
|
||||||
@@ -117,22 +122,29 @@ impl WidgetLoad {
|
|||||||
|
|
||||||
impl Widget {
|
impl Widget {
|
||||||
pub fn new_static(e: BoxedElement) -> Self {
|
pub fn new_static(e: BoxedElement) -> Self {
|
||||||
Self::Static(StaticWidget(Mutex::new(e), Mutex::new(HashMap::new())))
|
Self::Static(StaticWidget::new(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_dyn<F: FnMut() -> WidgetLoad + 'static + Send + Sync>(mut e: F) -> Self {
|
pub fn new_dyn<F: FnMut() -> WidgetLoad + 'static + Send + Sync>(e: F) -> Self {
|
||||||
let wl = e();
|
Self::Dynamic(DynWidget::new(e))
|
||||||
Self::Dynamic(DynWidget(
|
|
||||||
Mutex::new(wl.0),
|
|
||||||
Mutex::new(wl.1),
|
|
||||||
Mutex::new(Box::new(e)),
|
|
||||||
Mutex::new(Vec::new()),
|
|
||||||
Mutex::new(None),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Widget {
|
impl Widget {
|
||||||
|
pub fn is_focused(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Dynamic(w) => *w.focused.lock().unwrap(),
|
||||||
|
Self::Static(w) => *w.focused.lock().unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_focused(&self, f: bool) {
|
||||||
|
match self {
|
||||||
|
Self::Dynamic(w) => *w.focused.lock().unwrap() = f,
|
||||||
|
Self::Static(w) => *w.focused.lock().unwrap() = f,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_elem(&self) -> MutexGuard<BoxedElement> {
|
pub fn get_elem(&self) -> MutexGuard<BoxedElement> {
|
||||||
match self {
|
match self {
|
||||||
Widget::Static(w) => w.get_elem(),
|
Widget::Static(w) => w.get_elem(),
|
||||||
@@ -188,7 +200,7 @@ impl Widget {
|
|||||||
}
|
}
|
||||||
Widget::Static(w) => {
|
Widget::Static(w) => {
|
||||||
let wl = WidgetLoad::new(String::new());
|
let wl = WidgetLoad::new(String::new());
|
||||||
w.1.lock().unwrap().extend(f(wl).1);
|
w.components.lock().unwrap().extend(f(wl).1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -236,12 +248,14 @@ impl Widget {
|
|||||||
wrapper.call(self, e);
|
wrapper.call(self, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
match &**self {
|
if self.is_focused() {
|
||||||
Widget::Dynamic(w) => {
|
match &**self {
|
||||||
w.get_elem().event(e);
|
Widget::Dynamic(w) => {
|
||||||
}
|
w.get_elem().event(e);
|
||||||
Widget::Static(w) => {
|
}
|
||||||
w.get_elem().event(e);
|
Widget::Static(w) => {
|
||||||
|
w.get_elem().event(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -250,17 +264,21 @@ impl Widget {
|
|||||||
impl StaticWidget {
|
impl StaticWidget {
|
||||||
fn after_render(&self) {}
|
fn after_render(&self) {}
|
||||||
fn get_elem(&self) -> MutexGuard<BoxedElement> {
|
fn get_elem(&self) -> MutexGuard<BoxedElement> {
|
||||||
self.0.lock().unwrap()
|
self.element.lock().unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StaticWidget {
|
impl StaticWidget {
|
||||||
pub fn new(e: BoxedElement) -> Self {
|
pub fn new(e: BoxedElement) -> Self {
|
||||||
Self(Mutex::new(e), Mutex::new(HashMap::new()))
|
Self {
|
||||||
|
element: Mutex::new(e),
|
||||||
|
components: Mutex::new(HashMap::new()),
|
||||||
|
focused: Mutex::new(false),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn component<C: Component + 'static>(&self, c: C) {
|
pub fn component<C: Component + 'static>(&self, c: C) {
|
||||||
self.1
|
self.components
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.entry(c.type_id())
|
.entry(c.type_id())
|
||||||
@@ -268,11 +286,14 @@ impl StaticWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_component<C: Component + 'static>(&self, c: C) {
|
pub fn set_component<C: Component + 'static>(&self, c: C) {
|
||||||
self.1.lock().unwrap().insert(c.type_id(), Box::new(c));
|
self.components
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.insert(c.type_id(), Box::new(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get<C: Component + 'static + Clone>(&self) -> Option<C> {
|
pub fn get<C: Component + 'static + Clone>(&self) -> Option<C> {
|
||||||
self.1
|
self.components
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.get(&TypeId::of::<C>())
|
.get(&TypeId::of::<C>())
|
||||||
@@ -284,43 +305,44 @@ impl StaticWidget {
|
|||||||
impl DynWidget {
|
impl DynWidget {
|
||||||
fn after_render(&self) {}
|
fn after_render(&self) {}
|
||||||
fn get_elem(&self) -> MutexGuard<BoxedElement> {
|
fn get_elem(&self) -> MutexGuard<BoxedElement> {
|
||||||
self.0.lock().unwrap()
|
self.element.lock().unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DynWidget {
|
impl DynWidget {
|
||||||
pub fn new<F: FnMut() -> WidgetLoad + 'static + Send + Sync>(mut e: F) -> Self {
|
pub fn new<F: FnMut() -> WidgetLoad + 'static + Send + Sync>(mut e: F) -> Self {
|
||||||
let wl = e();
|
let wl = e();
|
||||||
Self(
|
Self {
|
||||||
Mutex::new(wl.0),
|
element: Mutex::new(wl.0),
|
||||||
Mutex::new(wl.1),
|
components: Mutex::new(wl.1),
|
||||||
Mutex::new(Box::new(e)),
|
load: Mutex::new(Box::new(e)),
|
||||||
Mutex::new(Vec::new()),
|
dependencies: Mutex::new(Vec::new()),
|
||||||
Mutex::new(None),
|
injection: Mutex::new(None),
|
||||||
)
|
focused: Mutex::new(false),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Replace or modify the widget's structure on reload and init.
|
/// Replace or modify the widget's structure on reload and init.
|
||||||
pub fn inject<F: FnMut(WidgetLoad) -> WidgetLoad + 'static + Send + Sync>(&self, f: F) {
|
pub fn inject<F: FnMut(WidgetLoad) -> WidgetLoad + 'static + Send + Sync>(&self, f: F) {
|
||||||
*self.4.lock().unwrap() = Some(Box::new(f));
|
*self.injection.lock().unwrap() = Some(Box::new(f));
|
||||||
self.refresh();
|
self.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Rebuild the widget's content by re-evaluating the original function.
|
/// Rebuild the widget's content by re-evaluating the original function.
|
||||||
pub fn refresh(&self) {
|
pub fn refresh(&self) {
|
||||||
let mut w = (self.2.lock().unwrap())();
|
let mut w = (self.load.lock().unwrap())();
|
||||||
|
|
||||||
if let Some(we) = &mut *self.4.lock().unwrap() {
|
if let Some(we) = &mut *self.injection.lock().unwrap() {
|
||||||
w = (we)(w);
|
w = (we)(w);
|
||||||
}
|
}
|
||||||
|
|
||||||
*self.0.lock().unwrap() = w.0;
|
*self.element.lock().unwrap() = w.0;
|
||||||
*self.1.lock().unwrap() = w.1;
|
*self.components.lock().unwrap() = w.1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Re-evaluates the widget if any dependency has changed.
|
/// Re-evaluates the widget if any dependency has changed.
|
||||||
pub fn auto_refresh(&self) {
|
pub fn auto_refresh(&self) {
|
||||||
for d in self.3.lock().unwrap().iter() {
|
for d in self.dependencies.lock().unwrap().iter() {
|
||||||
if d.check() {
|
if d.check() {
|
||||||
self.refresh();
|
self.refresh();
|
||||||
}
|
}
|
||||||
@@ -330,17 +352,17 @@ impl DynWidget {
|
|||||||
/// Adds a dependency to this widget.
|
/// Adds a dependency to this widget.
|
||||||
pub fn dependency<D: DependencyHandler + 'static>(&self, d: D) {
|
pub fn dependency<D: DependencyHandler + 'static>(&self, d: D) {
|
||||||
d.add();
|
d.add();
|
||||||
self.3.lock().unwrap().push(Box::new(d));
|
self.dependencies.lock().unwrap().push(Box::new(d));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a boxed dependency.
|
/// Adds a boxed dependency.
|
||||||
pub fn dependency_box(&self, d: Box<dyn DependencyHandler>) {
|
pub fn dependency_box(&self, d: Box<dyn DependencyHandler>) {
|
||||||
d.add();
|
d.add();
|
||||||
self.3.lock().unwrap().push(d);
|
self.dependencies.lock().unwrap().push(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn component<C: Component + 'static>(&self, c: C) {
|
pub fn component<C: Component + 'static>(&self, c: C) {
|
||||||
self.1
|
self.components
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.entry(c.type_id())
|
.entry(c.type_id())
|
||||||
@@ -348,11 +370,14 @@ impl DynWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_component<C: Component + 'static>(&self, c: C) {
|
pub fn set_component<C: Component + 'static>(&self, c: C) {
|
||||||
self.1.lock().unwrap().insert(c.type_id(), Box::new(c));
|
self.components
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.insert(c.type_id(), Box::new(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get<C: Component + 'static + Clone>(&self) -> Option<C> {
|
pub fn get<C: Component + 'static + Clone>(&self) -> Option<C> {
|
||||||
self.1
|
self.components
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.get(&TypeId::of::<C>())
|
.get(&TypeId::of::<C>())
|
||||||
|
|||||||
Reference in New Issue
Block a user