Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support simple focusing of items with mouse and Tab key #3

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 30 additions & 13 deletions examples/simple_flexbox.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use cursive::{
theme::{BorderStyle, ColorStyle, Palette, Theme},
view::IntoBoxedView,
views::{Layer, Panel, TextView},
Cursive, CursiveExt,
view::{IntoBoxedView, Nameable, Resizable},
views::{Button, EditView, Layer, Panel, TextArea, TextView},
Cursive, CursiveExt, With,
};

use cursive_flexbox::prelude::*;
Expand All @@ -27,8 +27,12 @@ fn main() {
))
.into_boxed_view(),
Panel::new(Layer::with_color(
TextView::new("I doubt I will be wrapped..."),
ColorStyle::back(cursive::theme::BaseColor::Green.dark()),
EditView::new()
.with(|v| {
v.set_content("I doubt I will be wrapped...");
})
.min_width(28),
ColorStyle::back(cursive::theme::BaseColor::Blue.dark()),
))
.into_boxed_view(),
Panel::new(Layer::with_color(
Expand All @@ -37,16 +41,29 @@ fn main() {
))
.into_boxed_view(),
Panel::new(Layer::with_color(
TextView::new(
"And a bigger container\nto test out the alignment\nof items in the main \
axis\na bit better.",
),
ColorStyle::back(cursive::theme::BaseColor::Green.dark()),
TextArea::new()
.with(|v| {
v.set_content(
"And a bigger container\nto test out the alignment\nof items in the main \
axis\na bit better.\n\nEdit me.",
);
})
.min_width(20),
ColorStyle::back(cursive::theme::BaseColor::Blue.dark()),
))
.into_boxed_view(),
Panel::new(Layer::with_color(
TextView::new("And a final item for good luck."),
ColorStyle::back(cursive::theme::BaseColor::Green.dark()),
Button::new("And a final button for good luck.", |c| {
let mut flexbox = c.find_name::<Flexbox>("flexbox").unwrap();
let new_align = match flexbox.align_items() {
AlignItems::FlexStart => AlignItems::Center,
AlignItems::Center => AlignItems::FlexEnd,
AlignItems::FlexEnd => AlignItems::Stretch,
AlignItems::Stretch => AlignItems::FlexStart,
};
flexbox.set_align_items(new_align);
}),
ColorStyle::back(cursive::theme::BaseColor::Red.dark()),
))
.into_boxed_view(),
]);
Expand Down Expand Up @@ -77,7 +94,7 @@ fn main() {
flexbox.set_flex_direction(FlexDirection::Row);

// Add the flexbox to the ui.
cursive.add_fullscreen_layer(flexbox);
cursive.add_fullscreen_layer(flexbox.with_name("flexbox"));

// Start running the eventloop.
cursive.run();
Expand Down
76 changes: 67 additions & 9 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@ use std::{
rc::{Rc, Weak},
};

use cursive_core::{event::EventResult, view::IntoBoxedView, Rect, Vec2, View, XY};
use cursive_core::{
direction::{Absolute, Direction, Relative},
event::{Event, EventResult, Key},
view::IntoBoxedView,
Rect, Vec2, View, XY,
};
use layout::{Layout, PlacedElement};

/// A container that can be used to display a list of items in a flexible way.
Expand Down Expand Up @@ -940,16 +945,54 @@ impl Flexbox {
});
result
}

/// Attempt to focus the given item from the given source.
fn try_focus(&mut self, child_index: usize, source: Direction) -> Option<EventResult> {
if let Some(view) = self.content.get(child_index) {
if let Ok(result) = view.borrow_mut().view.take_focus(source) {
self.focused = Some(child_index);
return Some(result);
}
}
None
}

/// Handle relative focus cycle towards the front or back of the container.
fn move_focus_rel(&mut self, target: Relative) -> EventResult {
let adv = if target == Relative::Back {
-1
} else {
1
};
let child_count = self.content.len();
let get_next = |n| (n as isize + adv).rem_euclid(child_count as isize) as usize;
let mut attempts = child_count - 1;
let mut next = if let Some(n) = self.focused {
get_next(n)
} else {
0
};
while attempts > 0 {
if let Some(result) = self.try_focus(next, Direction::Rel(target)) {
return result;
}
next = get_next(next);
attempts -= 1;
}
EventResult::Ignored
}
}

impl View for Flexbox {
/// Draw this view using the printer.
fn draw(&self, printer: &cursive_core::Printer<'_, '_>) {
if let Some(ref layout) = self.layout {
for placed_element in layout {
RefCell::borrow(&placed_element.element)
.view
.draw(&printer.windowed(placed_element.position));
for (i, placed_element) in layout.iter().enumerate() {
RefCell::borrow(&placed_element.element).view.draw(
&printer
.windowed(placed_element.position)
.focused(Some(i) == self.focused),
);
}
}
}
Expand Down Expand Up @@ -992,7 +1035,13 @@ impl View for Flexbox {
&mut self,
mut event: cursive_core::event::Event,
) -> cursive_core::event::EventResult {
if let cursive_core::event::Event::Mouse {
if let Some(result) = match event {
Event::Shift(Key::Tab) => Some(self.move_focus_rel(Relative::Back)),
Event::Key(Key::Tab) => Some(self.move_focus_rel(Relative::Front)),
_ => None,
} {
result
} else if let cursive_core::event::Event::Mouse {
ref mut offset,
ref mut position,
..
Expand All @@ -1003,9 +1052,18 @@ impl View for Flexbox {
layout.element_at(global_to_view_coordinates(*position, *offset))
{
*offset = *offset + placed_element.position.top_left();
RefCell::borrow_mut(&placed_element.element)
.view
.on_event(event)
if let Some(index) = self
.content
.iter()
.position(|v| v.as_ptr() == placed_element.element.as_ptr())
{
self.try_focus(index, Direction::Abs(Absolute::None));
self.content[index].borrow_mut().view.on_event(event)
} else {
RefCell::borrow_mut(&placed_element.element)
.view
.on_event(event)
}
} else {
EventResult::Ignored
}
Expand Down