Fix overlapping events to only split columns for overlapping event groups
Implemented clustering algorithm in calculate_event_layout that: - Only creates column splits for events that actually overlap - Non-overlapping events maintain full width display - Uses greedy column assignment for overlapping groups - Preserves proper column indices for each event 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1169,59 +1169,88 @@ fn events_overlap(event1: &VEvent, event2: &VEvent) -> bool {
|
||||
|
||||
// Calculate layout columns for overlapping events
|
||||
fn calculate_event_layout(events: &[VEvent], date: NaiveDate) -> Vec<(usize, usize)> {
|
||||
// Filter events that should appear on this date and sort by start time
|
||||
|
||||
// Filter and sort events that should appear on this date
|
||||
let mut day_events: Vec<_> = events.iter()
|
||||
.filter(|event| {
|
||||
.enumerate()
|
||||
.filter_map(|(idx, event)| {
|
||||
let (_, _, _) = calculate_event_position(event, date);
|
||||
// Only include events that would be positioned (non-zero dimensions or all-day)
|
||||
let local_start = event.dtstart.with_timezone(&Local);
|
||||
let event_date = local_start.date_naive();
|
||||
event_date == date ||
|
||||
(event_date == date - chrono::Duration::days(1) && local_start.hour() >= 20)
|
||||
if event_date == date ||
|
||||
(event_date == date - chrono::Duration::days(1) && local_start.hour() >= 20) {
|
||||
Some((idx, event))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Sort by start time
|
||||
day_events.sort_by_key(|event| event.dtstart.with_timezone(&Local).naive_local());
|
||||
day_events.sort_by_key(|(_, event)| event.dtstart.with_timezone(&Local).naive_local());
|
||||
|
||||
// Calculate layout: (column_index, total_columns)
|
||||
let mut layout = Vec::with_capacity(day_events.len());
|
||||
let mut columns: Vec<Vec<&VEvent>> = Vec::new();
|
||||
// For each event, find all events it overlaps with
|
||||
let mut event_columns = vec![(0, 1); events.len()]; // (column_idx, total_columns)
|
||||
|
||||
for event in &day_events {
|
||||
// Find the first column where this event doesn't overlap with any existing event
|
||||
let mut placed = false;
|
||||
for (col_idx, column) in columns.iter_mut().enumerate() {
|
||||
if !column.iter().any(|existing_event| events_overlap(event, existing_event)) {
|
||||
column.push(event);
|
||||
layout.push((col_idx, 0)); // total_columns will be set later
|
||||
placed = true;
|
||||
break;
|
||||
for i in 0..day_events.len() {
|
||||
let (orig_idx_i, event_i) = day_events[i];
|
||||
|
||||
// Find all events that overlap with this event
|
||||
let mut overlapping_events = vec![i];
|
||||
for j in 0..day_events.len() {
|
||||
if i != j {
|
||||
let (_, event_j) = day_events[j];
|
||||
if events_overlap(event_i, event_j) {
|
||||
overlapping_events.push(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !placed {
|
||||
// Create new column
|
||||
columns.push(vec![event]);
|
||||
layout.push((columns.len() - 1, 0)); // total_columns will be set later
|
||||
}
|
||||
}
|
||||
|
||||
// Update total_columns for all events
|
||||
let total_columns = columns.len();
|
||||
for (_, total_cols) in layout.iter_mut() {
|
||||
*total_cols = total_columns;
|
||||
}
|
||||
|
||||
// Create result mapping original events to their layout
|
||||
let mut result = Vec::with_capacity(events.len());
|
||||
for event in events {
|
||||
if let Some(pos) = day_events.iter().position(|e| e.uid == event.uid) {
|
||||
result.push(layout[pos]);
|
||||
// If this event doesn't overlap with anything, it gets full width
|
||||
if overlapping_events.len() == 1 {
|
||||
event_columns[orig_idx_i] = (0, 1);
|
||||
} else {
|
||||
result.push((0, 1)); // Default: single column
|
||||
// This event overlaps - we need to calculate column layout
|
||||
// Sort the overlapping group by start time
|
||||
overlapping_events.sort_by_key(|&idx| day_events[idx].1.dtstart.with_timezone(&Local).naive_local());
|
||||
|
||||
// Assign columns using a greedy algorithm
|
||||
let mut columns: Vec<Vec<usize>> = Vec::new();
|
||||
|
||||
for &event_idx in &overlapping_events {
|
||||
let (orig_idx, event) = day_events[event_idx];
|
||||
|
||||
// Find the first column where this event doesn't overlap with existing events
|
||||
let mut placed = false;
|
||||
for (col_idx, column) in columns.iter_mut().enumerate() {
|
||||
let can_place = column.iter().all(|&existing_idx| {
|
||||
let (_, existing_event) = day_events[existing_idx];
|
||||
!events_overlap(event, existing_event)
|
||||
});
|
||||
|
||||
if can_place {
|
||||
column.push(event_idx);
|
||||
event_columns[orig_idx] = (col_idx, columns.len());
|
||||
placed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if !placed {
|
||||
// Create new column
|
||||
columns.push(vec![event_idx]);
|
||||
event_columns[orig_idx] = (columns.len() - 1, columns.len());
|
||||
}
|
||||
}
|
||||
|
||||
// Update total_columns for all events in this overlapping group
|
||||
let total_columns = columns.len();
|
||||
for &event_idx in &overlapping_events {
|
||||
let (orig_idx, _) = day_events[event_idx];
|
||||
event_columns[orig_idx].1 = total_columns;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
event_columns
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user