buflist: Add support for multiple columns
All checks were successful
CI / lint (push) Successful in 1m44s
CI / Build and test (, beta) (push) Successful in 4m3s
CI / Build and test (, 1.73.0) (push) Successful in 4m21s
CI / Build and test (, nightly) (push) Successful in 5m6s

This commit is contained in:
2023-11-18 11:30:35 +01:00
parent 56bab04a5a
commit f4ea84b862
4 changed files with 128 additions and 34 deletions

View File

@ -36,6 +36,19 @@ impl Buflist {
config,
}
}
pub fn num_columns(&mut self, area: Rect, buffers: &crate::buffers::Buffers) -> u16 {
let borders_height = 2;
buffers
.len()
.div_ceil((area.height - borders_height).into())
.try_into()
.unwrap_or(u16::MAX)
}
pub fn borders_width(&self) -> u16 {
2
}
}
impl Component for Buflist {
@ -54,35 +67,59 @@ impl Component for Buflist {
area: Rect,
buffers: &crate::buffers::Buffers,
) -> Result<()> {
let block = Block::new().borders(Borders::ALL);
let inner_area = block.inner(area);
block.render(area, frame.buffer_mut());
let area = inner_area;
let num_digits = u32::min(5, buffers.len().ilog10() + 1) as usize;
let right_pad = " ".repeat(area.width.into());
let mut stack = Vec::new(); // List of parent buffers of the current one
frame.render_widget(
Paragraph::new(
buffers
.iter()
.enumerate()
.map(|(i, buf)| {
match buffers
.parents()
.get(&buf.id())
.into_iter()
.flatten()
.flat_map(|parent| stack.iter().position(|id| *id == *parent))
.max() // if in multiple spaces, the last one is most likely to matter
{
Some(parent_position) => stack.truncate(parent_position + 1),
None => stack.clear(),
}
stack.push(buf.id());
let buf_number = format!("{}.", i + 1);
let mut base_style = Style::default();
if i == buffers.active_index() {
base_style = base_style.on_blue();
}
let tree_pad = " ".repeat(stack.len() - 1);
let buf_number_style = base_style.green();
let left_pad = " ".repeat((num_digits + 1).saturating_sub(buf_number.len()));
let mut buffers_iter = buffers.iter().enumerate();
let max_columns = u16::min(
self.config.layout.buflist.max_columns,
area.width.div_ceil(self.config.layout.buflist.column_width),
);
let column_width = self.config.layout.buflist.column_width;
for col in 0..max_columns {
let x = area.y + col * column_width;
let allow_overflow = if col == max_columns - 2 {
self.config.layout.buflist.penultimate_right_overflow
} else if col == max_columns - 1 {
// Always allow the last column to overflow
true
} else {
false
};
for y in area.x..(area.x + area.height) {
let Some((i, buf)) = buffers_iter.next() else {
return Ok(());
};
match buffers
.parents()
.get(&buf.id())
.into_iter()
.flatten()
.flat_map(|parent| stack.iter().position(|id| *id == *parent))
.max() // if in multiple spaces, the last one is most likely to matter
{
Some(parent_position) => stack.truncate(parent_position + 1),
None => stack.clear(),
}
stack.push(buf.id());
let buf_number = format!("{}.", i + 1);
let mut base_style = Style::default();
if i == buffers.active_index() {
base_style = base_style.on_blue();
} else {
base_style = base_style.bg(Color::Reset);
}
let tree_pad = " ".repeat(stack.len() - 1);
let buf_number_style = base_style.green();
let left_pad = " ".repeat((num_digits + 1).saturating_sub(buf_number.len()));
frame.render_widget(
Paragraph::new::<Line<'_>>(
vec![
Span::styled(left_pad, base_style),
Span::styled(buf_number, buf_number_style),
@ -90,13 +127,25 @@ impl Component for Buflist {
Span::styled(buf.short_name(), base_style),
Span::styled(right_pad.clone(), base_style),
]
.into()
})
.collect::<Vec<Line<'_>>>(),
)
.block(Block::new().borders(Borders::ALL)),
area,
);
.into(),
),
Rect {
x,
y,
width: if allow_overflow {
area.width
} else {
column_width
},
height: 2,
}
.intersection(area),
);
}
}
// TODO: Do something with the remaining buffers, if any. eg. print "XX more buffers"
// at the end or display a scrollbar
Ok(())
}

View File

@ -118,7 +118,17 @@ impl Component for Home {
) -> Result<()> {
let layout = Layout::default()
.direction(Direction::Horizontal)
.constraints(vec![Constraint::Percentage(20), Constraint::Percentage(80)])
.constraints(vec![
Constraint::Length(
self.config.layout.buflist.column_width
* u16::min(
self.buflist.num_columns(area, buffers),
self.config.layout.buflist.max_columns,
)
+ self.buflist.borders_width(),
),
Constraint::Min(self.config.layout.backlog.min_width),
])
.split(area);
self

View File

@ -40,6 +40,24 @@ fn default_device_name() -> String {
}
}
#[derive(Clone, Debug, Deserialize)]
pub struct BuflistLayoutConfig {
pub column_width: u16,
pub max_columns: u16,
pub penultimate_right_overflow: bool,
}
#[derive(Clone, Debug, Deserialize)]
pub struct BacklogLayoutConfig {
pub min_width: u16,
}
#[derive(Clone, Debug, Deserialize)]
pub struct LayoutConfig {
pub buflist: BuflistLayoutConfig,
pub backlog: BacklogLayoutConfig,
}
#[derive(Clone, Debug, Deserialize)]
pub struct Config {
#[serde(default, flatten)]
@ -47,6 +65,7 @@ pub struct Config {
pub accounts: nonempty::NonEmpty<AccountConfig>,
#[serde(default)]
pub keybindings: KeyBindings,
pub layout: LayoutConfig,
#[serde(default)]
pub styles: Styles,
}

View File

@ -5,3 +5,19 @@
"<Ctrl-z>" = "/suspend"
"<Alt-left>" = "/previous"
"<Alt-right>" = "/next"
# Configuration for the list of rooms on the right.
[layout.buflist]
# How long room names can be before being truncated.
column_width = 30
# Maximum number of columns allowed. Rooms which do not fit will not be visible.
# If the screen is not large enough, fewer columns may be displayed.
max_columns = 3
# If the last column has unused space at the end, allows the last-but-one column to use
# it when its own room names are too long
penultimate_right_overflow = true
[layout.backlog]
# Minimum width of the main chat history area and text input.
# This takes precedence over the buflist settings.
min_width = 30