Ely Office: six offline-first desktop apps in Rust and GPUI
ElyDoc, ElyTable, ElyShow (LibreOfficeKit), ElyPDF (MuPDF), ElyTree (Git), ElyMark (Milkdown and CodeMirror in CEF). ElyMail and ElyAgent follow.
This commit is contained in:
817 files changed
+143101
No files matched your search
@@ -0,0 +1,171 @@
|
||||
//! The commit box, and history in the left panel.
|
||||
|
||||
use ely_gpui_component::{
|
||||
buttons::{Button, ButtonVariant},
|
||||
feedback::{EmptyState, ErrorView},
|
||||
forms::{Choice, SearchInput, Select},
|
||||
git::{CommitInput, CommitList},
|
||||
primitives::IconName,
|
||||
theme::{ActiveTheme as _, ControlSize},
|
||||
};
|
||||
use gpui::{
|
||||
AnyElement, Context, Entity, IntoElement, ParentElement as _, SharedString, Styled as _, div,
|
||||
prelude::FluentBuilder as _,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
changes::Changes,
|
||||
commits::Find,
|
||||
tree::{Tree, text},
|
||||
};
|
||||
|
||||
/// Who commits, how Git signs, and the message.
|
||||
pub fn commit_box(
|
||||
tree: &Entity<Tree>,
|
||||
branch: SharedString,
|
||||
staged: usize,
|
||||
cx: &mut Context<Changes>,
|
||||
) -> AnyElement {
|
||||
let read = tree.read(cx);
|
||||
let colors = cx.theme().colors.clone();
|
||||
let (who, signs) = match read.active().and_then(|opened| opened.me.as_ref()) {
|
||||
Some(Ok(me)) => {
|
||||
let who = match (&me.name, &me.email) {
|
||||
(Some(name), Some(email)) => format!("{name} <{email}>").into(),
|
||||
_ => text(cx, "tree.me.unset", &[]),
|
||||
};
|
||||
let signs = me
|
||||
.signs
|
||||
.as_deref()
|
||||
.map(|format| text(cx, "tree.me.signs", &[("format", format)]));
|
||||
(who, signs)
|
||||
}
|
||||
Some(Err(error)) => (text(cx, "tree.me.unread", &[("error", error)]), None),
|
||||
None => (SharedString::default(), None),
|
||||
};
|
||||
let (me, amend, commit) = (tree.downgrade(), tree.downgrade(), tree.downgrade());
|
||||
let input = CommitInput::new("tree-commit", &read.subject, &read.body, branch, staged)
|
||||
.amend(read.amend)
|
||||
.on_amend(move |on, _, cx| {
|
||||
amend.update(cx, |tree, cx| tree.set_amend(on, cx)).ok();
|
||||
})
|
||||
.when(!read.amend_loading, |input| {
|
||||
input.on_commit(move |_, cx| {
|
||||
commit.update(cx, |tree, cx| tree.commit(cx)).ok();
|
||||
})
|
||||
});
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_1()
|
||||
.p_2()
|
||||
.border_t_1()
|
||||
.border_color(colors.border)
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_2()
|
||||
.child(
|
||||
Button::new("tree-me", who)
|
||||
.variant(ButtonVariant::Ghost)
|
||||
.size(ControlSize::Sm)
|
||||
.on_click(move |_, window, cx| {
|
||||
me.update(cx, |tree, cx| tree.ask_identity(window, cx)).ok();
|
||||
}),
|
||||
)
|
||||
.children(signs.map(|signs| div().text_color(colors.fg_muted).child(signs))),
|
||||
)
|
||||
.child(input)
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn find_key(find: Find) -> &'static str {
|
||||
match find {
|
||||
Find::Message => "message",
|
||||
Find::Author => "author",
|
||||
Find::Hash => "hash",
|
||||
Find::Content => "content",
|
||||
}
|
||||
}
|
||||
|
||||
/// The search row, then the commits.
|
||||
pub fn history(tree: &Entity<Tree>, cx: &mut Context<Changes>) -> AnyElement {
|
||||
let read = tree.read(cx);
|
||||
let Some(opened) = read.active() else {
|
||||
return div().into_any_element();
|
||||
};
|
||||
let (id, hist) = (opened.id, &opened.hist);
|
||||
let choices = ["message", "author", "hash", "content"]
|
||||
.map(|key| Choice::new(key, text(cx, &format!("tree.find.{key}"), &[])));
|
||||
let finding = tree.downgrade();
|
||||
let find = Select::new("tree-find", choices)
|
||||
.label(text(cx, "tree.find", &[]))
|
||||
.size(ControlSize::Sm)
|
||||
.selected(find_key(read.find))
|
||||
.on_change(move |key, _, cx| {
|
||||
let find = match key.as_ref() {
|
||||
"author" => Find::Author,
|
||||
"hash" => Find::Hash,
|
||||
"content" => Find::Content,
|
||||
_ => Find::Message,
|
||||
};
|
||||
finding
|
||||
.update(cx, |tree, cx| {
|
||||
tree.find = find;
|
||||
tree.search(cx);
|
||||
})
|
||||
.ok();
|
||||
});
|
||||
let list = match (&hist.error, hist.rows.is_empty()) {
|
||||
(Some(error), _) => ErrorView::new(
|
||||
"tree-log-error",
|
||||
IconName::TriangleAlert,
|
||||
text(cx, "tree.log.unread", &[]),
|
||||
)
|
||||
.detail(error.clone())
|
||||
.into_any_element(),
|
||||
(None, true) if hist.read && !hist.loading => {
|
||||
let key = if hist.query.is_some() {
|
||||
"tree.log.none_found"
|
||||
} else {
|
||||
"tree.log.none"
|
||||
};
|
||||
EmptyState::new(
|
||||
"tree-log-empty",
|
||||
IconName::GitCommitHorizontal,
|
||||
text(cx, key, &[]),
|
||||
)
|
||||
.into_any_element()
|
||||
}
|
||||
_ => {
|
||||
let (pick, more) = (tree.downgrade(), tree.downgrade());
|
||||
CommitList::new(("tree-log", id), hist.rows.clone())
|
||||
.when(hist.query.is_none(), |list| list.graph(hist.graph.clone()))
|
||||
.when_some(hist.pick.commit.clone(), |list, picked| {
|
||||
list.selected(picked)
|
||||
})
|
||||
.on_pick(move |commit, _, cx| {
|
||||
pick.update(cx, |tree, cx| tree.pick_commit(commit, cx))
|
||||
.ok();
|
||||
})
|
||||
.on_end(move |cx| {
|
||||
more.update(cx, |tree, cx| tree.more_log(cx)).ok();
|
||||
})
|
||||
.into_any_element()
|
||||
}
|
||||
};
|
||||
div()
|
||||
.size_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.child(
|
||||
div().flex().gap_2().p_2().child(find).child(
|
||||
div()
|
||||
.flex_1()
|
||||
.child(SearchInput::new("tree-search", &read.search).size(ControlSize::Sm)),
|
||||
),
|
||||
)
|
||||
.child(div().flex_1().min_h_0().child(list))
|
||||
.into_any_element()
|
||||
}
|
||||
Reference in new issue
Block a user