Skip to content

Commit d0bf60a

Browse files
committed
feat: 完成指令系统 (fix #179)
1 parent 2971439 commit d0bf60a

File tree

3 files changed

+133
-42
lines changed

3 files changed

+133
-42
lines changed

crates/project_graph/src/fonts.rs

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,36 +3,47 @@ pub fn setup_custom_fonts(ctx: &egui::Context) {
33
let mut fonts = egui::FontDefinitions::empty();
44

55
// 在桌面平台上尝试加载系统字体
6-
#[cfg(desktop)]
6+
#[cfg(linux)]
77
{
88
use font_kit::family_name::FamilyName;
99
use font_kit::handle::Handle;
1010
use font_kit::properties::Properties;
1111
use font_kit::source::SystemSource;
1212

1313
let source = SystemSource::new();
14-
if let Ok(handle) = source.select_best_match(
15-
&[FamilyName::SansSerif, FamilyName::Title("system-ui".into())],
16-
&Properties::new(),
17-
) {
18-
let font_data = match handle {
19-
Handle::Path { path, .. } => std::fs::read(path).ok(),
20-
Handle::Memory { bytes, .. } => Some(bytes.to_vec()),
14+
15+
let mut load_system_font =
16+
|family_names: &[FamilyName], egui_family: egui::FontFamily, key: &str| {
17+
if let Ok(handle) = source.select_best_match(family_names, &Properties::new()) {
18+
let font_data = match handle {
19+
Handle::Path { path, .. } => std::fs::read(path).ok(),
20+
Handle::Memory { bytes, .. } => Some(bytes.to_vec()),
21+
};
22+
23+
if let Some(data) = font_data {
24+
fonts
25+
.font_data
26+
.insert(key.to_owned(), egui::FontData::from_owned(data).into());
27+
// 将字体插入到对应家族的最前端(最高优先级)
28+
fonts
29+
.families
30+
.get_mut(&egui_family)
31+
.unwrap()
32+
.insert(0, key.to_owned());
33+
}
34+
}
2135
};
2236

23-
if let Some(data) = font_data {
24-
fonts.font_data.insert(
25-
"system_ui".to_owned(),
26-
egui::FontData::from_owned(data).into(),
27-
);
28-
// 插入到首位:最高优先级
29-
fonts
30-
.families
31-
.get_mut(&egui::FontFamily::Proportional)
32-
.unwrap()
33-
.insert(0, "system_ui".to_owned());
34-
}
35-
}
37+
load_system_font(
38+
&[FamilyName::Title("system-ui".into()), FamilyName::SansSerif],
39+
egui::FontFamily::Proportional,
40+
"system_ui",
41+
);
42+
load_system_font(
43+
&[FamilyName::Monospace],
44+
egui::FontFamily::Monospace,
45+
"system_mono",
46+
);
3647
}
3748
// 在不支持使用 font-kit 的平台上,使用内置的 MiSans 字体作为替代
3849
#[cfg(any(wasm, android))]

crates/project_graph/src/stage/structs.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ pub trait EntityTrait {
1414
#[derive(knus::Decode, Debug)]
1515
pub struct Text {
1616
#[knus(argument, default = nanoid!())]
17-
pub id: String,
17+
id: String,
1818
#[knus(child, default = KdlPos2 { x: 0.0, y: 0.0 })]
19-
pub pos: KdlPos2,
19+
pos: KdlPos2,
2020
#[knus(child, unwrap(argument), default = String::new())]
21-
pub val: String,
21+
val: String,
2222

2323
text_width: std::sync::OnceLock<f32>,
2424
}
Lines changed: 98 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,115 @@
1-
use crate::stage::{
2-
Stage,
3-
structs::{Entity, Text},
4-
};
1+
use egui::Margin;
2+
3+
use crate::stage::{Stage, structs::Entity};
54

65
pub struct Terminal {
76
input: String,
7+
history: Vec<HistoryItem>,
88
}
99

1010
impl Terminal {
1111
pub fn new() -> Self {
1212
Terminal {
1313
input: String::new(),
14+
history: Vec::new(),
1415
}
1516
}
1617

1718
pub fn ui(&mut self, ui: &mut egui::Ui, stage: &mut Stage) {
18-
ui.heading("Terminal");
19-
ui.separator();
20-
ui.text_edit_multiline(&mut self.input);
21-
if ui.button("execute").clicked() {
22-
match knus::parse::<Vec<Entity>>("terminal_input.kdl", &self.input) {
23-
Ok(doc) => {
24-
for entity in doc {
25-
stage.context.add(entity.into());
19+
egui::ScrollArea::vertical()
20+
.stick_to_bottom(true)
21+
.show(ui, |ui| {
22+
// 历史记录
23+
for item in &self.history {
24+
match item {
25+
HistoryItem::In(cmd) => {
26+
for (i, line) in cmd.lines().enumerate() {
27+
let prefix = if i == 0 { "> " } else { " " };
28+
ui.label(
29+
egui::RichText::new(format!("{}{}", prefix, line))
30+
.color(egui::Color32::LIGHT_BLUE)
31+
.font(egui::FontId::monospace(14.0)),
32+
);
33+
}
34+
}
35+
HistoryItem::Out(output) => {
36+
ui.label(
37+
egui::RichText::new(output)
38+
.color(egui::Color32::WHITE)
39+
.font(egui::FontId::monospace(14.0)),
40+
);
41+
}
42+
HistoryItem::Err(err) => {
43+
ui.label(
44+
egui::RichText::new(err)
45+
.color(egui::Color32::LIGHT_RED)
46+
.font(egui::FontId::monospace(14.0)),
47+
);
48+
}
2649
}
2750
}
28-
Err(e) => {
29-
log::error!("{:?}", miette::Report::new(e));
30-
}
31-
}
32-
self.input.clear();
33-
}
51+
52+
// 输入框
53+
ui.horizontal(|ui| {
54+
ui.label(
55+
egui::RichText::new(">")
56+
.color(egui::Color32::LIGHT_BLUE)
57+
.font(egui::FontId::monospace(14.0)),
58+
);
59+
60+
let response = ui.add(
61+
egui::TextEdit::multiline(&mut self.input)
62+
.id(ui.make_persistent_id("terminal_input"))
63+
.desired_width(f32::INFINITY)
64+
.desired_rows(1)
65+
.lock_focus(true)
66+
.text_color(egui::Color32::LIGHT_BLUE)
67+
.font(egui::FontId::monospace(14.0))
68+
.margin(Margin::ZERO)
69+
.frame(false),
70+
);
71+
if response.has_focus()
72+
&& ui.input_mut(|i| i.consume_key(egui::Modifiers::NONE, egui::Key::Enter))
73+
{
74+
let modifiers = ui.input(|i| i.modifiers);
75+
if !modifiers.shift && !modifiers.ctrl && !modifiers.alt {
76+
self.history
77+
.push(HistoryItem::In(self.input.clone().trim().to_string()));
78+
match knus::parse::<Vec<Entity>>("terminal_input.kdl", &self.input) {
79+
Ok(doc) => {
80+
for entity in doc {
81+
stage.context.add(entity.into());
82+
}
83+
}
84+
Err(e) => {
85+
// log::error!("{:?}", miette::Report::new(e.into()));
86+
self.history.push(HistoryItem::Err(get_plain_report_string(
87+
miette::Report::new(e),
88+
)));
89+
}
90+
}
91+
self.input.clear();
92+
response.request_focus();
93+
}
94+
}
95+
});
96+
97+
ui.allocate_space(ui.available_size());
98+
});
3499
}
35100
}
101+
102+
enum HistoryItem {
103+
In(String),
104+
Out(String),
105+
Err(String),
106+
}
107+
108+
fn get_plain_report_string(report: miette::Report) -> String {
109+
let mut out = String::new();
110+
// 创建一个不带颜色的渲染处理器
111+
miette::GraphicalReportHandler::new_themed(miette::GraphicalTheme::unicode_nocolor())
112+
.render_report(&mut out, report.as_ref())
113+
.unwrap();
114+
out
115+
}

0 commit comments

Comments
 (0)