diff --git a/src/calendar/widgets/MonthCalendar.py b/src/calendar/widgets/MonthCalendar.py index ee9907e..58dbcc0 100644 --- a/src/calendar/widgets/MonthCalendar.py +++ b/src/calendar/widgets/MonthCalendar.py @@ -16,15 +16,21 @@ from textual.reactive import reactive from textual.strip import Strip from textual.widget import Widget +from src.calendar import config -def get_month_calendar(year: int, month: int) -> list[list[Optional[date]]]: + +def get_month_calendar( + year: int, month: int, week_start_day: int = 0 +) -> list[list[Optional[date]]]: """Generate a calendar grid for a month. Returns a list of weeks, where each week is a list of 7 dates (or None for empty cells). - Week starts on Monday. - """ - import calendar + Args: + year: The year + month: The month (1-12) + week_start_day: Config format (0=Sunday, 1=Monday, ..., 6=Saturday) + """ # Get first day of month and number of days first_day = date(year, month, 1) if month == 12: @@ -32,11 +38,17 @@ def get_month_calendar(year: int, month: int) -> list[list[Optional[date]]]: else: last_day = date(year, month + 1, 1) - timedelta(days=1) - # Monday = 0, Sunday = 6 - first_weekday = first_day.weekday() + # Convert config week start to python weekday + # Config: 0=Sunday, 1=Monday, ..., 6=Saturday + # Python: 0=Monday, 1=Tuesday, ..., 6=Sunday + python_week_start = (week_start_day - 1) % 7 + + # Calculate which day of the week the first day falls on (relative to week start) + first_python_weekday = first_day.weekday() # 0=Monday, 6=Sunday + days_offset = (first_python_weekday - python_week_start) % 7 weeks: list[list[Optional[date]]] = [] - current_week: list[Optional[date]] = [None] * first_weekday + current_week: list[Optional[date]] = [None] * days_offset current = first_day while current <= last_day: @@ -55,6 +67,22 @@ def get_month_calendar(year: int, month: int) -> list[list[Optional[date]]]: return weeks +def get_day_names(week_start_day: int = 0) -> str: + """Get day name headers based on week start day. + + Args: + week_start_day: Config format (0=Sunday, 1=Monday, ..., 6=Saturday) + + Returns: + String like "Su Mo Tu We Th Fr Sa" or "Mo Tu We Th Fr Sa Su" + """ + # Full list starting from Sunday (config day 0) + all_days = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"] + # Rotate to start from week_start_day + rotated = all_days[week_start_day:] + all_days[:week_start_day] + return " ".join(rotated) + + class MonthCalendar(Widget): """A compact month calendar widget for sidebars.""" @@ -122,7 +150,11 @@ class MonthCalendar(Widget): @property def _weeks(self) -> list[list[Optional[date]]]: """Get the weeks for the current display month.""" - return get_month_calendar(self.display_month.year, self.display_month.month) + return get_month_calendar( + self.display_month.year, + self.display_month.month, + config.week_start_day(), + ) def get_content_height(self, container, viewport, width: int) -> int: """Calculate height: header + day names + weeks.""" @@ -152,8 +184,8 @@ class MonthCalendar(Widget): return Strip([Segment(header, style)]) def _render_day_names(self) -> Strip: - """Render the day name headers (Mo Tu We ...).""" - day_names = "Mo Tu We Th Fr Sa Su" + """Render the day name headers based on week start setting.""" + day_names = get_day_names(config.week_start_day()) # Pad to widget width line = day_names[: self.size.width].ljust(self.size.width) style = Style(color="bright_black")