-
-
Notifications
You must be signed in to change notification settings - Fork 9
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
Overflow scroll support for fullscreen? #68
Comments
I think I re-exported I went ahead and took care of that in #70 (now released in version 0.7.0) and added a basic scrolling example to the examples directory: ![]() That should get you on the right track. The same sort of idea should work in full screen and with scroll wheel input. Let me know if you have any issues or if that doesn't meet your need! |
That was insanely fast wow. Thanks so much! Will try it out soon :D |
Ok got a chance to play with it. So the way you're doing it is like this? The overflow hidden is like a window, and you basically just slide the inner div up and down through the window using Is there a way to stay scrolled to the bottom if the text is dynamically changing? I tried something like diff --git a/examples/scrolling.rs b/examples/scrolling.rs
index 3cb8e4d..e887b64 100644
--- a/examples/scrolling.rs
+++ b/examples/scrolling.rs
@@ -1,3 +1,5 @@
+use std::time::Duration;
+
use iocraft::prelude::*;
#[derive(Default, Props)]
@@ -10,6 +12,7 @@ fn Example<'a>(props: &Props<'a>, mut hooks: Hooks) -> impl Into<AnyElement<'sta
let mut system = hooks.use_context_mut::<SystemContext>();
let mut scroll_offset = hooks.use_state(|| 0i32);
let mut should_exit = hooks.use_state(|| false);
+ let mut text = hooks.use_state(|| props.text.to_owned());
hooks.use_terminal_events({
move |event| match event {
@@ -25,6 +28,14 @@ fn Example<'a>(props: &Props<'a>, mut hooks: Hooks) -> impl Into<AnyElement<'sta
}
});
+ hooks.use_future(async move {
+ loop {
+ smol::Timer::after(Duration::from_secs(1)).await;
+ let mut text = text.write();
+ *text = text.to_owned() + "Testing!\n";
+ }
+ });
+
if should_exit.get() {
system.exit();
}
@@ -45,10 +56,9 @@ fn Example<'a>(props: &Props<'a>, mut hooks: Hooks) -> impl Into<AnyElement<'sta
overflow: Overflow::Hidden,
) {
View(
- position: Position::Absolute,
top: -scroll_offset.get(),
) {
- Text(content: props.text)
+ Text(content: text.read().to_owned())
}
}
}
|
Yeah, if you want the view to stay anchored at the bottom, just use diff --git a/examples/scrolling.rs b/examples/scrolling.rs
index 35adb68..9522a82 100644
--- a/examples/scrolling.rs
+++ b/examples/scrolling.rs
@@ -18,8 +18,8 @@ fn Example<'a>(props: &Props<'a>, mut hooks: Hooks) -> impl Into<AnyElement<'sta
TerminalEvent::Key(KeyEvent { code, kind, .. }) if kind != KeyEventKind::Release => {
match code {
KeyCode::Char('q') => should_exit.set(true),
- KeyCode::Up => scroll_offset.set((scroll_offset.get() - 1).max(0)),
- KeyCode::Down => scroll_offset.set(scroll_offset.get() + 1),
+ KeyCode::Down => scroll_offset.set((scroll_offset.get() - 1).max(0)),
+ KeyCode::Up => scroll_offset.set(scroll_offset.get() + 1),
_ => {}
}
}
@@ -56,7 +56,7 @@ fn Example<'a>(props: &Props<'a>, mut hooks: Hooks) -> impl Into<AnyElement<'sta
) {
View(
position: Position::Absolute,
- top: -scroll_offset.get(),
+ bottom: -scroll_offset.get(),
) {
Text(content: text.read().to_owned())
} I imagine next you'll want to make sure that if the view isn't scrolled all the way to the bottom, it stays put so the visible text doesn't shift on the user. This isn't nearly as easy as I'd like it to be, but it is doable: The general approach would be this: If the user is scrolling, use To do this, you'll need to do some math based on the height of the content. We can make a custom hook to get a view's current height: pub trait UseComponentHeight {
fn use_component_height(&mut self) -> u16;
}
impl<'a> UseComponentHeight for Hooks<'a, '_> {
fn use_component_height(&mut self) -> u16 {
self.use_hook(move || UseComponentHeightImpl(0)).0
}
}
struct UseComponentHeightImpl(u16);
impl Hook for UseComponentHeightImpl {
fn pre_component_draw(&mut self, drawer: &mut ComponentDrawer) {
self.0 = drawer.size().height;
}
} Then, we can make a
#[derive(Clone, Copy, Default, PartialEq)]
enum Scrolling {
#[default]
Auto,
Top(i32),
}
#[derive(Default, Props)]
struct ContentViewProps<'a> {
children: Vec<AnyElement<'a>>,
scrolling: Scrolling,
content_height_out: Option<State<u16>>,
}
#[component]
fn ContentView<'a>(
props: &mut ContentViewProps<'a>,
mut hooks: Hooks,
) -> impl Into<AnyElement<'a>> {
let height = hooks.use_component_height();
if let Some(out) = props.content_height_out.as_mut() {
if height != out.get() {
out.set(height);
}
}
element! {
View(
position: Position::Absolute,
bottom: if props.scrolling == Scrolling::Auto {
Inset::Length(0)
} else {
Inset::Auto
},
top: if let Scrolling::Top(offset) = props.scrolling {
Inset::Length(-(offset as i32))
} else {
Inset::Auto
},
) {
#(props.children.iter_mut())
}
}
} Then its parent can implement the math to switch between modes gracefully based on whether the user is scrolling: #[derive(Default, Props)]
struct Props<'a> {
text: &'a str,
}
#[component]
fn Example<'a>(props: &Props<'a>, mut hooks: Hooks) -> impl Into<AnyElement<'static>> {
let mut system = hooks.use_context_mut::<SystemContext>();
let mut scrolling = hooks.use_state(|| Scrolling::Auto);
let mut should_exit = hooks.use_state(|| false);
let mut text = hooks.use_state(|| props.text.to_owned());
let content_height = hooks.use_state(|| 0);
let scroll_area_height = 8;
hooks.use_terminal_events({
move |event| match event {
TerminalEvent::Key(KeyEvent { code, kind, .. }) if kind != KeyEventKind::Release => {
match code {
KeyCode::Char('q') => should_exit.set(true),
KeyCode::Down => {
if let Scrolling::Top(offset) = scrolling.get() {
if offset >= content_height.get() as i32 - scroll_area_height as i32 {
scrolling.set(Scrolling::Auto);
} else {
scrolling.set(Scrolling::Top(offset + 1));
}
}
}
KeyCode::Up => match scrolling.get() {
Scrolling::Auto => {
scrolling.set(Scrolling::Top(
(content_height.get() as i32 - scroll_area_height as i32 - 1)
.max(0),
));
}
Scrolling::Top(offset) => scrolling.set(Scrolling::Top(offset.max(1) - 1)),
},
_ => {}
}
}
_ => {}
}
});
hooks.use_future(async move {
loop {
smol::Timer::after(Duration::from_secs(1)).await;
let mut text = text.write();
*text = text.to_owned() + "Testing!\n";
}
});
if should_exit.get() {
system.exit();
}
element! {
View(
flex_direction: FlexDirection::Column,
padding: 2,
align_items: AlignItems::Center
) {
Text(content: "Use arrow keys to scroll. Press \"q\" to exit.")
View(
border_style: BorderStyle::DoubleLeftRight,
border_color: Color::Green,
margin: 1,
width: 78,
height: scroll_area_height + 2,
overflow: Overflow::Hidden,
) {
ContentView(
scrolling: scrolling.get(),
content_height_out: content_height,
) {
Text(content: text.read().to_owned())
}
}
}
}
} A built-in component for easy scroll views would definitely be a welcome addition, and I'll likely add them it some point. Creating a new issue here: #71 |
Hi! Thanks for making this library. It's very cool and exactly what I'd want.
I assume this isn't so trivial, but I was wondering if there's a way to handle text that overflows? The context is that I want to build a "hello world" sort of fullscreen chat application, and I'm not sure how to handle when there's more message than the size of a view can support. I saw there's https://docs.rs/iocraft/0.6.4/iocraft/enum.Overflow.html, but I think that's just inherited from taffy.
Do you have any examples of how to do scrolling?
The text was updated successfully, but these errors were encountered: