//! 占位状态(加载 / 空):**同一套占位规则——内容在可用区居中**(M94 r1 / P2-1)。 //! //! | 状态 | 条件(都能从快照推出来) | 画什么 | //! |---|---|---| //! | 加载 | `model.is_loading()`:快照里还没有版本列表 | 标题 + 一句说明(**没有任何进度形状**) | //! | 空 | `model.is_empty()`:列表拉到了,但过滤后一行不剩 | 真 [`empty_state`](图标块 96 + 24 标题 + 说明 + 刷新 CTA) | //! //! ## 为什么是「都居中」(P2-1) //! //! `components::feedback::empty_state` 的既有口径(`12b §15.4`)就是「内容在可用区居中」; //! 加载态原先贴顶,等于同一个页面里两套占位规则,两态互切时内容会纵向跳一次。所以本轮把加载态 //! 也放进同一条规则里。**有数据时列表仍然贴顶**——那是有内容的状态,不属于占位规则。 //! //! ## 为什么加载态没有进度条(P2-3) //! //! 页面**没有帧订阅**(`app/subscription.rs` 的 `needs_frames` 归 app 层),所以 //! `Progress::indeterminate(phase)` 拿到的是一个**静止的**相位——用户看到的是「17% 卡住」。 //! 与其留一个「看起来有进度但不动」的控件,不如给一句明确的「正在加载」文字;等 app 层真有了 //! 帧订阅再来带动画(那时这里换回 indeterminate 才有意义)。 //! //! ## 文案口径(P2-2) //! //! 只说页面真能做到的动作:说明里那句「点右上角的刷新」对应页头那个真的 Refresh 按钮 //! (`Message::Action(Action::RefreshSources)`);没有写「稍后会自动重试」这种 UI 做不到的承诺。 use iced::widget::{column, container}; use iced::{Alignment, Element, Length}; use unlucky_design::color::Role; use unlucky_design::icons::IconKind; use unlucky_design::spacing::Space; use unlucky_design::typography::TypeRole; use unlucky_i18n::{Catalog, Key}; use crate::app::components::feedback::{empty_state, EmptyState}; use crate::theme::Ui; use super::super::frame; use super::super::{Action, Message}; /// 两个占位状态共用的外壳:水平 + 垂直都居中(见模块文档的两条理由)。 fn centered<'a>(content: impl Into>) -> Element<'a, Message> { container(content) .width(Length::Fill) .height(Length::Fill) .align_x(Alignment::Center) .align_y(Alignment::Center) .into() } /// 加载态:一句「正在加载」+ 一句这个页面真能做的事(点刷新)。 pub(super) fn loading<'a>(ui: &'a Ui, catalog: Catalog) -> Element<'a, Message> { centered( column![ frame::text_role_centered( ui, TypeRole::TitleMedium, true, catalog.text(Key::DownloadLoading), ui.color(Role::OnSurface), ), frame::text_role_centered( ui, TypeRole::BodySmall, false, catalog.text(Key::DownloadLoadingHint), ui.color(Role::OnSurfaceVariant), ), ] .spacing(ui.space(Space::X3)) .width(Length::Fill) .align_x(Alignment::Center), ) } /// 空态(真 `EmptyState`:CTA 就是标准 Filled 按钮,走按钮家族)。 pub(super) fn empty<'a>(ui: &'a Ui, catalog: Catalog) -> Element<'a, Message> { centered(empty_state( ui, EmptyState::new( IconKind::Download, catalog.text(Key::DownloadEmptyTitle), catalog.text(Key::DownloadEmptyBody), ) .action( catalog.text(Key::DownloadRefresh), Message::Action(Action::RefreshSources), ), )) }