From e9226512ad41a2189d9f67a0376289536843074a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Mon, 5 Oct 2026 12:59:42 -0400 Subject: [PATCH] Display: review r3 fixes --- docs/components/014-image.md | 3 +- image.go | 173 +++++++++++----------- image_state_test.go | 105 +++++++++++++ image_test.go | 5 +- testdata/snapshots/darwin/image-dark.png | Bin 6980 -> 7629 bytes testdata/snapshots/darwin/image-light.png | Bin 7028 -> 7615 bytes 6 files changed, 197 insertions(+), 89 deletions(-) diff --git a/docs/components/014-image.md b/docs/components/014-image.md index f730439..37cf062 100644 --- a/docs/components/014-image.md +++ b/docs/components/014-image.md @@ -43,6 +43,7 @@ view := func(c *ui.Context) { - 占位:浅色框内显示图片图标,名称“暂无图片”。 - 加载中:框内显示 Spinner,名称“正在加载图片”。 +- 各状态共用同一个外层盒子:图片元素始终参与布局(非加载状态下隐藏),所以在默认拉伸的 Column、Row 和固定尺寸的父容器里,盒子大小完全相同。 - 错误:盒子中央是 28×28 DIP 的仅图标“重试”按钮(`refresh-cw`),`Err.Error()` 作为按钮的悬停提示和描述,也是盒子的无障碍错误说明。盒子足够宽时,按钮旁边还会显示“图片加载失败”文字。 ## 事件 @@ -55,7 +56,7 @@ view := func(c *ui.Context) { ## 限制 -- 盒子高度至少 28 DIP 时,“重试”按钮才能完整显示。更矮的盒子会裁掉按钮的上下边缘,但按钮中心仍然可以点击,键盘也可以操作。 +- “重试”按钮画在不裁切的图层上,以盒子为中心,始终保持 28×28 DIP 的点击区域和完整的焦点环。盒子矮于 28 DIP 时,按钮会超出盒子的上下边缘,可能盖住相邻内容;需要按钮完全落在盒子内时,请让盒子至少 28 DIP 高。 - “图片加载失败”文字只在盒子宽度足够时显示。宽度按上一帧的布局判断,盒子尺寸变化时组件会多布局一帧来修正。 - 组件不读取文件,也不访问网络。解码(`ui.DecodeBitmap`、`ui.ParseSVG`)和异步加载由调用方完成,结果通过 `Window.Update` 回到界面线程,再设置 `Loading` 与 `Err`。 diff --git a/image.go b/image.go index 7dfc000..83d5682 100644 --- a/image.go +++ b/image.go @@ -30,77 +30,100 @@ type ImageView struct { Retry bool } -// Image shows a bitmap or SVG; nil shows a placeholder. -func Image(c *ui.Context, src ui.ImageSource, opts ImageOptions) ImageView { - if opts.Width < 0 || opts.Height < 0 { - panic("mujica: Image Width and Height must not be negative") - } - if opts.Err == nil && !opts.Loading && src != nil { - e := ui.Image(c, src).Radius(theme.ControlRadius) - if opts.Crop { - e.Fit(ui.Cover) - } - if opts.Width > 0 { - e.Width(opts.Width) - } - if opts.Height > 0 { - e.Height(opts.Height) - } - if opts.Alt != "" { - e.Label(opts.Alt) - } - return ImageView{Element: e} - } - k := Tokens(c) - u := Density(c).Unit() - w, h := imageBox(src, opts.Width, opts.Height) - var v ImageView - // Same size rules as the loaded image. - frame := ui.Row(c).AspectRatio(w/h).Center().Gap(u*2).Padding(0, u).Clip(). - Radius(theme.ControlRadius).Background(k.SurfaceHover).Border(theme.BorderWidth, k.Border) - if opts.Width > 0 || opts.Height == 0 { - frame.Width(w) - } - if opts.Height > 0 { - frame.Height(h) - } - v.Element = frame - room := frame.Bounds() - // Build again when the box changed, settling the error text. - if last := ui.Local(frame, imageRoomKey{}, func() ui.Rect { return ui.Rect{W: -1} }); *last != room { - *last = room - c.AnimationFrame() - } - frame.Children(func() { - switch { - case opts.Err != nil: - frame.Role(ui.RoleGroup).Label(msg(c, "image.error")).Description(opts.Alt).Error(opts.Err.Error()) - if imageRetry(c, k, opts.Err.Error()).Clicked() { - v.Retry = true - slog.Debug("mujica: image retry", "alt", opts.Alt) - } - if room.W >= theme.MinHitSize+u*4+imageErrorTextWidth { - ui.Text(c, msg(c, "image.error")).FontSize(FontSize(c, theme.CaptionSize)).TextColor(k.Danger).SingleLine() - } - case opts.Loading: - frame.Role(ui.RoleImage).Label(msg(c, "image.loading")).Description(opts.Alt) - Spinner(c, SpinnerOptions{Size: min(20, room.H), Label: msg(c, "image.loading")}) - default: - frame.Role(ui.RoleImage).Label(msg(c, "image.placeholder")).Description(opts.Alt) - Icon(c, "image", IconOptions{Size: 24, Tone: IconMuted}) - } - }) - return v -} +// imageBlank sizes the box of a nil src: 160×120. +var imageBlank = ui.MustParseSVG([]byte(``)) type imageRoomKey struct{} // imageErrorTextWidth is the room the error text needs. const imageErrorTextWidth = 120 +// Image shows a bitmap or SVG; nil shows a placeholder. +func Image(c *ui.Context, src ui.ImageSource, opts ImageOptions) ImageView { + if opts.Width < 0 || opts.Height < 0 { + panic("mujica: Image Width and Height must not be negative") + } + loaded := opts.Err == nil && !opts.Loading && src != nil + var v ImageView + v.Element = imageSized(c, src, opts, func(box, pic *ui.Element) { + if loaded { + if opts.Alt != "" { + pic.Label(opts.Alt) + } + return + } + pic.Invisible() + v.Retry = imageLayers(c, box, opts) + }) + return v +} + +// imageLayers builds the state layers over the hidden picture. +func imageLayers(c *ui.Context, box *ui.Element, opts ImageOptions) (retry bool) { + k := Tokens(c) + u := Density(c).Unit() + room := box.Bounds() + // Build again when the box changed, settling the error text. + if last := ui.Local(box, imageRoomKey{}, func() ui.Rect { return ui.Rect{W: -1} }); *last != room { + *last = room + c.AnimationFrame() + } + { + // The clipped backdrop; Retry sits above it, unclipped. + back := ui.Box(c).Absolute().Left(0).Top(0).Right(0).Bottom(0).Center().Clip(). + Radius(theme.ControlRadius).Background(k.SurfaceHover).Border(theme.BorderWidth, k.Border) + switch { + case opts.Err != nil: + box.Role(ui.RoleGroup).Label(msg(c, "image.error")).Description(opts.Alt).Error(opts.Err.Error()) + ui.Row(c).Absolute().Left(0).Top(0).Right(0).Bottom(0).Center().Gap(u * 2).Children(func() { + if imageRetry(c, k, opts.Err.Error()).Clicked() { + retry = true + slog.Debug("mujica: image retry", "alt", opts.Alt) + } + if room.W >= theme.MinHitSize+u*4+imageErrorTextWidth && room.H >= theme.MinHitSize { + ui.Text(c, msg(c, "image.error")).FontSize(FontSize(c, theme.CaptionSize)).TextColor(k.Danger).SingleLine() + } + }) + case opts.Loading: + box.Role(ui.RoleImage).Label(msg(c, "image.loading")).Description(opts.Alt) + back.Children(func() { + Spinner(c, SpinnerOptions{Size: max(8, min(20, room.H-4)), Label: msg(c, "image.loading")}) + }) + default: + box.Role(ui.RoleImage).Label(msg(c, "image.placeholder")).Description(opts.Alt) + back.Children(func() { Icon(c, "image", IconOptions{Size: 24, Tone: IconMuted}) }) + } + } + return retry +} + +// imageSized builds the outer box and its picture, sized alike in every state. +func imageSized(c *ui.Context, src ui.ImageSource, opts ImageOptions, layers func(box, pic *ui.Element)) *ui.Element { + if src == nil { + src = imageBlank + } + box := ui.Box(c) + box.Children(func() { + pic := ui.Image(c, src).Radius(theme.ControlRadius) + if opts.Crop { + pic.Fit(ui.Cover) + } + if opts.Width > 0 { + box.Width(opts.Width) + pic.Width(opts.Width) + } + if opts.Height > 0 { + box.Height(opts.Height) + pic.Height(opts.Height) + } + layers(box, pic) + }) + return box +} + // imageRetry is the 28 DIP icon-only retry button. func imageRetry(c *ui.Context, k theme.Tokens, problem string) *ui.Element { - b := ui.ButtonBase(c).Size(theme.MinHitSize, theme.MinHitSize).Radius(theme.ControlRadius). + b := ui.ButtonBase(c).Size(theme.MinHitSize, theme.MinHitSize).Shrink(0).Radius(theme.ControlRadius). Label(msg(c, "image.retry")).Description(problem).Tooltip(problem) bg := k.Surface if b.Pressed() { @@ -114,27 +137,3 @@ func imageRetry(c *ui.Context, k theme.Tokens, problem string) *ui.Element { b.Children(func() { Icon(c, "refresh-cw", IconOptions{Size: 16}) }) return b } - -// imageBox fills unset sides from src's aspect, else 160×120. -func imageBox(src ui.ImageSource, w, h float32) (float32, float32) { - var sw, sh float32 - switch s := src.(type) { - case *ui.Bitmap: - iw, ih := s.Size() - sw, sh = float32(iw), float32(ih) - case *ui.SVG: - sw, sh = s.Size() - } - if sw <= 0 || sh <= 0 { - sw, sh = 160, 120 - } - switch { - case w == 0 && h == 0: - return sw, sh - case w == 0: - return h * sw / sh, h - case h == 0: - return w, w * sh / sw - } - return w, h -} diff --git a/image_state_test.go b/image_state_test.go index 75a800c..4c26d16 100644 --- a/image_state_test.go +++ b/image_state_test.go @@ -5,6 +5,8 @@ import ( "testing" "github.com/egoist/mygo/ui" + + "mujicaui/theme" ) func TestImageErrorTextNeedsRoom(t *testing.T) { @@ -33,3 +35,106 @@ func TestImageErrorTextNeedsRoom(t *testing.T) { } } } + +// imageStateBoxes returns the box of loaded, loading and error states. +func imageStateBoxes(t *testing.T, opts ImageOptions, parent func(c *ui.Context, fn func())) []ui.Rect { + t.Helper() + bmp := imageTestBitmap() + var got []ui.Rect + for _, state := range []ImageOptions{{}, {Loading: true}, {Err: errors.New("x")}} { + o := opts + o.Loading, o.Err = state.Loading, state.Err + var r ui.Rect + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + parent(c, func() { r = Image(c, bmp, o).Element.Bounds() }) + }, 400, 400) + tt.Frame() + got = append(got, r) + } + return got +} + +func TestImageStatesShareBoxInColumn(t *testing.T) { + got := imageStateBoxes(t, ImageOptions{}, func(c *ui.Context, fn func()) { + ui.Column(c).Width(200).Children(fn) + }) + if got[0].W != 200 || got[0].H != 100 || got[1] != got[0] || got[2] != got[0] { + t.Fatalf("loaded, loading, error boxes %v", got) + } +} + +func TestImageStatesShareBoxInSizedParent(t *testing.T) { + got := imageStateBoxes(t, ImageOptions{}, func(c *ui.Context, fn func()) { + ui.Column(c).Size(120, 60).Children(fn) + }) + if got[0].W != 120 || got[1] != got[0] || got[2] != got[0] { + t.Fatalf("loaded, loading, error boxes %v", got) + } +} + +func TestImageStatesShareExplicitSize(t *testing.T) { + got := imageStateBoxes(t, ImageOptions{Width: 120, Height: 60}, func(c *ui.Context, fn func()) { + ui.Column(c).Width(300).Children(fn) + }) + if got[0].W != 120 || got[0].H != 60 || got[1] != got[0] || got[2] != got[0] { + t.Fatalf("loaded, loading, error boxes %v", got) + } +} + +func TestImageStatesShareBoxInNarrowRow(t *testing.T) { + got := imageStateBoxes(t, ImageOptions{}, func(c *ui.Context, fn func()) { + ui.Row(c).Width(40).AlignItems(ui.Start).Children(fn) + }) + if got[0].W != 40 || got[0].H != 20 || got[1] != got[0] || got[2] != got[0] { + t.Fatalf("loaded, loading, error boxes %v", got) + } +} + +func TestImageRetryUnclippedInSmallBox(t *testing.T) { + retries := 0 + var box ui.Rect + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{Mode: Light}) + ui.Row(c).Padding(40).AlignItems(ui.Start).Children(func() { + ui.Row(c).Width(40).AlignItems(ui.Start).Children(func() { + v := Image(c, imageTestBitmap(), ImageOptions{Err: errors.New("timeout")}) + box = v.Element.Bounds() + if v.Retry { + retries++ + } + }) + }) + }, 300, 200) + tt.SetPreferences(ui.Preferences{ReduceMotion: true, TextScale: 1}) + if box.W != 40 || box.H != 20 { + t.Fatalf("box %v", box) + } + r, ok := tt.Find("Retry") + if !ok || r.W != 28 || r.H != 28 { + t.Fatalf("retry bounds %v", r) + } + tt.ClickAt(r.X+r.W/2, r.Y+r.H/2) + if retries != 1 { + t.Fatalf("retries %d, want 1", retries) + } + cx := int(r.X + r.W/2) + ring := func() (above, below ui.Color) { + img := tt.Image() + a := img.RGBAAt(cx, int(r.Y-theme.FocusGap-theme.FocusWidth/2)) + b := img.RGBAAt(cx, int(r.Y+r.H+theme.FocusGap+theme.FocusWidth/2)) + return ui.Color{R: a.R, G: a.G, B: a.B, A: a.A}, ui.Color{R: b.R, G: b.G, B: b.B, A: b.A} + } + tt.Key(0, ui.KeyTab) + if !tt.Focused("Retry") { + t.Fatal("retry not focused") + } + above, below := ring() + want := theme.Light().Focus + if above != want || below != want { + t.Fatalf("ring above %v below %v, want %v", above, below, want) + } + if r.Y-theme.FocusGap >= box.Y || r.Y+r.H+theme.FocusGap <= box.Y+box.H { + t.Fatalf("ring rows not outside box %v (retry %v)", box, r) + } +} diff --git a/image_test.go b/image_test.go index 657c173..32318be 100644 --- a/image_test.go +++ b/image_test.go @@ -105,13 +105,16 @@ func TestImageNegativeSizePanics(t *testing.T) { func TestImageSnapshot(t *testing.T) { bmp := imageTestBitmap() - snapshot(t, "image", 760, 170, func(c *ui.Context) { + snapshot(t, "image", 820, 170, func(c *ui.Context) { ui.Row(c).Padding(20).Gap(12).AlignItems(ui.Start).Children(func() { Image(c, bmp, ImageOptions{Width: 120, Height: 120, Crop: true, Alt: "Cropped"}) Image(c, nil, ImageOptions{Width: 120, Height: 120}) Image(c, nil, ImageOptions{Width: 120, Height: 120, Loading: true}) Image(c, nil, ImageOptions{Width: 200, Height: 120, Err: errors.New("404")}) Image(c, bmp, ImageOptions{Err: errors.New("404")}) + ui.Row(c).Width(40).AlignItems(ui.Start).Margin(20, 0, 0, 0).Children(func() { + Image(c, bmp, ImageOptions{Err: errors.New("404")}) + }) }) }) } diff --git a/testdata/snapshots/darwin/image-dark.png b/testdata/snapshots/darwin/image-dark.png index dd44a888a87feaeed2a1de457124609bc7570f89..2b4ba738a0d63e33e74bd5bf1744e7e41df3ed37 100644 GIT binary patch literal 7629 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 literal 7028 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 Sf2q@N{!o-tlP#Bi75HCaXdK=E