implemented recurrence exception (EXDATE) support for custom events, refactored timetable break and holiday generation logic, and refined RRule editor UI/theming and tile layouts

This commit is contained in:
2026-05-14 12:58:29 +02:00
parent 194d8d1857
commit 2cb8321d07
8 changed files with 221 additions and 48 deletions
@@ -281,26 +281,22 @@ class _CustomEventEditDialogState extends State<CustomEventEditDialog> {
),
),
const Divider(),
RRuleGenerator(
config: RRuleGeneratorConfig(
selectDayStyle: RRuleSelectDayStyle(
dayStyle: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context).colorScheme.secondary,
),
dayTextStyle: const TextStyle(color: Colors.black),
selectedDayStyle: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context).primaryColor,
),
),
// The RRuleGenerator widget has zero outer padding while every
// surrounding ListTile uses the default 16px horizontal indent.
// Wrap it to match — keeps the rule editor visually aligned with
// the date/time/color rows above instead of hugging the dialog
// border.
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
child: RRuleGenerator(
config: _rruleConfig(context),
initialRRule: _rrule,
locale: RRuleLocale.de_DE,
onChange: (newValue) {
log('Rule: $newValue');
setState(() => _rrule = newValue);
},
),
initialRRule: _rrule,
locale: RRuleLocale.de_DE,
onChange: (newValue) {
log('Rule: $newValue');
setState(() => _rrule = newValue);
},
),
],
),
@@ -312,4 +308,69 @@ class _CustomEventEditDialogState extends State<CustomEventEditDialog> {
),
],
);
/// Maps the rrule_generator widget onto Marianum's Material theme so its
/// pickers blend in with the rest of the form (the package's defaults
/// have an out-of-place red switch outline and bold ALL-CAPS-feeling
/// headers). Layout itself stays as the package provides — fully custom
/// styling beyond what `RRuleGeneratorConfig` exposes isn't possible
/// without forking it.
RRuleGeneratorConfig _rruleConfig(BuildContext context) {
final theme = Theme.of(context);
final cs = theme.colorScheme;
return RRuleGeneratorConfig(
headerStyle: RRuleHeaderStyle(
textStyle:
theme.textTheme.titleSmall?.copyWith(
fontWeight: FontWeight.w600,
color: cs.onSurface,
) ??
const TextStyle(),
),
labelStyle:
theme.textTheme.bodyMedium?.copyWith(color: cs.onSurfaceVariant) ??
const TextStyle(),
inputTextStyle: RRuleInputTextStyle(
inputTextDecoration: const InputDecoration(
isDense: true,
border: OutlineInputBorder(),
),
inputTextStyle:
theme.textTheme.bodyMedium ?? const TextStyle(),
),
dropdownStyle: RRuleDropdownStyle(
dropdownMenuTextStyle: theme.textTheme.bodyMedium ?? const TextStyle(),
dropdownMenuItemTextStyle:
theme.textTheme.bodyMedium ?? const TextStyle(),
),
switchStyle: RRuleSwitchStyle(
thumbColor: cs.onPrimary,
activeTrackColor: cs.primary,
inactiveTrackColor: cs.surfaceContainerHighest,
trackOutlineWidth: 0,
trackOutlineColor: Colors.transparent,
switchTextStyle:
theme.textTheme.bodyMedium?.copyWith(
color: cs.onSurface,
) ??
const TextStyle(),
),
selectDayStyle: RRuleSelectDayStyle(
dayStyle: BoxDecoration(
shape: BoxShape.circle,
color: cs.surfaceContainerHighest,
),
dayTextStyle: TextStyle(color: cs.onSurface),
selectedDayStyle: BoxDecoration(
shape: BoxShape.circle,
color: cs.primary,
),
selectedDayTextStyle: TextStyle(color: cs.onPrimary),
),
datePickerStyle: RRuleDatePickerStyle(
datePickerTextStyle: theme.textTheme.bodyMedium ?? const TextStyle(),
),
divider: const Divider(height: 1),
);
}
}