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
@@ -59,6 +59,7 @@ pub(super) fn assemble<'a, M: Clone + 'a>(
|
||||
let geometry = Geometry::sized(ui, variant, spec.leading.is_some(), spec.size);
|
||||
let (fill, content, _border) = variant.roles();
|
||||
let forced = spec.forced();
|
||||
let enabled = spec.on_press.is_some();
|
||||
|
||||
// 按钮家族没有浮层级高程:L1(Elevated)只用容器色调差表达(rubric §7 不画阴影)
|
||||
debug_assert!(
|
||||
@@ -125,9 +126,17 @@ pub(super) fn assemble<'a, M: Clone + 'a>(
|
||||
.padding(geometry.padding)
|
||||
.style(style);
|
||||
|
||||
match spec.on_press {
|
||||
let element: Element<'a, M> = match spec.on_press {
|
||||
Some(message) => element.on_press(message).into(),
|
||||
None => element.into(),
|
||||
};
|
||||
|
||||
// A8:**运行期态**才套状态取样(gallery 的 `state(..)` 强制态是取证,不许有过渡);
|
||||
// 禁用的按钮不跟踪(它没有状态层可动,白占帧)。
|
||||
if spec.state.is_some() {
|
||||
element
|
||||
} else {
|
||||
interaction::tracked(element, enabled)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,13 +210,13 @@ pub(super) fn style_closure(
|
||||
(spec.fill.map(|_| container), content)
|
||||
}
|
||||
None => {
|
||||
// 有填充:状态层叠在**容器角色**上;透明容器:叠在表面色上(否则 hover 看不见)
|
||||
// 有填充:状态层叠在**容器角色**上;透明容器:叠在脚下那一层(`base`)上
|
||||
// (否则 hover 看不见)。两条都经 `layer_color`:A8 起它带时间轴,
|
||||
// 没有取样控件时**逐位**退回 `overlay_color`。
|
||||
let background = match spec.fill {
|
||||
Some(role) => match state.layer() {
|
||||
Some(_) => interaction::overlay_color(ui, state, spec.content, role)
|
||||
.or(Some(ui.color(role))),
|
||||
None => Some(ui.color(role)),
|
||||
},
|
||||
// 有填充:静息就是容器色(不是「没有背景」),过渡中才换成插值色。
|
||||
Some(role) => interaction::layer_color(ui, state, spec.content, role)
|
||||
.or(Some(ui.color(role))),
|
||||
None => transparent_state_color(ui, spec.base, state, spec.content),
|
||||
};
|
||||
(background, ui.color(spec.content))
|
||||
@@ -245,19 +254,16 @@ pub(super) fn style_closure(
|
||||
}
|
||||
}
|
||||
|
||||
/// 透明容器的状态层:`Outlined` / `Text` / `StandardIcon` 没有填充,可见反馈只能来自
|
||||
/// 叠在**内容角色**上的状态层——把它们当作「叠在 `base`(脚下那一层)上」求值,得到不透明实色。
|
||||
/// 透明容器的状态层(`Outlined` / `Text` / `StandardIcon`):可见反馈只能来自叠在
|
||||
/// **脚下那一层**([`Base`])上的状态层。
|
||||
///
|
||||
/// 之所以单独一个函数:这是 M3 里唯一「容器不存在但状态层存在」的情形,
|
||||
/// 三个变体共用一条口径,免得各自发挥。
|
||||
///
|
||||
/// M234 复审 P2-3:`base` 之前写死 `Role::Surface`,卡片里的 Text/Outlined 按钮抬起时
|
||||
/// 状态层叠在画布色上(与禁用支的口径不一致)。
|
||||
/// M257 起它只是 [`interaction::layer_color`] 的一次调用(两者的区别只在「用哪个角色当底」),
|
||||
/// 保留这个具名入口是为了让「透明容器的状态层叠在 base 上」这条口径在源码里仍然只有一处。
|
||||
pub(super) fn transparent_state_color(
|
||||
ui: &Ui,
|
||||
base: Base,
|
||||
state: Interaction,
|
||||
content: Role,
|
||||
) -> Option<iced::Color> {
|
||||
interaction::overlay_color(ui, state, content, base.role())
|
||||
interaction::layer_color(ui, state, content, base.role())
|
||||
}
|
||||
@@ -54,7 +54,7 @@ use unlucky_design::shape::ShapeScale;
|
||||
use crate::icons;
|
||||
use crate::theme::Ui;
|
||||
|
||||
use super::interaction::{Base, Interaction};
|
||||
use super::interaction::{self, Base, Interaction};
|
||||
use super::layout;
|
||||
|
||||
mod assemble;
|
||||
@@ -359,6 +359,7 @@ pub fn icon_on<'a, M: Clone + 'a>(
|
||||
let base = base.into();
|
||||
let geometry = IconGeometry::of(ui);
|
||||
let (fill, content, _) = variant.roles();
|
||||
let enabled = on_press.is_some();
|
||||
let forced = state.or(if on_press.is_some() {
|
||||
None
|
||||
} else {
|
||||
@@ -383,8 +384,15 @@ pub fn icon_on<'a, M: Clone + 'a>(
|
||||
.padding(Padding::default())
|
||||
.style(style);
|
||||
|
||||
match on_press {
|
||||
let element: Element<'a, M> = match on_press {
|
||||
Some(message) => element.on_press(message).into(),
|
||||
None => element.into(),
|
||||
};
|
||||
|
||||
// A8:与文字按钮同一条口径——强制态不套取样控件,禁用不跟踪。
|
||||
if state.is_some() {
|
||||
element
|
||||
} else {
|
||||
interaction::tracked(element, enabled)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user