From 16cfcfb796acd7939f2cac06295bf44965fdeb71 Mon Sep 17 00:00:00 2001 From: "Randall C. O'Reilly" Date: Wed, 30 Oct 2024 17:53:49 -0700 Subject: [PATCH 1/4] fix for slow table updating with many rows -- UpdateMaxWidths is the culprit --- core/table.go | 2 +- examples/demo/demo.go | 20 +++++++++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/core/table.go b/core/table.go index e80f06de0e..a481e0c75e 100644 --- a/core/table.go +++ b/core/table.go @@ -189,7 +189,7 @@ func (tb *Table) cacheVisibleFields() { } func (tb *Table) UpdateMaxWidths() { - if tb.SliceSize == 0 { + if tb.SliceSize == 0 || tb.SliceSize > 10000 { return } for fli := 0; fli < tb.numVisibleFields; fli++ { diff --git a/examples/demo/demo.go b/examples/demo/demo.go index 373a5ff8a3..54ea4ee281 100644 --- a/examples/demo/demo.go +++ b/examples/demo/demo.go @@ -389,12 +389,21 @@ func collections(ts *core.Tabs) { ktab, _ := vts.NewTab("Keyed lists") core.NewKeyedList(ktab).SetMap(&mp) - tbl := make([]*tableStruct, 50) + // tbl := make([]*tableStruct, 50) + // for i := range tbl { + // ts := &tableStruct{Age: i, Score: float32(i) / 10} + // tbl[i] = ts + // } + // tbl[0].Name = "this is a particularly long field" + // ttab, _ := vts.NewTab("Tables") + // core.NewTable(ttab).SetSlice(&tbl) + + tbl := make([]*smallStruct, 100_000) for i := range tbl { - ts := &tableStruct{Age: i, Score: float32(i) / 10} + ts := &smallStruct{Value: float32(i)} tbl[i] = ts } - tbl[0].Name = "this is a particularly long field" + // tbl[0].Name = "this is a particularly long field" ttab, _ := vts.NewTab("Tables") core.NewTable(ttab).SetSlice(&tbl) @@ -528,6 +537,11 @@ func (ts *testStruct) ShouldDisplay(field string) bool { return true } +type smallStruct struct { //types:add + String string + Value float32 +} + func dialogs(ts *core.Tabs) { tab, _ := ts.NewTab("Dialogs") From 7a810fa814506bed8205a3514f0742eae6fa1bda Mon Sep 17 00:00:00 2001 From: "Randall C. O'Reilly" Date: Wed, 30 Oct 2024 22:14:54 -0700 Subject: [PATCH 2/4] actual proper fix for slow table updating with many rows -- UpdateMaxWidths should not be called in Updater -- moved to Styler, which would be called if anything is added. --- core/list.go | 3 ++- core/table.go | 3 +-- tensor/tensorcore/tensoreditor.go | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/core/list.go b/core/list.go index c6cc7d4fcc..d1303ba186 100644 --- a/core/list.go +++ b/core/list.go @@ -255,6 +255,8 @@ func (lb *ListBase) Init() { // absorb horizontal here, vertical in view s.Overflow.X = styles.OverflowAuto s.Grow.Set(1, 1) + svi := lb.This.(Lister) + svi.UpdateMaxWidths() }) if !lb.IsReadOnly() { lb.On(events.DragStart, func(e events.Event) { @@ -360,7 +362,6 @@ func (lb *ListBase) Init() { lb.Updater(func() { lb.UpdateStartIndex() - svi.UpdateMaxWidths() }) lb.MakeGrid(p, func(p *tree.Plan) { diff --git a/core/table.go b/core/table.go index a481e0c75e..8795931e3c 100644 --- a/core/table.go +++ b/core/table.go @@ -94,7 +94,6 @@ func (tb *Table) Init() { tb.Updater(func() { tb.UpdateStartIndex() - svi.UpdateMaxWidths() }) tb.makeHeader(p) @@ -189,7 +188,7 @@ func (tb *Table) cacheVisibleFields() { } func (tb *Table) UpdateMaxWidths() { - if tb.SliceSize == 0 || tb.SliceSize > 10000 { + if tb.SliceSize == 0 { return } for fli := 0; fli < tb.numVisibleFields; fli++ { diff --git a/tensor/tensorcore/tensoreditor.go b/tensor/tensorcore/tensoreditor.go index 26af50675a..a23ff20a5d 100644 --- a/tensor/tensorcore/tensoreditor.go +++ b/tensor/tensorcore/tensoreditor.go @@ -71,7 +71,6 @@ func (tb *TensorEditor) Init() { tb.Updater(func() { tb.UpdateStartIndex() - tb.UpdateMaxWidths() }) tb.MakeHeader(p) From 19d44c602ae8206789ed42f6e465f586d257fbc1 Mon Sep 17 00:00:00 2001 From: "Randall C. O'Reilly" Date: Fri, 1 Nov 2024 11:43:46 -0700 Subject: [PATCH 3/4] revert demo.go to original; rename svi -> ls --- core/list.go | 30 +++++++++++++++--------------- examples/demo/demo.go | 20 +++----------------- 2 files changed, 18 insertions(+), 32 deletions(-) diff --git a/core/list.go b/core/list.go index d1303ba186..1c415b049a 100644 --- a/core/list.go +++ b/core/list.go @@ -255,8 +255,8 @@ func (lb *ListBase) Init() { // absorb horizontal here, vertical in view s.Overflow.X = styles.OverflowAuto s.Grow.Set(1, 1) - svi := lb.This.(Lister) - svi.UpdateMaxWidths() + ls := lb.This.(Lister) + ls.UpdateMaxWidths() }) if !lb.IsReadOnly() { lb.On(events.DragStart, func(e events.Event) { @@ -339,8 +339,8 @@ func (lb *ListBase) Init() { }) lb.Maker(func(p *tree.Plan) { - svi := lb.This.(Lister) - svi.UpdateSliceSize() + ls := lb.This.(Lister) + ls.UpdateSliceSize() scrollTo := -1 if lb.SelectedValue != nil { @@ -366,7 +366,7 @@ func (lb *ListBase) Init() { lb.MakeGrid(p, func(p *tree.Plan) { for i := 0; i < lb.VisibleRows; i++ { - svi.MakeRow(p, i) + ls.MakeRow(p, i) } }) }) @@ -554,7 +554,7 @@ func (lb *ListBase) MakeGrid(p *tree.Plan, maker func(p *tree.Plan)) { } func (lb *ListBase) MakeValue(w Value, i int) { - svi := lb.This.(Lister) + ls := lb.This.(Lister) wb := w.AsWidget() wb.SetProperty(ListRowProperty, i) wb.Styler(func(s *styles.Style) { @@ -565,9 +565,9 @@ func (lb *ListBase) MakeValue(w Value, i int) { } row, col := lb.widgetIndex(w) row += lb.StartIndex - svi.StyleValue(w, s, row, col) + ls.StyleValue(w, s, row, col) if row < lb.SliceSize { - svi.StyleRow(w, row, col) + ls.StyleRow(w, row, col) } }) wb.OnSelect(func(e events.Event) { @@ -587,8 +587,8 @@ func (lb *ListBase) MakeValue(w Value, i int) { } func (lb *ListBase) MakeRow(p *tree.Plan, i int) { - svi := lb.This.(Lister) - si, vi, invis := svi.SliceIndex(i) + ls := lb.This.(Lister) + si, vi, invis := ls.SliceIndex(i) itxt := strconv.Itoa(i) val := lb.sliceElementValue(vi) @@ -609,7 +609,7 @@ func (lb *ListBase) MakeRow(p *tree.Plan, i int) { } wb.Updater(func() { wb := w.AsWidget() - _, vi, invis := svi.SliceIndex(i) + _, vi, invis := ls.SliceIndex(i) val := lb.sliceElementValue(vi) Bind(val.Addr().Interface(), w) wb.SetReadOnly(lb.IsReadOnly()) @@ -1348,23 +1348,23 @@ func (lb *ListBase) pasteIndex(idx int) { //types:add // makePasteMenu makes the menu of options for paste events func (lb *ListBase) makePasteMenu(m *Scene, md mimedata.Mimes, idx int, mod events.DropMods, fun func()) { - svi := lb.This.(Lister) + ls := lb.This.(Lister) if mod == events.DropCopy { NewButton(m).SetText("Assign to").OnClick(func(e events.Event) { - svi.PasteAssign(md, idx) + ls.PasteAssign(md, idx) if fun != nil { fun() } }) } NewButton(m).SetText("Insert before").OnClick(func(e events.Event) { - svi.PasteAtIndex(md, idx) + ls.PasteAtIndex(md, idx) if fun != nil { fun() } }) NewButton(m).SetText("Insert after").OnClick(func(e events.Event) { - svi.PasteAtIndex(md, idx+1) + ls.PasteAtIndex(md, idx+1) if fun != nil { fun() } diff --git a/examples/demo/demo.go b/examples/demo/demo.go index 54ea4ee281..373a5ff8a3 100644 --- a/examples/demo/demo.go +++ b/examples/demo/demo.go @@ -389,21 +389,12 @@ func collections(ts *core.Tabs) { ktab, _ := vts.NewTab("Keyed lists") core.NewKeyedList(ktab).SetMap(&mp) - // tbl := make([]*tableStruct, 50) - // for i := range tbl { - // ts := &tableStruct{Age: i, Score: float32(i) / 10} - // tbl[i] = ts - // } - // tbl[0].Name = "this is a particularly long field" - // ttab, _ := vts.NewTab("Tables") - // core.NewTable(ttab).SetSlice(&tbl) - - tbl := make([]*smallStruct, 100_000) + tbl := make([]*tableStruct, 50) for i := range tbl { - ts := &smallStruct{Value: float32(i)} + ts := &tableStruct{Age: i, Score: float32(i) / 10} tbl[i] = ts } - // tbl[0].Name = "this is a particularly long field" + tbl[0].Name = "this is a particularly long field" ttab, _ := vts.NewTab("Tables") core.NewTable(ttab).SetSlice(&tbl) @@ -537,11 +528,6 @@ func (ts *testStruct) ShouldDisplay(field string) bool { return true } -type smallStruct struct { //types:add - String string - Value float32 -} - func dialogs(ts *core.Tabs) { tab, _ := ts.NewTab("Dialogs") From 446418273968fc30ee12054245f163c01b7c0657 Mon Sep 17 00:00:00 2001 From: "Randall C. O'Reilly" Date: Fri, 1 Nov 2024 13:51:58 -0700 Subject: [PATCH 4/4] list / table: more precise calling of UpdateMaxWidths: when first setting slice and when widget changes values. --- core/list.go | 6 +++--- core/table.go | 3 ++- tensor/tensorcore/table.go | 4 ++-- tensor/tensorcore/tensoreditor.go | 3 ++- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/core/list.go b/core/list.go index 1c415b049a..4b337e01df 100644 --- a/core/list.go +++ b/core/list.go @@ -255,8 +255,6 @@ func (lb *ListBase) Init() { // absorb horizontal here, vertical in view s.Overflow.X = styles.OverflowAuto s.Grow.Set(1, 1) - ls := lb.This.(Lister) - ls.UpdateMaxWidths() }) if !lb.IsReadOnly() { lb.On(events.DragStart, func(e events.Event) { @@ -401,6 +399,7 @@ func (lb *ListBase) SetSliceBase() { lb.SelectedIndex = -1 } lb.ResetSelectedIndexes() + lb.This.(Lister).UpdateMaxWidths() } // SetSlice sets the source slice that we are viewing. @@ -425,11 +424,11 @@ func (lb *ListBase) SetSlice(sl any) *ListBase { return lb } - lb.SetSliceBase() lb.Slice = sl lb.sliceUnderlying = reflectx.Underlying(reflect.ValueOf(lb.Slice)) lb.isArray = reflectx.NonPointerType(reflect.TypeOf(sl)).Kind() == reflect.Array lb.elementValue = reflectx.SliceElementValue(sl) + lb.SetSliceBase() return lb } @@ -604,6 +603,7 @@ func (lb *ListBase) MakeRow(p *tree.Plan, i int) { lb.MakeValue(w, i) if !lb.IsReadOnly() { wb.OnChange(func(e events.Event) { + lb.This.(Lister).UpdateMaxWidths() lb.SendChange(e) }) } diff --git a/core/table.go b/core/table.go index 8795931e3c..8187d602b0 100644 --- a/core/table.go +++ b/core/table.go @@ -145,10 +145,10 @@ func (tb *Table) SetSlice(sl any) *Table { return tb } - tb.SetSliceBase() tb.Slice = sl tb.sliceUnderlying = reflectx.Underlying(reflect.ValueOf(tb.Slice)) tb.elementValue = reflectx.Underlying(reflectx.SliceElementValue(sl)) + tb.SetSliceBase() tb.cacheVisibleFields() return tb } @@ -315,6 +315,7 @@ func (tb *Table) MakeRow(p *tree.Plan, i int) { w.AsTree().SetProperty(ListColProperty, fli) if !tb.IsReadOnly() && !readOnlyTag { wb.OnChange(func(e events.Event) { + tb.This.(Lister).UpdateMaxWidths() tb.SendChange() }) } diff --git a/tensor/tensorcore/table.go b/tensor/tensorcore/table.go index 27ab0998fa..8036d9fee5 100644 --- a/tensor/tensorcore/table.go +++ b/tensor/tensorcore/table.go @@ -95,7 +95,6 @@ func (tb *Table) Init() { tb.Updater(func() { tb.UpdateStartIndex() - tb.UpdateMaxWidths() }) tb.MakeHeader(p) @@ -138,9 +137,9 @@ func (tb *Table) SetTable(et *table.Table) *Table { return nil } - tb.SetSliceBase() tb.Table = table.NewIndexView(et) tb.This.(core.Lister).UpdateSliceSize() + tb.SetSliceBase() tb.Update() return tb } @@ -321,6 +320,7 @@ func (tb *Table) MakeRow(p *tree.Plan, i int) { tb.Table.Table.SetFloatIndex(fli, tb.Table.Indexes[si], fval) } } + tb.This.(core.Lister).UpdateMaxWidths() tb.SendChange() }) } diff --git a/tensor/tensorcore/tensoreditor.go b/tensor/tensorcore/tensoreditor.go index a23ff20a5d..9f6e93f40e 100644 --- a/tensor/tensorcore/tensoreditor.go +++ b/tensor/tensorcore/tensoreditor.go @@ -110,9 +110,9 @@ func (tb *TensorEditor) SetTensor(et tensor.Tensor) *TensorEditor { return nil } - tb.SetSliceBase() tb.Tensor = et tb.This.(core.Lister).UpdateSliceSize() + tb.SetSliceBase() tb.Update() return tb } @@ -246,6 +246,7 @@ func (tb *TensorEditor) MakeRow(p *tree.Plan, i int) { tensor.Projection2DSet(tb.Tensor, tb.Layout.OddRow, vi, fli, fval) } } + tb.This.(core.Lister).UpdateMaxWidths() tb.SendChange() }) }