mirror of
https://github.com/ClementTsang/bottom.git
synced 2025-04-08 17:05:59 +02:00
tmp commit
This commit is contained in:
parent
d0a0cc5e8c
commit
ef44e2cf32
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -247,6 +247,7 @@ dependencies = [
|
||||
"float-ord",
|
||||
"futures",
|
||||
"futures-timer",
|
||||
"gapbuffer",
|
||||
"heim",
|
||||
"indextree",
|
||||
"itertools",
|
||||
@ -708,6 +709,12 @@ dependencies = [
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gapbuffer"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e929b3ff01e4accdce7f5596a044890b5052ab7418ba8ce9ce5865d26ae4417"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.3"
|
||||
|
@ -18,7 +18,6 @@ path = "src/bin/main.rs"
|
||||
doc = false
|
||||
|
||||
[lib]
|
||||
test = false
|
||||
doctest = false
|
||||
doc = false
|
||||
|
||||
@ -43,6 +42,7 @@ enum_dispatch = "0.3.7"
|
||||
float-ord = "0.3.2"
|
||||
futures = "0.3.14"
|
||||
futures-timer = "3.0.2" # TODO: Remove?
|
||||
gapbuffer = "0.1.1"
|
||||
indextree = "4.3.1" # TODO: Remove?
|
||||
itertools = "0.10.0"
|
||||
once_cell = "1.5.2"
|
||||
|
@ -12,6 +12,13 @@ pub struct StyleSheet {
|
||||
pub border: Style,
|
||||
}
|
||||
|
||||
struct BorderOffsets {
|
||||
left: u16,
|
||||
right: u16,
|
||||
top: u16,
|
||||
bottom: u16,
|
||||
}
|
||||
|
||||
/// A [`Block`] is a widget that draws a border around a child [`Component`], as well as optional
|
||||
/// titles.
|
||||
pub struct Block<Message, Child>
|
||||
@ -51,49 +58,30 @@ where
|
||||
self
|
||||
}
|
||||
|
||||
fn inner_rect(&self, original: Rect) -> Rect {
|
||||
let mut inner = original;
|
||||
|
||||
if self.borders.intersects(Borders::LEFT) {
|
||||
inner.x = inner.x.saturating_add(1).min(inner.right());
|
||||
inner.width = inner.width.saturating_sub(1);
|
||||
}
|
||||
if self.borders.intersects(Borders::TOP)
|
||||
|| self.left_text.is_some()
|
||||
|| self.right_text.is_some()
|
||||
{
|
||||
inner.y = inner.y.saturating_add(1).min(inner.bottom());
|
||||
inner.height = inner.height.saturating_sub(1);
|
||||
}
|
||||
if self.borders.intersects(Borders::RIGHT) {
|
||||
inner.width = inner.width.saturating_sub(1);
|
||||
}
|
||||
if self.borders.intersects(Borders::BOTTOM) {
|
||||
inner.height = inner.height.saturating_sub(1);
|
||||
}
|
||||
inner
|
||||
pub fn borders(mut self, borders: Borders) -> Self {
|
||||
self.borders = borders;
|
||||
self
|
||||
}
|
||||
|
||||
fn outer_size(&self, original: Size) -> Size {
|
||||
let mut outer = original;
|
||||
|
||||
if self.borders.intersects(Borders::LEFT) {
|
||||
outer.width = outer.width.saturating_add(1);
|
||||
}
|
||||
if self.borders.intersects(Borders::TOP)
|
||||
|| self.left_text.is_some()
|
||||
|| self.right_text.is_some()
|
||||
{
|
||||
outer.height = outer.height.saturating_add(1);
|
||||
}
|
||||
if self.borders.intersects(Borders::RIGHT) {
|
||||
outer.width = outer.width.saturating_add(1);
|
||||
}
|
||||
if self.borders.intersects(Borders::BOTTOM) {
|
||||
outer.height = outer.height.saturating_add(1);
|
||||
fn border_offsets(&self) -> BorderOffsets {
|
||||
fn border_val(has_val: bool) -> u16 {
|
||||
if has_val {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
outer
|
||||
BorderOffsets {
|
||||
left: border_val(self.borders.intersects(Borders::LEFT)),
|
||||
right: border_val(self.borders.intersects(Borders::RIGHT)),
|
||||
top: border_val(
|
||||
self.borders.intersects(Borders::TOP)
|
||||
|| self.left_text.is_some()
|
||||
|| self.right_text.is_some(),
|
||||
),
|
||||
bottom: border_val(self.borders.intersects(Borders::BOTTOM)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,17 +96,17 @@ where
|
||||
B: Backend,
|
||||
{
|
||||
let rect = draw_ctx.global_rect();
|
||||
|
||||
frame.render_widget(
|
||||
tui::widgets::Block::default()
|
||||
.borders(self.borders)
|
||||
.border_style(self.style_sheet.border),
|
||||
rect,
|
||||
);
|
||||
|
||||
if let Some(child) = &mut self.child {
|
||||
if let Some(child_draw_ctx) = draw_ctx.children().next() {
|
||||
child.draw(state_ctx, &child_draw_ctx, frame)
|
||||
if rect.area() > 0 {
|
||||
frame.render_widget(
|
||||
tui::widgets::Block::default()
|
||||
.borders(self.borders)
|
||||
.border_style(self.style_sheet.border),
|
||||
rect,
|
||||
);
|
||||
if let Some(child) = &mut self.child {
|
||||
if let Some(child_draw_ctx) = draw_ctx.children().next() {
|
||||
child.draw(state_ctx, &child_draw_ctx, frame)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -136,29 +124,45 @@ where
|
||||
Status::Ignored
|
||||
}
|
||||
|
||||
fn layout(&self, bounds: Bounds, node: &mut LayoutNode) -> crate::tuine::Size {
|
||||
fn layout(&self, bounds: Bounds, node: &mut LayoutNode) -> Size {
|
||||
if let Some(child) = &self.child {
|
||||
// Reduce bounds based on borders
|
||||
let inner_rect = self.inner_rect(Rect::new(0, 0, bounds.max_width, bounds.max_height));
|
||||
let child_bounds = Bounds {
|
||||
min_width: bounds.min_width,
|
||||
min_height: bounds.min_height,
|
||||
max_width: inner_rect.width,
|
||||
max_height: inner_rect.height,
|
||||
};
|
||||
let BorderOffsets {
|
||||
left: left_offset,
|
||||
right: right_offset,
|
||||
top: top_offset,
|
||||
bottom: bottom_offset,
|
||||
} = self.border_offsets();
|
||||
|
||||
let mut child_node = LayoutNode::default();
|
||||
let child_size = child.layout(child_bounds, &mut child_node);
|
||||
let vertical_offset = top_offset + bottom_offset;
|
||||
let horizontal_offset = left_offset + right_offset;
|
||||
|
||||
child_node.rect = Rect::new(
|
||||
inner_rect.x,
|
||||
inner_rect.y,
|
||||
child_size.width,
|
||||
child_size.height,
|
||||
);
|
||||
node.children = vec![child_node];
|
||||
if bounds.max_height > vertical_offset && bounds.max_width > horizontal_offset {
|
||||
let max_width = bounds.max_width - horizontal_offset;
|
||||
let max_height = bounds.max_height - vertical_offset;
|
||||
|
||||
self.outer_size(child_size)
|
||||
let child_bounds = Bounds {
|
||||
min_width: bounds.min_width,
|
||||
min_height: bounds.min_height,
|
||||
max_width,
|
||||
max_height,
|
||||
};
|
||||
let mut child_node = LayoutNode::default();
|
||||
let child_size = child.layout(child_bounds, &mut child_node);
|
||||
|
||||
child_node.rect =
|
||||
Rect::new(left_offset, top_offset, child_size.width, child_size.height);
|
||||
node.children = vec![child_node];
|
||||
|
||||
Size {
|
||||
width: child_size.width + horizontal_offset,
|
||||
height: child_size.height + vertical_offset,
|
||||
}
|
||||
} else {
|
||||
Size {
|
||||
width: 0,
|
||||
height: 0,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Size {
|
||||
width: 0,
|
||||
@ -167,3 +171,278 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tuine::Empty;
|
||||
|
||||
use super::*;
|
||||
|
||||
fn assert_border_offset(block: Block<(), Empty>, left: u16, right: u16, top: u16, bottom: u16) {
|
||||
let offsets = block.border_offsets();
|
||||
assert_eq!(offsets.left, left, "left offset should be equal");
|
||||
assert_eq!(offsets.right, right, "right offset should be equal");
|
||||
assert_eq!(offsets.top, top, "top offset should be equal");
|
||||
assert_eq!(offsets.bottom, bottom, "bottom offset should be equal");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_border_offset() {
|
||||
let block: Block<(), Empty> = Block::with_child(Empty::default()).borders(Borders::empty());
|
||||
assert_border_offset(block, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_border_offset() {
|
||||
let block: Block<(), Empty> = Block::with_child(Empty::default());
|
||||
assert_border_offset(block, 1, 1, 1, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn horizontal_border_offset() {
|
||||
let block: Block<(), Empty> =
|
||||
Block::with_child(Empty::default()).borders(Borders::LEFT.union(Borders::RIGHT));
|
||||
assert_border_offset(block, 1, 1, 0, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vertical_border_offset() {
|
||||
let block: Block<(), Empty> =
|
||||
Block::with_child(Empty::default()).borders(Borders::BOTTOM.union(Borders::TOP));
|
||||
assert_border_offset(block, 0, 0, 1, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn top_right() {
|
||||
let block: Block<(), Empty> =
|
||||
Block::with_child(Empty::default()).borders(Borders::RIGHT.union(Borders::TOP));
|
||||
assert_border_offset(block, 0, 1, 1, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bottom_left() {
|
||||
let block: Block<(), Empty> =
|
||||
Block::with_child(Empty::default()).borders(Borders::BOTTOM.union(Borders::LEFT));
|
||||
assert_border_offset(block, 1, 0, 0, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn full_layout() {
|
||||
let block: Block<(), Empty> = Block::with_child(Empty::default());
|
||||
let mut layout_node = LayoutNode::default();
|
||||
let bounds = Bounds {
|
||||
min_width: 0,
|
||||
min_height: 0,
|
||||
max_width: 10,
|
||||
max_height: 10,
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
block.layout(bounds, &mut layout_node),
|
||||
Size {
|
||||
width: 10,
|
||||
height: 10,
|
||||
},
|
||||
"the block should have dimensions (10, 10)."
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
layout_node.children[0].rect,
|
||||
Rect {
|
||||
x: 1,
|
||||
y: 1,
|
||||
width: 8,
|
||||
height: 8
|
||||
},
|
||||
"the only child should have an offset of (1, 1), and dimensions (8, 8)"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vertical_layout() {
|
||||
let block: Block<(), Empty> =
|
||||
Block::with_child(Empty::default()).borders(Borders::BOTTOM.union(Borders::TOP));
|
||||
let mut layout_node = LayoutNode::default();
|
||||
let bounds = Bounds {
|
||||
min_width: 0,
|
||||
min_height: 0,
|
||||
max_width: 10,
|
||||
max_height: 10,
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
block.layout(bounds, &mut layout_node),
|
||||
Size {
|
||||
width: 10,
|
||||
height: 10,
|
||||
},
|
||||
"the block should have dimensions (10, 10)."
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
layout_node.children[0].rect,
|
||||
Rect {
|
||||
x: 0,
|
||||
y: 1,
|
||||
width: 10,
|
||||
height: 8
|
||||
},
|
||||
"the only child should have an offset of (0, 1), and dimensions (10, 8)"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn horizontal_layout() {
|
||||
let block: Block<(), Empty> =
|
||||
Block::with_child(Empty::default()).borders(Borders::LEFT.union(Borders::RIGHT));
|
||||
let mut layout_node = LayoutNode::default();
|
||||
let bounds = Bounds {
|
||||
min_width: 0,
|
||||
min_height: 0,
|
||||
max_width: 10,
|
||||
max_height: 10,
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
block.layout(bounds, &mut layout_node),
|
||||
Size {
|
||||
width: 10,
|
||||
height: 10,
|
||||
},
|
||||
"the block should have dimensions (10, 10)."
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
layout_node.children[0].rect,
|
||||
Rect {
|
||||
x: 1,
|
||||
y: 0,
|
||||
width: 8,
|
||||
height: 10
|
||||
},
|
||||
"the only child should have an offset of (1, 0), and dimensions (8, 10)"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn irregular_layout_one() {
|
||||
let block: Block<(), Empty> =
|
||||
Block::with_child(Empty::default()).borders(Borders::LEFT.union(Borders::TOP));
|
||||
let mut layout_node = LayoutNode::default();
|
||||
let bounds = Bounds {
|
||||
min_width: 0,
|
||||
min_height: 0,
|
||||
max_width: 10,
|
||||
max_height: 10,
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
block.layout(bounds, &mut layout_node),
|
||||
Size {
|
||||
width: 10,
|
||||
height: 10,
|
||||
},
|
||||
"the block should have dimensions (10, 10)."
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
layout_node.children[0].rect,
|
||||
Rect {
|
||||
x: 1,
|
||||
y: 1,
|
||||
width: 9,
|
||||
height: 9
|
||||
},
|
||||
"the only child should have an offset of (1, 1), and dimensions (9, 9)"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn irregular_layout_two() {
|
||||
let block: Block<(), Empty> =
|
||||
Block::with_child(Empty::default()).borders(Borders::BOTTOM.union(Borders::RIGHT));
|
||||
let mut layout_node = LayoutNode::default();
|
||||
let bounds = Bounds {
|
||||
min_width: 0,
|
||||
min_height: 0,
|
||||
max_width: 10,
|
||||
max_height: 10,
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
block.layout(bounds, &mut layout_node),
|
||||
Size {
|
||||
width: 10,
|
||||
height: 10,
|
||||
},
|
||||
"the block should have dimensions (10, 10)."
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
layout_node.children[0].rect,
|
||||
Rect {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 9,
|
||||
height: 9
|
||||
},
|
||||
"the only child should have an offset of (0, 0), and dimensions (9, 9)"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn irregular_layout_three() {
|
||||
let block: Block<(), Empty> =
|
||||
Block::with_child(Empty::default()).borders(Borders::RIGHT.union(Borders::TOP));
|
||||
let mut layout_node = LayoutNode::default();
|
||||
let bounds = Bounds {
|
||||
min_width: 0,
|
||||
min_height: 0,
|
||||
max_width: 10,
|
||||
max_height: 10,
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
block.layout(bounds, &mut layout_node),
|
||||
Size {
|
||||
width: 10,
|
||||
height: 10,
|
||||
},
|
||||
"the block should have dimensions (10, 10)."
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
layout_node.children[0].rect,
|
||||
Rect {
|
||||
x: 0,
|
||||
y: 1,
|
||||
width: 9,
|
||||
height: 9
|
||||
},
|
||||
"the only child should have an offset of (0, 1), and dimensions (9, 9)"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn too_small_layout() {
|
||||
let block: Block<(), Empty> = Block::with_child(Empty::default());
|
||||
let mut layout_node = LayoutNode::default();
|
||||
let bounds = Bounds {
|
||||
min_width: 0,
|
||||
min_height: 0,
|
||||
max_width: 2,
|
||||
max_height: 2,
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
block.layout(bounds, &mut layout_node),
|
||||
Size {
|
||||
width: 0,
|
||||
height: 0,
|
||||
},
|
||||
"the area should be 0"
|
||||
);
|
||||
|
||||
assert_eq!(layout_node.children.len(), 0, "layout node should be empty");
|
||||
}
|
||||
}
|
||||
|
@ -191,14 +191,16 @@ impl<Message> TmpComponent<Message> for Flex<Message> {
|
||||
// If there is still remaining space after, distribute the rest if
|
||||
// appropriate (e.x. current_size is too small for the bounds).
|
||||
if current_size.width < bounds.min_width {
|
||||
// For now, we'll cheat and just set it to be equal.
|
||||
// FIXME: For now, we'll cheat and just set it to be equal.
|
||||
current_size.width = bounds.min_width;
|
||||
}
|
||||
if current_size.height < bounds.min_height {
|
||||
// For now, we'll cheat and just set it to be equal.
|
||||
// FIXME: For now, we'll cheat and just set it to be equal.
|
||||
current_size.height = bounds.min_height;
|
||||
}
|
||||
|
||||
// FIXME: Remove area 0 children
|
||||
|
||||
// Now that we're done determining sizes, convert all children into the appropriate
|
||||
// layout nodes. Remember - parents determine children, and so, we determine
|
||||
// children here!
|
||||
|
@ -0,0 +1,16 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::tuine::TmpComponent;
|
||||
|
||||
/// A [`Padding`] surrounds a child widget with spacing.
|
||||
pub struct Padding<Child, Message>
|
||||
where
|
||||
Child: TmpComponent<Message>,
|
||||
{
|
||||
_pd: PhantomData<Message>,
|
||||
padding_left: u16,
|
||||
padding_right: u16,
|
||||
padding_up: u16,
|
||||
padding_down: u16,
|
||||
child: Option<Child>,
|
||||
}
|
@ -8,8 +8,8 @@ use rustc_hash::FxHashMap;
|
||||
use tui::{backend::Backend, layout::Rect, Frame};
|
||||
|
||||
use crate::tuine::{
|
||||
Bounds, DrawContext, Event, Key, LayoutNode, Size, StateContext, StatefulComponent, Status,
|
||||
TmpComponent,
|
||||
Bounds, BuildContext, DrawContext, Event, Key, LayoutNode, Size, StateContext,
|
||||
StatefulComponent, Status, TmpComponent,
|
||||
};
|
||||
|
||||
const MAX_TIMEOUT: Duration = Duration::from_millis(400);
|
||||
@ -145,7 +145,7 @@ where
|
||||
|
||||
type ComponentState = ShortcutState;
|
||||
|
||||
fn build(ctx: &mut crate::tuine::BuildContext<'_>, props: Self::Properties) -> Self {
|
||||
fn build(ctx: &mut BuildContext<'_>, props: Self::Properties) -> Self {
|
||||
let (key, state) =
|
||||
ctx.register_and_mut_state::<_, Self::ComponentState>(Location::caller());
|
||||
let mut forest: FxHashMap<Vec<Event>, bool> = FxHashMap::default();
|
||||
|
@ -90,7 +90,7 @@ impl<Message> TextTable<Message> {
|
||||
0
|
||||
} else {
|
||||
// +1 for the spacing
|
||||
width_remaining -= width + 1;
|
||||
width_remaining = width_remaining.saturating_sub(width + 1);
|
||||
width
|
||||
}
|
||||
})
|
||||
@ -372,7 +372,7 @@ impl<Message> TmpComponent<Message> for TextTable<Message> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tuine::{
|
||||
text_table::SortType, StateMap, StatefulComponent, TextTableProps, BuildContext,
|
||||
text_table::SortType, BuildContext, StateMap, StatefulComponent, TextTableProps,
|
||||
};
|
||||
|
||||
use super::{DataRow, TextTable};
|
||||
|
@ -27,28 +27,3 @@ pub struct TimeGraph {
|
||||
}
|
||||
|
||||
impl TimeGraph {}
|
||||
|
||||
impl<Message> TmpComponent<Message> for TimeGraph {
|
||||
fn draw<Backend>(
|
||||
&mut self, state_ctx: &mut StateContext<'_>, draw_ctx: &DrawContext<'_>,
|
||||
frame: &mut Frame<'_, Backend>,
|
||||
) where
|
||||
Backend: tui::backend::Backend,
|
||||
{
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
&mut self, state_ctx: &mut StateContext<'_>, draw_ctx: &DrawContext<'_>, event: Event,
|
||||
messages: &mut Vec<Message>,
|
||||
) -> Status {
|
||||
Status::Ignored
|
||||
}
|
||||
|
||||
fn layout(&self, bounds: Bounds, node: &mut LayoutNode) -> crate::tuine::Size {
|
||||
crate::tuine::Size {
|
||||
width: bounds.max_width,
|
||||
height: bounds.max_height,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,8 +10,7 @@ pub use stateful::*;
|
||||
pub mod banner;
|
||||
pub use banner::*;
|
||||
|
||||
// pub mod stateless;
|
||||
// pub use stateless::*;
|
||||
|
||||
|
||||
use enum_dispatch::enum_dispatch;
|
||||
use tui::Frame;
|
||||
|
35
src/tuine/context/context.rs
Normal file
35
src/tuine/context/context.rs
Normal file
@ -0,0 +1,35 @@
|
||||
use gapbuffer::GapBuffer;
|
||||
|
||||
use crate::tuine::{Key, KeyCreator, State};
|
||||
|
||||
/// A [`Context`] is used to create a [`Component`](super::Component).
|
||||
///
|
||||
/// The internal implementation is based on Jetpack Compose's [Positional Memoization](https://medium.com/androiddevelopers/under-the-hood-of-jetpack-compose-part-2-of-2-37b2c20c6cdd),
|
||||
/// in addition to [Crochet](https://github.com/raphlinus/crochet/blob/master/src/tree.rs) in its entirety.
|
||||
pub struct Context {
|
||||
component_key_creator: KeyCreator,
|
||||
buffer: GapBuffer<Slot>,
|
||||
}
|
||||
|
||||
enum Payload {
|
||||
State(Box<dyn State>),
|
||||
View,
|
||||
}
|
||||
|
||||
struct Item {
|
||||
key: Key,
|
||||
payload: Payload,
|
||||
}
|
||||
|
||||
enum Slot {
|
||||
Begin(Item),
|
||||
End,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
pub fn use_state(&self) {}
|
||||
|
||||
pub fn start(&mut self) {}
|
||||
|
||||
pub fn end(&mut self) {}
|
||||
}
|
@ -9,3 +9,6 @@ pub use build_context::BuildContext;
|
||||
|
||||
pub mod state_context;
|
||||
pub use state_context::StateContext;
|
||||
|
||||
pub mod context;
|
||||
pub use context::Context;
|
||||
|
@ -30,3 +30,19 @@ impl Key {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
pub struct KeyCreator {
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl KeyCreator {
|
||||
pub fn new_key(&mut self, caller: impl Into<Caller>) -> Key {
|
||||
self.index += 1;
|
||||
Key::new(caller, self.index)
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.index = 0;
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ use std::ops::{Add, AddAssign};
|
||||
///
|
||||
/// A [`Size`] is sent from a child component back up to its parents after
|
||||
/// first being given a [`Bounds`](super::Bounds) from the parent.
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
pub struct Size {
|
||||
/// The width that the component has determined.
|
||||
pub width: u16,
|
||||
|
@ -1,14 +1,22 @@
|
||||
//! tuine is a wrapper around tui-rs that expands on it with state management and
|
||||
//! event handling.
|
||||
//!
|
||||
//! tuine is inspired by a **ton** of other libraries and frameworks, like:
|
||||
//! tuine is inspired by a **ton** of other libraries and frameworks:
|
||||
//!
|
||||
//! - [Crochet](https://github.com/raphlinus/crochet)
|
||||
//! - [Dioxus](https://github.com/DioxusLabs/dioxus)
|
||||
//! - [Druid](https://github.com/linebender/druid)
|
||||
//! - [Flutter](https://flutter.dev/)
|
||||
//! - [Iced](https://github.com/iced-rs/iced)
|
||||
//! - [Jetpack Compose](https://developer.android.com/jetpack/compose)
|
||||
//! - [React](https://reactjs.org/)
|
||||
//! - [Yew](https://yew.rs/)
|
||||
//!
|
||||
//! In addition, Raph Levien's post,
|
||||
//! [*Towards principled reactive UI](https://raphlinus.github.io/rust/druid/2020/09/25/principled-reactive-ui.html),
|
||||
//! was a fantastic source of information for someone like me who had basically zero knowledge heading in.
|
||||
//!
|
||||
//! None of this would be possible without these as reference points and sources of inspiration and learning!
|
||||
|
||||
mod tui_rs;
|
||||
|
||||
|
@ -25,7 +25,7 @@ fn test_empty_layout() {
|
||||
.arg("./tests/invalid_configs/empty_layout.toml")
|
||||
.assert()
|
||||
.failure()
|
||||
.stderr(predicate::str::contains("Configuration file error")); // FIXME: [Urgent] Use a const for the error pattern
|
||||
.stderr(predicate::str::contains("cannot be empty")); // FIXME: [Urgent] Use a const for the error pattern
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
Loading…
x
Reference in New Issue
Block a user