Skip to content

Commit 317d5ec

Browse files
authored
Merge pull request #85 from togglebyte/dev
A massive rewrite. Look at the guide for more information on how to use Anathema
2 parents b05d865 + 4ad8662 commit 317d5ec

File tree

196 files changed

+13995
-9177
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

196 files changed

+13995
-9177
lines changed

.github/workflows/rust.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
- name: Configure cache
2121
uses: Swatinem/rust-cache@v2
2222
- name: Run tests
23-
run: cargo test --workspace --all-features --verbose
23+
run: cargo test --workspace --verbose
2424

2525
lint:
2626
runs-on: ubuntu-latest

CONTRIBUTING.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Contributing to Anathema
2+
3+
Base your fork on the "dev" branch:
4+
```
5+
git clone https://github.com/togglebyte/anathema
6+
git switch dev
7+
git switch -c feature/my-new-feature
8+
```
9+
If applicable: always write tests for any new functionality being introduced.
10+
11+
Submit any PR to be merged back into the "dev" branch.
12+
13+
## Getting help
14+
15+
For help ask on [Discord](https://discord.gg/yZgdmXKf6p).

Cargo.toml

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
name = "anathema"
3-
edition = "2021"
4-
version = "0.3.0"
3+
edition = "2024"
4+
version = "0.2.0"
55
license = "MIT"
66
description = "Create beautiful, easily customisable terminal applications"
77
keywords = ["tui", "terminal", "widgets", "ui"]
@@ -12,7 +12,6 @@ homepage = "https://github.com/togglebyte/anathema"
1212
repository = "https://github.com/togglebyte/anathema"
1313

1414
[dependencies]
15-
anathema-debug = { path = "./anathema-debug" }
1615
anathema-default-widgets = { path = "./anathema-default-widgets" }
1716
anathema-backend = { path = "./anathema-backend" }
1817
anathema-runtime = { path = "./anathema-runtime" }
@@ -22,20 +21,27 @@ anathema-store = { path = "./anathema-store" }
2221
anathema-templates = { path = "./anathema-templates" }
2322
anathema-widgets = { path = "./anathema-widgets" }
2423
anathema-geometry = { path = "./anathema-geometry" }
24+
anathema-value-resolver = { path = "./anathema-value-resolver" }
25+
26+
[features]
27+
default = []
28+
profile = ["anathema-runtime/profile", "anathema-widgets/profile", "anathema-backend/profile"]
29+
# filelog = ["anathema-debug/filelog"]
2530

2631
[lints]
2732
workspace = true
2833

2934
[workspace.package]
30-
version = "0.3.0"
31-
edition = "2021"
35+
version = "0.2.0"
36+
edition = "2024"
3237

3338
[workspace.dependencies]
3439
bitflags = "2.4.1"
3540
crossterm = "0.28.1"
3641
unicode-width = "0.1.11"
3742
flume = "0.11.0"
3843
notify = "6.1.1"
44+
anathema-debug = { path = "testutils" }
3945

4046
[workspace]
4147
members = [
@@ -49,6 +55,7 @@ members = [
4955
"anathema-store",
5056
"anathema-templates",
5157
"anathema-widgets",
58+
"anathema-value-resolver",
5259
]
5360

5461
[workspace.lints.rust]
@@ -62,3 +69,6 @@ should_implement_trait = "allow"
6269
type_complexity = "allow"
6370
too_many_arguments = "allow"
6471
wrong_self_convention = "allow"
72+
73+
[dev-dependencies]
74+
testutils = { path = "testutils" }

anathema-backend/Cargo.toml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,18 @@ edition.workspace = true
77
anathema-geometry = { path = "../anathema-geometry" }
88
anathema-state = { path = "../anathema-state" }
99
anathema-store = { path = "../anathema-store" }
10-
anathema-widgets = { path = "../anathema-widgets" }
10+
anathema-strings = { path = "../anathema-strings" }
1111
anathema-templates = { path = "../anathema-templates" }
12+
anathema-value-resolver = { path = "../anathema-value-resolver" }
13+
anathema-widgets = { path = "../anathema-widgets" }
1214
crossterm = { workspace = true }
1315
unicode-width = { workspace = true }
1416
bitflags = { workspace = true }
17+
puffin = { version = "0.19.1", optional = true }
18+
19+
[features]
20+
default = []
21+
profile = ["puffin"]
1522

1623
[lints]
1724
workspace = true

anathema-backend/src/lib.rs

Lines changed: 81 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,34 @@
1+
use std::ops::ControlFlow;
12
use std::time::Duration;
23

34
use anathema_geometry::{Pos, Size};
4-
use anathema_store::tree::{AsNodePath, Node, TreeValues};
5+
use anathema_value_resolver::{AttributeStorage, Scope};
56
use anathema_widgets::components::events::Event;
6-
use anathema_widgets::layout::{layout_widget, position_widget, Constraints, LayoutCtx, LayoutFilter, Viewport};
7-
use anathema_widgets::{AttributeStorage, Element, FloatingWidgets, WidgetKind, WidgetTree};
7+
use anathema_widgets::error::Result;
8+
use anathema_widgets::layout::{Constraints, LayoutCtx, LayoutFilter, PositionFilter, Viewport};
9+
use anathema_widgets::paint::PaintFilter;
10+
use anathema_widgets::{GlyphMap, LayoutForEach, PaintChildren, PositionChildren, WidgetTreeView};
811

9-
pub mod test;
12+
pub mod testing;
1013
pub mod tui;
1114

1215
pub trait Backend {
1316
fn size(&self) -> Size;
1417

1518
fn next_event(&mut self, timeout: Duration) -> Option<Event>;
1619

17-
fn resize(&mut self, new_size: Size);
20+
fn resize(&mut self, new_size: Size, glyph_map: &mut GlyphMap);
1821

1922
/// Paint the widgets
2023
fn paint<'bp>(
2124
&mut self,
22-
element: &mut Element<'bp>,
23-
children: &[Node],
24-
values: &mut TreeValues<WidgetKind<'bp>>,
25+
glyph_map: &mut GlyphMap,
26+
widgets: PaintChildren<'_, 'bp>,
2527
attribute_storage: &AttributeStorage<'bp>,
26-
ignore_floats: bool,
2728
);
2829

2930
/// Called by the runtime at the end of the frame.
30-
fn render(&mut self);
31+
fn render(&mut self, glyph_map: &mut GlyphMap);
3132

3233
/// Clear is called immediately after `render` is called.
3334
fn clear(&mut self);
@@ -41,94 +42,91 @@ pub trait Backend {
4142
// a less silly name
4243
pub struct WidgetCycle<'rt, 'bp, T> {
4344
backend: &'rt mut T,
44-
tree: &'rt mut WidgetTree<'bp>,
45+
tree: WidgetTreeView<'rt, 'bp>,
4546
constraints: Constraints,
46-
attribute_storage: &'rt AttributeStorage<'bp>,
47-
floating_widgets: &'rt FloatingWidgets,
48-
viewport: Viewport,
4947
}
5048

5149
impl<'rt, 'bp, T: Backend> WidgetCycle<'rt, 'bp, T> {
52-
pub fn new(
53-
backend: &'rt mut T,
54-
tree: &'rt mut WidgetTree<'bp>,
55-
constraints: Constraints,
56-
attribute_storage: &'rt AttributeStorage<'bp>,
57-
floating_widgets: &'rt FloatingWidgets,
58-
viewport: Viewport,
59-
) -> Self {
50+
pub fn new(backend: &'rt mut T, tree: WidgetTreeView<'rt, 'bp>, constraints: Constraints) -> Self {
6051
Self {
6152
backend,
6253
tree,
6354
constraints,
64-
attribute_storage,
65-
floating_widgets,
66-
viewport,
6755
}
6856
}
6957

70-
fn floating(&mut self) {
71-
// Floating widgets
72-
for widget_id in self.floating_widgets.iter() {
73-
// Find the parent widget and get the position
74-
// If no parent element is found assume Pos::ZERO
75-
let mut parent = self.tree.path_ref(*widget_id).parent();
76-
let (pos, constraints) = loop {
77-
match parent {
78-
None => break (Pos::ZERO, self.constraints),
79-
Some(p) => match self.tree.get_ref_by_path(p) {
80-
Some(WidgetKind::Element(el)) => {
81-
let bounds = el.inner_bounds();
82-
break (bounds.start, Constraints::from(bounds));
83-
}
84-
_ => parent = p.parent(),
85-
},
86-
}
87-
};
88-
89-
self.tree.with_nodes_and_values(*widget_id, |widget, children, values| {
90-
let WidgetKind::Element(el) = widget else { unreachable!("this is always a floating widget") };
91-
let mut layout_ctx = LayoutCtx::new(self.attribute_storage, &self.viewport);
92-
93-
layout_widget(el, children, values, constraints, &mut layout_ctx, true);
94-
95-
// Position
96-
position_widget(pos, el, children, values, self.attribute_storage, true, self.viewport);
97-
98-
// Paint
99-
self.backend.paint(el, children, values, self.attribute_storage, true);
100-
});
58+
fn fixed(&mut self, ctx: &mut LayoutCtx<'_, 'bp>, needs_layout: bool) -> Result<()> {
59+
// -----------------------------------------------------------------------------
60+
// - Layout -
61+
// -----------------------------------------------------------------------------
62+
if needs_layout {
63+
self.layout(ctx, LayoutFilter)?;
10164
}
65+
66+
// -----------------------------------------------------------------------------
67+
// - Position -
68+
// -----------------------------------------------------------------------------
69+
self.position(ctx.attribute_storage, *ctx.viewport, PositionFilter::fixed());
70+
71+
// -----------------------------------------------------------------------------
72+
// - Paint -
73+
// -----------------------------------------------------------------------------
74+
self.paint(ctx, PaintFilter::fixed());
75+
76+
Ok(())
77+
}
78+
79+
fn floating(&mut self, ctx: &mut LayoutCtx<'_, 'bp>) -> Result<()> {
80+
// -----------------------------------------------------------------------------
81+
// - Position -
82+
// -----------------------------------------------------------------------------
83+
self.position(ctx.attribute_storage, *ctx.viewport, PositionFilter::floating());
84+
85+
// -----------------------------------------------------------------------------
86+
// - Paint -
87+
// -----------------------------------------------------------------------------
88+
self.paint(ctx, PaintFilter::floating());
89+
90+
Ok(())
10291
}
10392

104-
pub fn run(&mut self) {
105-
let mut filter = LayoutFilter::new(true, self.attribute_storage);
106-
self.tree.for_each(&mut filter).first(&mut |widget, children, values| {
107-
// Layout
108-
// TODO: once the text buffer can be read-only for the paint
109-
// the context can be made outside of this closure.
110-
//
111-
// That doesn't have as much of an impact here
112-
// as it will do when dealing with the floating widgets
113-
let mut layout_ctx = LayoutCtx::new(self.attribute_storage, &self.viewport);
114-
layout_widget(widget, children, values, self.constraints, &mut layout_ctx, true);
115-
116-
// Position
117-
position_widget(
118-
Pos::ZERO,
119-
widget,
120-
children,
121-
values,
122-
self.attribute_storage,
123-
true,
124-
self.viewport,
125-
);
126-
127-
// Paint
128-
self.backend
129-
.paint(widget, children, values, self.attribute_storage, true);
93+
pub fn run(&mut self, ctx: &mut LayoutCtx<'_, 'bp>, needs_layout: bool) -> Result<()> {
94+
self.fixed(ctx, needs_layout)?;
95+
self.floating(ctx)?;
96+
Ok(())
97+
}
98+
99+
fn layout(&mut self, ctx: &mut LayoutCtx<'_, 'bp>, filter: LayoutFilter) -> Result<()> {
100+
#[cfg(feature = "profile")]
101+
puffin::profile_function!();
102+
let tree = self.tree.view_mut();
103+
104+
let scope = Scope::root();
105+
let mut for_each = LayoutForEach::new(tree, &scope, filter, None);
106+
let constraints = self.constraints;
107+
_ = for_each.each(ctx, |ctx, widget, children| {
108+
_ = widget.layout(children, constraints, ctx)?;
109+
Ok(ControlFlow::Break(()))
110+
})?;
111+
Ok(())
112+
}
113+
114+
fn position(&mut self, attributes: &AttributeStorage<'bp>, viewport: Viewport, filter: PositionFilter) {
115+
#[cfg(feature = "profile")]
116+
puffin::profile_function!();
117+
118+
let mut for_each = PositionChildren::new(self.tree.view_mut(), attributes, filter);
119+
_ = for_each.each(|widget, children| {
120+
widget.position(children, Pos::ZERO, attributes, viewport);
121+
ControlFlow::Break(())
130122
});
123+
}
124+
125+
fn paint(&mut self, ctx: &mut LayoutCtx<'_, 'bp>, filter: PaintFilter) {
126+
#[cfg(feature = "profile")]
127+
puffin::profile_function!();
131128

132-
self.floating();
129+
let for_each = PaintChildren::new(self.tree.view_mut(), ctx.attribute_storage, filter);
130+
self.backend.paint(ctx.glyph_map, for_each, ctx.attribute_storage);
133131
}
134132
}

0 commit comments

Comments
 (0)