diff --git a/MyTimeline.py b/MyTimeline.py index a09bb76..074d6f0 100644 --- a/MyTimeline.py +++ b/MyTimeline.py @@ -616,8 +616,10 @@ class MyTimelineView(NavigationView): timeline_x = TIMELINE_MARGIN_LEFT marker_area_width = EVENT_MARKER_SIZE + 20 - # Allow person selection from anywhere on the line - # Clicking anywhere on the event line selects the person + # Clicking anywhere on the event line selects the person (like selecting the event) + # Always collapse any expanded event when selecting + self.expanded_event_index = None + if clicked_person: person_handle = clicked_person.get_handle() if self.selected_person_handle == person_handle: @@ -630,13 +632,6 @@ class MyTimelineView(NavigationView): # No person for this event, deselect 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() return False @@ -709,35 +704,75 @@ class MyTimelineView(NavigationView): date_range = 1 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) events_with_y_pos = [] for i, event_data in enumerate(self.events): 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 - ) * (height - TIMELINE_MARGIN_TOP - TIMELINE_MARGIN_BOTTOM) - events_with_y_pos.append((i, y_pos, event_data)) + ) * (timeline_y_end - timeline_y_start) + events_with_y_pos.append((date_sort, date_obj, event, person, event_type, expanded, y_pos)) - # Check each event using adjusted positions - # We need to simulate the collision detection, but for simplicity, - # we'll check against the calculated positions and use a wider tolerance - for i, y_pos, event_data in events_with_y_pos: - date_sort, date_obj, event, person, event_type, expanded, _ = event_data + # Apply same collision detection as in on_draw + # Create a temporary Cairo surface for text measurement + import cairo + surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 1, 1) + temp_context = cairo.Context(surface) + layout = PangoCairo.create_layout(temp_context) + layout.set_font_description(Pango.font_description_from_string("Sans 11")) + + 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 clickable area - wider to include label area - marker_size = EVENT_MARKER_SIZE - label_x = timeline_x + 25 + # 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}" - # 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_height = max(marker_size * 2, 30) # At least marker size or 30px + 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 + label_x = timeline_x + 25 + clickable_width = 600 # Reasonable width for label area + 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) if (scaled_x >= timeline_x - marker_size - 10 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 None @@ -925,6 +960,11 @@ class MyTimelineView(NavigationView): is_selected = (self.selected_person_handle is not None and 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 self.draw_event_marker(context, timeline_x, y_pos, event_type, is_hovered, is_selected)