feat: thumbnail grid, aspect-aware templates, and UI hierarchy polish

Material list:
- Switch asset list from one-per-row to a wrapping thumbnail grid
  (IconMode cards: thumbnail + corner checkbox + elided name, subfolder
  moved to tooltip).

Templates:
- Template.to_transform_state now contains-fits the print inside the
  target box by the print's native aspect ratio (no stretch); falls back
  to the box when print size is unknown.
- Feed print native size into template_panel for aspect-aware fitting.
- Shrink built-in template boxes to realistic print proportions.

UI hierarchy:
- Promote 打开文件夹 buttons to primary (filled accent).
- Add inline 归零 (reset angle) and a 重置位置/尺寸/角度 button to the
  transform panel; wire it to re-apply the selected template.
- Hide 保存 for built-in templates instead of leaving it greyed.

Docs: record template box/contain behavior (PRD) and the grid layout,
button hierarchy, and reset/save-visibility rules (UI design).
Tests: add 3 aspect-fit cases for to_transform_state.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-16 15:11:56 +08:00
co-authored by Claude Opus 4.8
parent df9145abdb
commit 8a34fe93ff
9 changed files with 273 additions and 78 deletions
+36 -6
View File
@@ -59,13 +59,43 @@ class Template:
rotation: float = 0.0
type: str = "custom" # "builtin" | "custom"
def to_transform_state(self, garment_width: float, garment_height: float) -> TransformState:
"""将比例参数转换为针对指定衣服尺寸的像素坐标 TransformState。"""
def to_transform_state(
self,
garment_width: float,
garment_height: float,
print_width: float = None,
print_height: float = None,
) -> TransformState:
"""将比例参数转换为针对指定衣服尺寸的像素坐标 TransformState。
width_ratio / height_ratio 定义衣服上的目标框。若提供印花原始尺寸
(print_width / print_height),印花按原始宽高比缩放后 contain 进目标框
并居中,避免被拉伸变形;未提供时退化为直接铺满目标框(旧行为)。
"""
box_x = self.x_ratio * garment_width
box_y = self.y_ratio * garment_height
box_w = self.width_ratio * garment_width
box_h = self.height_ratio * garment_height
if print_width and print_height and print_width > 0 and print_height > 0:
scale = min(box_w / print_width, box_h / print_height)
draw_w = print_width * scale
draw_h = print_height * scale
cx = box_x + box_w / 2.0
cy = box_y + box_h / 2.0
return TransformState(
x=cx - draw_w / 2.0,
y=cy - draw_h / 2.0,
width=draw_w,
height=draw_h,
rotation=self.rotation,
)
return TransformState(
x=self.x_ratio * garment_width,
y=self.y_ratio * garment_height,
width=self.width_ratio * garment_width,
height=self.height_ratio * garment_height,
x=box_x,
y=box_y,
width=box_w,
height=box_h,
rotation=self.rotation,
)