Add year selector controls to date range chooser
- Add Gtk.SpinButton widgets above each calendar for quick year selection - Implement bidirectional synchronization between year selectors and calendars - Set year range from 1000 to current year + 10 for genealogical data - Update clear date range and filter state management to handle year selectors - Improve UX by allowing direct year input instead of incrementing one year at a time
This commit is contained in:
parent
9976a95b97
commit
2874fe35d1
454
MyTimeline.py
454
MyTimeline.py
@ -393,6 +393,7 @@ class MyTimelineView(NavigationView):
|
||||
self.filter_enabled: bool = False
|
||||
self.active_event_types: Set[EventType] = set() # Empty = all enabled
|
||||
self.date_range_filter: Optional[Tuple[int, int]] = None # (min_date, max_date) in sort values
|
||||
self.date_range_explicit: bool = False # Track if date range was explicitly set
|
||||
self.person_filter: Optional[Set[str]] = None # Set of person handles to include, None = all
|
||||
self.category_filter: Optional[Set[str]] = None # Set of event categories to include, None = all
|
||||
self.all_events: List[TimelineEvent] = [] # Store all events before filtering
|
||||
@ -772,45 +773,138 @@ class MyTimelineView(NavigationView):
|
||||
|
||||
def _build_date_range_filter_page(self) -> Gtk.Widget:
|
||||
"""
|
||||
Build the date range filter page with date entry fields.
|
||||
Build the date range filter page with date chooser widgets.
|
||||
|
||||
Returns:
|
||||
Gtk.Widget: The date range filter page.
|
||||
"""
|
||||
scrolled = Gtk.ScrolledWindow()
|
||||
scrolled.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
|
||||
|
||||
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
|
||||
box.set_margin_start(10)
|
||||
box.set_margin_end(10)
|
||||
box.set_margin_top(10)
|
||||
box.set_margin_bottom(10)
|
||||
|
||||
info_label = Gtk.Label(label=_("Enter date range to filter events. Leave empty to show all dates."))
|
||||
info_label = Gtk.Label(label=_("Select date range to filter events. Leave unselected to show all dates."))
|
||||
info_label.set_line_wrap(True)
|
||||
box.pack_start(info_label, False, False, 0)
|
||||
|
||||
# From date
|
||||
from_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
|
||||
from_label = Gtk.Label(label=_("From:"))
|
||||
from_label.set_size_request(80, -1)
|
||||
from_entry = Gtk.Entry()
|
||||
from_entry.set_placeholder_text(_("YYYY-MM-DD or YYYY"))
|
||||
from_box.pack_start(from_label, False, False, 0)
|
||||
from_box.pack_start(from_entry, True, True, 0)
|
||||
box.pack_start(from_box, False, False, 0)
|
||||
# Calendar container
|
||||
calendar_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=15)
|
||||
calendar_box.set_homogeneous(True)
|
||||
|
||||
# To date
|
||||
to_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
|
||||
to_label = Gtk.Label(label=_("To:"))
|
||||
to_label.set_size_request(80, -1)
|
||||
to_entry = Gtk.Entry()
|
||||
to_entry.set_placeholder_text(_("YYYY-MM-DD or YYYY"))
|
||||
to_box.pack_start(to_label, False, False, 0)
|
||||
to_box.pack_start(to_entry, True, True, 0)
|
||||
box.pack_start(to_box, False, False, 0)
|
||||
# Year range for genealogical data (1000 to current year + 10)
|
||||
import datetime
|
||||
current_year = datetime.date.today().year
|
||||
min_year = 1000
|
||||
max_year = current_year + 10
|
||||
|
||||
self._filter_widgets['date_from_entry'] = from_entry
|
||||
self._filter_widgets['date_to_entry'] = to_entry
|
||||
# From date calendar with year selector
|
||||
from_frame = Gtk.Frame(label=_("From Date"))
|
||||
from_frame.set_label_align(0.5, 0.5)
|
||||
from_calendar_container = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
|
||||
from_calendar_container.set_margin_start(5)
|
||||
from_calendar_container.set_margin_end(5)
|
||||
from_calendar_container.set_margin_top(5)
|
||||
from_calendar_container.set_margin_bottom(5)
|
||||
|
||||
return box
|
||||
# Year selector for From date
|
||||
from_year_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
|
||||
from_year_label = Gtk.Label(label=_("Year:"))
|
||||
from_year_adjustment = Gtk.Adjustment(
|
||||
value=current_year,
|
||||
lower=min_year,
|
||||
upper=max_year,
|
||||
step_increment=1,
|
||||
page_increment=10
|
||||
)
|
||||
from_year_spin = Gtk.SpinButton()
|
||||
from_year_spin.set_adjustment(from_year_adjustment)
|
||||
from_year_spin.set_numeric(True)
|
||||
from_year_spin.set_update_policy(Gtk.SpinButtonUpdatePolicy.IF_VALID)
|
||||
from_year_spin.set_width_chars(6)
|
||||
from_year_spin.connect("value-changed", self._on_from_year_changed)
|
||||
from_year_box.pack_start(from_year_label, False, False, 0)
|
||||
from_year_box.pack_start(from_year_spin, False, False, 0)
|
||||
from_calendar_container.pack_start(from_year_box, False, False, 0)
|
||||
|
||||
from_calendar = Gtk.Calendar()
|
||||
from_calendar.set_display_options(
|
||||
Gtk.CalendarDisplayOptions.SHOW_HEADING |
|
||||
Gtk.CalendarDisplayOptions.SHOW_DAY_NAMES |
|
||||
Gtk.CalendarDisplayOptions.SHOW_WEEK_NUMBERS
|
||||
)
|
||||
from_calendar.connect("day-selected", self._on_from_date_selected)
|
||||
from_calendar.connect("month-changed", self._on_from_calendar_changed)
|
||||
from_calendar_container.pack_start(from_calendar, True, True, 0)
|
||||
from_frame.add(from_calendar_container)
|
||||
calendar_box.pack_start(from_frame, True, True, 0)
|
||||
|
||||
# To date calendar with year selector
|
||||
to_frame = Gtk.Frame(label=_("To Date"))
|
||||
to_frame.set_label_align(0.5, 0.5)
|
||||
to_calendar_container = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
|
||||
to_calendar_container.set_margin_start(5)
|
||||
to_calendar_container.set_margin_end(5)
|
||||
to_calendar_container.set_margin_top(5)
|
||||
to_calendar_container.set_margin_bottom(5)
|
||||
|
||||
# Year selector for To date
|
||||
to_year_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
|
||||
to_year_label = Gtk.Label(label=_("Year:"))
|
||||
to_year_adjustment = Gtk.Adjustment(
|
||||
value=current_year,
|
||||
lower=min_year,
|
||||
upper=max_year,
|
||||
step_increment=1,
|
||||
page_increment=10
|
||||
)
|
||||
to_year_spin = Gtk.SpinButton()
|
||||
to_year_spin.set_adjustment(to_year_adjustment)
|
||||
to_year_spin.set_numeric(True)
|
||||
to_year_spin.set_update_policy(Gtk.SpinButtonUpdatePolicy.IF_VALID)
|
||||
to_year_spin.set_width_chars(6)
|
||||
to_year_spin.connect("value-changed", self._on_to_year_changed)
|
||||
to_year_box.pack_start(to_year_label, False, False, 0)
|
||||
to_year_box.pack_start(to_year_spin, False, False, 0)
|
||||
to_calendar_container.pack_start(to_year_box, False, False, 0)
|
||||
|
||||
to_calendar = Gtk.Calendar()
|
||||
to_calendar.set_display_options(
|
||||
Gtk.CalendarDisplayOptions.SHOW_HEADING |
|
||||
Gtk.CalendarDisplayOptions.SHOW_DAY_NAMES |
|
||||
Gtk.CalendarDisplayOptions.SHOW_WEEK_NUMBERS
|
||||
)
|
||||
to_calendar.connect("day-selected", self._on_to_date_selected)
|
||||
to_calendar.connect("month-changed", self._on_to_calendar_changed)
|
||||
to_calendar_container.pack_start(to_calendar, True, True, 0)
|
||||
to_frame.add(to_calendar_container)
|
||||
calendar_box.pack_start(to_frame, True, True, 0)
|
||||
|
||||
box.pack_start(calendar_box, True, True, 0)
|
||||
|
||||
# Clear button
|
||||
clear_button = Gtk.Button(label=_("Clear Dates"))
|
||||
clear_button.connect("clicked", self._on_clear_date_range)
|
||||
button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||
button_box.pack_start(clear_button, False, False, 0)
|
||||
box.pack_start(button_box, False, False, 0)
|
||||
|
||||
# Validation label
|
||||
self.date_validation_label = Gtk.Label()
|
||||
self.date_validation_label.set_line_wrap(True)
|
||||
self.date_validation_label.set_markup("<span color='red'></span>")
|
||||
box.pack_start(self.date_validation_label, False, False, 0)
|
||||
|
||||
self._filter_widgets['date_from_calendar'] = from_calendar
|
||||
self._filter_widgets['date_to_calendar'] = to_calendar
|
||||
self._filter_widgets['date_from_year_spin'] = from_year_spin
|
||||
self._filter_widgets['date_to_year_spin'] = to_year_spin
|
||||
|
||||
scrolled.add(box)
|
||||
return scrolled
|
||||
|
||||
def _update_filter_dialog_state(self) -> None:
|
||||
"""
|
||||
@ -882,18 +976,61 @@ class MyTimelineView(NavigationView):
|
||||
except (AttributeError, KeyError) as e:
|
||||
logger.warning(f"Error updating person filter: {e}", exc_info=True)
|
||||
|
||||
# Update date range entries
|
||||
if 'date_from_entry' in self._filter_widgets and 'date_to_entry' in self._filter_widgets:
|
||||
from_entry = self._filter_widgets['date_from_entry']
|
||||
to_entry = self._filter_widgets['date_to_entry']
|
||||
if self.date_range_filter:
|
||||
min_date, max_date = self.date_range_filter
|
||||
# Convert sort values back to dates for display (simplified)
|
||||
from_entry.set_text("")
|
||||
to_entry.set_text("")
|
||||
# Update date range calendars and year selectors
|
||||
if 'date_from_calendar' in self._filter_widgets and 'date_to_calendar' in self._filter_widgets:
|
||||
from_calendar = self._filter_widgets['date_from_calendar']
|
||||
to_calendar = self._filter_widgets['date_to_calendar']
|
||||
|
||||
if self.date_range_filter and self.date_range_explicit:
|
||||
min_sort, max_sort = self.date_range_filter
|
||||
# Convert sort values back to dates for calendar display
|
||||
# Approximate conversion: extract year from sort value
|
||||
# Sort value is roughly: year * 10000 + month * 100 + day
|
||||
from_year = min_sort // 10000
|
||||
to_year = max_sort // 10000
|
||||
|
||||
# Set calendar years (approximate)
|
||||
current_from_year, current_from_month, current_from_day = from_calendar.get_date()
|
||||
current_to_year, current_to_month, current_to_day = to_calendar.get_date()
|
||||
|
||||
from_calendar.select_month(current_from_month, from_year)
|
||||
to_calendar.select_month(current_to_month, to_year)
|
||||
|
||||
# Update year spin buttons
|
||||
if 'date_from_year_spin' in self._filter_widgets:
|
||||
from_year_spin = self._filter_widgets['date_from_year_spin']
|
||||
from_year_spin.handler_block_by_func(self._on_from_year_changed)
|
||||
from_year_spin.set_value(from_year)
|
||||
from_year_spin.handler_unblock_by_func(self._on_from_year_changed)
|
||||
|
||||
if 'date_to_year_spin' in self._filter_widgets:
|
||||
to_year_spin = self._filter_widgets['date_to_year_spin']
|
||||
to_year_spin.handler_block_by_func(self._on_to_year_changed)
|
||||
to_year_spin.set_value(to_year)
|
||||
to_year_spin.handler_unblock_by_func(self._on_to_year_changed)
|
||||
else:
|
||||
from_entry.set_text("")
|
||||
to_entry.set_text("")
|
||||
# Reset to current date
|
||||
import datetime
|
||||
now = datetime.date.today()
|
||||
from_calendar.select_month(now.month - 1, now.year)
|
||||
to_calendar.select_month(now.month - 1, now.year)
|
||||
|
||||
# Reset year spin buttons
|
||||
if 'date_from_year_spin' in self._filter_widgets:
|
||||
from_year_spin = self._filter_widgets['date_from_year_spin']
|
||||
from_year_spin.handler_block_by_func(self._on_from_year_changed)
|
||||
from_year_spin.set_value(now.year)
|
||||
from_year_spin.handler_unblock_by_func(self._on_from_year_changed)
|
||||
|
||||
if 'date_to_year_spin' in self._filter_widgets:
|
||||
to_year_spin = self._filter_widgets['date_to_year_spin']
|
||||
to_year_spin.handler_block_by_func(self._on_to_year_changed)
|
||||
to_year_spin.set_value(now.year)
|
||||
to_year_spin.handler_unblock_by_func(self._on_to_year_changed)
|
||||
|
||||
# Clear validation message
|
||||
if hasattr(self, 'date_validation_label'):
|
||||
self.date_validation_label.set_text("")
|
||||
|
||||
def _on_filter_dialog_response(self, dialog: Gtk.Dialog, response_id: int) -> None:
|
||||
"""
|
||||
@ -908,6 +1045,7 @@ class MyTimelineView(NavigationView):
|
||||
self.filter_enabled = False
|
||||
self.active_event_types = set()
|
||||
self.date_range_filter = None
|
||||
self.date_range_explicit = False
|
||||
self.person_filter = None
|
||||
self.category_filter = None
|
||||
self.apply_filters()
|
||||
@ -943,57 +1081,68 @@ class MyTimelineView(NavigationView):
|
||||
all_persons = set(self._filter_widgets['person_checkboxes'].keys())
|
||||
self.person_filter = active_persons if active_persons != all_persons else None
|
||||
|
||||
# Update date range filter
|
||||
if 'date_from_entry' in self._filter_widgets and 'date_to_entry' in self._filter_widgets:
|
||||
from_text = self._filter_widgets['date_from_entry'].get_text().strip()
|
||||
to_text = self._filter_widgets['date_to_entry'].get_text().strip()
|
||||
# Update date range filter from calendar widgets
|
||||
if 'date_from_calendar' in self._filter_widgets and 'date_to_calendar' in self._filter_widgets:
|
||||
from_calendar = self._filter_widgets['date_from_calendar']
|
||||
to_calendar = self._filter_widgets['date_to_calendar']
|
||||
|
||||
# Check if dates were explicitly set (stored in widget data)
|
||||
# We'll use a simple approach: if user clicked calendars, use the dates
|
||||
# For now, we'll always use calendar dates if they're valid
|
||||
# The "Clear Dates" button will reset the explicit flag
|
||||
|
||||
# Get selected dates from calendars
|
||||
# Gtk.Calendar.get_date() returns (year, month, day) where month is 0-11
|
||||
from_year, from_month, from_day = from_calendar.get_date()
|
||||
to_year, to_month, to_day = to_calendar.get_date()
|
||||
|
||||
if from_text or to_text:
|
||||
# Parse dates using Gramps Date objects
|
||||
try:
|
||||
min_sort = None
|
||||
max_sort = None
|
||||
|
||||
if from_text:
|
||||
# Try to parse the date string
|
||||
# Support formats: YYYY, YYYY-MM, YYYY-MM-DD
|
||||
parts = from_text.split('-')
|
||||
year = int(parts[0])
|
||||
month = int(parts[1]) if len(parts) > 1 else 1
|
||||
day = int(parts[2]) if len(parts) > 2 else 1
|
||||
|
||||
# Create Date objects from calendar selections
|
||||
# Note: month from calendar is 0-11, Date.set_yr_mon_day expects 1-12
|
||||
from_date = Date()
|
||||
from_date.set_yr_mon_day(year, month, day)
|
||||
from_date.set_yr_mon_day(from_year, from_month + 1, from_day)
|
||||
min_sort = from_date.get_sort_value()
|
||||
|
||||
if to_text:
|
||||
# Try to parse the date string
|
||||
parts = to_text.split('-')
|
||||
year = int(parts[0])
|
||||
month = int(parts[1]) if len(parts) > 1 else 12
|
||||
day = int(parts[2]) if len(parts) > 2 else 31
|
||||
|
||||
to_date = Date()
|
||||
to_date.set_yr_mon_day(year, month, day)
|
||||
to_date.set_yr_mon_day(to_year, to_month + 1, to_day)
|
||||
max_sort = to_date.get_sort_value()
|
||||
|
||||
# If only one date is provided, set reasonable defaults
|
||||
if min_sort is None:
|
||||
min_sort = 0
|
||||
if max_sort is None:
|
||||
max_sort = 99999999
|
||||
# Validate date range
|
||||
if min_sort > max_sort:
|
||||
# Show error message
|
||||
if hasattr(self, 'date_validation_label'):
|
||||
self.date_validation_label.set_markup(
|
||||
f"<span color='red'>{_('Error: From date must be before To date')}</span>"
|
||||
)
|
||||
self.date_range_filter = None
|
||||
self.date_range_explicit = False
|
||||
return
|
||||
else:
|
||||
# Clear error message
|
||||
if hasattr(self, 'date_validation_label'):
|
||||
self.date_validation_label.set_text("")
|
||||
|
||||
# Set filter - user has selected dates in calendars
|
||||
self.date_range_filter = (min_sort, max_sort)
|
||||
self.date_range_explicit = True
|
||||
|
||||
self.date_range_filter = (min_sort, max_sort) if (from_text or to_text) else None
|
||||
except (ValueError, AttributeError, TypeError) as e:
|
||||
logger.warning(f"Error parsing date range: {e}", exc_info=True)
|
||||
self.date_range_filter = None
|
||||
self.date_range_explicit = False
|
||||
if hasattr(self, 'date_validation_label'):
|
||||
self.date_validation_label.set_markup(
|
||||
f"<span color='red'>{_('Error: Invalid date')}</span>"
|
||||
)
|
||||
else:
|
||||
# No calendar widgets, clear filter
|
||||
self.date_range_filter = None
|
||||
self.date_range_explicit = False
|
||||
|
||||
# Enable filter if any filter is active
|
||||
self.filter_enabled = (
|
||||
self.active_event_types or
|
||||
self.date_range_filter is not None or
|
||||
(self.date_range_filter is not None and self.date_range_explicit) or
|
||||
self.person_filter is not None or
|
||||
self.category_filter is not None
|
||||
)
|
||||
@ -1035,6 +1184,175 @@ class MyTimelineView(NavigationView):
|
||||
for checkbox in self._filter_widgets['event_type_checkboxes'].values():
|
||||
checkbox.set_active(False)
|
||||
|
||||
def _on_from_date_selected(self, calendar: Gtk.Calendar) -> None:
|
||||
"""
|
||||
Handle From date calendar selection.
|
||||
|
||||
Args:
|
||||
calendar: The calendar widget that was selected.
|
||||
"""
|
||||
# Update year spin button to match calendar
|
||||
if 'date_from_year_spin' in self._filter_widgets:
|
||||
year, month, day = calendar.get_date()
|
||||
year_spin = self._filter_widgets['date_from_year_spin']
|
||||
year_spin.handler_block_by_func(self._on_from_year_changed)
|
||||
year_spin.set_value(year)
|
||||
year_spin.handler_unblock_by_func(self._on_from_year_changed)
|
||||
|
||||
self._validate_date_range()
|
||||
|
||||
def _on_to_date_selected(self, calendar: Gtk.Calendar) -> None:
|
||||
"""
|
||||
Handle To date calendar selection.
|
||||
|
||||
Args:
|
||||
calendar: The calendar widget that was selected.
|
||||
"""
|
||||
# Update year spin button to match calendar
|
||||
if 'date_to_year_spin' in self._filter_widgets:
|
||||
year, month, day = calendar.get_date()
|
||||
year_spin = self._filter_widgets['date_to_year_spin']
|
||||
year_spin.handler_block_by_func(self._on_to_year_changed)
|
||||
year_spin.set_value(year)
|
||||
year_spin.handler_unblock_by_func(self._on_to_year_changed)
|
||||
|
||||
self._validate_date_range()
|
||||
|
||||
def _on_from_calendar_changed(self, calendar: Gtk.Calendar) -> None:
|
||||
"""
|
||||
Handle From calendar month/year change.
|
||||
|
||||
Args:
|
||||
calendar: The calendar widget that changed.
|
||||
"""
|
||||
# Update year spin button to match calendar
|
||||
if 'date_from_year_spin' in self._filter_widgets:
|
||||
year, month, day = calendar.get_date()
|
||||
year_spin = self._filter_widgets['date_from_year_spin']
|
||||
year_spin.handler_block_by_func(self._on_from_year_changed)
|
||||
year_spin.set_value(year)
|
||||
year_spin.handler_unblock_by_func(self._on_from_year_changed)
|
||||
|
||||
def _on_to_calendar_changed(self, calendar: Gtk.Calendar) -> None:
|
||||
"""
|
||||
Handle To calendar month/year change.
|
||||
|
||||
Args:
|
||||
calendar: The calendar widget that changed.
|
||||
"""
|
||||
# Update year spin button to match calendar
|
||||
if 'date_to_year_spin' in self._filter_widgets:
|
||||
year, month, day = calendar.get_date()
|
||||
year_spin = self._filter_widgets['date_to_year_spin']
|
||||
year_spin.handler_block_by_func(self._on_to_year_changed)
|
||||
year_spin.set_value(year)
|
||||
year_spin.handler_unblock_by_func(self._on_to_year_changed)
|
||||
|
||||
def _on_from_year_changed(self, spin_button: Gtk.SpinButton) -> None:
|
||||
"""
|
||||
Handle From year spin button change.
|
||||
|
||||
Args:
|
||||
spin_button: The year spin button that changed.
|
||||
"""
|
||||
if 'date_from_calendar' in self._filter_widgets:
|
||||
calendar = self._filter_widgets['date_from_calendar']
|
||||
new_year = int(spin_button.get_value())
|
||||
current_year, current_month, current_day = calendar.get_date()
|
||||
# Update calendar to new year, keeping same month and day
|
||||
calendar.select_month(current_month, new_year)
|
||||
# Trigger validation
|
||||
self._validate_date_range()
|
||||
|
||||
def _on_to_year_changed(self, spin_button: Gtk.SpinButton) -> None:
|
||||
"""
|
||||
Handle To year spin button change.
|
||||
|
||||
Args:
|
||||
spin_button: The year spin button that changed.
|
||||
"""
|
||||
if 'date_to_calendar' in self._filter_widgets:
|
||||
calendar = self._filter_widgets['date_to_calendar']
|
||||
new_year = int(spin_button.get_value())
|
||||
current_year, current_month, current_day = calendar.get_date()
|
||||
# Update calendar to new year, keeping same month and day
|
||||
calendar.select_month(current_month, new_year)
|
||||
# Trigger validation
|
||||
self._validate_date_range()
|
||||
|
||||
def _on_clear_date_range(self, button: Gtk.Button) -> None:
|
||||
"""
|
||||
Clear the date range selection.
|
||||
|
||||
Args:
|
||||
button: The clear button that was clicked.
|
||||
"""
|
||||
if 'date_from_calendar' in self._filter_widgets and 'date_to_calendar' in self._filter_widgets:
|
||||
from_calendar = self._filter_widgets['date_from_calendar']
|
||||
to_calendar = self._filter_widgets['date_to_calendar']
|
||||
|
||||
# Reset to current date (calendars always show a date)
|
||||
# Get current date and set calendars to it
|
||||
import datetime
|
||||
now = datetime.date.today()
|
||||
from_calendar.select_month(now.month - 1, now.year) # month is 0-11
|
||||
from_calendar.select_day(now.day)
|
||||
to_calendar.select_month(now.month - 1, now.year)
|
||||
to_calendar.select_day(now.day)
|
||||
|
||||
# Reset year spin buttons
|
||||
if 'date_from_year_spin' in self._filter_widgets:
|
||||
from_year_spin = self._filter_widgets['date_from_year_spin']
|
||||
from_year_spin.handler_block_by_func(self._on_from_year_changed)
|
||||
from_year_spin.set_value(now.year)
|
||||
from_year_spin.handler_unblock_by_func(self._on_from_year_changed)
|
||||
|
||||
if 'date_to_year_spin' in self._filter_widgets:
|
||||
to_year_spin = self._filter_widgets['date_to_year_spin']
|
||||
to_year_spin.handler_block_by_func(self._on_to_year_changed)
|
||||
to_year_spin.set_value(now.year)
|
||||
to_year_spin.handler_unblock_by_func(self._on_to_year_changed)
|
||||
|
||||
# Clear validation message
|
||||
if hasattr(self, 'date_validation_label'):
|
||||
self.date_validation_label.set_text("")
|
||||
|
||||
# Mark that date range should not be applied
|
||||
self.date_range_explicit = False
|
||||
|
||||
def _validate_date_range(self) -> None:
|
||||
"""
|
||||
Validate that From date is not after To date.
|
||||
"""
|
||||
if 'date_from_calendar' not in self._filter_widgets or 'date_to_calendar' not in self._filter_widgets:
|
||||
return
|
||||
|
||||
from_calendar = self._filter_widgets['date_from_calendar']
|
||||
to_calendar = self._filter_widgets['date_to_calendar']
|
||||
|
||||
from_year, from_month, from_day = from_calendar.get_date()
|
||||
to_year, to_month, to_day = to_calendar.get_date()
|
||||
|
||||
try:
|
||||
from_date = Date()
|
||||
from_date.set_yr_mon_day(from_year, from_month + 1, from_day)
|
||||
from_sort = from_date.get_sort_value()
|
||||
|
||||
to_date = Date()
|
||||
to_date.set_yr_mon_day(to_year, to_month + 1, to_day)
|
||||
to_sort = to_date.get_sort_value()
|
||||
|
||||
if from_sort > to_sort:
|
||||
if hasattr(self, 'date_validation_label'):
|
||||
self.date_validation_label.set_markup(
|
||||
f"<span color='red'>{_('Warning: From date is after To date')}</span>"
|
||||
)
|
||||
else:
|
||||
if hasattr(self, 'date_validation_label'):
|
||||
self.date_validation_label.set_text("")
|
||||
except (ValueError, AttributeError, TypeError):
|
||||
pass
|
||||
|
||||
def build_tree(self) -> None:
|
||||
"""
|
||||
Rebuilds the current display. Called when the view becomes visible.
|
||||
@ -1307,7 +1625,7 @@ class MyTimelineView(NavigationView):
|
||||
Returns:
|
||||
bool: True if date is in range (or no filter active), False otherwise.
|
||||
"""
|
||||
if not self.date_range_filter:
|
||||
if not self.date_range_filter or not self.date_range_explicit:
|
||||
return True
|
||||
min_date, max_date = self.date_range_filter
|
||||
return min_date <= date_sort <= max_date
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user