From 8880865021747432987e7fa6eca25581efb5e05e 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 13:35:53 -0400 Subject: [PATCH] Refactor: results expose Element as a named field Every component result type now names its element Element instead of embedding *ui.Element. The eleven element-plus-changed results (AccordionView, CarouselView, CollapsibleView, ImageViewerView, PaginationResult, StepsResult, SidebarResult, TabsResult, RatingParts, FileDropZoneParts, TransferParts) fold into ChoiceResult. Fixes four Gallery demos where .Width(...).Changed() read the element's flag instead of the component's: Accordion, MultiSelect, Cascader, TreeSelect. TestDataAccordionDemoLogsChange covers the Accordion. --- accordion.go | 15 +++---------- app_shell.go | 2 +- breadcrumb.go | 4 ++-- button_group.go | 2 +- carousel.go | 25 +++++++--------------- carousel_test.go | 6 +++--- collapsible.go | 15 +++---------- command_palette.go | 4 ++-- data_table.go | 4 ++-- data_table_test.go | 4 ++-- datetime.go | 2 +- description_list.go | 10 ++++----- docs/components/001-app-shell.md | 2 +- docs/components/008-split-pane.md | 4 ++-- docs/components/024-text-input.md | 2 +- docs/components/046-rating.md | 2 +- docs/components/049-file-drop-zone.md | 2 +- docs/components/050-transfer.md | 2 +- docs/components/051-calendar.md | 2 +- docs/components/052-date-picker.md | 2 +- docs/components/053-date-range-picker.md | 2 +- docs/components/054-time-picker.md | 2 +- docs/components/055-time-range-picker.md | 2 +- docs/components/056-date-time-picker.md | 2 +- docs/components/057-month-picker.md | 2 +- docs/components/058-year-picker.md | 2 +- docs/components/059-tabs.md | 4 ++-- docs/components/061-breadcrumb.md | 2 +- docs/components/062-pagination.md | 4 ++-- docs/components/063-steps.md | 4 ++-- docs/components/064-sidebar.md | 4 ++-- docs/components/066-menubar.md | 2 +- docs/components/070-list.md | 4 ++-- docs/components/071-data-table.md | 2 +- docs/components/072-tree.md | 2 +- docs/components/073-tree-table.md | 2 +- docs/components/074-accordion.md | 4 ++-- docs/components/075-collapsible.md | 4 ++-- docs/components/079-image-viewer.md | 4 ++-- docs/components/080-carousel.md | 6 +++--- example_data_test.go | 10 ++++----- example_layout_test.go | 2 +- examples/gallery/composite_files.go | 4 ++-- examples/gallery/pages_choice.go | 11 +++++++--- examples/gallery/pages_data_collections.go | 8 +++---- examples/gallery/pages_data_content.go | 8 ++++--- examples/gallery/pages_data_test.go | 10 +++++++++ examples/gallery/pages_input.go | 10 ++++----- examples/gallery/pages_layout.go | 2 +- file_drop_zone.go | 15 +++---------- image_viewer.go | 15 +++---------- list.go | 4 ++-- list_test.go | 6 +++--- menubar.go | 4 ++-- pagination.go | 15 +++---------- rating.go | 13 ++--------- rating_test.go | 2 +- sidebar.go | 13 ++--------- sidebar_test.go | 2 +- slider_test.go | 2 +- split_pane.go | 2 +- split_pane_test.go | 10 ++++----- status_bar.go | 2 +- steps.go | 13 ++--------- tabs.go | 13 ++--------- tabs_test.go | 2 +- text_input.go | 2 +- toggle_group.go | 2 +- transfer.go | 13 ++--------- tree.go | 4 ++-- tree_table.go | 2 +- tree_table_test.go | 8 +++---- tree_test.go | 8 +++---- 73 files changed, 163 insertions(+), 245 deletions(-) diff --git a/accordion.go b/accordion.go index a13e865..4e752f7 100644 --- a/accordion.go +++ b/accordion.go @@ -28,17 +28,8 @@ type AccordionOptions struct { KeepOne bool } -// AccordionView is a built Accordion and what happened to it in this frame. -type AccordionView struct { - *ui.Element - changed bool -} - -// Changed reports that a section opened or closed. -func (v AccordionView) Changed() bool { return v.changed } - // Accordion shows sections whose open IDs live in *open, which the caller controls. -func Accordion(c *ui.Context, open *[]string, items []AccordionItem, opts AccordionOptions) AccordionView { +func Accordion(c *ui.Context, open *[]string, items []AccordionItem, opts AccordionOptions) ChoiceResult { seen := map[string]bool{} for _, it := range items { if it.ID == "" || seen[it.ID] || it.Content == nil { @@ -49,10 +40,10 @@ func Accordion(c *ui.Context, open *[]string, items []AccordionItem, opts Accord k := Tokens(c) d := Density(c) u := d.Unit() - var v AccordionView + var v ChoiceResult v.Element = ui.Column(c).Background(k.Surface).Border(theme.BorderWidth, k.Border).Radius(theme.CardRadius) focusTo := ui.Local(v.Element, "focus", func() int { return -1 }) - v.Children(func() { + v.Element.Children(func() { for i, it := range items { ui.Column(c).Key(it.ID).Children(func() { if i > 0 { diff --git a/app_shell.go b/app_shell.go index 36fdbb5..cce3dea 100644 --- a/app_shell.go +++ b/app_shell.go @@ -28,7 +28,7 @@ type AppShellOptions struct { // AppShellResult is an AppShell and its event in this frame. type AppShellResult struct { - *ui.Element + Element *ui.Element // Toggled reports the sidebar opened or collapsed. Toggled bool } diff --git a/breadcrumb.go b/breadcrumb.go index d0977fb..29f3e7d 100644 --- a/breadcrumb.go +++ b/breadcrumb.go @@ -28,8 +28,8 @@ type BreadcrumbOptions struct { // BreadcrumbResult is the built Breadcrumb. type BreadcrumbResult struct { - *ui.Element - chosen int + Element *ui.Element + chosen int } // Chosen returns the index of the item the user activated in this frame. diff --git a/button_group.go b/button_group.go index ab3d6b8..061f635 100644 --- a/button_group.go +++ b/button_group.go @@ -28,7 +28,7 @@ type ButtonGroupOptions struct { // ButtonGroupResult is the group element and the item clicked this frame. type ButtonGroupResult struct { - *ui.Element + Element *ui.Element clicked int } diff --git a/carousel.go b/carousel.go index 2b27b87..13c4406 100644 --- a/carousel.go +++ b/carousel.go @@ -24,15 +24,6 @@ type CarouselOptions struct { Loop bool } -// CarouselView is a built Carousel and what happened to it in this frame. -type CarouselView struct { - *ui.Element - changed bool -} - -// Changed reports a new slide. -func (v CarouselView) Changed() bool { return v.changed } - type carouselState struct { paused bool next time.Time @@ -41,7 +32,7 @@ type carouselState struct { } // Carousel shows slide *index of n with previous, next and position controls. -func Carousel(c *ui.Context, index *int, n int, opts CarouselOptions, slide func(i int)) CarouselView { +func Carousel(c *ui.Context, index *int, n int, opts CarouselOptions, slide func(i int)) ChoiceResult { if n < 0 || opts.Interval < 0 { panic("mujica: Carousel with negative count or Interval") } @@ -51,7 +42,7 @@ func Carousel(c *ui.Context, index *int, n int, opts CarouselOptions, slide func } k := Tokens(c) u := Density(c).Unit() - var v CarouselView + var v ChoiceResult v.Element = ui.Column(c).Gap(u * 2).Label(opts.Label) st := ui.Local(v.Element, "state", func() carouselState { return carouselState{} }) if n > 0 && (*index < 0 || *index >= n) { @@ -75,7 +66,7 @@ func Carousel(c *ui.Context, index *int, n int, opts CarouselOptions, slide func slog.Debug("mujica: carousel slide", "index", i) } now := c.Now() - playing := opts.Autoplay && n > 1 && !st.paused && !v.Hovered() && !v.FocusWithin() + playing := opts.Autoplay && n > 1 && !st.paused && !v.Element.Hovered() && !v.Element.FocusWithin() if !playing { st.next = time.Time{} } else if st.next.IsZero() { @@ -89,16 +80,16 @@ func Carousel(c *ui.Context, index *int, n int, opts CarouselOptions, slide func } switch { case st.dotFocus: - case v.Shortcut(0, ui.KeyLeft): + case v.Element.Shortcut(0, ui.KeyLeft): goTo(*index - 1) - case v.Shortcut(0, ui.KeyRight): + case v.Element.Shortcut(0, ui.KeyRight): goTo(*index + 1) - case v.Shortcut(0, ui.KeyHome): + case v.Element.Shortcut(0, ui.KeyHome): goTo(0) - case v.Shortcut(0, ui.KeyEnd): + case v.Element.Shortcut(0, ui.KeyEnd): goTo(n - 1) } - v.Children(func() { + v.Element.Children(func() { ui.Row(c).Grow(1).MinHeight(120).AlignItems(ui.Center).Gap(u * 2).Children(func() { prev := Button(c, "", ButtonOptions{Variant: Ghost, Icon: icons.Must("chevron-left"), Label: msg(c, "carousel.prev"), Disabled: n < 2 || !wrap && *index == 0}) ui.Column(c).Grow(1).MinWidth(0).FillHeight().Clip().Background(k.Surface).Border(theme.BorderWidth, k.Border).Radius(theme.CardRadius). diff --git a/carousel_test.go b/carousel_test.go index 59d8435..c2c0bda 100644 --- a/carousel_test.go +++ b/carousel_test.go @@ -20,7 +20,7 @@ func (h *carouselHarness) view(c *ui.Context) { v := Carousel(c, &h.index, 4, h.opts, func(i int) { ui.Text(c, fmt.Sprintf("Window %d", i+1)).Padding(20) }) - v.Grow(1) + v.Element.Grow(1) if v.Changed() { h.changes++ } @@ -126,7 +126,7 @@ func TestCarouselSnapshot(t *testing.T) { ui.Column(c).Grow(1).Center().Children(func() { ui.Text(c, fmt.Sprintf("Rose window %d", n+1)).FontSize(FontSize(c, 18)) }) - }).Grow(1) + }).Element.Grow(1) }) }) } @@ -135,7 +135,7 @@ func TestCarouselEmptyAutoplayKeys(t *testing.T) { index := 0 tt := ui.NewTester(func(c *ui.Context) { Use(c, Settings{}) - Carousel(c, &index, 0, CarouselOptions{Autoplay: true}, func(int) {}).Size(300, 200) + Carousel(c, &index, 0, CarouselOptions{Autoplay: true}, func(int) {}).Element.Size(300, 200) }, 320, 240) for range 4 { tt.Key(0, ui.KeyTab) diff --git a/collapsible.go b/collapsible.go index 262e7d2..bb9b6e5 100644 --- a/collapsible.go +++ b/collapsible.go @@ -19,25 +19,16 @@ type CollapsibleOptions struct { Disabled bool } -// CollapsibleView is a built Collapsible and what happened to it in this frame. -type CollapsibleView struct { - *ui.Element - changed bool -} - -// Changed reports that the trigger opened or closed the content. -func (v CollapsibleView) Changed() bool { return v.changed } - // Collapsible shows content below a trigger while *open; closed content is not built. -func Collapsible(c *ui.Context, open *bool, opts CollapsibleOptions, content func()) CollapsibleView { +func Collapsible(c *ui.Context, open *bool, opts CollapsibleOptions, content func()) ChoiceResult { if opts.Title == "" && opts.Trigger == nil { panic("mujica: Collapsible needs Title or Trigger") } k := Tokens(c) u := Density(c).Unit() - var v CollapsibleView + var v ChoiceResult v.Element = ui.Column(c).Shrink(0) - v.Children(func() { + v.Element.Children(func() { was := *open p := ui.CollapsibleBase(c, open) tr := p.Trigger diff --git a/command_palette.go b/command_palette.go index 1237bbe..770411a 100644 --- a/command_palette.go +++ b/command_palette.go @@ -32,8 +32,8 @@ type CommandPaletteOptions struct { // CommandPaletteResult is the built CommandPalette; Element is nil while closed. type CommandPaletteResult struct { - *ui.Element - chosen string + Element *ui.Element + chosen string } // Chosen returns the ID of the command run in this frame; running closes the palette. diff --git a/data_table.go b/data_table.go index 508b20a..b5ea36c 100644 --- a/data_table.go +++ b/data_table.go @@ -85,7 +85,7 @@ func (s *DataTableState[K]) Rows() int { return len(s.cache.view) } // DataTableView is a built DataTable and what happened to it in this frame. type DataTableView struct { - *ui.Element + Element *ui.Element changed, submitted, sorted, retry bool } @@ -114,7 +114,7 @@ func DataTable[R any, K comparable](c *ui.Context, s *DataTableState[K], rows [] before := s.Sort var v DataTableView v.Element = ui.Column(c).MinHeight(0).Gap(d.Unit() * 2) - v.Children(func() { + v.Element.Children(func() { if len(rows) > 0 { ui.Row(c).Justify(ui.End).Children(func() { tableColumnMenu(c, dataTableMenu(opts), &s.Sort, &s.Layout, &s.Hidden) diff --git a/data_table_test.go b/data_table_test.go index c153dee..5929626 100644 --- a/data_table_test.go +++ b/data_table_test.go @@ -54,7 +54,7 @@ func (h *tableHarness) view(c *ui.Context) { cols.Columns[i].Text = func(r relic) string { h.seen[r.Name]++; return text(r) } } v := DataTable(c, &h.st, h.rows, cols) - v.Size(360, 300) + v.Element.Size(360, 300) if v.Changed() { h.changes++ } @@ -347,7 +347,7 @@ func TestDataTableSnapshot(t *testing.T) { return h }) ui.Column(c).Padding(16).Children(func() { - DataTable(c, &(*h).st, (*h).rows, (*h).opts).Size(600, 260) + DataTable(c, &(*h).st, (*h).rows, (*h).opts).Element.Size(600, 260) }) }) } diff --git a/datetime.go b/datetime.go index 6a9bf2c..2fff036 100644 --- a/datetime.go +++ b/datetime.go @@ -68,7 +68,7 @@ func (d Date) within(lo, hi Date) bool { // DateTimeResult is a date or time component and what happened to it this frame. type DateTimeResult struct { - *ui.Element + Element *ui.Element // Input is the text field inside, for labels and focus; nil without one. Input *ui.Element changed bool diff --git a/description_list.go b/description_list.go index 9d89dca..93a7782 100644 --- a/description_list.go +++ b/description_list.go @@ -31,8 +31,8 @@ type DescriptionListOptions struct { // DescriptionListView is a built DescriptionList and what happened to it in this frame. type DescriptionListView struct { - *ui.Element - copied int + Element *ui.Element + copied int } // Copied returns the index of the item whose value was copied in this frame. @@ -69,15 +69,15 @@ func DescriptionList(c *ui.Context, items []DescriptionItem, opts DescriptionLis } return max(1, min(most, int(math.Floor(float64((w+gap)/(minW+gap)))))) } - cols := fit(v.Bounds().W) - v.Draw(func(p *ui.Painter, r ui.Rect) { + cols := fit(v.Element.Bounds().W) + v.Element.Draw(func(p *ui.Painter, r ui.Rect) { if fit(r.W) != cols { // The new width needs other columns: build again. c.Invalidate() } }) now := c.Now() - v.Columns(cols).Children(func() { + v.Element.Columns(cols).Children(func() { for i, it := range items { cell := ui.Column(c).Gap(u).MinWidth(0) if it.Span < 0 || it.Span > cols { diff --git a/docs/components/001-app-shell.md b/docs/components/001-app-shell.md index f3ccaca..699415f 100644 --- a/docs/components/001-app-shell.md +++ b/docs/components/001-app-shell.md @@ -29,7 +29,7 @@ mujica.AppShell(c, &collapsed, mujica.AppShellOptions{ | `SidebarWidth` | `float32` | 240 | 展开时侧栏宽度(DIP);负数 panic。 | | `ContentScroll` | `*ui.ScrollState` | nil | 保存主内容滚动位置;nil 时按位置保留。 | -返回 `AppShellResult`,内嵌 `*ui.Element`。 +返回 `AppShellResult`,`Element` 字段是组件元素。 ## 状态 diff --git a/docs/components/008-split-pane.md b/docs/components/008-split-pane.md index ea1e9b6..9a4659d 100644 --- a/docs/components/008-split-pane.md +++ b/docs/components/008-split-pane.md @@ -11,7 +11,7 @@ split := mujica.SplitPaneState{Size: 160} mujica.SplitPane(c, &split, mujica.SplitPaneOptions{MinFirst: 120, Collapsible: mujica.SplitPaneFirst}, func() { ui.Text(c, "Files") }, func() { ui.Text(c, "Editor") }, -).Fill() +).Element.Fill() ``` 签名:`func SplitPane(c *ui.Context, state *SplitPaneState, opts SplitPaneOptions, first, second func()) SplitPaneResult` @@ -33,7 +33,7 @@ mujica.SplitPane(c, &split, mujica.SplitPaneOptions{MinFirst: 120, Collapsible: | `MinFirst`, `MinSecond` | `float32` | 10 单位(默认 40 DIP) | 两栏最小尺寸;负数 panic。 | | `Collapsible` | `SplitPaneSide` | `SplitPaneNeither` | 可折叠的栏:`SplitPaneFirst` 或 `SplitPaneSecond`。 | -返回 `SplitPaneResult`,内嵌 `*ui.Element`。 +返回 `SplitPaneResult`,`Element` 字段是组件元素。 ## 状态 diff --git a/docs/components/024-text-input.md b/docs/components/024-text-input.md index 32d4e5f..6cdd2f5 100644 --- a/docs/components/024-text-input.md +++ b/docs/components/024-text-input.md @@ -15,7 +15,7 @@ if r.Submitted() { 签名:`func TextInput(c *ui.Context, value *string, opts TextInputOptions) InputResult` -`InputResult` 内嵌外层 `*ui.Element`,`Input` 字段是内部编辑元素,`Changed()`、`Submitted()` 报告事件。 +`InputResult` 的 `Element` 字段是外层元素,`Input` 字段是内部编辑元素,`Changed()`、`Submitted()` 报告事件。 ## 参数 diff --git a/docs/components/046-rating.md b/docs/components/046-rating.md index 6e7dd1c..35f1ad1 100644 --- a/docs/components/046-rating.md +++ b/docs/components/046-rating.md @@ -35,7 +35,7 @@ ui.Render(view, 400, 300, 1) ## 事件 -`RatingParts.Changed()` 用户设置了新值;同值点击不触发。 +`ChoiceResult.Changed()` 用户设置了新值;同值点击不触发。 ## 键盘操作 diff --git a/docs/components/049-file-drop-zone.md b/docs/components/049-file-drop-zone.md index f285161..4168091 100644 --- a/docs/components/049-file-drop-zone.md +++ b/docs/components/049-file-drop-zone.md @@ -36,7 +36,7 @@ ui.Render(view, 400, 300, 1) ## 事件 -`FileDropZoneParts.Changed()` 添加或移除了文件。被拒绝的条目只显示在组件内,可单独关闭。 +`ChoiceResult.Changed()` 添加或移除了文件。被拒绝的条目只显示在组件内,可单独关闭。 ## 键盘操作 diff --git a/docs/components/050-transfer.md b/docs/components/050-transfer.md index 7ace8e6..7be5399 100644 --- a/docs/components/050-transfer.md +++ b/docs/components/050-transfer.md @@ -34,7 +34,7 @@ ui.Render(view, 400, 300, 1) ## 事件 -`TransferParts.Changed()` 有条目移动。 +`ChoiceResult.Changed()` 有条目移动。 ## 键盘操作 diff --git a/docs/components/051-calendar.md b/docs/components/051-calendar.md index e30d0cd..a971a8f 100644 --- a/docs/components/051-calendar.md +++ b/docs/components/051-calendar.md @@ -19,7 +19,7 @@ ui.Render(view, 400, 300, 1) ## 参数 -`func Calendar(c *ui.Context, value *Date, opts CalendarOptions) DateTimeResult`,返回值嵌入 `*ui.Element`。 +`func Calendar(c *ui.Context, value *Date, opts CalendarOptions) DateTimeResult`,返回值的 `Element` 字段是组件元素。 | 字段 | 类型 | 默认值 | 含义 | | --- | --- | --- | --- | diff --git a/docs/components/052-date-picker.md b/docs/components/052-date-picker.md index 0a3352c..abe7c9a 100644 --- a/docs/components/052-date-picker.md +++ b/docs/components/052-date-picker.md @@ -21,7 +21,7 @@ ui.Render(view, 400, 300, 1) ## 参数 -`func DatePicker(c *ui.Context, value *Date, opts DatePickerOptions) DateTimeResult`,返回值嵌入 `*ui.Element`。 +`func DatePicker(c *ui.Context, value *Date, opts DatePickerOptions) DateTimeResult`,返回值的 `Element` 字段是组件元素。 | 字段 | 类型 | 默认值 | 含义 | | --- | --- | --- | --- | diff --git a/docs/components/053-date-range-picker.md b/docs/components/053-date-range-picker.md index 9a50075..14549cd 100644 --- a/docs/components/053-date-range-picker.md +++ b/docs/components/053-date-range-picker.md @@ -20,7 +20,7 @@ ui.Render(view, 400, 300, 1) ## 参数 -`func DateRangePicker(c *ui.Context, value *DateRange, opts DateRangePickerOptions) DateTimeResult`,返回值嵌入 `*ui.Element`。 +`func DateRangePicker(c *ui.Context, value *DateRange, opts DateRangePickerOptions) DateTimeResult`,返回值的 `Element` 字段是组件元素。 | 字段 | 类型 | 默认值 | 含义 | | --- | --- | --- | --- | diff --git a/docs/components/054-time-picker.md b/docs/components/054-time-picker.md index 0014447..8efb00d 100644 --- a/docs/components/054-time-picker.md +++ b/docs/components/054-time-picker.md @@ -20,7 +20,7 @@ ui.Render(view, 400, 300, 1) ## 参数 -`func TimePicker(c *ui.Context, value *TimeOfDay, opts TimePickerOptions) DateTimeResult`,返回值嵌入 `*ui.Element`。 +`func TimePicker(c *ui.Context, value *TimeOfDay, opts TimePickerOptions) DateTimeResult`,返回值的 `Element` 字段是组件元素。 | 字段 | 类型 | 默认值 | 含义 | | --- | --- | --- | --- | diff --git a/docs/components/055-time-range-picker.md b/docs/components/055-time-range-picker.md index 4aac9bf..03f1a27 100644 --- a/docs/components/055-time-range-picker.md +++ b/docs/components/055-time-range-picker.md @@ -20,7 +20,7 @@ ui.Render(view, 400, 300, 1) ## 参数 -`func TimeRangePicker(c *ui.Context, value *TimeRange, opts TimeRangePickerOptions) DateTimeResult`,返回值嵌入 `*ui.Element`。 +`func TimeRangePicker(c *ui.Context, value *TimeRange, opts TimeRangePickerOptions) DateTimeResult`,返回值的 `Element` 字段是组件元素。 | 字段 | 类型 | 默认值 | 含义 | | --- | --- | --- | --- | diff --git a/docs/components/056-date-time-picker.md b/docs/components/056-date-time-picker.md index d2a7efa..f3eaf42 100644 --- a/docs/components/056-date-time-picker.md +++ b/docs/components/056-date-time-picker.md @@ -24,7 +24,7 @@ ui.Render(view, 400, 300, 1) ## 参数 -`func DateTimePicker(c *ui.Context, value *time.Time, opts DateTimePickerOptions) DateTimeResult`,返回值嵌入 `*ui.Element`。 +`func DateTimePicker(c *ui.Context, value *time.Time, opts DateTimePickerOptions) DateTimeResult`,返回值的 `Element` 字段是组件元素。 | 字段 | 类型 | 默认值 | 含义 | | --- | --- | --- | --- | diff --git a/docs/components/057-month-picker.md b/docs/components/057-month-picker.md index 562bf37..4179854 100644 --- a/docs/components/057-month-picker.md +++ b/docs/components/057-month-picker.md @@ -20,7 +20,7 @@ ui.Render(view, 400, 300, 1) ## 参数 -`func MonthPicker(c *ui.Context, value *YearMonth, opts MonthPickerOptions) DateTimeResult`,返回值嵌入 `*ui.Element`。 +`func MonthPicker(c *ui.Context, value *YearMonth, opts MonthPickerOptions) DateTimeResult`,返回值的 `Element` 字段是组件元素。 | 字段 | 类型 | 默认值 | 含义 | | --- | --- | --- | --- | diff --git a/docs/components/058-year-picker.md b/docs/components/058-year-picker.md index d832172..4e1d204 100644 --- a/docs/components/058-year-picker.md +++ b/docs/components/058-year-picker.md @@ -20,7 +20,7 @@ ui.Render(view, 400, 300, 1) ## 参数 -`func YearPicker(c *ui.Context, value *int, opts YearPickerOptions) DateTimeResult`,返回值嵌入 `*ui.Element`。 +`func YearPicker(c *ui.Context, value *int, opts YearPickerOptions) DateTimeResult`,返回值的 `Element` 字段是组件元素。 | 字段 | 类型 | 默认值 | 含义 | | --- | --- | --- | --- | diff --git a/docs/components/059-tabs.md b/docs/components/059-tabs.md index 436f668..ec6257e 100644 --- a/docs/components/059-tabs.md +++ b/docs/components/059-tabs.md @@ -14,7 +14,7 @@ mujica.Tabs(c, &tab, []mujica.Tab{ }, mujica.TabsOptions{Label: "Settings"}) ``` -签名:`func Tabs(c *ui.Context, selected *int, tabs []Tab, opts TabsOptions) TabsResult` +签名:`func Tabs(c *ui.Context, selected *int, tabs []Tab, opts TabsOptions) ChoiceResult` ## 参数 @@ -36,7 +36,7 @@ mujica.Tabs(c, &tab, []mujica.Tab{ | `Vertical` | `bool` | false | 标签竖排在左侧,面板在右侧。 | | `Label` | `string` | "" | 标签列表的无障碍名称。 | -返回 `TabsResult`,内嵌包含标签与面板的 `*ui.Element`。 +返回 `ChoiceResult`,`Element` 字段是包含标签与面板的元素。 ## 状态 diff --git a/docs/components/061-breadcrumb.md b/docs/components/061-breadcrumb.md index 482f51a..31829d4 100644 --- a/docs/components/061-breadcrumb.md +++ b/docs/components/061-breadcrumb.md @@ -33,7 +33,7 @@ if i, ok := mujica.Breadcrumb(c, path, mujica.BreadcrumbOptions{}).Chosen(); ok | `MaxItemWidth` | `float32` | 0(即 160 DIP) | 单项文字最大宽度,超出以省略号截断。 | | `Label` | `string` | 内置“路径导航” | 整组的无障碍名称。 | -返回 `BreadcrumbResult`,内嵌 `*ui.Element`。 +返回 `BreadcrumbResult`,`Element` 字段是组件元素。 ## 状态 diff --git a/docs/components/062-pagination.md b/docs/components/062-pagination.md index 3900d93..d4fa70e 100644 --- a/docs/components/062-pagination.md +++ b/docs/components/062-pagination.md @@ -12,7 +12,7 @@ if mujica.Pagination(c, &page, &size, mujica.PaginationOptions{Total: 420, PageS } ``` -签名:`func Pagination(c *ui.Context, page, pageSize *int, opts PaginationOptions) PaginationResult` +签名:`func Pagination(c *ui.Context, page, pageSize *int, opts PaginationOptions) ChoiceResult` ## 参数 @@ -28,7 +28,7 @@ if mujica.Pagination(c, &page, &size, mujica.PaginationOptions{Total: 420, PageS | `Siblings` | `int` | 0(即 1) | 当前页两侧显示的页码数;为负 panic。 | | `Disabled` | `bool` | false | 整体禁用。 | -返回 `PaginationResult`,内嵌 `*ui.Element`。 +返回 `ChoiceResult`,`Element` 字段是组件元素。 ## 状态 diff --git a/docs/components/063-steps.md b/docs/components/063-steps.md index b9e7051..3d83ccc 100644 --- a/docs/components/063-steps.md +++ b/docs/components/063-steps.md @@ -14,7 +14,7 @@ mujica.Steps(c, ¤t, []mujica.Step{ }, mujica.StepsOptions{Label: "Sign-up"}) ``` -签名:`func Steps(c *ui.Context, current *int, steps []Step, opts StepsOptions) StepsResult` +签名:`func Steps(c *ui.Context, current *int, steps []Step, opts StepsOptions) ChoiceResult` ## 参数 @@ -36,7 +36,7 @@ mujica.Steps(c, ¤t, []mujica.Step{ | `Vertical` | `bool` | false | 纵向排列,连接线在标记下方。 | | `Label` | `string` | "" | 整组的无障碍名称。 | -返回 `StepsResult`,内嵌 `*ui.Element`。 +返回 `ChoiceResult`,`Element` 字段是组件元素。 ## 状态 diff --git a/docs/components/064-sidebar.md b/docs/components/064-sidebar.md index ff174ed..3b76d6b 100644 --- a/docs/components/064-sidebar.md +++ b/docs/components/064-sidebar.md @@ -15,7 +15,7 @@ mujica.Sidebar(c, &selected, []mujica.SidebarSection{ }, mujica.SidebarOptions{}) ``` -签名:`func Sidebar(c *ui.Context, selected *string, sections []SidebarSection, opts SidebarOptions) SidebarResult` +签名:`func Sidebar(c *ui.Context, selected *string, sections []SidebarSection, opts SidebarOptions) ChoiceResult` ## 参数 @@ -45,7 +45,7 @@ mujica.Sidebar(c, &selected, []mujica.SidebarSection{ | `Compact` | `bool` | false | 图标栏:隐藏文字与组标题,组之间以旧金细线分隔,名称作为悬停提示。 | | `Width` | `float32` | 0(240,紧凑时 56) | 宽度,DIP。高度填满父容器。 | -返回 `SidebarResult`,内嵌 `*ui.Element`。展开折叠状态由组件保存。 +返回 `ChoiceResult`,`Element` 字段是组件元素。展开折叠状态由组件保存。 ## 状态 diff --git a/docs/components/066-menubar.md b/docs/components/066-menubar.md index ea969cd..c78c291 100644 --- a/docs/components/066-menubar.md +++ b/docs/components/066-menubar.md @@ -45,7 +45,7 @@ if id, ok := res.Chosen(); ok && id == "wrap" { | --- | --- | --- | --- | | `Label` | `string` | 内置“菜单栏” | 菜单栏的无障碍名称。 | -返回 `MenubarResult`,内嵌菜单栏 `*ui.Element`。 +返回 `MenubarResult`,`Element` 字段是菜单栏元素。 ## 状态 diff --git a/docs/components/070-list.md b/docs/components/070-list.md index 3623837..d8f9667 100644 --- a/docs/components/070-list.md +++ b/docs/components/070-list.md @@ -15,7 +15,7 @@ view := func(c *ui.Context) { Key: func(i int) string { return names[i] }, Label: func(i int) string { return names[i] }, }, func(i int) { ui.Text(c, names[i]) }) - v.Size(240, 200) + v.Element.Size(240, 200) if v.Changed() { key, _ := state.Selected() fmt.Println("chose", key) @@ -26,7 +26,7 @@ ui.Render(view, 400, 300, 1) ## 参数 -`func List[K comparable](c *ui.Context, s *ListState[K], n int, opts ListOptions[K], item func(i int)) ListView`。`ListView` 嵌入 `*ui.Element`;`ListState` 由调用方持有,零值表示未选择。`state.Selected()` 返回所选键,多选时 `state.Selection` 保存全部键;`state.ScrollTo(i)` 滚动到第 i 项。 +`func List[K comparable](c *ui.Context, s *ListState[K], n int, opts ListOptions[K], item func(i int)) ListView`。`ListView` 的 `Element` 字段是列表元素;`ListState` 由调用方持有,零值表示未选择。`state.Selected()` 返回所选键,多选时 `state.Selection` 保存全部键;`state.ScrollTo(i)` 滚动到第 i 项。 | 字段 | 类型 | 默认值 | 含义 | | --- | --- | --- | --- | diff --git a/docs/components/071-data-table.md b/docs/components/071-data-table.md index 68ab9d8..f62dbb4 100644 --- a/docs/components/071-data-table.md +++ b/docs/components/071-data-table.md @@ -27,7 +27,7 @@ opts := mujica.DataTableOptions[relic, int]{ } view := func(c *ui.Context) { mujica.Use(c, mujica.Settings{}) - mujica.DataTable(c, &state, rows, opts).Size(360, 240) + mujica.DataTable(c, &state, rows, opts).Element.Size(360, 240) } ui.Render(view, 400, 300, 1) ``` diff --git a/docs/components/072-tree.md b/docs/components/072-tree.md index 7b6c049..65d4244 100644 --- a/docs/components/072-tree.md +++ b/docs/components/072-tree.md @@ -15,7 +15,7 @@ view := func(c *ui.Context) { Roots: []string{"Nave", "Sacristy"}, Children: func(n string) []string { return kids[n] }, Label: func(n string) string { return n }, - }).Size(280, 240) + }).Element.Size(280, 240) } ui.Render(view, 400, 300, 1) ``` diff --git a/docs/components/073-tree-table.md b/docs/components/073-tree-table.md index dac451b..ae408ad 100644 --- a/docs/components/073-tree-table.md +++ b/docs/components/073-tree-table.md @@ -22,7 +22,7 @@ view := func(c *ui.Context) { {ID: "size", Title: "Size", Width: 80, Align: ui.End, Cell: func(n string) { ui.Text(c, strconv.Itoa(size[n])) }}, }, - }).Size(360, 240) + }).Element.Size(360, 240) } ui.Render(view, 400, 300, 1) ``` diff --git a/docs/components/074-accordion.md b/docs/components/074-accordion.md index fe0a242..df78411 100644 --- a/docs/components/074-accordion.md +++ b/docs/components/074-accordion.md @@ -20,7 +20,7 @@ ui.Render(view, 400, 300, 1) ## 参数 -`func Accordion(c *ui.Context, open *[]string, items []AccordionItem, opts AccordionOptions) AccordionView`。`*open` 是展开区段的 ID 列表;`AccordionItem` 字段:`ID`(必填、唯一)、`Title`、`Content`(必填)、`Disabled`。 +`func Accordion(c *ui.Context, open *[]string, items []AccordionItem, opts AccordionOptions) ChoiceResult`。`*open` 是展开区段的 ID 列表;`AccordionItem` 字段:`ID`(必填、唯一)、`Title`、`Content`(必填)、`Disabled`。 | 字段 | 类型 | 默认值 | 含义 | | --- | --- | --- | --- | @@ -33,7 +33,7 @@ ui.Render(view, 400, 300, 1) ## 事件 -`AccordionView.Changed()`:某区段因用户操作而展开或关闭,`*open` 已更新。外部直接修改 `*open` 不触发事件。 +`ChoiceResult.Changed()`:某区段因用户操作而展开或关闭,`*open` 已更新。外部直接修改 `*open` 不触发事件。 ## 键盘操作 diff --git a/docs/components/075-collapsible.md b/docs/components/075-collapsible.md index 417263d..9747308 100644 --- a/docs/components/075-collapsible.md +++ b/docs/components/075-collapsible.md @@ -19,7 +19,7 @@ ui.Render(view, 400, 300, 1) ## 参数 -`func Collapsible(c *ui.Context, open *bool, opts CollapsibleOptions, content func()) CollapsibleView`。 +`func Collapsible(c *ui.Context, open *bool, opts CollapsibleOptions, content func()) ChoiceResult`。 | 字段 | 类型 | 默认值 | 含义 | | --- | --- | --- | --- | @@ -33,7 +33,7 @@ ui.Render(view, 400, 300, 1) ## 事件 -`CollapsibleView.Changed()`:用户点击或按键切换了展开状态。 +`ChoiceResult.Changed()`:用户点击或按键切换了展开状态。 ## 键盘操作 diff --git a/docs/components/079-image-viewer.md b/docs/components/079-image-viewer.md index c2a038c..d0f59b4 100644 --- a/docs/components/079-image-viewer.md +++ b/docs/components/079-image-viewer.md @@ -18,7 +18,7 @@ ui.Render(view, 400, 300, 1) ## 参数 -`func ImageViewer(c *ui.Context, open *bool, index *int, images []ViewerImage) ImageViewerView`。`ViewerImage` 字段:`Source`(`*ui.Bitmap` 或 `*ui.SVG`,必填)、`Alt`(说明文字与无障碍名称)。关闭时返回的 `Element` 为 nil。 +`func ImageViewer(c *ui.Context, open *bool, index *int, images []ViewerImage) ChoiceResult`。`ViewerImage` 字段:`Source`(`*ui.Bitmap` 或 `*ui.SVG`,必填)、`Alt`(说明文字与无障碍名称)。关闭时返回的 `Element` 为 nil。 | 字段 | 类型 | 默认值 | 含义 | | --- | --- | --- | --- | @@ -32,7 +32,7 @@ ui.Render(view, 400, 300, 1) ## 事件 -`ImageViewerView.Changed()`:切换了图片,`*index` 已更新。关闭通过 `*open` 变为 false 观察。 +`ChoiceResult.Changed()`:切换了图片,`*index` 已更新。关闭通过 `*open` 变为 false 观察。 ## 键盘操作 diff --git a/docs/components/080-carousel.md b/docs/components/080-carousel.md index e9db9e3..6233f17 100644 --- a/docs/components/080-carousel.md +++ b/docs/components/080-carousel.md @@ -12,14 +12,14 @@ view := func(c *ui.Context) { mujica.Use(c, mujica.Settings{}) mujica.Carousel(c, &index, 3, mujica.CarouselOptions{Label: "Windows", Autoplay: true}, func(i int) { ui.Text(c, fmt.Sprintf("Rose window %d", i+1)).Padding(24) - }).Size(360, 220) + }).Element.Size(360, 220) } ui.Render(view, 400, 300, 1) ``` ## 参数 -`func Carousel(c *ui.Context, index *int, n int, opts CarouselOptions, slide func(i int)) CarouselView`。 +`func Carousel(c *ui.Context, index *int, n int, opts CarouselOptions, slide func(i int)) ChoiceResult`。 | 字段 | 类型 | 默认值 | 含义 | | --- | --- | --- | --- | @@ -34,7 +34,7 @@ ui.Render(view, 400, 300, 1) ## 事件 -`CarouselView.Changed()`:当前项变化,`*index` 已更新;同时向读屏软件播报“第 N 项,共 M 项”。 +`ChoiceResult.Changed()`:当前项变化,`*index` 已更新;同时向读屏软件播报“第 N 项,共 M 项”。 ## 键盘操作 diff --git a/example_data_test.go b/example_data_test.go index a79859c..81cd578 100644 --- a/example_data_test.go +++ b/example_data_test.go @@ -31,7 +31,7 @@ func ExampleList() { Key: func(i int) string { return names[i] }, Label: func(i int) string { return names[i] }, }, func(i int) { ui.Text(c, names[i]) }) - v.Size(240, 200) + v.Element.Size(240, 200) if v.Changed() { key, _ := state.Selected() fmt.Println("chose", key) @@ -61,7 +61,7 @@ func ExampleDataTable() { } view := func(c *ui.Context) { mujica.Use(c, mujica.Settings{}) - mujica.DataTable(c, &state, rows, opts).Size(360, 240) + mujica.DataTable(c, &state, rows, opts).Element.Size(360, 240) } ui.Render(view, 400, 300, 1) } @@ -75,7 +75,7 @@ func ExampleTree() { Roots: []string{"Nave", "Sacristy"}, Children: func(n string) []string { return kids[n] }, Label: func(n string) string { return n }, - }).Size(280, 240) + }).Element.Size(280, 240) } ui.Render(view, 400, 300, 1) } @@ -96,7 +96,7 @@ func ExampleTreeTable() { {ID: "size", Title: "Size", Width: 80, Align: ui.End, Cell: func(n string) { ui.Text(c, strconv.Itoa(size[n])) }}, }, - }).Size(360, 240) + }).Element.Size(360, 240) } ui.Render(view, 400, 300, 1) } @@ -176,7 +176,7 @@ func ExampleCarousel() { mujica.Use(c, mujica.Settings{}) mujica.Carousel(c, &index, 3, mujica.CarouselOptions{Label: "Windows", Autoplay: true}, func(i int) { ui.Text(c, fmt.Sprintf("Rose window %d", i+1)).Padding(24) - }).Size(360, 220) + }).Element.Size(360, 220) } ui.Render(view, 400, 300, 1) } diff --git a/example_layout_test.go b/example_layout_test.go index 2926e55..e3e4b19 100644 --- a/example_layout_test.go +++ b/example_layout_test.go @@ -100,7 +100,7 @@ func ExampleSplitPane() { mujica.SplitPane(c, &split, mujica.SplitPaneOptions{MinFirst: 120, Collapsible: mujica.SplitPaneFirst}, func() { ui.Text(c, "Files") }, func() { ui.Text(c, "Editor") }, - ).Fill() + ).Element.Fill() } ui.Render(view, 400, 300, 1) } diff --git a/examples/gallery/composite_files.go b/examples/gallery/composite_files.go index b42b472..0ceda62 100644 --- a/examples/gallery/composite_files.go +++ b/examples/gallery/composite_files.go @@ -152,7 +152,7 @@ func (g *gallery) filesView(c *ui.Context, log func(string)) { v := mujica.DataTable(c, &s.table, s.entries, mujica.DataTableOptions[fileEntry, int]{ Columns: cols, Key: func(f fileEntry) int { return f.ID }, Multiple: true, Version: s.version, }) - v.FillWidth().Height(430) + v.Element.FillWidth().Height(430) mujica.ContextMenu(c, v.Element, mujica.ContextMenuOptions{}, func(m *mujica.PopupMenu) { g.fileMenu(m, log, targets) }) if v.Changed() { log(fmt.Sprintf("selection %d checked", s.table.Selection.Len())) @@ -174,7 +174,7 @@ func (g *gallery) filesView(c *ui.Context, log func(string)) { {ID: "shown", Text: fmt.Sprintf(g.t("%d shown", "显示 %d 个"), shown), Secondary: true}, }, Right: []mujica.StatusItem{{ID: "selected", Text: fmt.Sprintf(g.t("%d selected", "已选 %d 个"), s.table.Selection.Len())}}, - }).Radius(theme.CardRadius).Border(theme.BorderWidth, k.Border) + }).Element.Radius(theme.CardRadius).Border(theme.BorderWidth, k.Border) g.fileAdders(c, log) g.renameDialog(c, log) g.deleteDialog(c, log) diff --git a/examples/gallery/pages_choice.go b/examples/gallery/pages_choice.go index 05adf56..d703472 100644 --- a/examples/gallery/pages_choice.go +++ b/examples/gallery/pages_choice.go @@ -330,7 +330,8 @@ func multiSelectDemo(c *ui.Context, log func(string)) { } choiceLabeled(c, "Fruits", func() { r := mujica.MultiSelect(c, &s.values, choiceFruits, mujica.MultiSelectOptions{Max: limit, MaxTags: 2, Disabled: s.disable, Label: "Fruits"}) - if r.Width(320).Changed() { + r.Element.Width(320) + if r.Changed() { log(fmt.Sprintf("Fruits: %v", s.values)) } }) @@ -435,7 +436,9 @@ func cascaderDemo(c *ui.Context, log func(string)) { }, func() { choiceLabeled(c, "City (children load on demand)", func() { opts := mujica.CascaderOptions{Level: level, Load: load, AnyLevel: s.anyLevel, Disabled: s.ds, Label: "City"} - if mujica.Cascader(c, &s.path, opts).Width(320).Changed() { + r := mujica.Cascader(c, &s.path, opts) + r.Element.Width(320) + if r.Changed() { log(fmt.Sprintf("path: %v", s.path)) } }) @@ -471,7 +474,9 @@ func treeSelectDemo(c *ui.Context, log func(string)) { } choiceLabeled(c, "Roles", func() { opts := mujica.TreeSelectOptions{Multiple: s.multiple, Rule: rule, MaxTags: 2, Disabled: s.disabled, Label: "Roles"} - if mujica.TreeSelect(c, &s.sel, choiceRoles, opts).Width(320).Changed() { + r := mujica.TreeSelect(c, &s.sel, choiceRoles, opts) + r.Element.Width(320) + if r.Changed() { log(fmt.Sprintf("Roles: %v", s.sel)) } }) diff --git a/examples/gallery/pages_data_collections.go b/examples/gallery/pages_data_collections.go index 0b37378..4d469d5 100644 --- a/examples/gallery/pages_data_collections.go +++ b/examples/gallery/pages_data_collections.go @@ -58,7 +58,7 @@ func dataListDemo(c *ui.Context, log func(string)) { ui.Text(c, "Office").TextColor(mujica.Tokens(c).TextMuted) } }) - v.Size(480, 320) + v.Element.Size(480, 320) if v.Changed() { k, _ := s.list.Selected() log(fmt.Sprintf("selected %d, %d chosen", k, s.list.Selection.Len())) @@ -138,7 +138,7 @@ func dataTableDemo(c *ui.Context, log func(string)) { v := mujica.DataTable(c, &s.table, rows, mujica.DataTableOptions[dataRelic, int]{ Columns: cols, Key: func(r dataRelic) int { return r.ID }, Multiple: s.multiple, Status: st, }) - v.Size(620, 360) + v.Element.Size(620, 360) ui.Text(c, fmt.Sprintf("%d of %d rows shown", s.table.Rows(), len(rows))).TextColor(k.TextMuted) if v.SortChanged() { log(fmt.Sprintf("sort %s descending=%v", s.table.Sort.Column, s.table.Sort.Descending)) @@ -224,7 +224,7 @@ func dataTreeDemo(c *ui.Context, log func(string)) { ui.Checkbox(c, &s.multiple, "Multiple") }) v := mujica.Tree(c, ts, opts) - v.Size(420, 340) + v.Element.Size(420, 340) if v.Changed() { n, _ := ts.Selected() log(fmt.Sprintf("selected %s, %d chosen", n, ts.Selection.Len())) @@ -290,7 +290,7 @@ func dataTreeTableDemo(c *ui.Context, log func(string)) { {ID: "kids", Title: "Rooms", Width: 80, Align: ui.End, Cell: func(n string) { ui.Text(c, strconv.Itoa(len(s.lazy.kids[n]))) }}, }, }) - v.Size(520, 320) + v.Element.Size(520, 320) if v.SortChanged() { log(fmt.Sprintf("sort %s descending=%v", s.table.Sort.Column, s.table.Sort.Descending)) } diff --git a/examples/gallery/pages_data_content.go b/examples/gallery/pages_data_content.go index 92d1b95..6e93449 100644 --- a/examples/gallery/pages_data_content.go +++ b/examples/gallery/pages_data_content.go @@ -58,7 +58,7 @@ func dataAccordionDemo(c *ui.Context, log func(string)) { log("closed all from outside") } }) - if mujica.Accordion(c, &s.open, []mujica.AccordionItem{ + r := mujica.Accordion(c, &s.open, []mujica.AccordionItem{ {ID: "choir", Title: "Choir", Content: func() { ui.Text(c, "Twelve voices, two at the altar.") }}, {ID: "nave", Title: "Nave", Content: func() { ui.Text(c, "Pews of black oak, polished for the feast.") }}, {ID: "crypt", Title: "Crypt (disabled)", Disabled: true, Content: func() {}}, @@ -67,7 +67,9 @@ func dataAccordionDemo(c *ui.Context, log func(string)) { log("ring the bells") } }}, - }, mujica.AccordionOptions{Multiple: s.multiple, KeepOne: s.keepOne}).Width(420).Changed() { + }, mujica.AccordionOptions{Multiple: s.multiple, KeepOne: s.keepOne}) + r.Element.Width(420) + if r.Changed() { log(fmt.Sprintf("open %v", s.open)) } }) @@ -235,7 +237,7 @@ func dataCarouselDemo(c *ui.Context, log func(string)) { v := mujica.Carousel(c, &s.index, len(dataPictures), mujica.CarouselOptions{Label: "Windows", Autoplay: s.autoplay, Loop: s.loop, Interval: d}, func(i int) { ui.Image(c, dataPictures[i].Source).Grow(1).Label(dataPictures[i].Alt) }) - v.Size(520, 300) + v.Element.Size(520, 300) if v.Changed() { log(fmt.Sprintf("slide %d", s.index+1)) } diff --git a/examples/gallery/pages_data_test.go b/examples/gallery/pages_data_test.go index 8593aeb..d278852 100644 --- a/examples/gallery/pages_data_test.go +++ b/examples/gallery/pages_data_test.go @@ -47,3 +47,13 @@ func TestDataDemoButtonsLog(t *testing.T) { t.Fatalf("Light candles not logged: %q", *events) } } + +func TestDataAccordionDemoLogsChange(t *testing.T) { + tt, events := dataDemo(t, "074") + if err := tt.Click("Bells"); err != nil { + t.Fatal(err) + } + if !slices.Contains(*events, "open [bells]") { + t.Fatalf("Accordion change not logged: %q", *events) + } +} diff --git a/examples/gallery/pages_input.go b/examples/gallery/pages_input.go index f1db221..19f4ea2 100644 --- a/examples/gallery/pages_input.go +++ b/examples/gallery/pages_input.go @@ -180,9 +180,9 @@ if r.Submitted() { inputSection(c, "States", func() { ui.Row(c).Gap(12).Children(func() { e, ro, off := "1789-13-01", "Read only", "Disabled" - mujica.TextInput(c, &e, mujica.TextInputOptions{Label: "Error state", Error: "No such date"}).Grow(1) - mujica.TextInput(c, &ro, mujica.TextInputOptions{Label: "Read-only state", ReadOnly: true}).Grow(1) - mujica.TextInput(c, &off, mujica.TextInputOptions{Label: "Disabled state", Disabled: true}).Grow(1) + mujica.TextInput(c, &e, mujica.TextInputOptions{Label: "Error state", Error: "No such date"}).Element.Grow(1) + mujica.TextInput(c, &ro, mujica.TextInputOptions{Label: "Read-only state", ReadOnly: true}).Element.Grow(1) + mujica.TextInput(c, &off, mujica.TextInputOptions{Label: "Disabled state", Disabled: true}).Element.Grow(1) }) }) }}, @@ -244,8 +244,8 @@ if r.Submitted() { ui.Row(c).Gap(12).AlignItems(ui.Start).Children(func() { a, b, e := 4.0, 2.0, 7.5 mujica.NumberInput(c, &a, mujica.NumberInputOptions{Min: 1, Max: 8, StepperOnly: true, Label: "Stepper state"}) - mujica.NumberInput(c, &b, mujica.NumberInputOptions{Label: "Disabled state", Disabled: true}).Width(160) - mujica.NumberInput(c, &e, mujica.NumberInputOptions{Step: 0.5, Label: "Error state", Error: "Must be whole"}).Width(160) + mujica.NumberInput(c, &b, mujica.NumberInputOptions{Label: "Disabled state", Disabled: true}).Element.Width(160) + mujica.NumberInput(c, &e, mujica.NumberInputOptions{Step: 0.5, Label: "Error state", Error: "Must be whole"}).Element.Width(160) }) }) }}, diff --git a/examples/gallery/pages_layout.go b/examples/gallery/pages_layout.go index a6ebd67..04d90a2 100644 --- a/examples/gallery/pages_layout.go +++ b/examples/gallery/pages_layout.go @@ -417,7 +417,7 @@ func layoutSplitPaneDemo(c *ui.Context, log func(string)) { Collapsible: sides[s.collapsible]}, pane("Files", "Drag the divider, or focus it and use the arrows.", k.Surface), pane("Editor", "Enter on the divider folds the collapsible pane.", k.Background)) - r.Fill() + r.Element.Fill() if r.Resized { log(fmt.Sprintf("resized to %.0f", s.split.Size)) } diff --git a/file_drop_zone.go b/file_drop_zone.go index 5420580..828b88e 100644 --- a/file_drop_zone.go +++ b/file_drop_zone.go @@ -37,15 +37,6 @@ type FileDropZoneOptions struct { Disabled bool } -// FileDropZoneParts are the parts of a FileDropZone. -type FileDropZoneParts struct { - Element *ui.Element - changed bool -} - -// Changed reports whether files were added or removed this frame. -func (p FileDropZoneParts) Changed() bool { return p.changed } - // fileDropZoneReject is a file that failed a check, shown until dismissed. type fileDropZoneReject struct { path, reason string @@ -66,7 +57,7 @@ type fileDropZoneState struct { } // FileDropZone checks dropped or browsed files and lists them; it never reads or uploads them. -func FileDropZone(c *ui.Context, files *[]DroppedFile, opts FileDropZoneOptions) FileDropZoneParts { +func FileDropZone(c *ui.Context, files *[]DroppedFile, opts FileDropZoneOptions) ChoiceResult { if opts.MaxFiles < 0 || opts.MaxSize < 0 { panic("mujica: FileDropZone limits must not be negative") } @@ -77,7 +68,7 @@ func FileDropZone(c *ui.Context, files *[]DroppedFile, opts FileDropZoneOptions) } k := Tokens(c) d := Density(c) - parts := FileDropZoneParts{} + parts := ChoiceResult{} col := ui.Column(c).Gap(d.Unit() * 2) parts.Element = col st := ui.Local(col, "zone", func() fileDropZoneState { return fileDropZoneState{} }) @@ -213,7 +204,7 @@ func fileDropZoneCheck(c *ui.Context, st *fileDropZoneState) { } // fileDropZoneList shows accepted, checking and rejected files. -func fileDropZoneList(c *ui.Context, st *fileDropZoneState, files *[]DroppedFile, parts *FileDropZoneParts, disabled bool) { +func fileDropZoneList(c *ui.Context, st *fileDropZoneState, files *[]DroppedFile, parts *ChoiceResult, disabled bool) { k := Tokens(c) d := Density(c) row := func(key string, icon string, color ui.Color, name, detail string, remove func()) { diff --git a/image_viewer.go b/image_viewer.go index 2f2fa69..a5c3ee5 100644 --- a/image_viewer.go +++ b/image_viewer.go @@ -20,15 +20,6 @@ type ViewerImage struct { Alt string } -// ImageViewerView is a built ImageViewer; Element is nil while closed. -type ImageViewerView struct { - *ui.Element - changed bool -} - -// Changed reports that another image was chosen. -func (v ImageViewerView) Changed() bool { return v.changed } - // Zoom limits, as multiples of the fitted size. const ( viewerMinZoom = 0.25 @@ -42,9 +33,9 @@ type viewerState struct { zoom, x, y float32 } -// ImageViewer shows images[*index] over the window while *open, with zoom, pan and switching. -func ImageViewer(c *ui.Context, open *bool, index *int, images []ViewerImage) ImageViewerView { - var v ImageViewerView +// ImageViewer shows images[*index] over the window while *open, with zoom, pan and switching; Element is nil while closed. +func ImageViewer(c *ui.Context, open *bool, index *int, images []ViewerImage) ChoiceResult { + var v ChoiceResult if !*open { return v } diff --git a/list.go b/list.go index 0275742..32205b0 100644 --- a/list.go +++ b/list.go @@ -65,7 +65,7 @@ func (s *ListState[K]) ScrollTo(i int) { // ListView is a built List and what happened to it in this frame. type ListView struct { - *ui.Element + Element *ui.Element changed, submitted, retry bool } @@ -93,7 +93,7 @@ func List[K comparable](c *ui.Context, s *ListState[K], n int, opts ListOptions[ k := Tokens(c) var v ListView v.Element = ui.Column(c).MinHeight(0) - v.Children(func() { + v.Element.Children(func() { if n == 0 { v.retry = dataPlaceholder(c, opts.Status, opts.Error, opts.Empty, msg(c, "list.empty")) return diff --git a/list_test.go b/list_test.go index a3eeacb..6b8c4f8 100644 --- a/list_test.go +++ b/list_test.go @@ -40,7 +40,7 @@ func (h *listHarness) view(c *ui.Context) { h.built++ ui.Text(c, h.opts.Label(i)).SingleLine() }) - v.Size(h.listSize[0], h.listSize[1]) + v.Element.Size(h.listSize[0], h.listSize[1]) if v.Changed() { h.changes++ } @@ -207,13 +207,13 @@ func TestListSnapshot(t *testing.T) { g := ui.Local(c.Root(), "g", func() *ListState[int] { return &ListState[int]{started: true, cursor: 4} }) opts := ListOptions[int]{Key: func(i int) int { return i }, Label: func(i int) string { return names[i] }, Multiple: true} ui.Row(c).Padding(16).Gap(16).Children(func() { - List(c, *st, len(names), opts, func(i int) { ui.Text(c, names[i]) }).Size(220, 228) + List(c, *st, len(names), opts, func(i int) { ui.Text(c, names[i]) }).Element.Size(220, 228) gopts := opts gopts.Grid, gopts.Multiple, gopts.ItemWidth, gopts.ItemHeight = true, false, 100, 64 List(c, *g, len(names), gopts, func(i int) { ui.Text(c, names[i]).FontWeight(600) ui.Text(c, "Office").TextColor(Tokens(c).TextMuted) - }).Size(360, 228) + }).Element.Size(360, 228) }) }) } diff --git a/menubar.go b/menubar.go index e1e6189..00ce82d 100644 --- a/menubar.go +++ b/menubar.go @@ -56,8 +56,8 @@ type MenubarOptions struct { // MenubarResult is the built Menubar. type MenubarResult struct { - *ui.Element - chosen string + Element *ui.Element + chosen string } // Chosen returns the ID of the item the user chose in this frame. diff --git a/pagination.go b/pagination.go index f73b1aa..45c2dfb 100644 --- a/pagination.go +++ b/pagination.go @@ -27,17 +27,8 @@ type PaginationOptions struct { Disabled bool } -// PaginationResult is the built Pagination. -type PaginationResult struct { - *ui.Element - changed bool -} - -// Changed reports a new page or page size in this frame, from the user or a correction. -func (r PaginationResult) Changed() bool { return r.changed } - // Pagination pages *page (from 1) through Total records, clamping it when Total shrinks. -func Pagination(c *ui.Context, page, pageSize *int, opts PaginationOptions) PaginationResult { +func Pagination(c *ui.Context, page, pageSize *int, opts PaginationOptions) ChoiceResult { if opts.Total < 0 { panic("mujica: Pagination Total is negative") } @@ -58,7 +49,7 @@ func Pagination(c *ui.Context, page, pageSize *int, opts PaginationOptions) Pagi d := Density(c) u := d.Unit() row := ui.Row(c).AlignItems(ui.Center).Gap(u * 3).Wrap().Role(ui.RoleGroup).Label(msg(c, "pagination.label")).Disabled(opts.Disabled) - res := PaginationResult{Element: row} + res := ChoiceResult{Element: row} pages := paginationPages(opts.Total, *pageSize) if p := max(1, min(*page, pages)); p != *page { slog.Debug("mujica: pagination correct", "from", *page, "to", p, "pages", pages) @@ -166,7 +157,7 @@ func paginationButton(c *ui.Context, text string, icon *ui.SVG, name string, on, return b } -func paginationSizes(c *ui.Context, page, pageSize *int, sizes []int, res *PaginationResult) { +func paginationSizes(c *ui.Context, page, pageSize *int, sizes []int, res *ChoiceResult) { k := Tokens(c) d := Density(c) u := d.Unit() diff --git a/rating.go b/rating.go index f0110f9..1f3dea4 100644 --- a/rating.go +++ b/rating.go @@ -25,17 +25,8 @@ type RatingOptions struct { ShowValue bool } -// RatingParts are the parts of a Rating. -type RatingParts struct { - Element *ui.Element - changed bool -} - -// Changed reports whether the user set a new value this frame. -func (p RatingParts) Changed() bool { return p.changed } - // Rating shows *value as stars and lets the pointer and arrows set it in steps. -func Rating(c *ui.Context, value *float64, opts RatingOptions) RatingParts { +func Rating(c *ui.Context, value *float64, opts RatingOptions) ChoiceResult { n, step := opts.Max, opts.Step if n == 0 { n = 5 @@ -54,7 +45,7 @@ func Rating(c *ui.Context, value *float64, opts RatingOptions) RatingParts { if label == "" { label = msg(c, "rating.label") } - parts := RatingParts{} + parts := ChoiceResult{} set := func(v float64) { v = math.Max(0, math.Min(float64(n), math.Round(v/step)*step)) if v != *value { diff --git a/rating_test.go b/rating_test.go index c48634e..57d90e5 100644 --- a/rating_test.go +++ b/rating_test.go @@ -105,7 +105,7 @@ func TestRatingSnapshot(t *testing.T) { func TestRatingHitAreaCompact(t *testing.T) { v := 2.0 - var r RatingParts + var r ChoiceResult tt := ui.NewTester(func(c *ui.Context) { Use(c, Settings{Density: theme.Compact}) ui.Column(c).AlignItems(ui.Start).Children(func() { diff --git a/sidebar.go b/sidebar.go index fa0398d..5e5a5c5 100644 --- a/sidebar.go +++ b/sidebar.go @@ -37,17 +37,8 @@ type SidebarOptions struct { Width float32 } -// SidebarResult is the built Sidebar. -type SidebarResult struct { - *ui.Element - changed bool -} - -// Changed reports a new choice in this frame. -func (r SidebarResult) Changed() bool { return r.changed } - // Sidebar is a source list choosing an item ID into *selected, built on MyGo's Sidebar. -func Sidebar(c *ui.Context, selected *string, sections []SidebarSection, opts SidebarOptions) SidebarResult { +func Sidebar(c *ui.Context, selected *string, sections []SidebarSection, opts SidebarOptions) ChoiceResult { k := Tokens(c) width := opts.Width if width == 0 { @@ -58,7 +49,7 @@ func Sidebar(c *ui.Context, selected *string, sections []SidebarSection, opts Si } wrap := ui.Column(c).Width(width).FillHeight().AlignItems(ui.Stretch).Shrink(0) closed := ui.Local(wrap, "closed", func() map[string]*bool { return map[string]*bool{} }) - res := SidebarResult{Element: wrap} + res := ChoiceResult{Element: wrap} prev := *selected wrap.Children(func() { // MyGo draws icons in Accent: swap in readable state colors for the sidebar. diff --git a/sidebar_test.go b/sidebar_test.go index 0ae2c17..bd55bf4 100644 --- a/sidebar_test.go +++ b/sidebar_test.go @@ -82,7 +82,7 @@ func TestSidebarCompact(t *testing.T) { tt := ui.NewTester(func(c *ui.Context) { Use(c, Settings{}) ui.Row(c).FillHeight().Children(func() { - w = Sidebar(c, &sel, sidebarSample(), SidebarOptions{Compact: true}).Bounds().W + w = Sidebar(c, &sel, sidebarSample(), SidebarOptions{Compact: true}).Element.Bounds().W }) }, 400, 500) tt.Frame() diff --git a/slider_test.go b/slider_test.go index 2ebaf2e..284606f 100644 --- a/slider_test.go +++ b/slider_test.go @@ -90,7 +90,7 @@ func TestSliderSnapshot(t *testing.T) { ui.Column(c).Padding(20).Gap(8).Width(380).Children(func() { Slider(c, &a, SliderOptions{Step: 5}) s := Slider(c, &b, SliderOptions{Step: 25, Marks: marks, ShowValue: true, Format: func(v float64) string { return fmt.Sprintf("%.0f%%", v) }}) - s.Focus() + s.Element.Focus() Slider(c, &d, SliderOptions{Disabled: true}) }) }) diff --git a/split_pane.go b/split_pane.go index 3851056..0b90209 100644 --- a/split_pane.go +++ b/split_pane.go @@ -39,7 +39,7 @@ type SplitPaneOptions struct { // SplitPaneResult is a SplitPane and its events in this frame. type SplitPaneResult struct { - *ui.Element + Element *ui.Element Resized bool Toggled bool } diff --git a/split_pane_test.go b/split_pane_test.go index 47fb4a2..f263d8f 100644 --- a/split_pane_test.go +++ b/split_pane_test.go @@ -14,7 +14,7 @@ func splitPaneTester(st *SplitPaneState, opts SplitPaneOptions, p *splitPaneProb return ui.NewTester(func(c *ui.Context) { Use(c, Settings{}) r := SplitPane(c, st, opts, func() { ui.Text(c, "Files") }, func() { ui.Text(c, "Editor") }) - r.Fill() + r.Element.Fill() if r.Resized { p.resized++ } @@ -136,7 +136,7 @@ func TestSplitPaneCollapseSecondVertical(t *testing.T) { tt := ui.NewTester(func(c *ui.Context) { Use(c, Settings{}) SplitPane(c, st, SplitPaneOptions{Vertical: true, MinSecond: 60, Collapsible: SplitPaneSecond}, - func() { ui.Text(c, "Editor") }, func() { ui.Text(c, "Console") }).Fill() + func() { ui.Text(c, "Editor") }, func() { ui.Text(c, "Console") }).Element.Fill() }, 300, 201) tt.Key(0, ui.KeyTab) tt.Key(0, ui.KeyDown) @@ -184,7 +184,7 @@ func TestSplitPaneSnapshot(t *testing.T) { } ui.Column(c).Padding(16).Children(func() { SplitPane(c, st, SplitPaneOptions{}, pane("Files", k.Surface), pane("Editor", k.Background)). - Height(128).Border(1, k.Border).Radius(4).Clip() + Element.Height(128).Border(1, k.Border).Radius(4).Clip() }) }) } @@ -196,7 +196,7 @@ func TestSplitPaneSizesInsideBorder(t *testing.T) { Use(c, Settings{}) SplitPane(c, st, SplitPaneOptions{MinSecond: 120}, func() { ui.Text(c, "Files") }, func() { second = ui.Box(c).Fill().Absolute().Bounds() - }).Fill().Border(1, ui.RGB(0, 0, 0)) + }).Element.Fill().Border(1, ui.RGB(0, 0, 0)) }, 401, 200) tt.Key(0, ui.KeyTab) tt.Key(0, ui.KeyEnd) @@ -216,7 +216,7 @@ func TestSplitPaneClampsOnResizeAlone(t *testing.T) { st := &SplitPaneState{Size: 299} tt := ui.NewTester(func(c *ui.Context) { Use(c, Settings{}) - SplitPane(c, st, SplitPaneOptions{}, func() { ui.Text(c, "Files") }, func() { ui.Text(c, "Editor") }).Fill() + SplitPane(c, st, SplitPaneOptions{}, func() { ui.Text(c, "Files") }, func() { ui.Text(c, "Editor") }).Element.Fill() }, 400, 200) tt.SetSize(250, 200) // 250 less the divider and the 40 DIP second minimum. diff --git a/status_bar.go b/status_bar.go index 8e9d1a2..4bec842 100644 --- a/status_bar.go +++ b/status_bar.go @@ -46,7 +46,7 @@ type StatusBarOptions struct { // StatusBarResult is a StatusBar and its event in this frame. type StatusBarResult struct { - *ui.Element + Element *ui.Element // Clicked is the ID of the action item clicked, or empty. Clicked string } diff --git a/steps.go b/steps.go index cae4dde..7ffbddb 100644 --- a/steps.go +++ b/steps.go @@ -29,15 +29,6 @@ type StepsOptions struct { Label string } -// StepsResult is the built Steps. -type StepsResult struct { - *ui.Element - changed bool -} - -// Changed reports that the user chose a clickable step in this frame. -func (r StepsResult) Changed() bool { return r.changed } - type stepsState int const ( @@ -48,7 +39,7 @@ const ( ) // Steps shows a process whose step *current is under way: earlier steps are done. -func Steps(c *ui.Context, current *int, steps []Step, opts StepsOptions) StepsResult { +func Steps(c *ui.Context, current *int, steps []Step, opts StepsOptions) ChoiceResult { if len(steps) == 0 { panic("mujica: Steps needs at least one step") } @@ -63,7 +54,7 @@ func Steps(c *ui.Context, current *int, steps []Step, opts StepsOptions) StepsRe if opts.Label != "" { root.Label(opts.Label) } - res := StepsResult{Element: root} + res := ChoiceResult{Element: root} root.Children(func() { for i, s := range steps { if s.Title == "" { diff --git a/tabs.go b/tabs.go index 36afb61..1b89d6d 100644 --- a/tabs.go +++ b/tabs.go @@ -27,17 +27,8 @@ type TabsOptions struct { Label string } -// TabsResult is the built Tabs: the element holding tabs and panel. -type TabsResult struct { - *ui.Element - changed bool -} - -// Changed reports a new choice made by the user in this frame. -func (r TabsResult) Changed() bool { return r.changed } - // Tabs shows tabs choosing *selected and the chosen tab's panel. Disabled tabs are skipped by the arrows. -func Tabs(c *ui.Context, selected *int, tabs []Tab, opts TabsOptions) TabsResult { +func Tabs(c *ui.Context, selected *int, tabs []Tab, opts TabsOptions) ChoiceResult { if len(tabs) == 0 { panic("mujica: Tabs needs at least one tab") } @@ -66,7 +57,7 @@ func Tabs(c *ui.Context, selected *int, tabs []Tab, opts TabsOptions) TabsResult if opts.Vertical { root.Row() } - res := TabsResult{Element: root} + res := ChoiceResult{Element: root} root.Children(func() { var strip *ui.Element if opts.Vertical { diff --git a/tabs_test.go b/tabs_test.go index 5148ea5..f6beb01 100644 --- a/tabs_test.go +++ b/tabs_test.go @@ -87,7 +87,7 @@ func TestTabsOverflowScrolls(t *testing.T) { tt := ui.NewTester(func(c *ui.Context) { Use(c, Settings{}) ui.Column(c).Width(240).Children(func() { - w = Tabs(c, &sel, tabs, TabsOptions{}).Bounds().W + w = Tabs(c, &sel, tabs, TabsOptions{}).Element.Bounds().W }) }, 400, 200) tt.Key(0, ui.KeyTab) diff --git a/text_input.go b/text_input.go index 961e082..4f5fecb 100644 --- a/text_input.go +++ b/text_input.go @@ -12,7 +12,7 @@ import ( // InputResult is a value input's outer element and its events. type InputResult struct { - *ui.Element + Element *ui.Element // Input is the text field inside, for focus and keys. Input *ui.Element changed bool diff --git a/toggle_group.go b/toggle_group.go index 246344e..e8617ec 100644 --- a/toggle_group.go +++ b/toggle_group.go @@ -12,7 +12,7 @@ import ( // ChoiceResult is a choice control's element and whether its value changed this frame. type ChoiceResult struct { - *ui.Element + Element *ui.Element changed bool } diff --git a/transfer.go b/transfer.go index 64771b2..4292457 100644 --- a/transfer.go +++ b/transfer.go @@ -29,22 +29,13 @@ type TransferOptions struct { Disabled bool } -// TransferParts are the parts of a Transfer. -type TransferParts struct { - Element *ui.Element - changed bool -} - -// Changed reports whether items moved this frame. -func (p TransferParts) Changed() bool { return p.changed } - type transferState struct { checked map[string]bool query [2]string } // Transfer moves items between a source list and *target, the chosen IDs in order. -func Transfer(c *ui.Context, target *[]string, items []TransferItem, opts TransferOptions) TransferParts { +func Transfer(c *ui.Context, target *[]string, items []TransferItem, opts TransferOptions) ChoiceResult { byID := make(map[string]TransferItem, len(items)) for _, it := range items { if _, dup := byID[it.ID]; dup || it.ID == "" { @@ -58,7 +49,7 @@ func Transfer(c *ui.Context, target *[]string, items []TransferItem, opts Transf } } d := Density(c) - parts := TransferParts{} + parts := ChoiceResult{} row := ui.Row(c).Gap(d.Unit() * 3).AlignItems(ui.Center).Disabled(opts.Disabled) parts.Element = row st := ui.Local(row, "transfer", func() transferState { return transferState{checked: map[string]bool{}} }) diff --git a/tree.go b/tree.go index efd0c91..e7c745c 100644 --- a/tree.go +++ b/tree.go @@ -160,7 +160,7 @@ func (t *treeCore[K]) itemStatus(c *ui.Context, item K, status func(K) DataStatu // TreeView is a built Tree and what happened to it in this frame. type TreeView struct { - *ui.Element + Element *ui.Element changed, submitted, retry bool } @@ -185,7 +185,7 @@ func Tree[K comparable](c *ui.Context, s *TreeState[K], opts TreeOptions[K]) Tre t.begin(opts.Label, &s.Selection, opts.Multiple) var v TreeView v.Element = ui.Column(c).MinHeight(0) - v.Children(func() { + v.Element.Children(func() { if len(opts.Roots) == 0 { t.clear(&s.Selection) v.retry = dataPlaceholder(c, opts.Status, opts.Error, opts.Empty, msg(c, "list.empty")) diff --git a/tree_table.go b/tree_table.go index 41e4ba7..ddbb3f5 100644 --- a/tree_table.go +++ b/tree_table.go @@ -122,7 +122,7 @@ func TreeTable[K comparable](c *ui.Context, s *TreeTableState[K], opts TreeTable before := s.Sort var v DataTableView v.Element = ui.Column(c).MinHeight(0).Gap(d.Unit() * 2) - v.Children(func() { + v.Element.Children(func() { if len(opts.Roots) == 0 { t.clear(&s.Selection) v.retry = dataPlaceholder(c, opts.Status, opts.Error, opts.Empty, msg(c, "list.empty")) diff --git a/tree_table_test.go b/tree_table_test.go index 8a3c572..efacea2 100644 --- a/tree_table_test.go +++ b/tree_table_test.go @@ -50,7 +50,7 @@ func (h *ttHarness) view(c *ui.Context) { h.c = c ui.Column(c).Padding(10).Children(func() { v := TreeTable(c, &h.st, h.opts) - v.Size(360, 320) + v.Element.Size(360, 320) if v.SortChanged() { h.sorts++ } @@ -154,7 +154,7 @@ func TestTreeTableLargeIsVirtualized(t *testing.T) { Use(c, Settings{}) clear(rows) TreeTable(c, &st, TreeTableOptions[int]{Roots: roots, Children: kids, Label: strconv.Itoa, - Columns: []TreeColumn[int]{{ID: "n", Title: "N", Cell: cell(c), Compare: cmp.Compare[int]}, {ID: "m", Title: "M", Cell: cell(c)}}}).Size(320, 360) + Columns: []TreeColumn[int]{{ID: "n", Title: "N", Cell: cell(c), Compare: cmp.Compare[int]}, {ID: "m", Title: "M", Cell: cell(c)}}}).Element.Size(320, 360) } tt := ui.NewTester(view, 400, 400) const bound = 26 // see TestTreeLargeIsVirtualized @@ -187,7 +187,7 @@ func TestTreeTableSnapshot(t *testing.T) { }) (*h).c = c ui.Column(c).Padding(16).Children(func() { - TreeTable(c, &(*h).st, (*h).opts).Size(388, 208) + TreeTable(c, &(*h).st, (*h).opts).Element.Size(388, 208) }) }) } @@ -208,7 +208,7 @@ func TestTreeTableSelectionFollowsLargeSort(t *testing.T) { return nil }, Columns: []TreeColumn[int]{{ID: "n", Title: "N", Cell: func(n int) { ui.Text(c, "#"+strconv.Itoa(n)) }, Compare: cmp.Compare[int]}}, - }).Size(320, 360) + }).Element.Size(320, 360) } st.SetOpen(0, true) tt := ui.NewTester(view, 400, 400) diff --git a/tree_test.go b/tree_test.go index e238b3a..02408e7 100644 --- a/tree_test.go +++ b/tree_test.go @@ -53,7 +53,7 @@ func (h *treeHarness) view(c *ui.Context) { opts.Label = func(n string) string { h.built[n] = true; return label(n) } ui.Column(c).Padding(10).Children(func() { v := Tree(c, &h.st, opts) - v.Size(320, 360) + v.Element.Size(320, 360) if v.Changed() { h.changes++ } @@ -186,7 +186,7 @@ func TestTreeLargeIsVirtualized(t *testing.T) { Tree(c, &st, TreeOptions[int]{Roots: roots, Children: kids, Label: func(n int) string { built++ return fmt.Sprintf("Node %d", n) - }}).Size(320, 360) + }}).Element.Size(320, 360) } tt := ui.NewTester(view, 400, 400) // Two Label calls per row; a jump builds 2×(10 rows + 2 overscan) + 2 edges. @@ -220,7 +220,7 @@ func TestTreeSnapshot(t *testing.T) { return h }) ui.Column(c).Padding(16).Children(func() { - Tree(c, &(*h).st, (*h).opts).Size(360, 228) + Tree(c, &(*h).st, (*h).opts).Element.Size(360, 228) }) }) } @@ -234,7 +234,7 @@ func TestTreeSelectionFollowsLargeReorder(t *testing.T) { view := func(c *ui.Context) { Use(c, Settings{Mode: Light}) Tree(c, &st, TreeOptions[int]{Roots: roots, Children: func(int) []int { return nil }, - Label: func(n int) string { return fmt.Sprintf("Node %d", n) }}).Size(320, 360) + Label: func(n int) string { return fmt.Sprintf("Node %d", n) }}).Element.Size(320, 360) } tt := ui.NewTester(view, 400, 400) if err := tt.Click("Node 2"); err != nil {