Add priorities to VJoin/HJoin Segments
This commit is contained in:
parent
f3b804347d
commit
a50f81f9b3
2 changed files with 151 additions and 55 deletions
|
|
@ -30,7 +30,7 @@ pub fn msg<M: Msg>(highlighted: bool, indent: usize, msg: &M) -> BoxedWidget {
|
|||
Segment::new(Padding::new(Text::new(msg.nick())).right(1)),
|
||||
// TODO Minimum content width
|
||||
// TODO Minimizing and maximizing messages
|
||||
Segment::new(Text::new(msg.content()).wrap(true)),
|
||||
Segment::new(Text::new(msg.content()).wrap(true)).priority(1),
|
||||
])
|
||||
.into()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use super::{BoxedWidget, Widget};
|
|||
pub struct Segment {
|
||||
widget: BoxedWidget,
|
||||
expanding: bool,
|
||||
priority: Option<u8>,
|
||||
}
|
||||
|
||||
impl Segment {
|
||||
|
|
@ -13,32 +14,141 @@ impl Segment {
|
|||
Self {
|
||||
widget: widget.into(),
|
||||
expanding: false,
|
||||
priority: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Expand this segment into the remaining space after all segment minimum
|
||||
/// sizes have been determined. The remaining space is split up evenly.
|
||||
pub fn expanding(mut self, active: bool) -> Self {
|
||||
self.expanding = active;
|
||||
self
|
||||
}
|
||||
|
||||
/// The size of segments with a priority is calculated in order of
|
||||
/// increasing priority, using the remaining available space as maximum
|
||||
/// space for the widget during size calculations.
|
||||
///
|
||||
/// Widgets without priority are processed first without size restrictions.
|
||||
pub fn priority(mut self, priority: u8) -> Self {
|
||||
self.priority = Some(priority);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
fn expand(amounts: &mut [(u16, bool)], total: u16) {
|
||||
let any_expanding = amounts.iter().any(|(_, expanding)| *expanding);
|
||||
if !any_expanding {
|
||||
struct SizedSegment {
|
||||
idx: usize,
|
||||
size: Size,
|
||||
expanding: bool,
|
||||
priority: Option<u8>,
|
||||
}
|
||||
|
||||
impl SizedSegment {
|
||||
pub fn new(idx: usize, segment: &Segment) -> Self {
|
||||
Self {
|
||||
idx,
|
||||
size: Size::ZERO,
|
||||
expanding: segment.expanding,
|
||||
priority: segment.priority,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn sizes_horiz(
|
||||
segments: &[Segment],
|
||||
frame: &mut Frame,
|
||||
max_width: Option<u16>,
|
||||
max_height: Option<u16>,
|
||||
) -> Vec<SizedSegment> {
|
||||
let mut sized = segments
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, s)| SizedSegment::new(i, s))
|
||||
.collect::<Vec<_>>();
|
||||
sized.sort_by_key(|s| s.priority);
|
||||
|
||||
let mut total_width = 0;
|
||||
for s in &mut sized {
|
||||
let available_width = max_width
|
||||
.filter(|_| s.priority.is_some())
|
||||
.map(|w| w.saturating_sub(total_width));
|
||||
s.size = segments[s.idx]
|
||||
.widget
|
||||
.size(frame, available_width, max_height);
|
||||
total_width += s.size.width;
|
||||
}
|
||||
|
||||
sized
|
||||
}
|
||||
|
||||
fn sizes_vert(
|
||||
segments: &[Segment],
|
||||
frame: &mut Frame,
|
||||
max_width: Option<u16>,
|
||||
max_height: Option<u16>,
|
||||
) -> Vec<SizedSegment> {
|
||||
let mut sized = segments
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, s)| SizedSegment::new(i, s))
|
||||
.collect::<Vec<_>>();
|
||||
sized.sort_by_key(|s| s.priority);
|
||||
|
||||
let mut total_height = 0;
|
||||
for s in &mut sized {
|
||||
let available_height = max_height
|
||||
.filter(|_| s.priority.is_some())
|
||||
.map(|w| w.saturating_sub(total_height));
|
||||
s.size = segments[s.idx]
|
||||
.widget
|
||||
.size(frame, max_width, available_height);
|
||||
total_height += s.size.height;
|
||||
}
|
||||
|
||||
sized
|
||||
}
|
||||
|
||||
fn expand_horiz(segments: &mut [SizedSegment], available_width: u16) {
|
||||
if !segments.iter().any(|s| s.expanding) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Weirdly, rustc needs this type annotation while rust-analyzer manages to
|
||||
// derive the correct type in an inlay hint.
|
||||
let actual: u16 = amounts.iter().map(|(a, _)| *a).sum();
|
||||
if actual < total {
|
||||
let mut remaining = total - actual;
|
||||
while remaining > 0 {
|
||||
for (amount, expanding) in amounts.iter_mut() {
|
||||
if *expanding {
|
||||
if remaining > 0 {
|
||||
*amount += 1;
|
||||
remaining -= 1;
|
||||
// Interestingly, rustc needs this type annotation while rust-analyzer
|
||||
// manages to derive the correct type in an inlay hint.
|
||||
let current_width = segments.iter().map(|s| s.size.width).sum::<u16>();
|
||||
if current_width < available_width {
|
||||
let mut remaining_width = available_width - current_width;
|
||||
while remaining_width > 0 {
|
||||
for segment in segments.iter_mut() {
|
||||
if segment.expanding {
|
||||
if remaining_width > 0 {
|
||||
segment.size.width += 1;
|
||||
remaining_width -= 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_vert(segments: &mut [SizedSegment], available_height: u16) {
|
||||
if !segments.iter().any(|s| s.expanding) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Interestingly, rustc needs this type annotation while rust-analyzer
|
||||
// manages to derive the correct type in an inlay hint.
|
||||
let current_height = segments.iter().map(|s| s.size.height).sum::<u16>();
|
||||
if current_height < available_height {
|
||||
let mut remaining_height = available_height - current_height;
|
||||
while remaining_height > 0 {
|
||||
for segment in segments.iter_mut() {
|
||||
if segment.expanding {
|
||||
if remaining_height > 0 {
|
||||
segment.size.height += 1;
|
||||
remaining_height -= 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
|
@ -61,34 +171,27 @@ impl HJoin {
|
|||
|
||||
#[async_trait]
|
||||
impl Widget for HJoin {
|
||||
fn size(&self, frame: &mut Frame, _max_width: Option<u16>, max_height: Option<u16>) -> Size {
|
||||
let mut size = Size::ZERO;
|
||||
for segment in &self.segments {
|
||||
let widget_size = segment.widget.size(frame, None, max_height);
|
||||
size.width += widget_size.width;
|
||||
size.height = size.height.max(widget_size.height);
|
||||
}
|
||||
size
|
||||
fn size(&self, frame: &mut Frame, max_width: Option<u16>, max_height: Option<u16>) -> Size {
|
||||
let sizes = sizes_horiz(&self.segments, frame, max_width, max_height);
|
||||
let width = sizes.iter().map(|s| s.size.width).sum::<u16>();
|
||||
let height = sizes.iter().map(|s| s.size.height).max().unwrap_or(0);
|
||||
Size::new(width, height)
|
||||
}
|
||||
|
||||
async fn render(self: Box<Self>, frame: &mut Frame) {
|
||||
let size = frame.size();
|
||||
let mut widths = self
|
||||
.segments
|
||||
.iter()
|
||||
.map(|s| {
|
||||
let width = s.widget.size(frame, None, Some(size.height)).width;
|
||||
(width, s.expanding)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
expand(&mut widths, size.width);
|
||||
|
||||
let mut sizes = sizes_horiz(&self.segments, frame, Some(size.width), Some(size.height));
|
||||
expand_horiz(&mut sizes, size.width);
|
||||
|
||||
sizes.sort_by_key(|s| s.idx);
|
||||
let mut x = 0;
|
||||
for (segment, (width, _)) in self.segments.into_iter().zip(widths.into_iter()) {
|
||||
frame.push(Pos::new(x, 0), Size::new(width, size.height));
|
||||
for (segment, sized) in self.segments.into_iter().zip(sizes.into_iter()) {
|
||||
frame.push(Pos::new(x, 0), Size::new(sized.size.width, size.height));
|
||||
segment.widget.render(frame).await;
|
||||
frame.pop();
|
||||
x += width as i32;
|
||||
|
||||
x += sized.size.width as i32;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -106,34 +209,27 @@ impl VJoin {
|
|||
|
||||
#[async_trait]
|
||||
impl Widget for VJoin {
|
||||
fn size(&self, frame: &mut Frame, max_width: Option<u16>, _max_height: Option<u16>) -> Size {
|
||||
let mut size = Size::ZERO;
|
||||
for segment in &self.segments {
|
||||
let widget_size = segment.widget.size(frame, max_width, None);
|
||||
size.width = size.width.max(widget_size.width);
|
||||
size.height += widget_size.height;
|
||||
}
|
||||
size
|
||||
fn size(&self, frame: &mut Frame, max_width: Option<u16>, max_height: Option<u16>) -> Size {
|
||||
let sizes = sizes_vert(&self.segments, frame, max_width, max_height);
|
||||
let width = sizes.iter().map(|s| s.size.width).max().unwrap_or(0);
|
||||
let height = sizes.iter().map(|s| s.size.height).sum::<u16>();
|
||||
Size::new(width, height)
|
||||
}
|
||||
|
||||
async fn render(self: Box<Self>, frame: &mut Frame) {
|
||||
let size = frame.size();
|
||||
let mut heights = self
|
||||
.segments
|
||||
.iter()
|
||||
.map(|s| {
|
||||
let height = s.widget.size(frame, Some(size.width), None).height;
|
||||
(height, s.expanding)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
expand(&mut heights, size.height);
|
||||
|
||||
let mut sizes = sizes_vert(&self.segments, frame, Some(size.width), Some(size.height));
|
||||
expand_vert(&mut sizes, size.height);
|
||||
|
||||
sizes.sort_by_key(|s| s.idx);
|
||||
let mut y = 0;
|
||||
for (segment, (height, _)) in self.segments.into_iter().zip(heights.into_iter()) {
|
||||
frame.push(Pos::new(0, y), Size::new(size.width, height));
|
||||
for (segment, sized) in self.segments.into_iter().zip(sizes.into_iter()) {
|
||||
frame.push(Pos::new(0, y), Size::new(size.width, sized.size.height));
|
||||
segment.widget.render(frame).await;
|
||||
frame.pop();
|
||||
y += height as i32;
|
||||
|
||||
y += sized.size.height as i32;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue