diff --git a/crates/git_ui/src/branch_picker.rs b/crates/git_ui/src/branch_picker.rs index 7269a14ab3c0931e71feb83673172b301c6f1087..d7186ef018437fde8d54f8f7f7ddd19bc9b68e1a 100644 --- a/crates/git_ui/src/branch_picker.rs +++ b/crates/git_ui/src/branch_picker.rs @@ -864,7 +864,7 @@ impl PickerDelegate for BranchListDelegate { ) -> Option { let entry = &self.matches.get(ix)?; - let (commit_time, author_name, subject) = entry + let (commit_time, absolute_time, author_name, subject) = entry .as_branch() .and_then(|branch| { branch.most_recent_commit.as_ref().map(|commit| { @@ -879,11 +879,22 @@ impl PickerDelegate for BranchListDelegate { local_offset, time_format::TimestampFormat::Relative, ); + let absolute_time = time_format::format_localized_timestamp( + commit_time, + OffsetDateTime::now_utc(), + local_offset, + time_format::TimestampFormat::EnhancedAbsolute, + ); let author = commit.author_name.clone(); - (Some(formatted_time), Some(author), Some(subject)) + ( + Some(formatted_time), + Some(absolute_time), + Some(author), + Some(subject), + ) }) }) - .unwrap_or_else(|| (None, None, None)); + .unwrap_or_else(|| (None, None, None, None)); let is_head_branch = entry.as_branch().is_some_and(|branch| branch.is_head); @@ -1076,19 +1087,31 @@ impl PickerDelegate for BranchListDelegate { .when_some( entry.as_branch().map(|b| b.name().to_string()), |this, branch_name| { - this.map(|this| { - if is_head_branch { - this.tooltip(move |_, cx| { - Tooltip::with_meta( - branch_name.clone(), - None, - "Current Branch", - cx, + let absolute_time = absolute_time.clone(); + this.tooltip({ + let is_head = is_head_branch; + Tooltip::element(move |_, _| { + v_flex() + .child(Label::new(branch_name.clone())) + .when(is_head, |this| { + this.child( + Label::new("Current Branch") + .size(LabelSize::Small) + .color(Color::Muted), + ) + }) + .when_some( + absolute_time.clone(), + |this, time| { + this.child( + Label::new(time) + .size(LabelSize::Small) + .color(Color::Muted), + ) + }, ) - }) - } else { - this.tooltip(Tooltip::text(branch_name)) - } + .into_any_element() + }) }) }, ), diff --git a/crates/git_ui/src/stash_picker.rs b/crates/git_ui/src/stash_picker.rs index 2d3515e833e4d353c323f533f1f0f39bb1d76561..963fa9d22bc78a6c559642bc3e7bb2b553436824 100644 --- a/crates/git_ui/src/stash_picker.rs +++ b/crates/git_ui/src/stash_picker.rs @@ -223,6 +223,7 @@ struct StashEntryMatch { entry: StashEntry, positions: Vec, formatted_timestamp: String, + formatted_absolute_timestamp: String, } pub struct StashListDelegate { @@ -264,6 +265,17 @@ impl StashListDelegate { } fn format_timestamp(timestamp: i64, timezone: UtcOffset) -> String { + let timestamp = + OffsetDateTime::from_unix_timestamp(timestamp).unwrap_or(OffsetDateTime::now_utc()); + time_format::format_localized_timestamp( + timestamp, + OffsetDateTime::now_utc(), + timezone, + time_format::TimestampFormat::Relative, + ) + } + + fn format_absolute_timestamp(timestamp: i64, timezone: UtcOffset) -> String { let timestamp = OffsetDateTime::from_unix_timestamp(timestamp).unwrap_or(OffsetDateTime::now_utc()); time_format::format_localized_timestamp( @@ -388,11 +400,14 @@ impl PickerDelegate for StashListDelegate { .into_iter() .map(|entry| { let formatted_timestamp = Self::format_timestamp(entry.timestamp, timezone); + let formatted_absolute_timestamp = + Self::format_absolute_timestamp(entry.timestamp, timezone); StashEntryMatch { entry, positions: Vec::new(), formatted_timestamp, + formatted_absolute_timestamp, } }) .collect() @@ -421,11 +436,14 @@ impl PickerDelegate for StashListDelegate { .map(|candidate| { let entry = all_stash_entries[candidate.candidate_id].clone(); let formatted_timestamp = Self::format_timestamp(entry.timestamp, timezone); + let formatted_absolute_timestamp = + Self::format_absolute_timestamp(entry.timestamp, timezone); StashEntryMatch { entry, positions: candidate.positions, formatted_timestamp, + formatted_absolute_timestamp, } }) .collect() @@ -544,6 +562,7 @@ impl PickerDelegate for StashListDelegate { .toggle_state(selected) .child( h_flex() + .min_w_0() .w_full() .gap_2p5() .child( @@ -551,7 +570,33 @@ impl PickerDelegate for StashListDelegate { .size(IconSize::Small) .color(Color::Muted), ) - .child(div().w_full().child(stash_label).child(branch_info)), + .child( + v_flex() + .id(format!("stash-tooltip-{ix}")) + .min_w_0() + .w_full() + .child(stash_label) + .child(branch_info) + .tooltip({ + let stash_message = Self::format_message( + entry_match.entry.index, + &entry_match.entry.message, + ); + let absolute_timestamp = + entry_match.formatted_absolute_timestamp.clone(); + + Tooltip::element(move |_, _| { + v_flex() + .child(Label::new(stash_message.clone())) + .child( + Label::new(absolute_timestamp.clone()) + .size(LabelSize::Small) + .color(Color::Muted), + ) + .into_any_element() + }) + }), + ), ) .end_slot( h_flex() diff --git a/crates/git_ui/src/worktree_picker.rs b/crates/git_ui/src/worktree_picker.rs index 2503c2ec6c4f5a669b0302ea45891434b901ef20..d544243cb88fa251adf8ef93f7c53c2855fb59ba 100644 --- a/crates/git_ui/src/worktree_picker.rs +++ b/crates/git_ui/src/worktree_picker.rs @@ -889,7 +889,7 @@ impl PickerDelegate for WorktreeListDelegate { }) .size(IconSize::Small), ) - .child(v_flex().w_full().child(branch_name).map(|this| { + .child(v_flex().w_full().min_w_0().child(branch_name).map(|this| { if entry.is_new { this.child( Label::new(sublabel)