Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 94 additions & 3 deletions src/ui/call_window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,10 +270,15 @@ pub fn capacity(area: Vec2) -> usize {
pub struct Layout {
/// The share's area, if one is shown.
pub share: Option<Rect>,
/// Each tile.
/// Each tile, as many as there is room for.
pub tiles: Vec<Rect>,
/// How many tiles there is room for.
pub room: usize,
/// The tiles' part of the stage.
pub area: Rect,
/// The tiles are in a row under the share (a tall stage): more of
/// them than fit scroll sideways.
pub sideways: bool,
}

/// Lays out the stage for a share (or none) and `tiles` tiles: without a
Expand Down Expand Up @@ -328,9 +333,35 @@ pub fn layout(stage: Rect, share: bool, tiles: usize) -> Layout {
share,
tiles: rects,
room,
area,
sideways: area.top() > stage.top(),
}
}

/// Where every one of `count` tiles goes when only `room` of them fit
/// `area`: all at the size the room's grid gives them, in that grid's
/// columns (or one row, `sideways`), from the top left; and the size of
/// the whole, for scrolling to the rest.
pub fn overflow(count: usize, room: usize, area: Vec2, sideways: bool) -> (Vec<Rect>, Vec2) {
let (columns, _, size) = grid(room.max(1), area);
let columns = if sideways { count.max(1) } else { columns };
let rows = count.max(1).div_ceil(columns);
let rects = (0..count)
.map(|i| {
let (column, row) = (i % columns, i / columns);
Rect::from_min_size(
egui::pos2(column as f32 * (size.x + GAP), row as f32 * (size.y + GAP)),
size,
)
})
.collect();
let whole = Vec2::new(
columns as f32 * size.x + (columns - 1) as f32 * GAP,
rows as f32 * size.y + (rows - 1) as f32 * GAP,
);
(rects, whole)
}

/// A tile's size in pixels as the session hears it: rounded up to
/// [`TILE_STEP`].
pub fn tile_pixels(size: Vec2, pixels_per_point: f32) -> [u32; 2] {
Expand Down Expand Up @@ -403,8 +434,13 @@ pub fn show(
if let Some(share) = layout.share {
shown.share = share_stage(ui, view, share, pixels);
}
for (rect, tile) in layout.tiles.iter().zip(&view.tiles) {
camera_tile(ui, palette, *rect, tile, view.no_video);
if view.tiles.len() > layout.room {
// More than fit: all of them, scrolled to.
scrolled_tiles(ui, palette, view, &layout);
} else {
for (rect, tile) in layout.tiles.iter().zip(&view.tiles) {
camera_tile(ui, palette, *rect, tile, view.no_video);
}
}
if layout.share.is_none() && view.tiles.is_empty() {
note(ui, area, &t("No one has their camera on"));
Expand All @@ -421,6 +457,40 @@ pub fn show(
shown
}

/// Every tile when more than fit the tiles' part of the stage: at the
/// size the room's grid gives them, the rest scrolled to (down, or
/// sideways under a share).
fn scrolled_tiles(ui: &mut egui::Ui, palette: &Palette, view: &CallView, layout: &Layout) {
let (rects, whole) = overflow(
view.tiles.len(),
layout.room,
layout.area.size(),
layout.sideways,
);
ui.scope_builder(egui::UiBuilder::new().max_rect(layout.area), |ui| {
let scroll = if layout.sideways {
egui::ScrollArea::horizontal()
} else {
egui::ScrollArea::vertical()
};
scroll
.id_salt("call-window-tiles")
.auto_shrink(false)
.show(ui, |ui| {
let (origin, _) = ui.allocate_exact_size(whole, Sense::hover());
for (rect, tile) in rects.iter().zip(&view.tiles) {
camera_tile(
ui,
palette,
rect.translate(origin.min.to_vec2()),
tile,
view.no_video,
);
}
});
});
}

/// The share in its part of the stage; gives the size it is shown at.
fn share_stage(ui: &mut egui::Ui, view: &CallView, area: Rect, pixels: f32) -> [usize; 2] {
match view.picture {
Expand Down Expand Up @@ -880,6 +950,27 @@ mod tests {
assert_eq!(capacity(Vec2::new(5000.0, 3000.0)), huddles::MAX_TILES);
}

#[test]
fn more_tiles_than_fit_keep_the_grids_columns_and_scroll_down() {
let area = Vec2::new(640.0, 360.0);
let room = capacity(area);
let (columns, _, size) = grid(room, area);
let (rects, whole) = overflow(14, room, area, false);
assert_eq!(rects.len(), 14);
assert!(rects.iter().all(|r| r.size() == size));
assert_eq!(
rects[columns].min.x, 0.0,
"the second row starts at the left"
);
assert!(rects[columns].min.y > rects[0].max.y);
assert!(whole.y > area.y, "there is something to scroll to");
assert!(whole.x <= area.x);
// Under a share: one row, scrolled sideways.
let (row, whole) = overflow(5, 2, Vec2::new(700.0, 200.0), true);
assert!(row.iter().all(|r| r.min.y == 0.0));
assert!(whole.x > 700.0);
}

#[test]
fn tiles_go_beside_a_share_in_a_wide_window_and_below_in_a_tall_one() {
let wide = layout(rect(1264.0, 732.0), true, 3);
Expand Down
Loading