Improve selection behavior: click anywhere to select, single-line text for selected events

- Allow clicking anywhere on event line to select person (like selecting event)
- Selected events always show single-line text (no multi-line expansion)
- Force is_expanded=False for selected events to prevent text switching
- Always collapse expanded events when selecting person
- Selection and expansion are now completely separate actions
This commit is contained in:
Daniel Viegas 2025-11-28 23:54:27 +01:00
parent 52fece65a9
commit 079049952a

View File

@ -616,8 +616,10 @@ class MyTimelineView(NavigationView):
timeline_x = TIMELINE_MARGIN_LEFT timeline_x = TIMELINE_MARGIN_LEFT
marker_area_width = EVENT_MARKER_SIZE + 20 marker_area_width = EVENT_MARKER_SIZE + 20
# Allow person selection from anywhere on the line # Clicking anywhere on the event line selects the person (like selecting the event)
# Clicking anywhere on the event line selects the person # Always collapse any expanded event when selecting
self.expanded_event_index = None
if clicked_person: if clicked_person:
person_handle = clicked_person.get_handle() person_handle = clicked_person.get_handle()
if self.selected_person_handle == person_handle: if self.selected_person_handle == person_handle:
@ -630,13 +632,6 @@ class MyTimelineView(NavigationView):
# No person for this event, deselect # No person for this event, deselect
self.selected_person_handle = None self.selected_person_handle = None
# Also toggle expansion if clicking on label area (right side)
if scaled_x > timeline_x + marker_area_width:
if self.expanded_event_index == clicked_index:
self.expanded_event_index = None
else:
self.expanded_event_index = clicked_index
self.drawing_area.queue_draw() self.drawing_area.queue_draw()
return False return False
@ -709,35 +704,75 @@ class MyTimelineView(NavigationView):
date_range = 1 date_range = 1
timeline_x = TIMELINE_MARGIN_LEFT timeline_x = TIMELINE_MARGIN_LEFT
timeline_y_start = TIMELINE_MARGIN_TOP
timeline_y_end = height - TIMELINE_MARGIN_BOTTOM
# Calculate initial Y positions (same as in on_draw) # Calculate initial Y positions (same as in on_draw)
events_with_y_pos = [] events_with_y_pos = []
for i, event_data in enumerate(self.events): for i, event_data in enumerate(self.events):
date_sort, date_obj, event, person, event_type, expanded, _ = event_data date_sort, date_obj, event, person, event_type, expanded, _ = event_data
y_pos = TIMELINE_MARGIN_TOP + ( y_pos = timeline_y_start + (
(date_sort - min_date) / date_range (date_sort - min_date) / date_range
) * (height - TIMELINE_MARGIN_TOP - TIMELINE_MARGIN_BOTTOM) ) * (timeline_y_end - timeline_y_start)
events_with_y_pos.append((i, y_pos, event_data)) events_with_y_pos.append((date_sort, date_obj, event, person, event_type, expanded, y_pos))
# Check each event using adjusted positions # Apply same collision detection as in on_draw
# We need to simulate the collision detection, but for simplicity, # Create a temporary Cairo surface for text measurement
# we'll check against the calculated positions and use a wider tolerance import cairo
for i, y_pos, event_data in events_with_y_pos: surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 1, 1)
date_sort, date_obj, event, person, event_type, expanded, _ = event_data temp_context = cairo.Context(surface)
layout = PangoCairo.create_layout(temp_context)
layout.set_font_description(Pango.font_description_from_string("Sans 11"))
# Calculate clickable area - wider to include label area adjusted_events = []
min_spacing = 30 # Minimum spacing between labels in pixels
for event_data in events_with_y_pos:
date_sort, date_obj, event, person, event_type, expanded, y_pos = event_data
# Calculate label height (same as detect_label_overlaps)
if person:
person_name = name_displayer.display(person)
date_str = get_date(event)
event_type_str = str(event_type)
label_text = f"{date_str} - {event_type_str} - {person_name}"
else:
date_str = get_date(event)
event_type_str = str(event_type)
label_text = f"{date_str} - {event_type_str}"
layout.set_text(label_text, -1)
text_width, text_height = layout.get_pixel_size()
label_height = text_height + 16 # Add padding
# Check for overlap with previous events
adjusted_y = y_pos
for prev_data in adjusted_events:
prev_y_pos = prev_data[6] # Get y_pos from previous event
# Check if labels would overlap
if abs(adjusted_y - prev_y_pos) < min_spacing:
# Adjust downward
adjusted_y = prev_y_pos + min_spacing
# Ensure adjusted position is within bounds
adjusted_y = max(timeline_y_start, min(adjusted_y, timeline_y_end))
# Create new event data with adjusted Y position
adjusted_events.append((date_sort, date_obj, event, person, event_type, expanded, adjusted_y))
# Now check each event using adjusted positions
marker_size = EVENT_MARKER_SIZE marker_size = EVENT_MARKER_SIZE
label_x = timeline_x + 25 label_x = timeline_x + 25
# Estimate label width (we'll use a reasonable default)
# For whole-line selection, check if click is in the event's horizontal band
clickable_width = 600 # Reasonable width for label area clickable_width = 600 # Reasonable width for label area
clickable_height = max(marker_size * 2, 30) # At least marker size or 30px clickable_height = max(marker_size * 2, 30) # At least marker size or 30px
for i, event_data in enumerate(adjusted_events):
date_sort, date_obj, event, person, event_type, expanded, adjusted_y = event_data
# Check if click is in the event's area (marker + label) # Check if click is in the event's area (marker + label)
if (scaled_x >= timeline_x - marker_size - 10 and if (scaled_x >= timeline_x - marker_size - 10 and
scaled_x <= label_x + clickable_width and scaled_x <= label_x + clickable_width and
abs(scaled_y - y_pos) < clickable_height / 2): abs(scaled_y - adjusted_y) < clickable_height / 2):
return i return i
return None return None
@ -925,6 +960,11 @@ class MyTimelineView(NavigationView):
is_selected = (self.selected_person_handle is not None and is_selected = (self.selected_person_handle is not None and
person and person.get_handle() == self.selected_person_handle) person and person.get_handle() == self.selected_person_handle)
# Selected events should not show expanded (multi-line) text
# Only show expanded text if explicitly expanded AND not selected
if is_selected:
is_expanded = False
# Draw event marker with modern styling # Draw event marker with modern styling
self.draw_event_marker(context, timeline_x, y_pos, event_type, is_hovered, is_selected) self.draw_event_marker(context, timeline_x, y_pos, event_type, is_hovered, is_selected)