MailReader opens a message under its subject: the sender's picture, name and address, who it went to, when it came, and Reply, Reply all when it went to more than one, and Forward, each only with a handler; then its words and its files, each downloadable. MailThreadView lays a conversation oldest first: the newest stands open, the others fold to a line of the sender, their first words and when, which a press, Enter or Space opens. QuotedText folds words quoted from before behind a small mark; open, they sit behind a rule. Long names give way at the label width. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
81 lines
2.6 KiB
Rust
81 lines
2.6 KiB
Rust
use gpui::{
|
|
AnyElement, App, ElementId, InteractiveElement, IntoElement, ParentElement, RenderOnce, Styled,
|
|
Window, div,
|
|
};
|
|
use smallvec::SmallVec;
|
|
|
|
use crate::{
|
|
buttons::{ButtonVariant, IconButton},
|
|
layout::Collapsible,
|
|
primitives::IconName,
|
|
theme::{ActiveTheme, ControlSize},
|
|
};
|
|
|
|
/// Words quoted from an earlier message, folded behind "···" until it is pressed; open, they sit behind a rule. The fold is the text's own, logged.
|
|
#[derive(IntoElement)]
|
|
pub struct QuotedText {
|
|
id: ElementId,
|
|
body: SmallVec<[AnyElement; 2]>,
|
|
}
|
|
|
|
impl QuotedText {
|
|
pub fn new(id: impl Into<ElementId>) -> Self {
|
|
Self {
|
|
id: id.into(),
|
|
body: SmallVec::new(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl ParentElement for QuotedText {
|
|
fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
|
|
self.body.extend(elements);
|
|
}
|
|
}
|
|
|
|
impl RenderOnce for QuotedText {
|
|
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
|
|
let open = window.use_keyed_state((self.id.clone(), "open"), cx, |_, _| false);
|
|
let shown = *open.read(cx);
|
|
let theme = cx.theme();
|
|
let (id, named) = (self.id.clone(), self.id.clone());
|
|
div()
|
|
.flex()
|
|
.flex_col()
|
|
.gap_2()
|
|
.child(
|
|
div().flex().child(
|
|
IconButton::new((self.id.clone(), "toggle"), IconName::Ellipsis)
|
|
.variant(ButtonVariant::Subtle)
|
|
.size(ControlSize::Sm)
|
|
.tooltip(if shown {
|
|
"Hide quoted text"
|
|
} else {
|
|
"Show quoted text"
|
|
})
|
|
.on_click(move |_, _, cx| {
|
|
log::info!("quoted text {id}: {}", if shown { "hide" } else { "show" });
|
|
open.update(cx, |open, cx| {
|
|
*open = !*open;
|
|
cx.notify();
|
|
})
|
|
}),
|
|
),
|
|
)
|
|
.child(
|
|
Collapsible::new((self.id.clone(), "fold"), shown).child(
|
|
div()
|
|
.debug_selector(move || format!("quoted {named}"))
|
|
.pl_3()
|
|
.border_l_2()
|
|
.border_color(theme.colors.border_strong)
|
|
.text_color(theme.colors.fg_muted)
|
|
.flex()
|
|
.flex_col()
|
|
.gap_2()
|
|
.children(self.body),
|
|
),
|
|
)
|
|
}
|
|
}
|