//! 导航轨底部**账户头像位**的状态换算(M114)+ 轨自身的折叠档(M300)。 //! //! 为什么单独一个文件:`app/view.rs` 贴着 400 行硬线,而这块逻辑与页面树无关—— //! 它只回答「轨底那个头像位现在该显示成未登录,还是某个账户」,以及「这一帧轨多宽」。 use crate::app::components::motion; use crate::shell; use super::super::state::App; /// 导航轨收成「图标条」的窗口高度(M114)。 /// /// 判据不是「项数」而是**真实可用高度**:四项 + 底部头像位在 48 标题栏之上还要放得下 /// 4 × 52 + 4 × 4 + 52 ≈ 300;留出一屏正文的最小高度(424 行宽 + 页头 + 边距), /// 低于 720 就把标签收掉,只留图标与头像位(头像位**永远可见**,任务要求)。 const RAIL_COMPACT_HEIGHT: f32 = 720.0; impl App { /// 导航轨底部头像位的状态(M114):未登录 / 已登录两态要一眼能分。 /// /// M300:这里同时是轨的折叠档与过渡进度的**唯一构造点**——`shell::NavRail`(不在 scope 内) /// 只负责把它转交给 `nav::rail`,几何全部来自 [`shell::RailAccount::nav_width`]。 pub(super) fn rail_account(&self) -> shell::RailAccount { let compact = self.height < RAIL_COMPACT_HEIGHT; let (collapsed, progress) = self.nav_state(); match self.state.active_account() { Some(account) => { shell::RailAccount::signed_in(account.monogram(), account.name.clone()) .compact(compact) .nav(collapsed, progress) } // M106:未登录标签跟着**生效语言**走(早期钉死 `Lang::En` 的写法已删)。 None => shell::RailAccount::signed_out( self.lang() .catalog() .text(unlucky_i18n::Key::AccountRailSignedOut), ) .compact(compact) .nav(collapsed, progress), } } /// 轨的折叠档 + **这一帧**的过渡进度(0 = 折叠 64,1 = 展开 88)。 /// /// 进度是**采样**来的,不是自己算的:`motion::nav(open)` 在「帧泵的目标与 `open` 不符」时 /// 直接给终值,于是截图 harness / 单测直接摆状态也不会把界面卡在中间那一帧。 fn nav_state(&self) -> (bool, f32) { let collapsed = self.state.prefs.nav_collapsed; (collapsed, motion::nav(!collapsed)) } /// 这一帧导航轨的宽度(展开 88 ⇄ 折叠 64,中间帧按动效进度插值)。 /// /// **给用例用**:它读的就是 `view()` 递给 `nav::rail` 的那个值(同一个 `RailAccount`), /// 所以「点击后宽度真的变了」这条断言不会与视图各算一套而假绿。 #[cfg(test)] pub(in crate::app) fn nav_rail_width(&self) -> f32 { self.rail_account().nav_width(self.ui().metrics()) } }