Added focus #26
This commit is contained in:
+5
-4
@@ -46,16 +46,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 };
|
|
||||||
static Div { // static only affects the element, not children
|
|
||||||
%count
|
%count
|
||||||
"This will increment every second: {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 { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ impl Element for FlexRow {
|
|||||||
ctx: &crate::extensions::Context,
|
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);
|
||||||
|
|
||||||
@@ -91,6 +92,7 @@ impl Element for FlexRow {
|
|||||||
}
|
}
|
||||||
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>) {
|
||||||
@@ -123,6 +125,7 @@ impl Element for FlexCol {
|
|||||||
ctx: &crate::extensions::Context,
|
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);
|
||||||
|
|
||||||
@@ -160,6 +163,7 @@ impl Element for FlexCol {
|
|||||||
ctx.after_render(elem, scope);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,259 @@
|
|||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use super::Extension;
|
||||||
|
|
||||||
|
use crossterm::event::{Event as CrosstermEvent, KeyCode};
|
||||||
|
|
||||||
|
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) => 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() {
|
||||||
|
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())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Direction {
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
}
|
||||||
|
|
||||||
|
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,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::*;
|
||||||
@@ -24,6 +26,7 @@ pub trait Extension {
|
|||||||
fn init(&mut self, _ctx: &Context) {}
|
fn init(&mut self, _ctx: &Context) {}
|
||||||
fn event(&mut self, _ctx: &Context, _event: &dyn Event) {}
|
fn event(&mut self, _ctx: &Context, _event: &dyn Event) {}
|
||||||
fn on_close(&mut self) {}
|
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 render_widget(&mut self, _ctx: &Context, _scope: &mut RenderScope, _widget: &Arc<Widget>) {}
|
||||||
fn after_render_widget(
|
fn after_render_widget(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|||||||
@@ -183,6 +183,10 @@ impl Screen {
|
|||||||
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() {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user