Add HJoin and VJoin widgets
This commit is contained in:
parent
7a8a6d7418
commit
54ed495491
2 changed files with 135 additions and 0 deletions
|
|
@ -1,5 +1,6 @@
|
|||
pub mod background;
|
||||
pub mod empty;
|
||||
pub mod join;
|
||||
pub mod list;
|
||||
pub mod rules;
|
||||
pub mod text;
|
||||
|
|
|
|||
134
src/ui/widgets/join.rs
Normal file
134
src/ui/widgets/join.rs
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
use async_trait::async_trait;
|
||||
use toss::frame::{Frame, Pos, Size};
|
||||
|
||||
use super::Widget;
|
||||
|
||||
pub struct Segment {
|
||||
widget: Box<dyn Widget + Send>,
|
||||
expanding: bool,
|
||||
}
|
||||
|
||||
impl Segment {
|
||||
pub fn new<W: 'static + Widget + Send>(widget: W) -> Self {
|
||||
Self {
|
||||
widget: Box::new(widget),
|
||||
expanding: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expanding(mut self, active: bool) -> Self {
|
||||
self.expanding = active;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
fn expand(amounts: &mut [(u16, bool)], total: u16) {
|
||||
// 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;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Place multiple widgets next to each other horizontally.
|
||||
pub struct HJoin {
|
||||
segments: Vec<Segment>,
|
||||
}
|
||||
|
||||
impl HJoin {
|
||||
pub fn new(segments: Vec<Segment>) -> Self {
|
||||
Self { segments }
|
||||
}
|
||||
}
|
||||
|
||||
#[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
|
||||
}
|
||||
|
||||
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 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));
|
||||
segment.widget.render(frame).await;
|
||||
frame.pop();
|
||||
x += width as i32;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Place multiple widgets next to each other vertically.
|
||||
pub struct VJoin {
|
||||
segments: Vec<Segment>,
|
||||
}
|
||||
|
||||
impl VJoin {
|
||||
pub fn new(segments: Vec<Segment>) -> Self {
|
||||
Self { segments }
|
||||
}
|
||||
}
|
||||
|
||||
#[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
|
||||
}
|
||||
|
||||
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 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));
|
||||
segment.widget.render(frame).await;
|
||||
frame.pop();
|
||||
y += height as i32;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue