nudgeEvent method
Shifts event by hourDelta whole hours, clamped to
[config.minHour, config.maxHour], then re-lays out overlaps, repaints,
and fires PlannerConfig.onEntryMove. A screen-reader user can't drag, so
the accessibility layer exposes "Move earlier"/"Move later" nudges — the
keyboard-friendly equivalent of a drag-move. A nudge that would leave the
hour unchanged (already at the bound) is a no-op and fires nothing.
Implementation
void nudgeEvent(Event<T> event, int hourDelta) {
final time = event.entry.time;
final newHour =
(time.hour + hourDelta).clamp(config.minHour, config.maxHour);
if (newHour == time.hour) return;
// Immutable models (#27): swap in a new entry rather than mutating in place;
// _layoutOverlaps reads the new time, and onEntryMove reports the new entry.
event.entry = event.entry.copyWith(time: time.copyWith(hour: newHour));
_layoutOverlaps();
controller.triggerUpdate.value++;
config.onEntryMove?.call(event.entry);
}