fix(download): 窄档滚动视口底部加 12px 淡出遮罩,去掉被硬裁的「空壳蓝条」(M227 返工)
D30 视觉审 hold 的必改项:滚动视口在 y=660 硬裁,把版本菜单的容器上沿切成 639–659 = 21px、 **零字形墨迹**的一条色块(深色下 (22,33,41) vs 画布 (9,21,28),CR 1.99),读起来像渲染错 而不是「下面还有内容可滚」。 改法(reviewer 推荐方案①,保留发现性): - 新增 `options/fade.rs::viewport_fade` + `VIEWPORT_FADE_PX = 12`:12 条 1px、α 单调上升的 **画布色**实色块拼成的斜坡(iced 0.14 没有渐变原语;1px 步进的差别在 1/255 量级); - `options::section` 的窄档分支把滚动区包一层 `stack![滚动区, 底部对齐的遮罩]`,遮罩贴着 视口内沿(≤660),压不到钉死区的实例名(≥668); - 颜色取 `ui.layer(MaterialLayer::Canvas)`:浅色淡出到纸面、**深色淡出到墨底**(不是白雾), 与它下方那块背景同色,所以两档都不新增颜色; - 三栏档不铺这条遮罩:那里滚动区内容恒 76px < 视口 ≈118px,不会出现半截内容,铺了只是 「画布盖画布」,反而会动 M222 已验的 1180 像素; - `options/view.rs` 已 422 行、撞 400 行硬线,遮罩拆成 `options/fade.rs`(含一条几何自检单测)。 实测(720×760): - 浅色 x=600 竖列 648 (211,229,245) → 659 (246,250,255) 单调升到画布色;深色 648 (55,72,85) → 659 (9,21,28),无白雾;中文与浅色逐值同串; - 可见带 21px → 墨迹口径 18px + 底 12px 连续渐变(硬裁边消失,右侧滚动条仍在); - 逐像素零回退:`77d675b`(遮罩前)vs 本轮 720 浅/深/中 + 700 守门,差异 bbox 只有 (112,648)–(696,660) 这 12 行,其余像素一个都没动;1180×760 浅/深/中 与 1180×900 对 M222 基线仍 **IDENTICAL**。 门禁:fmt ✓ / check ✓ / test --workspace(喂四张守门图)exit 0(app-iced lib 860 passed、 四条像素守门 + 400 行门全绿)/ clippy --workspace --all-targets -D warnings exit 0 / .rs 全仓扫描 >400 行 0 个、<2 字节 0 个。 已知残留(本轮不动,按债务台账口径登记):menu 的 L2 阴影不随滚动视口裁切(压在实例名输入框上 约 6.9%,三栏档 fabric 同样存在)——根因在 `components::inputs::menu`,不在 M227 scope, 待专门 mission;finding `.tower/comms/findings/20261110-worker-m227-bug-l2.md`。
This commit is contained in:
1 parent
a70d5f8010
commit
122accdd02
3 files changed
+119
-12
No files matched your search
@@ -27,6 +27,7 @@
|
||||
//! 草稿(选中版本 + 三个选项)住在 `App`,由 `refresh_view` 带进页面模型——与分类筛选同一个做法,
|
||||
//! 因为 `refresh_view` 每次事件都会把整棵模型重建,页面自己存不住任何东西。
|
||||
|
||||
mod fade;
|
||||
mod model;
|
||||
#[cfg(test)]
|
||||
mod s1_tests;
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
//! 选项区滚动视口底部的**淡出遮罩**(M227 返工)。
|
||||
//!
|
||||
//! 单独一个文件只为守 400 行硬线(`components::gate::every_source_file_stays_under_the_line_limit`)。
|
||||
|
||||
use iced::widget::{column, container, space};
|
||||
use iced::{Color, Element, Length};
|
||||
|
||||
use unlucky_design::elevation::MaterialLayer;
|
||||
|
||||
use crate::theme::Ui;
|
||||
|
||||
use super::super::frame;
|
||||
use super::super::Message;
|
||||
|
||||
/// 窄档滚动视口底部的淡出高度(像素)。
|
||||
///
|
||||
/// 取值落在 8~12 这一段:视口在 660 处硬裁,版本菜单的容器上沿(639)露出来 21px 且**一个字形
|
||||
/// 都没有**——D30 返工把它判成「空壳蓝条」(深色下容器 `(22,33,41)` vs 画布 `(9,21,28)`,
|
||||
/// CR 1.99,像渲染错而不是「下面还有内容」)。12px 遮罩让最后 12px 单调渐变到画布色,
|
||||
/// 留下 8px 圆角上沿做发现性提示。
|
||||
pub(super) const VIEWPORT_FADE_PX: f32 = 12.0;
|
||||
|
||||
/// 滚动视口底部的**淡出遮罩**:把内容在最后 `height` 像素内渐变到**画布色**。
|
||||
///
|
||||
/// 为什么需要它:`scrollable` 会硬裁内容(渲染层的 `with_layer`),裁切位置落在什么上面是不确定的
|
||||
/// ——窄档正落在版本菜单容器上沿,用户看到的是一条没有字形的色块,而不是「还能往下滚」。
|
||||
///
|
||||
/// 为什么是**画布色的 α 阶梯**而不是白/黑雾:遮罩的颜色必须等于它下方那块背景的颜色,否则
|
||||
/// 浅色档会浮出一层灰幕、深色档会浮出白雾。`ui.layer(MaterialLayer::Canvas)` 就是页面底色
|
||||
/// (含面板 α),于是浅色淡出到纸面、深色淡出到墨底,两档都不新增颜色;方向也对
|
||||
/// (内容向背景淡出,不是叠一层高光)。
|
||||
///
|
||||
/// 为什么用 12 条 1px 实色块拼:iced 0.14 没有渐变原语。1px 步进的 α 斜坡在 100% DPI 下与
|
||||
/// 连续渐变的差别在 1/255 量级——D30 的逐像素量测只会看到 α 单调、无硬边。
|
||||
///
|
||||
/// 形状上它必须**贴在视口内**(调用侧把它放在滚动区底部的 `stack!` 上):视口下沿是 660,
|
||||
/// 钉死区的实例名从 668 起,所以遮罩压不到任何控件(M227 返工的零回退证据就是逐像素
|
||||
/// 差异只有 648–660 这 12 行)。
|
||||
pub(super) fn viewport_fade<'a>(ui: &'a Ui, height: f32) -> Element<'a, Message> {
|
||||
const STEPS: usize = 12;
|
||||
let canvas = ui.layer(MaterialLayer::Canvas);
|
||||
let band_height = height / STEPS as f32;
|
||||
|
||||
let mut bands = column![].width(Length::Fill);
|
||||
for index in 0..STEPS {
|
||||
// t:顶端 ≈0(几乎透出内容)→ 底端 = 1(完全是画布色,与视口外的背景接上)。
|
||||
let t = (index + 1) as f32 / STEPS as f32;
|
||||
let alpha = canvas.a * t.powf(1.6);
|
||||
bands = bands.push(
|
||||
container(space::horizontal())
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fixed(band_height))
|
||||
.style(move |_theme| frame::filled(Color { a: alpha, ..canvas })),
|
||||
);
|
||||
}
|
||||
bands.into()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// 遮罩的几何自检:12 条 1px 恰好铺满 12px,且 α 单调不减(顶端近透明、底端不透明)。
|
||||
///
|
||||
/// 抓得住:把步数改成 6(每档 2px ⇒ 能看到阶梯)、或把 α 曲线写成常数(等于一层纯色幕)。
|
||||
#[test]
|
||||
fn the_fade_steps_are_one_pixel_and_monotone() {
|
||||
let steps = 12.0_f32;
|
||||
assert_eq!(
|
||||
(VIEWPORT_FADE_PX / steps * steps * 100.0).round() / 100.0,
|
||||
VIEWPORT_FADE_PX,
|
||||
"步数要能整除 12,否则会出现半像素带"
|
||||
);
|
||||
let mut previous = -1.0_f32;
|
||||
for index in 0..12 {
|
||||
let t = (index + 1) as f32 / steps;
|
||||
let alpha = t.powf(1.6);
|
||||
assert!(alpha >= previous, "α 必须单调不减(第 {index} 档)");
|
||||
previous = alpha;
|
||||
}
|
||||
assert_eq!(previous, 1.0, "底端必须是完全不透明的画布色");
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@
|
||||
//! [原版][Forge][NeoForge][Fabric][Quilt][LiteLoader]
|
||||
//! Vanilla parent only — Forge goes through the CLI
|
||||
//! ┌ 1.12.2-SNAPSHOT ┐ ← 露在折线下方,滚得到(滚动条在右)
|
||||
//! ── 视口底 12px 淡出遮罩(`fade::viewport_fade`)────
|
||||
//! ── 钉死区 ──────────────────────────────────────
|
||||
//! [实例名 ]
|
||||
//! ```
|
||||
@@ -31,7 +32,10 @@
|
||||
//! 顺序铺开(`04` 基线图就是这一档,不能改);
|
||||
//! - 不兼容时的原因行(Error 色)在滚动区末尾(**窄档**可达;三栏档 + 带版本列表时
|
||||
//! `problem` 恒为 `None`,所以那支看不到它);
|
||||
//! - 说明只有 LiteLoader 有(`Key::DownloadOptionsLiteLoaderVanillaOnly`)。
|
||||
//! - 说明只有 LiteLoader 有(`Key::DownloadOptionsLiteLoaderVanillaOnly`);
|
||||
//! - 窄档的滚动视口底边会硬裁内容(M227 返工前正切在版本菜单容器上沿,露出 21px 没有字形的
|
||||
//! 色块),所以那里铺一条 12px 淡出遮罩把内容渐变到画布色——遮罩在视口内(≤660),
|
||||
//! 压不到钉死区的实例名(≥668)。
|
||||
//!
|
||||
//! ## 为什么加载器是**六枚一行**(M222 D30 r1 实测订正)
|
||||
//!
|
||||
@@ -46,7 +50,7 @@
|
||||
//! 现在的做法:五家永远都能点,选中不兼容的那家时下面出现一行具名原因,同时「安装」变成禁用态。
|
||||
//! 于是「为什么不能装」和「怎么才能装」永远同屏。
|
||||
|
||||
use iced::widget::{column, container, row, scrollable};
|
||||
use iced::widget::{column, container, row, scrollable, stack};
|
||||
use iced::{Alignment, Element, Length};
|
||||
|
||||
use unlucky_design::color::Role;
|
||||
@@ -65,6 +69,7 @@ use crate::theme::Ui;
|
||||
use super::super::frame;
|
||||
use super::super::rows::TailShare;
|
||||
use super::super::Message;
|
||||
use super::fade;
|
||||
use super::model::{Loader, LoaderVersionsView, OptionsView};
|
||||
|
||||
// 加载器 chip 的顺序与集合**委派**给 `Loader::ALL`(原版 / Forge / NeoForge / Fabric /
|
||||
@@ -240,16 +245,34 @@ pub(crate) fn section<'a>(
|
||||
Message::SetInstanceName,
|
||||
));
|
||||
|
||||
column![
|
||||
scrollable(body)
|
||||
.height(Length::Fill)
|
||||
.style(frame::scroll_style(ui)),
|
||||
pinned,
|
||||
]
|
||||
.spacing(ui.space(Space::X2))
|
||||
.height(Length::Fill)
|
||||
.width(Length::Fill)
|
||||
.into()
|
||||
let scroll_area: Element<'a, Message> = scrollable(body)
|
||||
.height(Length::Fill)
|
||||
.style(frame::scroll_style(ui))
|
||||
.into();
|
||||
|
||||
// 窄档:视口底部铺一条 12px 淡出遮罩(M227 返工 ①,理由见 `fade::viewport_fade`)。
|
||||
// 三栏档不铺:那里滚动区内容恒为 head 40 + 间距 4 + chips 32 = 76px,而视口 ≈118px,
|
||||
// 永远不会出现「半截内容」;铺上去只是「画布盖画布」,反而动了 M222 已验的 1180 像素。
|
||||
let scroll_area: Element<'a, Message> = if narrow {
|
||||
stack![
|
||||
scroll_area,
|
||||
container(fade::viewport_fade(ui, fade::VIEWPORT_FADE_PX))
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.align_y(Alignment::End),
|
||||
]
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.into()
|
||||
} else {
|
||||
scroll_area
|
||||
};
|
||||
|
||||
column![scroll_area, pinned]
|
||||
.spacing(ui.space(Space::X2))
|
||||
.height(Length::Fill)
|
||||
.width(Length::Fill)
|
||||
.into()
|
||||
}
|
||||
|
||||
/// 版本菜单上方那一行:**有中性说明就显示说明**(它讲的是「这一家的叠法」,位置就在版本菜单
|
||||
|
||||
Reference in new issue
Block a user