-
-
Notifications
You must be signed in to change notification settings - Fork 346
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
feat(barchart): allow to render charts smaller than 3 lines #532
Conversation
Codecov Report
@@ Coverage Diff @@
## main #532 +/- ##
==========================================
- Coverage 89.95% 89.65% -0.30%
==========================================
Files 41 41
Lines 11347 11437 +90
==========================================
+ Hits 10207 10254 +47
- Misses 1140 1183 +43
|
9f17095
to
11aac65
Compare
Hey, I'd really like to see the The other part that I'd suggest is moving away from is testing private methods like impl<'a> Widget for BarChart<'a> {
fn render(mut self, mut area: Rect, buf: &mut Buffer) {
buf.set_style(area, self.style);
self.render_block(area, buf);
let area = self.layout_block(area);
if area.is_emtpy() || self.groups.is_empty() || self.bar_width == 0 {
return;
}
match self.direction {
Direction::Vertical => self.render_vertical(area, buf),
Direction::Horizontal => self.render_horizontal(area, buf),
}
}
}
impl<'a> BarChart<'a> {
fn render_block(&self, area: Rect, buf: &mut Buffer) {
self.block.as_ref().map(|&block| block.render(area, buf));
}
fn layout_block(&self, area: Rect) -> Rect {
self.block
.as_ref()
.map_or_else(|| area, |&block| block.inner(area))
}
fn render_vertical(self, area: Rect, buf: &mut Buffer) {
let max_value = self.max_value();
let layout = self.layout_vertical(area);
let (bar_height, label_height, group_label_height) = self.heights(area);
for (group, group_area) in self.groups.iter().zip(layout) {
group.render_vertical_bars(
max_value,
bar_height,
self.bar_style,
self.value_style,
group_area,
buf,
);
group.render_vertical_labels(label_height, self.label_style, group_area, buf);
group.render_vertical_group_label(
group_label_height,
self.label_style,
group_area,
buf,
);
}
}
/// Returns a Rect for each group that will contain all the bars of this group.
fn layout_vertical(&self, area: Rect) -> Vec<Rect> {
self.groups
.iter()
.map(|g| g.layout_vertical(area, self.bar_width, self.bar_gap))
.scan(0, |x, mut group_area| {
// layout the groups from left to right ensuring that the group width is not
// greater than the available width. Groups beyond the available width are
// truncated
group_area.x += *x;
group_area.width = group_area.width.min(area.width.saturating_sub(*x));
*x = x
.saturating_add(group_area.width)
.saturating_add(self.group_gap);
Some(group_area)
})
.collect()
}
fn heights(&self, area: Rect) -> (u16, u16, u16) {
let has_group_labels = self.groups.iter().any(BarGroup::has_label);
let has_bar_labels = self.groups.iter().any(BarGroup::has_bar_labels);
// ensure that the height of the leftover area is at least 1 so that the bars can be
// rendered, and that labels get precedence over group labels
let bars_height = area
.height
.saturating_sub(has_group_labels as u16 + has_bar_labels as u16)
.max(1);
let labels_height = area.height.saturating_sub(bars_height).min(1);
let group_label_height = area
.height
.saturating_sub(bars_height)
.saturating_sub(labels_height)
.min(1);
(bars_height, labels_height, group_label_height)
}
fn max_value(&self) -> u64 {
self.max
.or_else(|| self.groups.iter().flat_map(BarGroup::max).max())
.unwrap_or_default()
.max(1)
} Does this make more sense? |
11aac65
to
ac0c627
Compare
I agree. I removed
done
maybe 😸 Anyway this is a second try, it is for sure not what you expect. But please, if it is far from your expectation or making the code worse, then don't hesitate to close the PR. Of course I will be happy to tweek/change small things, if this PR is more or less still acceptable. (I appreciate very much your taking the time and maintaining this wonderful library ❤️ ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks - looks good to me.
I'm approving this, (with a couple of quick nits to fix).
Regarding the "beloved antipattern" ;)
There's a strong rationale behind why I find it easier to read the refactored code. In general, code that is immutable is easier to reason about, as you never have to keep in mind which things have happened earlier to fields. The subjective readability measure comes from not having to read all of the entire code in order to understand what each field will be set to at various points in the creation / rendering of the widget, and not having to look for mutations inside several levels of for loops.
The remove invisible groups and bars approach (which mutates the state) works because the widget is consumed on render. If we change the widget to be borrowed in the future, then the entire logic for the widget needs to rewritten (this is something that we're considering).
I don't think I quite understand this comment. The |
Add an internal structure `LabelInfo`, which stores the reserved height for the labels (0, 1 or 2) and also the state of the labels, whether they will be shown or not. The bar values are not shown, if the value width is equal the bar width and the bar is height is less than one line fixes ratatui-org#513 Signed-off-by: Ben Fekih, Hichem <[email protected]>
ac0c627
to
2906948
Compare
@joshka I agree with everything you said. Maybe this weekend I will try to create a PR which removes my nice function |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Thanks for the PR and updates.
Merged with a small rewording of the commit message |
The bar values are not shown if the value width is equal the bar width and the bar is height is less than one line Add an internal structure `LabelInfo` which stores the reserved height for the labels (0, 1 or 2) and also whether the labels will be shown. Fixes ratatui-org#513 Signed-off-by: Ben Fekih, Hichem <[email protected]>
Add an internal structure
LabelInfo
, which stores the reserved height for the labels (0, 1 or 2) and also the state of the labels, whether they will be shown or not.Add internal property to the Bar
transformed_value
, which is used only for in the vertical charts. This value is needed in 2 reasons:transformed_value is line height multiplied by 8.
(8 is the number of element of symbols::bar::Set struct)
fixes #513