feat: 添加 README,更新 .gitignore,移除 JE-Skin/eskin-finger-sdk
- 添加 README.md 项目文档 - 更新 .gitignore 排除 JE-Skin/、eskin-finger-sdk/ 及构建产物 - 从 git 跟踪中移除 JE-Skin 和 eskin-finger-sdk Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
306
src/ui.rs
306
src/ui.rs
@@ -10,6 +10,9 @@ pub struct FloatingPanelState {
|
||||
pub visible: bool,
|
||||
default_pos: egui::Pos2,
|
||||
tag_pos: egui::Pos2,
|
||||
center_anim: f32,
|
||||
center_anim_target: bool,
|
||||
center_anim_last_time: Option<f64>,
|
||||
}
|
||||
|
||||
pub struct ConfigPanelState {
|
||||
@@ -63,6 +66,9 @@ impl FloatingPanelState {
|
||||
visible: true,
|
||||
default_pos: egui::pos2(default_pos[0], default_pos[1]),
|
||||
tag_pos: egui::pos2(tag_pos[0], tag_pos[1]),
|
||||
center_anim: 1.0,
|
||||
center_anim_target: true,
|
||||
center_anim_last_time: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -131,7 +137,7 @@ pub fn draw_connect_panel(
|
||||
ConnectionState::Connected | ConnectionState::Streaming
|
||||
);
|
||||
|
||||
draw_center_floating_panel(ctx, panel, "connect_center_panel", 64.0, |ui| {
|
||||
draw_center_floating_panel(ctx, panel, "connect_center_panel", 42.0, "连接", |ui| {
|
||||
ui.set_min_width(320.0);
|
||||
|
||||
ui.vertical(|ui| {
|
||||
@@ -654,18 +660,308 @@ fn draw_center_floating_panel(
|
||||
panel: &mut FloatingPanelState,
|
||||
id: &'static str,
|
||||
top_offset: f32,
|
||||
collapsed_label: &'static str,
|
||||
add_contents: impl FnOnce(&mut egui::Ui),
|
||||
) {
|
||||
if !panel.visible {
|
||||
const PANEL_WIDTH: f32 = 430.0;
|
||||
const SLIDE_DISTANCE: f32 = 18.0;
|
||||
|
||||
let anim = advance_center_panel_anim(ctx, panel);
|
||||
let eased = ease_in_out(anim);
|
||||
|
||||
if !panel.visible || anim < 0.28 {
|
||||
let handle_y = top_offset - (1.0 - eased) * 2.0;
|
||||
egui::Area::new(egui::Id::new(format!("{id}_handle")))
|
||||
.anchor(egui::Align2::CENTER_TOP, egui::vec2(0.0, handle_y))
|
||||
.order(egui::Order::Tooltip)
|
||||
.show(ctx, |ui| {
|
||||
let handle_rect = ui
|
||||
.allocate_exact_size(egui::vec2(112.0, 24.0), egui::Sense::hover())
|
||||
.0;
|
||||
if integrated_handle(ui, handle_rect, collapsed_label, false, "打开连接面板")
|
||||
.clicked()
|
||||
{
|
||||
panel.visible = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if anim <= 0.02 {
|
||||
return;
|
||||
}
|
||||
|
||||
egui::Area::new(egui::Id::new(id))
|
||||
.anchor(egui::Align2::CENTER_TOP, egui::vec2(0.0, top_offset))
|
||||
.order(egui::Order::Foreground)
|
||||
.anchor(
|
||||
egui::Align2::CENTER_TOP,
|
||||
egui::vec2(0.0, top_offset - (1.0 - eased) * SLIDE_DISTANCE),
|
||||
)
|
||||
.order(egui::Order::Tooltip)
|
||||
.show(ctx, |ui| {
|
||||
panel_frame(ctx).show(ui, |ui| {
|
||||
let response = center_panel_shell(ui, PANEL_WIDTH, |ui| {
|
||||
ui.set_width(PANEL_WIDTH);
|
||||
add_contents(ui);
|
||||
ui.add_space(6.0);
|
||||
let handle_rect = allocate_center_panel_handle(ui);
|
||||
if integrated_handle(ui, handle_rect, "", true, "收起连接面板").clicked() {
|
||||
panel.visible = false;
|
||||
}
|
||||
handle_rect
|
||||
});
|
||||
paint_integrated_center_panel(ui, response.response.rect, response.inner, true);
|
||||
});
|
||||
}
|
||||
|
||||
fn advance_center_panel_anim(ctx: &egui::Context, panel: &mut FloatingPanelState) -> f32 {
|
||||
let target = if panel.visible { 1.0 } else { 0.0 };
|
||||
let now = ctx.input(|input| input.time);
|
||||
let delta_time = panel
|
||||
.center_anim_last_time
|
||||
.map(|last_time| (now - last_time) as f32)
|
||||
.unwrap_or(1.0 / 60.0)
|
||||
.clamp(0.0, 0.05);
|
||||
|
||||
panel.center_anim_last_time = Some(now);
|
||||
panel.center_anim_target = panel.visible;
|
||||
|
||||
let speed = 7.5;
|
||||
let step = speed * delta_time;
|
||||
if panel.center_anim < target {
|
||||
panel.center_anim = (panel.center_anim + step).min(target);
|
||||
} else if panel.center_anim > target {
|
||||
panel.center_anim = (panel.center_anim - step).max(target);
|
||||
}
|
||||
|
||||
if (panel.center_anim - target).abs() > 0.001 {
|
||||
ctx.request_repaint();
|
||||
}
|
||||
|
||||
panel.center_anim
|
||||
}
|
||||
|
||||
fn ease_in_out(t: f32) -> f32 {
|
||||
let t = t.clamp(0.0, 1.0);
|
||||
t * t * (3.0 - 2.0 * t)
|
||||
}
|
||||
|
||||
fn center_panel_shell<R>(
|
||||
ui: &mut egui::Ui,
|
||||
width: f32,
|
||||
add_contents: impl FnOnce(&mut egui::Ui) -> R,
|
||||
) -> egui::InnerResponse<R> {
|
||||
egui::Frame::new()
|
||||
.fill(ENGINEERING_DARK.panel)
|
||||
.corner_radius(egui::CornerRadius::same(ENGINEERING_DARK.radius))
|
||||
.inner_margin(egui::Margin::symmetric(10, 8))
|
||||
.show(ui, |ui| {
|
||||
ui.set_width(width);
|
||||
add_contents(ui)
|
||||
})
|
||||
}
|
||||
|
||||
fn allocate_center_panel_handle(ui: &mut egui::Ui) -> egui::Rect {
|
||||
let handle_width = 112.0;
|
||||
let handle_height = 24.0;
|
||||
let left_space = ((ui.available_width() - handle_width) * 0.5).max(0.0);
|
||||
let mut handle_rect = egui::Rect::NOTHING;
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.add_space(left_space);
|
||||
let (rect, _) = ui.allocate_exact_size(
|
||||
egui::vec2(handle_width, handle_height),
|
||||
egui::Sense::hover(),
|
||||
);
|
||||
handle_rect = rect;
|
||||
});
|
||||
|
||||
handle_rect
|
||||
}
|
||||
|
||||
fn integrated_handle(
|
||||
ui: &mut egui::Ui,
|
||||
rect: egui::Rect,
|
||||
label: &str,
|
||||
expanded: bool,
|
||||
tooltip: &'static str,
|
||||
) -> egui::Response {
|
||||
let response = ui
|
||||
.interact(
|
||||
rect.expand2(egui::vec2(12.0, 5.0)),
|
||||
ui.id().with(("center_panel_handle", expanded)),
|
||||
egui::Sense::click(),
|
||||
)
|
||||
.on_hover_text(tooltip);
|
||||
|
||||
paint_integrated_handle(ui, rect, label, expanded, response.hovered());
|
||||
response
|
||||
}
|
||||
|
||||
fn paint_integrated_handle(
|
||||
ui: &mut egui::Ui,
|
||||
rect: egui::Rect,
|
||||
label: &str,
|
||||
expanded: bool,
|
||||
hovered: bool,
|
||||
) {
|
||||
let center = rect.center();
|
||||
if !expanded {
|
||||
let fill = if hovered {
|
||||
ENGINEERING_DARK.panel_strong
|
||||
} else {
|
||||
egui::Color32::from_rgb(31, 41, 52)
|
||||
};
|
||||
let points = vec![
|
||||
egui::pos2(rect.left() - 12.0, rect.top() - 1.0),
|
||||
egui::pos2(rect.right() + 12.0, rect.top() - 1.0),
|
||||
egui::pos2(rect.right() + 3.0, rect.bottom() + 8.0),
|
||||
egui::pos2(rect.left() - 3.0, rect.bottom() + 8.0),
|
||||
];
|
||||
ui.painter().add(egui::Shape::convex_polygon(
|
||||
points.clone(),
|
||||
fill,
|
||||
egui::Stroke::new(1.0, ENGINEERING_DARK.border),
|
||||
));
|
||||
ui.painter().line_segment(
|
||||
[points[2], points[3]],
|
||||
egui::Stroke::new(1.0, ENGINEERING_DARK.border_soft),
|
||||
);
|
||||
}
|
||||
|
||||
let arrow = if expanded { -1.0 } else { 1.0 };
|
||||
let arrow_center_x = if label.is_empty() {
|
||||
center.x
|
||||
} else {
|
||||
rect.right() - 18.0
|
||||
};
|
||||
let arrow_center = egui::pos2(arrow_center_x, center.y);
|
||||
let arrow_color = if hovered {
|
||||
ENGINEERING_DARK.accent_hot
|
||||
} else {
|
||||
ENGINEERING_DARK.text_dim
|
||||
};
|
||||
ui.painter().line_segment(
|
||||
[
|
||||
arrow_center + egui::vec2(-6.0, -2.0 * arrow),
|
||||
arrow_center + egui::vec2(0.0, 4.0 * arrow),
|
||||
],
|
||||
egui::Stroke::new(1.7, arrow_color),
|
||||
);
|
||||
ui.painter().line_segment(
|
||||
[
|
||||
arrow_center + egui::vec2(6.0, -2.0 * arrow),
|
||||
arrow_center + egui::vec2(0.0, 4.0 * arrow),
|
||||
],
|
||||
egui::Stroke::new(1.7, arrow_color),
|
||||
);
|
||||
|
||||
if !label.is_empty() {
|
||||
let galley = ui.fonts_mut(|fonts| {
|
||||
fonts.layout_no_wrap(
|
||||
label.to_owned(),
|
||||
egui::FontId::proportional(12.0),
|
||||
ENGINEERING_DARK.text,
|
||||
)
|
||||
});
|
||||
ui.painter().galley(
|
||||
egui::pos2(rect.left() + 14.0, center.y - galley.size().y * 0.5 - 1.0),
|
||||
galley,
|
||||
ENGINEERING_DARK.text,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn paint_integrated_center_panel(
|
||||
ui: &egui::Ui,
|
||||
rect: egui::Rect,
|
||||
handle_rect: egui::Rect,
|
||||
handle_on_bottom: bool,
|
||||
) {
|
||||
let painter = ui.painter();
|
||||
let stroke = egui::Stroke::new(1.0, ENGINEERING_DARK.border);
|
||||
let accent_stroke = egui::Stroke::new(1.2, ENGINEERING_DARK.border_soft);
|
||||
let top = rect.top();
|
||||
let left = rect.left();
|
||||
let right = rect.right();
|
||||
let bottom = rect.bottom();
|
||||
let radius = ENGINEERING_DARK.radius as f32;
|
||||
let tab_left = handle_rect.left() - 18.0;
|
||||
let tab_right = handle_rect.right() + 18.0;
|
||||
let tab_recess = if handle_on_bottom {
|
||||
handle_rect.top() - 2.0
|
||||
} else {
|
||||
handle_rect.bottom() + 2.0
|
||||
};
|
||||
|
||||
if handle_on_bottom {
|
||||
painter.line_segment(
|
||||
[
|
||||
egui::pos2(left + radius, top),
|
||||
egui::pos2(right - radius, top),
|
||||
],
|
||||
stroke,
|
||||
);
|
||||
} else {
|
||||
painter.line_segment(
|
||||
[egui::pos2(left + radius, top), egui::pos2(tab_left, top)],
|
||||
stroke,
|
||||
);
|
||||
painter.line_segment(
|
||||
[egui::pos2(tab_right, top), egui::pos2(right - radius, top)],
|
||||
stroke,
|
||||
);
|
||||
}
|
||||
painter.line_segment(
|
||||
[
|
||||
egui::pos2(left, top + radius),
|
||||
egui::pos2(left, bottom - radius),
|
||||
],
|
||||
stroke,
|
||||
);
|
||||
painter.line_segment(
|
||||
[
|
||||
egui::pos2(right, top + radius),
|
||||
egui::pos2(right, bottom - radius),
|
||||
],
|
||||
stroke,
|
||||
);
|
||||
if handle_on_bottom {
|
||||
painter.line_segment(
|
||||
[
|
||||
egui::pos2(left + radius, bottom),
|
||||
egui::pos2(tab_left, bottom),
|
||||
],
|
||||
stroke,
|
||||
);
|
||||
painter.line_segment(
|
||||
[
|
||||
egui::pos2(tab_right, bottom),
|
||||
egui::pos2(right - radius, bottom),
|
||||
],
|
||||
stroke,
|
||||
);
|
||||
} else {
|
||||
painter.line_segment(
|
||||
[
|
||||
egui::pos2(left + radius, bottom),
|
||||
egui::pos2(right - radius, bottom),
|
||||
],
|
||||
stroke,
|
||||
);
|
||||
}
|
||||
|
||||
let tab_points = if handle_on_bottom {
|
||||
vec![
|
||||
egui::pos2(tab_left, bottom),
|
||||
egui::pos2(handle_rect.left() - 8.0, tab_recess),
|
||||
egui::pos2(handle_rect.right() + 8.0, tab_recess),
|
||||
egui::pos2(tab_right, bottom),
|
||||
]
|
||||
} else {
|
||||
vec![
|
||||
egui::pos2(tab_left, top),
|
||||
egui::pos2(handle_rect.left() - 8.0, tab_recess),
|
||||
egui::pos2(handle_rect.right() + 8.0, tab_recess),
|
||||
egui::pos2(tab_right, top),
|
||||
]
|
||||
};
|
||||
painter.add(egui::Shape::line(tab_points, accent_stroke));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user