mirror of
https://github.com/ClementTsang/bottom.git
synced 2025-07-31 01:24:31 +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",
|
"float-ord",
|
||||||
"futures",
|
"futures",
|
||||||
"futures-timer",
|
"futures-timer",
|
||||||
|
"gapbuffer",
|
||||||
"heim",
|
"heim",
|
||||||
"indextree",
|
"indextree",
|
||||||
"itertools",
|
"itertools",
|
||||||
@ -708,6 +709,12 @@ dependencies = [
|
|||||||
"slab",
|
"slab",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gapbuffer"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3e929b3ff01e4accdce7f5596a044890b5052ab7418ba8ce9ce5865d26ae4417"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
|
@ -18,7 +18,6 @@ path = "src/bin/main.rs"
|
|||||||
doc = false
|
doc = false
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
test = false
|
|
||||||
doctest = false
|
doctest = false
|
||||||
doc = false
|
doc = false
|
||||||
|
|
||||||
@ -43,6 +42,7 @@ enum_dispatch = "0.3.7"
|
|||||||
float-ord = "0.3.2"
|
float-ord = "0.3.2"
|
||||||
futures = "0.3.14"
|
futures = "0.3.14"
|
||||||
futures-timer = "3.0.2" # TODO: Remove?
|
futures-timer = "3.0.2" # TODO: Remove?
|
||||||
|
gapbuffer = "0.1.1"
|
||||||
indextree = "4.3.1" # TODO: Remove?
|
indextree = "4.3.1" # TODO: Remove?
|
||||||
itertools = "0.10.0"
|
itertools = "0.10.0"
|
||||||
once_cell = "1.5.2"
|
once_cell = "1.5.2"
|
||||||
|
@ -12,6 +12,13 @@ pub struct StyleSheet {
|
|||||||
pub border: Style,
|
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
|
/// A [`Block`] is a widget that draws a border around a child [`Component`], as well as optional
|
||||||
/// titles.
|
/// titles.
|
||||||
pub struct Block<Message, Child>
|
pub struct Block<Message, Child>
|
||||||
@ -51,49 +58,30 @@ where
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inner_rect(&self, original: Rect) -> Rect {
|
pub fn borders(mut self, borders: Borders) -> Self {
|
||||||
let mut inner = original;
|
self.borders = borders;
|
||||||
|
self
|
||||||
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)
|
|
||||||
|
fn border_offsets(&self) -> BorderOffsets {
|
||||||
|
fn border_val(has_val: bool) -> u16 {
|
||||||
|
if has_val {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.left_text.is_some()
|
||||||
|| self.right_text.is_some()
|
|| self.right_text.is_some(),
|
||||||
{
|
),
|
||||||
inner.y = inner.y.saturating_add(1).min(inner.bottom());
|
bottom: border_val(self.borders.intersects(Borders::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
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
outer
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,20 +96,20 @@ where
|
|||||||
B: Backend,
|
B: Backend,
|
||||||
{
|
{
|
||||||
let rect = draw_ctx.global_rect();
|
let rect = draw_ctx.global_rect();
|
||||||
|
if rect.area() > 0 {
|
||||||
frame.render_widget(
|
frame.render_widget(
|
||||||
tui::widgets::Block::default()
|
tui::widgets::Block::default()
|
||||||
.borders(self.borders)
|
.borders(self.borders)
|
||||||
.border_style(self.style_sheet.border),
|
.border_style(self.style_sheet.border),
|
||||||
rect,
|
rect,
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(child) = &mut self.child {
|
if let Some(child) = &mut self.child {
|
||||||
if let Some(child_draw_ctx) = draw_ctx.children().next() {
|
if let Some(child_draw_ctx) = draw_ctx.children().next() {
|
||||||
child.draw(state_ctx, &child_draw_ctx, frame)
|
child.draw(state_ctx, &child_draw_ctx, frame)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn on_event(
|
fn on_event(
|
||||||
&mut self, state_ctx: &mut StateContext<'_>, draw_ctx: &DrawContext<'_>, event: Event,
|
&mut self, state_ctx: &mut StateContext<'_>, draw_ctx: &DrawContext<'_>, event: Event,
|
||||||
@ -136,29 +124,45 @@ where
|
|||||||
Status::Ignored
|
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 {
|
if let Some(child) = &self.child {
|
||||||
// Reduce bounds based on borders
|
let BorderOffsets {
|
||||||
let inner_rect = self.inner_rect(Rect::new(0, 0, bounds.max_width, bounds.max_height));
|
left: left_offset,
|
||||||
|
right: right_offset,
|
||||||
|
top: top_offset,
|
||||||
|
bottom: bottom_offset,
|
||||||
|
} = self.border_offsets();
|
||||||
|
|
||||||
|
let vertical_offset = top_offset + bottom_offset;
|
||||||
|
let horizontal_offset = left_offset + right_offset;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
let child_bounds = Bounds {
|
let child_bounds = Bounds {
|
||||||
min_width: bounds.min_width,
|
min_width: bounds.min_width,
|
||||||
min_height: bounds.min_height,
|
min_height: bounds.min_height,
|
||||||
max_width: inner_rect.width,
|
max_width,
|
||||||
max_height: inner_rect.height,
|
max_height,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut child_node = LayoutNode::default();
|
let mut child_node = LayoutNode::default();
|
||||||
let child_size = child.layout(child_bounds, &mut child_node);
|
let child_size = child.layout(child_bounds, &mut child_node);
|
||||||
|
|
||||||
child_node.rect = Rect::new(
|
child_node.rect =
|
||||||
inner_rect.x,
|
Rect::new(left_offset, top_offset, child_size.width, child_size.height);
|
||||||
inner_rect.y,
|
|
||||||
child_size.width,
|
|
||||||
child_size.height,
|
|
||||||
);
|
|
||||||
node.children = vec![child_node];
|
node.children = vec![child_node];
|
||||||
|
|
||||||
self.outer_size(child_size)
|
Size {
|
||||||
|
width: child_size.width + horizontal_offset,
|
||||||
|
height: child_size.height + vertical_offset,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Size {
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Size {
|
Size {
|
||||||
width: 0,
|
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
|
// If there is still remaining space after, distribute the rest if
|
||||||
// appropriate (e.x. current_size is too small for the bounds).
|
// appropriate (e.x. current_size is too small for the bounds).
|
||||||
if current_size.width < bounds.min_width {
|
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;
|
current_size.width = bounds.min_width;
|
||||||
}
|
}
|
||||||
if current_size.height < bounds.min_height {
|
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;
|
current_size.height = bounds.min_height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: Remove area 0 children
|
||||||
|
|
||||||
// Now that we're done determining sizes, convert all children into the appropriate
|
// Now that we're done determining sizes, convert all children into the appropriate
|
||||||
// layout nodes. Remember - parents determine children, and so, we determine
|
// layout nodes. Remember - parents determine children, and so, we determine
|
||||||
// children here!
|
// 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 tui::{backend::Backend, layout::Rect, Frame};
|
||||||
|
|
||||||
use crate::tuine::{
|
use crate::tuine::{
|
||||||
Bounds, DrawContext, Event, Key, LayoutNode, Size, StateContext, StatefulComponent, Status,
|
Bounds, BuildContext, DrawContext, Event, Key, LayoutNode, Size, StateContext,
|
||||||
TmpComponent,
|
StatefulComponent, Status, TmpComponent,
|
||||||
};
|
};
|
||||||
|
|
||||||
const MAX_TIMEOUT: Duration = Duration::from_millis(400);
|
const MAX_TIMEOUT: Duration = Duration::from_millis(400);
|
||||||
@ -145,7 +145,7 @@ where
|
|||||||
|
|
||||||
type ComponentState = ShortcutState;
|
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) =
|
let (key, state) =
|
||||||
ctx.register_and_mut_state::<_, Self::ComponentState>(Location::caller());
|
ctx.register_and_mut_state::<_, Self::ComponentState>(Location::caller());
|
||||||
let mut forest: FxHashMap<Vec<Event>, bool> = FxHashMap::default();
|
let mut forest: FxHashMap<Vec<Event>, bool> = FxHashMap::default();
|
||||||
|
@ -90,7 +90,7 @@ impl<Message> TextTable<Message> {
|
|||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
// +1 for the spacing
|
// +1 for the spacing
|
||||||
width_remaining -= width + 1;
|
width_remaining = width_remaining.saturating_sub(width + 1);
|
||||||
width
|
width
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -372,7 +372,7 @@ impl<Message> TmpComponent<Message> for TextTable<Message> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::tuine::{
|
use crate::tuine::{
|
||||||
text_table::SortType, StateMap, StatefulComponent, TextTableProps, BuildContext,
|
text_table::SortType, BuildContext, StateMap, StatefulComponent, TextTableProps,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{DataRow, TextTable};
|
use super::{DataRow, TextTable};
|
||||||
|
@ -27,28 +27,3 @@ pub struct TimeGraph {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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 mod banner;
|
||||||
pub use banner::*;
|
pub use banner::*;
|
||||||
|
|
||||||
// pub mod stateless;
|
|
||||||
// pub use stateless::*;
|
|
||||||
|
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
use tui::Frame;
|
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 mod state_context;
|
||||||
pub use state_context::StateContext;
|
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
|
/// A [`Size`] is sent from a child component back up to its parents after
|
||||||
/// first being given a [`Bounds`](super::Bounds) from the parent.
|
/// 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 {
|
pub struct Size {
|
||||||
/// The width that the component has determined.
|
/// The width that the component has determined.
|
||||||
pub width: u16,
|
pub width: u16,
|
||||||
|
@ -1,14 +1,22 @@
|
|||||||
//! tuine is a wrapper around tui-rs that expands on it with state management and
|
//! tuine is a wrapper around tui-rs that expands on it with state management and
|
||||||
//! event handling.
|
//! 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)
|
//! - [Crochet](https://github.com/raphlinus/crochet)
|
||||||
|
//! - [Dioxus](https://github.com/DioxusLabs/dioxus)
|
||||||
//! - [Druid](https://github.com/linebender/druid)
|
//! - [Druid](https://github.com/linebender/druid)
|
||||||
//! - [Flutter](https://flutter.dev/)
|
//! - [Flutter](https://flutter.dev/)
|
||||||
//! - [Iced](https://github.com/iced-rs/iced)
|
//! - [Iced](https://github.com/iced-rs/iced)
|
||||||
|
//! - [Jetpack Compose](https://developer.android.com/jetpack/compose)
|
||||||
//! - [React](https://reactjs.org/)
|
//! - [React](https://reactjs.org/)
|
||||||
//! - [Yew](https://yew.rs/)
|
//! - [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;
|
mod tui_rs;
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ fn test_empty_layout() {
|
|||||||
.arg("./tests/invalid_configs/empty_layout.toml")
|
.arg("./tests/invalid_configs/empty_layout.toml")
|
||||||
.assert()
|
.assert()
|
||||||
.failure()
|
.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]
|
#[test]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user