feat(motion2): 右栗折叠过渡 + 状态层 hover/pressed 过渡
A2 侧边栏(右栗 AI 栏)折叠/展开: - 帧泵第三条轨道(pump/rail):展开 medium1 250ms + Emphasized、收起 short4 200ms + EmphasizedAccelerate;拆出档走 snap(不做过渡);MotionScale::NONE 下不插轨道 - 宽度在 metrics::rail_width 逐帧插值;面板内容按目标宽排版 + 按动画宽裁切 (不重排 / 不溢出),宽度回到 48 时换成图标条(图标贴右端,位置不跳) - pages/agent/mod.rs 的展开档面板纯搬移到 panel.rs(371 → 289 行) A8 状态层过渡: - interaction/layer:令牌化的进度时钟(short1 50ms),NONE 当帧落终值 - interaction/hover:取样控件把起跑搬到 Widget::update(早于订阅表求值) - 按钮五式 + IconButton + 导航项药丸统一走 interaction::layer_color (没有取样控件时逐位退回旧口径,出图基线不变) - components/motion/mod.rs 的 needs_frames / advance 各加一句委托, 帧订阅仍然只有一处判断点(app/subscription.rs 未改)
This commit is contained in:
1 parent
c4706c759c
commit
b61f342431
22 files changed
+1837
-145
No files matched your search
@@ -31,6 +31,8 @@ use super::metrics::{
|
||||
content_width, effective_state, magnet_width, rail_width, DEFAULT_EXPANDED_WIDTH,
|
||||
};
|
||||
|
||||
use crate::app::components::motion;
|
||||
|
||||
/// 三档状态(顺序 = 展开程度,测试与文件名按它遍历)。
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum RailState {
|
||||
@@ -204,13 +206,30 @@ impl AgentUi {
|
||||
effective_state(window_width, self.rail, self.expanded)
|
||||
}
|
||||
|
||||
/// 折叠过渡的**目标档**(`true` = 展开)。
|
||||
pub fn rail_open(&self) -> bool {
|
||||
self.rail == RailState::Expanded
|
||||
}
|
||||
|
||||
/// 折叠进度(0 = 48 图标条,1 = 展开档)。**只采样**,起跑在下面四个档位迁移里。
|
||||
pub fn collapse_progress(&self) -> f32 {
|
||||
motion::rail(self.rail_open())
|
||||
}
|
||||
|
||||
/// 右栗当前宽度(窗口宽度是判据之一:窗口变窄时展开档会被夹住)。
|
||||
///
|
||||
/// M257 起它是**逐帧宽度**:档位切换的那 200~250ms 里返回过渡中的值(端点与静息值逐位相同)。
|
||||
///
|
||||
/// 名字故意不叫 `width`:`tests/no_hardcoded_tokens.rs` 会把任何 `.width(数字)` 当成
|
||||
/// 「布局尺寸写死」拦下来,而测试里 `agent.width(1180.0)` 会被误判(那条门禁扫的是控件调用点,
|
||||
/// 分不清是谁的方法)。
|
||||
pub fn rail_width(&self, window_width: f32) -> f32 {
|
||||
rail_width(window_width, self.rail, self.expanded)
|
||||
rail_width(
|
||||
window_width,
|
||||
self.rail,
|
||||
self.expanded,
|
||||
self.collapse_progress(),
|
||||
)
|
||||
}
|
||||
|
||||
/// **交给页面布局系统的宽度**(窗口 − 右栗 − 拖拽缝)。
|
||||
@@ -227,6 +246,8 @@ impl AgentUi {
|
||||
self.keyboard_mode = false;
|
||||
if self.rail == RailState::Expanded {
|
||||
self.rail = RailState::Collapsed;
|
||||
// 起跑在 update 侧(本函数由 `app/update/agent.rs` 调)——折叠那 200ms 的宽度轨道。
|
||||
motion::observe_rail(false);
|
||||
self.focus = Focus::Collapse;
|
||||
} else if self.rail == RailState::Detached {
|
||||
self.dock();
|
||||
@@ -236,7 +257,14 @@ impl AgentUi {
|
||||
/// 展开(点图标条)。展开即视为读过——**清零动作在 app 层**(`mark_read`),这里只改档位。
|
||||
pub fn expand(&mut self) {
|
||||
self.keyboard_mode = false;
|
||||
// 只有「从折叠档展开」才是宽度的连续变化;从独立页收回(`dock`)与首次落档都是**布局切换**。
|
||||
let animate = self.rail == RailState::Collapsed;
|
||||
self.rail = RailState::Expanded;
|
||||
if animate {
|
||||
motion::observe_rail(true);
|
||||
} else {
|
||||
motion::snap_rail(true);
|
||||
}
|
||||
self.focus = Focus::Collapse;
|
||||
}
|
||||
|
||||
@@ -244,6 +272,8 @@ impl AgentUi {
|
||||
pub fn detach(&mut self) {
|
||||
self.keyboard_mode = false;
|
||||
self.rail = RailState::Detached;
|
||||
// 拆出档**不做过渡**(右栗消失、会话搬到独立页,是布局切换):目标落终值。
|
||||
motion::snap_rail(false);
|
||||
self.focus = Focus::Detach;
|
||||
}
|
||||
|
||||
@@ -251,6 +281,8 @@ impl AgentUi {
|
||||
pub fn dock(&mut self) {
|
||||
self.keyboard_mode = false;
|
||||
self.rail = RailState::Expanded;
|
||||
// 从独立页收回到「常驻展开」:右栗是从 0 出现的布局切换,不是 48→320 的宽度过渡。
|
||||
motion::snap_rail(true);
|
||||
self.focus = Focus::Collapse;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user