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)),
|
Segment::new(Padding::new(Text::new(msg.nick())).right(1)),
|
||||||
// TODO Minimum content width
|
// TODO Minimum content width
|
||||||
// TODO Minimizing and maximizing messages
|
// TODO Minimizing and maximizing messages
|
||||||
Segment::new(Text::new(msg.content()).wrap(true)),
|
Segment::new(Text::new(msg.content()).wrap(true)).priority(1),
|
||||||
])
|
])
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ use super::{BoxedWidget, Widget};
|
||||||
pub struct Segment {
|
pub struct Segment {
|
||||||
widget: BoxedWidget,
|
widget: BoxedWidget,
|
||||||
expanding: bool,
|
expanding: bool,
|
||||||
|
priority: Option<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Segment {
|
impl Segment {
|
||||||
|
|
@ -13,32 +14,141 @@ impl Segment {
|
||||||
Self {
|
Self {
|
||||||
widget: widget.into(),
|
widget: widget.into(),
|
||||||
expanding: false,
|
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 {
|
pub fn expanding(mut self, active: bool) -> Self {
|
||||||
self.expanding = active;
|
self.expanding = active;
|
||||||
self
|
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) {
|
struct SizedSegment {
|
||||||
let any_expanding = amounts.iter().any(|(_, expanding)| *expanding);
|
idx: usize,
|
||||||
if !any_expanding {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Weirdly, rustc needs this type annotation while rust-analyzer manages to
|
// Interestingly, rustc needs this type annotation while rust-analyzer
|
||||||
// derive the correct type in an inlay hint.
|
// manages to derive the correct type in an inlay hint.
|
||||||
let actual: u16 = amounts.iter().map(|(a, _)| *a).sum();
|
let current_width = segments.iter().map(|s| s.size.width).sum::<u16>();
|
||||||
if actual < total {
|
if current_width < available_width {
|
||||||
let mut remaining = total - actual;
|
let mut remaining_width = available_width - current_width;
|
||||||
while remaining > 0 {
|
while remaining_width > 0 {
|
||||||
for (amount, expanding) in amounts.iter_mut() {
|
for segment in segments.iter_mut() {
|
||||||
if *expanding {
|
if segment.expanding {
|
||||||
if remaining > 0 {
|
if remaining_width > 0 {
|
||||||
*amount += 1;
|
segment.size.width += 1;
|
||||||
remaining -= 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 {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -61,34 +171,27 @@ impl HJoin {
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Widget for HJoin {
|
impl Widget for HJoin {
|
||||||
fn size(&self, frame: &mut Frame, _max_width: Option<u16>, max_height: Option<u16>) -> Size {
|
fn size(&self, frame: &mut Frame, max_width: Option<u16>, max_height: Option<u16>) -> Size {
|
||||||
let mut size = Size::ZERO;
|
let sizes = sizes_horiz(&self.segments, frame, max_width, max_height);
|
||||||
for segment in &self.segments {
|
let width = sizes.iter().map(|s| s.size.width).sum::<u16>();
|
||||||
let widget_size = segment.widget.size(frame, None, max_height);
|
let height = sizes.iter().map(|s| s.size.height).max().unwrap_or(0);
|
||||||
size.width += widget_size.width;
|
Size::new(width, height)
|
||||||
size.height = size.height.max(widget_size.height);
|
|
||||||
}
|
|
||||||
size
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn render(self: Box<Self>, frame: &mut Frame) {
|
async fn render(self: Box<Self>, frame: &mut Frame) {
|
||||||
let size = frame.size();
|
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;
|
let mut x = 0;
|
||||||
for (segment, (width, _)) in self.segments.into_iter().zip(widths.into_iter()) {
|
for (segment, sized) in self.segments.into_iter().zip(sizes.into_iter()) {
|
||||||
frame.push(Pos::new(x, 0), Size::new(width, size.height));
|
frame.push(Pos::new(x, 0), Size::new(sized.size.width, size.height));
|
||||||
segment.widget.render(frame).await;
|
segment.widget.render(frame).await;
|
||||||
frame.pop();
|
frame.pop();
|
||||||
x += width as i32;
|
|
||||||
|
x += sized.size.width as i32;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -106,34 +209,27 @@ impl VJoin {
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Widget for VJoin {
|
impl Widget for VJoin {
|
||||||
fn size(&self, frame: &mut Frame, max_width: Option<u16>, _max_height: Option<u16>) -> Size {
|
fn size(&self, frame: &mut Frame, max_width: Option<u16>, max_height: Option<u16>) -> Size {
|
||||||
let mut size = Size::ZERO;
|
let sizes = sizes_vert(&self.segments, frame, max_width, max_height);
|
||||||
for segment in &self.segments {
|
let width = sizes.iter().map(|s| s.size.width).max().unwrap_or(0);
|
||||||
let widget_size = segment.widget.size(frame, max_width, None);
|
let height = sizes.iter().map(|s| s.size.height).sum::<u16>();
|
||||||
size.width = size.width.max(widget_size.width);
|
Size::new(width, height)
|
||||||
size.height += widget_size.height;
|
|
||||||
}
|
|
||||||
size
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn render(self: Box<Self>, frame: &mut Frame) {
|
async fn render(self: Box<Self>, frame: &mut Frame) {
|
||||||
let size = frame.size();
|
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;
|
let mut y = 0;
|
||||||
for (segment, (height, _)) in self.segments.into_iter().zip(heights.into_iter()) {
|
for (segment, sized) in self.segments.into_iter().zip(sizes.into_iter()) {
|
||||||
frame.push(Pos::new(0, y), Size::new(size.width, height));
|
frame.push(Pos::new(0, y), Size::new(size.width, sized.size.height));
|
||||||
segment.widget.render(frame).await;
|
segment.widget.render(frame).await;
|
||||||
frame.pop();
|
frame.pop();
|
||||||
y += height as i32;
|
|
||||||
|
y += sized.size.height as i32;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue