Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Deferred functions, set by Defer method, which are called after… #1337

Merged
merged 8 commits into from
Nov 25, 2024
2 changes: 1 addition & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:

# we can't test gpu, xyz, and system on the CI since there is no Vulkan support
- name: Test
run: go test -v $(go list ./... | grep -v gpu | grep -v xyz | grep -v system) -coverprofile cover.out
run: go test -v $(go list ./... | grep -v gpu | grep -v xyz | grep -v system) -coverprofile cover.out -timeout 30s

- name: Update coverage report
uses: ncruces/go-coverage-report@v0
Expand Down
10 changes: 5 additions & 5 deletions core/enumgen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 15 additions & 15 deletions core/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,12 +180,12 @@ func (em *Events) handleFocusEvent(e events.Event) {
if DebugSettings.FocusTrace {
fmt.Println(em.scene, "StartFocus:", em.startFocus)
}
em.setFocusEvent(em.startFocus)
em.setFocus(em.startFocus)
case em.prevFocus != nil:
if DebugSettings.FocusTrace {
fmt.Println(em.scene, "PrevFocus:", em.prevFocus)
}
em.setFocusEvent(em.prevFocus)
em.setFocus(em.prevFocus)
em.prevFocus = nil
}
}
Expand Down Expand Up @@ -881,14 +881,14 @@ func (em *Events) focusClear() bool {
}
em.prevFocus = em.focus
}
return em.setFocusEvent(nil)
return em.setFocus(nil)
}

// setFocus sets focus to given item, and returns true if focus changed.
// setFocusQuiet sets focus to given item, and returns true if focus changed.
// If item is nil, then nothing has focus.
// This does NOT send the events.Focus event to the widget.
// See [SetFocusEvent] for version that does send event.
func (em *Events) setFocus(w Widget) bool {
// This does NOT send the [events.Focus] event to the widget.
// See [Events.setFocus] for version that does send an event.
func (em *Events) setFocusQuiet(w Widget) bool {
if DebugSettings.FocusTrace {
fmt.Println(em.scene, "SetFocus:", w)
}
Expand All @@ -905,11 +905,11 @@ func (em *Events) setFocus(w Widget) bool {
return got
}

// setFocusEvent sets focus to given item, and returns true if focus changed.
// setFocus sets focus to given item, and returns true if focus changed.
// If item is nil, then nothing has focus.
// This sends the [events.Focus] event to the widget.
// See [SetFocus] for a version that does not.
func (em *Events) setFocusEvent(w Widget) bool {
// See [Events.setFocusQuiet] for a version that does not.
func (em *Events) setFocus(w Widget) bool {
if DebugSettings.FocusTrace {
fmt.Println(em.scene, "SetFocusEvent:", w)
}
Expand Down Expand Up @@ -975,7 +975,7 @@ func (em *Events) FocusNextFrom(from Widget) bool {
wb := w.AsWidget()
return wb.IsVisible() && !wb.StateIs(states.Disabled) && wb.AbilityIs(abilities.Focusable)
})
em.setFocusEvent(next)
em.setFocus(next)
return next != nil
}

Expand All @@ -991,7 +991,7 @@ func (em *Events) focusOnOrNext(foc Widget) bool {
return false
}
if wb.AbilityIs(abilities.Focusable) {
em.setFocusEvent(foc)
em.setFocus(foc)
return true
}
return em.FocusNextFrom(foc)
Expand All @@ -1009,7 +1009,7 @@ func (em *Events) focusOnOrPrev(foc Widget) bool {
return false
}
if wb.AbilityIs(abilities.Focusable) {
em.setFocusEvent(foc)
em.setFocus(foc)
return true
}
return em.focusPrevFrom(foc)
Expand All @@ -1031,7 +1031,7 @@ func (em *Events) focusPrevFrom(from Widget) bool {
wb := w.AsWidget()
return wb.IsVisible() && !wb.StateIs(states.Disabled) && wb.AbilityIs(abilities.Focusable)
})
em.setFocusEvent(prev)
em.setFocus(prev)
return prev != nil
}

Expand Down Expand Up @@ -1072,7 +1072,7 @@ func (em *Events) activateStartFocus() bool {
em.focusFirst()
} else {
// fmt.Println("start focus on:", sf)
em.setFocusEvent(sf)
em.setFocus(sf)
}
return true
}
Expand Down
2 changes: 1 addition & 1 deletion core/filepicker.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func (fp *FilePicker) Init() {
case keymap.Search:
e.SetHandled()
sf := fp.selectField
sf.SetFocusEvent()
sf.SetFocus()
}
})

Expand Down
2 changes: 1 addition & 1 deletion core/frame.go
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ func (fr *Frame) focusOnName(e events.Event) bool {
if focel != nil {
em := fr.Events()
if em != nil {
em.setFocusEvent(focel.(Widget)) // this will also scroll by default!
em.setFocus(focel.(Widget)) // this will also scroll by default!
}
fr.focusNameLast = focel
return true
Expand Down
4 changes: 2 additions & 2 deletions core/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,7 @@ func (lb *ListBase) MakeGrid(p *tree.Plan, maker func(p *tree.Plan)) {
s.Min.Y.Em(6)
})
oc := func(e events.Event) {
lb.SetFocusEvent()
lb.SetFocus()
row, _, isValid := w.indexFromPixel(e.Pos())
if isValid {
lb.updateSelectRow(row, e.SelectMode())
Expand Down Expand Up @@ -868,7 +868,7 @@ func (lb *ListBase) RowGrabFocus(row int) *WidgetBase {
return w
}
lb.InFocusGrab = true
w.SetFocusEvent()
w.SetFocus()
lb.InFocusGrab = false
return w
}
Expand Down
2 changes: 1 addition & 1 deletion core/mainstage.go
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ func (st *Stage) runDialog() *Stage {

// if our main stages are nil, we wait until our context is shown and then try again
if ctx.Scene.Stage == nil || ctx.Scene.Stage.Mains == nil {
ctx.OnShow(func(e events.Event) {
ctx.Defer(func() {
st.runDialog()
})
return st
Expand Down
1 change: 1 addition & 0 deletions core/pages.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ func (pg *Pages) Init() {
}
pg.page = pg.Page
fun(pg)
pg.DeferShown()
})
}

Expand Down
2 changes: 1 addition & 1 deletion core/popupstage.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func (st *Stage) runPopup() *Stage {
// if our context stage is nil, we wait until
// our context is shown and then try again
if ctx.Scene.Stage == nil {
ctx.OnShow(func(e events.Event) {
ctx.Defer(func() {
st.runPopup()
})
return st
Expand Down
59 changes: 49 additions & 10 deletions core/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"cogentcore.org/core/base/profile"
"cogentcore.org/core/colors"
"cogentcore.org/core/colors/cam/hct"
"cogentcore.org/core/events"
"cogentcore.org/core/math32"
"cogentcore.org/core/styles"
"cogentcore.org/core/tree"
Expand Down Expand Up @@ -213,12 +214,8 @@ func (sc *Scene) doUpdate() bool {
}

if sc.showIter == sceneShowIters { // end of first pass
sc.showIter++
if !sc.hasFlag(sceneContentSizing) {
sc.Events.activateStartFocus()
}
sc.showIter++ // just go 1 past the iters cutoff
}

return true
}

Expand Down Expand Up @@ -398,8 +395,51 @@ func (wb *WidgetBase) renderChildren() {
})
}

////////////////////////////////////////////////////////////////////////////////
// Standard Box Model rendering
//////// Defer

// Defer adds a function to [WidgetBase.Deferred] that will be called after the next
// [Scene] update/render, including on the initial Scene render. After the function
// is called, it is removed and not called again. In the function, sending events
// etc will work as expected.
func (wb *WidgetBase) Defer(fun func()) {
wb.Deferred = append(wb.Deferred, fun)
if wb.Scene != nil {
wb.Scene.setFlag(true, sceneHasDeferred)
}
}

// runDeferred runs deferred functions on all widgets in the scene.
func (sc *Scene) runDeferred() {
sc.WidgetWalkDown(func(cw Widget, cwb *WidgetBase) bool {
for _, f := range cwb.Deferred {
f()
}
cwb.Deferred = nil
return tree.Continue
})
}

// DeferShown adds a [WidgetBase.Defer] function to call [WidgetBase.Shown]
// and activate [WidgetBase.StartFocus]. For example, this is called in [Tabs]
// and [Pages] when a tab/page is newly shown, so that elements can perform
// [WidgetBase.OnShow] updating as needed.
func (wb *WidgetBase) DeferShown() {
wb.Defer(func() {
wb.Shown()
wb.Scene.Events.activateStartFocus()
})
}

// Shown sends [events.Show] to all widgets from this one down. Also see
// [WidgetBase.DeferShown].
func (wb *WidgetBase) Shown() {
kkoreilly marked this conversation as resolved.
Show resolved Hide resolved
wb.WidgetWalkDown(func(cw Widget, cwb *WidgetBase) bool {
cwb.Send(events.Show)
return tree.Continue
})
}

//////// Standard Box Model rendering

// RenderBoxGeom renders a box with the given geometry.
func (wb *WidgetBase) RenderBoxGeom(pos math32.Vector2, sz math32.Vector2, bs styles.Border) {
Expand All @@ -413,8 +453,7 @@ func (wb *WidgetBase) RenderStandardBox() {
wb.Scene.PaintContext.DrawStandardBox(&wb.Styles, pos, sz, wb.parentActualBackground())
}

//////////////////////////////////////////////////////////////////
// Widget position functions
//////// Widget position functions

// PointToRelPos translates a point in Scene pixel coords
// into relative position within node, based on the Content BBox
Expand Down Expand Up @@ -445,7 +484,7 @@ func (wb *WidgetBase) winPos(x, y float32) image.Point {
return pt
}

// Profiling and Benchmarking, controlled by settings app bar:
//////// Profiling and Benchmarking, controlled by settings app bar

// ProfileToggle turns profiling on or off, which does both
// targeted profiling and global CPU and memory profiling.
Expand Down
2 changes: 1 addition & 1 deletion core/renderwindow.go
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ func (w *renderWindow) handleWindowEvents(e events.Event) {
rc.unlock() // one case where we need to break lock
w.renderWindow()
rc.lock()
w.mains.sendShowEvents()
w.mains.runDeferred() // note: must be outside of locks in renderWindow

case events.WindowResize:
e.SetHandled()
Expand Down
3 changes: 3 additions & 0 deletions core/scene.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ const (
// sceneNeedsLayout is whether the Scene needs a new layout pass.
sceneNeedsLayout

// sceneHasDeferred is whether the Scene has elements with Deferred functions.
sceneHasDeferred

// sceneImageUpdated indicates that the Scene's image has been updated
// e.g., due to a render or a resize. This is reset by the
// global [RenderWindow] rendering pass, so it knows whether it needs to
Expand Down
24 changes: 13 additions & 11 deletions core/stages.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ import (
"sync"

"cogentcore.org/core/base/ordmap"
"cogentcore.org/core/events"
"cogentcore.org/core/math32"
"cogentcore.org/core/tree"
)

// stages manages a stack of [Stage]s.
Expand Down Expand Up @@ -245,25 +243,29 @@ func (sm *stages) windowStage() *Stage {
return nil
}

func (sm *stages) sendShowEvents() {
func (sm *stages) runDeferred() {
for _, kv := range sm.stack.Order {
st := kv.Value
if st.Scene == nil {
continue
}
sc := st.Scene
if sc.hasFlag(sceneContentSizing) {
continue
}
if sc.hasFlag(sceneHasDeferred) {
sc.setFlag(false, sceneHasDeferred)
sc.runDeferred()
}

if sc.showIter == sceneShowIters+1 {
sc.showIter++
if !sc.hasFlag(sceneHasShown) {
if !sc.hasFlag(sceneContentSizing) {
sc.Events.activateStartFocus()
}
sc.setFlag(true, sceneHasShown)
// profile.Profiling = true
// pr := profile.Start("send show")
sc.WidgetWalkDown(func(cw Widget, cwb *WidgetBase) bool {
cwb.Send(events.Show)
return tree.Continue
})
// pr.End()
// profile.Report(time.Millisecond)
sc.Shown()
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion core/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,7 @@ func (tb *Table) RowGrabFocus(row int) *WidgetBase {
for fli := 0; fli < tb.numVisibleFields; fli++ {
w := lg.Child(ridx + idxOff + fli).(Widget).AsWidget()
if w.CanFocus() {
w.SetFocusEvent()
w.SetFocus()
return w
}
}
Expand Down
1 change: 1 addition & 0 deletions core/tabs.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ func (ts *Tabs) SelectTabIndex(idx int) *Frame {
tab.SetSelected(true)
fr.StackTop = idx
fr.Update()
frame.DeferShown()
ts.mu.Unlock()
return frame
}
Expand Down
2 changes: 1 addition & 1 deletion core/text.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ func (tx *Text) Init() {
})
tx.OnDoubleClick(func(e events.Event) {
tx.SetSelected(true)
tx.SetFocus()
tx.SetFocusQuiet()
})
tx.OnFocusLost(func(e events.Event) {
tx.SetSelected(false)
Expand Down
Loading