fix(settings): 设置页尾部不再渲染不属于本页的空态卡
用户 2026-11-08 在 M174 的助手页与 M175 的 Java 页截图里看到同一张卡(四格图标 + 「没有逐实例覆盖」+ 说明被窗口下沿截断):两张图 1180×1100、卡片位置完全一致、 独立居中在内容下方。 根因是 `pages/settings/mod.rs` 的 `form()` 在分组循环之后**无条件** push 了 `pages/settings/frame.rs` 的 `overrides_empty()`(M84 骨架期的占位,M99 纯搬移时原样 留下,固定 320dp 高):于是六个分组下面都长出同一张卡,与实例选中态无关。 修法:删掉该空态位及其唯一的三个 i18n key(`SettingsOverrides*`,金表同步删三行)。 设置页永远有内容(总有一组被选中),12b §15 的空态件是给实例/下载/日志页的; 「逐实例覆盖」全仓既无数据源也无消息落点,所以不挪给任何一组,直接不渲染。 守门(`settings/frame.rs` 的 `no_stray_empty_state` 三条,变异实测会红): 尾部不许再出现额外 push、`form()` 不认任何空态件名、设置页不读实例选择。
This commit is contained in:
1 parent
30b61e2684
commit
7fdebba961
4 files changed
+106
-34
No files matched your search
@@ -1,4 +1,4 @@
|
||||
//! 设置页骨架:页头、左列分组导航、表单列的滚动区、演示卡片、空态位。
|
||||
//! 设置页骨架:页头、左列分组导航、表单列的滚动区、演示卡片。
|
||||
//!
|
||||
//! ## 为什么这里还再导出一批 `pages::frame` 的名字
|
||||
//!
|
||||
@@ -8,8 +8,8 @@
|
||||
//! 这不是新 API,只是一条转发(新增控件仍归 [`crate::pages::frame`])。
|
||||
|
||||
pub(super) use super::super::frame::{
|
||||
card, divider, empty_state, glyph_box, left_aligned, page, panes, scroll_style, single_column,
|
||||
text_action, text_role, Header, ICON_PX, SCROLLBAR_MARGIN, SCROLLBAR_WIDTH,
|
||||
card, divider, glyph_box, left_aligned, page, panes, scroll_style, single_column, text_action,
|
||||
text_role, Header, ICON_PX, SCROLLBAR_MARGIN, SCROLLBAR_WIDTH,
|
||||
};
|
||||
|
||||
use iced::widget::{column, container, mouse_area, row};
|
||||
@@ -18,7 +18,6 @@ use iced::{Alignment, Background, Border, Element, Length, Padding, Theme};
|
||||
use unlucky_design::color::Role;
|
||||
use unlucky_design::component_map::Component;
|
||||
use unlucky_design::elevation::MaterialLayer;
|
||||
use unlucky_design::icons::IconKind;
|
||||
use unlucky_design::shape::ShapeScale;
|
||||
use unlucky_design::spacing::Space;
|
||||
use unlucky_design::typography::TypeRole;
|
||||
@@ -30,9 +29,6 @@ use crate::theme::Ui;
|
||||
|
||||
use super::Group;
|
||||
|
||||
/// 空态位高度:给图标块 96 + 标题 + 副文案 + 按钮留够空间(`[估]`,按 12b §15 的元素最小高度累加)。
|
||||
const EMPTY_SLOT_HEIGHT: f32 = 320.0;
|
||||
|
||||
/// 页头:标题 + 副标题 + 两个占位动作。
|
||||
///
|
||||
/// M106:三条文案走 catalog(`lang` 由 `settings::view` 传下来)。页标题复用 `NavSettings`——
|
||||
@@ -218,21 +214,98 @@ fn setting_row<'a>(ui: &'a Ui, label: &'a str, value: &'a str) -> Element<'a, Me
|
||||
.into()
|
||||
}
|
||||
|
||||
/// 空态位:没有逐实例覆盖时的样子(12b §15 的口径)。
|
||||
pub(super) fn overrides_empty<'a>(ui: &'a Ui, lang: Lang) -> Element<'a, Message> {
|
||||
let catalog = lang.catalog();
|
||||
container(empty_state(
|
||||
ui,
|
||||
IconKind::Instances,
|
||||
catalog.text(Key::SettingsOverridesEmptyTitle),
|
||||
catalog.text(Key::SettingsOverridesEmptyBody),
|
||||
vec![text_action(
|
||||
ui,
|
||||
catalog.text(Key::SettingsOverridesAdd),
|
||||
Action::AddOverride,
|
||||
)],
|
||||
))
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fixed(EMPTY_SLOT_HEIGHT))
|
||||
.into()
|
||||
// --------------------------------------------------------------- M196 守门
|
||||
|
||||
/// M196:设置页正文**不许**在最后一组之后挂一张空态卡。
|
||||
///
|
||||
/// ## 守的是哪一件事
|
||||
///
|
||||
/// 用户 2026-11-08 在 M174 的助手页与 M175 的 Java 页都看到同一张不属于本页的空态卡(四格图标 +
|
||||
/// 「没有逐实例覆盖」+ 说明被窗口下沿截断),两张图 1180×1100、卡片位置完全一致。根因是
|
||||
/// `settings/mod.rs` 的 `form()` 在分组循环之后**无条件**叠了一件固定 320dp 高的空态位
|
||||
/// (M84 骨架期的占位,M99 纯搬移时原样留下)——于是任何一组下面都长出同一张卡。
|
||||
///
|
||||
/// 这三条钉住的就是「尾部不许再多一件」,以及「本页不读实例选择」(于是「无选中实例」与
|
||||
/// 「有选中实例」两种状态下本页的渲染路径逐字相同)。
|
||||
///
|
||||
/// ## 为什么住在 `frame.rs` 而不是 `pages/settings/tests/`
|
||||
///
|
||||
/// `tests/` 归四条 section 线所有(各改各的),而这里守的是**本文件与 `mod.rs` 之间的分工**:
|
||||
/// 壳层不提供空态位、分发点不在尾部多堆一件。判据要是哪天挪进 `tests/`,也请原样保留这三条。
|
||||
///
|
||||
/// ## 抓得住 / 抓不住(据实写)
|
||||
///
|
||||
/// 抓得住:把那张卡(或**任何**尾部元素)加回 `form()` —— 变异实测:在 `frame::scroll` 之前插一句
|
||||
/// `stack = stack.push(...)`,[`the_form_stops_at_its_last_group`] 立刻红;把空态件按名字写回去,
|
||||
/// [`the_form_never_renders_an_empty_state`] 红;把某件东西挂到「当前选中实例」上,
|
||||
/// [`the_settings_page_never_reads_the_installation_selection`] 红。
|
||||
/// 抓不住:**另起一套名字**重画的空态(文本断言不是行为断言)。那一档只有真图能看出来 ——
|
||||
/// 证据在 `DevDocs/artifacts/m196/`(Java / 助手 × 1180×760 / 1180×1100)。
|
||||
#[cfg(test)]
|
||||
mod no_stray_empty_state {
|
||||
/// 设置页的分发点(本文件是它的被调用方)。
|
||||
const FORM: &str = include_str!("mod.rs");
|
||||
|
||||
/// `form()` 的函数体:从签名取到文件尾(它是 `mod.rs` 最后一件)。
|
||||
fn form_body() -> &'static str {
|
||||
let start = FORM
|
||||
.find("fn form<'a>(")
|
||||
.expect("settings/mod.rs 里找不到 form():分发点被改名了,本守门要跟着改");
|
||||
&FORM[start..]
|
||||
}
|
||||
|
||||
/// 判据 ①:循环之后**没有**任何额外的 `push`,且函数以滚出去收尾。
|
||||
///
|
||||
/// 循环体里只有一件(分组卡),所以「`stack = stack.push(` 恰好一次」等于「尾部空着」。
|
||||
#[test]
|
||||
fn the_form_stops_at_its_last_group() {
|
||||
let body = form_body();
|
||||
let pushes = body.matches("stack = stack.push(").count();
|
||||
assert_eq!(
|
||||
pushes, 1,
|
||||
"`form()` 里出现了 {pushes} 处 `stack = stack.push(`:分组循环里只该有一处。\
|
||||
尾部又被叠了一件东西的话,设置页每一组下面都会多出同一件(M196 那张\
|
||||
「没有逐实例覆盖」的卡就是这么来的)"
|
||||
);
|
||||
assert!(
|
||||
body.contains(" frame::scroll(ui, stack)\n}"),
|
||||
"`form()` 必须以 `frame::scroll(ui, stack)` 收尾(后面紧跟函数右花括号):\
|
||||
正文到最后一组就结束,别再往后叠东西"
|
||||
);
|
||||
}
|
||||
|
||||
/// 判据 ②:`form()` 不认任何空态件(名字层面)。
|
||||
///
|
||||
/// 抓得住「把那张卡原样加回来」;抓不住换名字重画的(见本模块文档)。
|
||||
#[test]
|
||||
fn the_form_never_renders_an_empty_state() {
|
||||
let body = form_body();
|
||||
for needle in ["empty_state", "EmptyState", "overrides"] {
|
||||
assert!(
|
||||
!body.contains(needle),
|
||||
"`form()` 里出现了 {needle:?}:设置页永远有内容(总有一组被选中),\
|
||||
空态件是实例 / 下载 / 日志页的东西(12b §15 的挡项就是不设空态的页),\
|
||||
设置页不该借它来充数"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 判据 ③:本页**不读实例选择** —— 于是「无选中实例」与「有选中实例」两种状态下,
|
||||
/// 设置页走的是同一组函数、同一拼装顺序,那一张卡在两种状态下都不可能出现。
|
||||
///
|
||||
/// 抓得住:谁把某件东西改成「按当前选中实例决定画不画」(那正是这张卡当初被误读成
|
||||
/// 「实例详情的空态」的那条路)。
|
||||
#[test]
|
||||
fn the_settings_page_never_reads_the_installation_selection() {
|
||||
let needles = [concat!("inst", "ance"), concat!("Inst", "ance")];
|
||||
for (name, source) in [("mod.rs", FORM), ("frame.rs", include_str!("frame.rs"))] {
|
||||
for needle in needles {
|
||||
assert!(
|
||||
!source.contains(needle),
|
||||
"{name} 里出现了 {needle:?}:设置页不读实例选择(`settings::view` 的形参里\
|
||||
没有 `Model`)。一旦本页开始按选中实例分化,M196 那张卡就有回来的路"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user