shiny/widget/node: generalize MarkNeedsPaint to Marks.

An upcoming change will distinguish between 'base' and 'top' paint
passes.

Change-Id: I8e2fcc97c1b7d90302c8f8ad8b3b89ab3bfc0e11
Reviewed-on: https://go-review.googlesource.com/24873
Reviewed-by: David Crawshaw <crawshaw@golang.org>
This commit is contained in:
Nigel Tao 2016-07-13 11:00:18 +10:00
Родитель a16ab4a434
Коммит f677a3c455
7 изменённых файлов: 64 добавлений и 23 удалений

Просмотреть файл

@ -56,7 +56,7 @@ func (w *custom) OnInputEvent(e interface{}, origin image.Point) node.EventHandl
if w.index == len(uniforms) {
w.index = 0
}
w.MarkNeedsPaint()
w.Mark(node.MarkNeedsPaint)
}
return node.Handled
}

Просмотреть файл

@ -44,7 +44,7 @@ func (w *Image) Measure(t *theme.Theme) {
}
func (w *Image) Paint(t *theme.Theme, dst *image.RGBA, origin image.Point) {
w.NeedsPaint = false
w.Marks.UnmarkNeedsPaint()
if w.Src == nil {
return
}

Просмотреть файл

@ -40,7 +40,7 @@ func (w *Label) Measure(t *theme.Theme) {
}
func (w *Label) Paint(t *theme.Theme, dst *image.RGBA, origin image.Point) {
w.NeedsPaint = false
w.Marks.UnmarkNeedsPaint()
dst = dst.SubImage(w.Rect.Add(origin)).(*image.RGBA)
if dst.Bounds().Empty() {
return

Просмотреть файл

@ -98,6 +98,16 @@ type Node interface {
// smaller dst images?
Paint(t *theme.Theme, dst *image.RGBA, origin image.Point)
// Mark adds the given marks to this node. It calls OnChildMarked on its
// parent if new marks were added.
Mark(m Marks)
// OnChildMarked handles a child being given new marks. By default, marks
// are propagated up the node tree towards the root. For example, a child
// being marked for needing paint will cause the parent being marked for
// needing paint.
OnChildMarked(child Node, newMarks Marks)
// OnInputEvent handles a key, mouse, touch or gesture event.
//
// origin is the parent widget's origin with respect to the event origin;
@ -107,9 +117,6 @@ type Node interface {
// TODO: other OnXxxEvent methods?
// MarkNeedsPaint marks this node (and its ancestors) that their previous
// Paint calls may no longer depict their current state.
MarkNeedsPaint()
}
// LeafEmbed is designed to be embedded in struct types for nodes with no
@ -127,9 +134,11 @@ func (m *LeafEmbed) Measure(t *theme.Theme) { m.MeasuredSize = image.Point{} }
func (m *LeafEmbed) Layout(t *theme.Theme) {}
func (m *LeafEmbed) Paint(t *theme.Theme, dst *image.RGBA, origin image.Point) {
m.NeedsPaint = false
m.Marks.UnmarkNeedsPaint()
}
func (m *LeafEmbed) OnChildMarked(child Node, newMarks Marks) {}
func (m *LeafEmbed) OnInputEvent(e interface{}, origin image.Point) EventHandled { return NotHandled }
// ShellEmbed is designed to be embedded in struct types for nodes with at most
@ -162,12 +171,16 @@ func (m *ShellEmbed) Layout(t *theme.Theme) {
}
func (m *ShellEmbed) Paint(t *theme.Theme, dst *image.RGBA, origin image.Point) {
m.NeedsPaint = false
m.Marks.UnmarkNeedsPaint()
if c := m.FirstChild; c != nil {
c.Wrapper.Paint(t, dst, origin.Add(m.Rect.Min))
}
}
func (m *ShellEmbed) OnChildMarked(child Node, newMarks Marks) {
m.Mark(newMarks)
}
func (m *ShellEmbed) OnInputEvent(e interface{}, origin image.Point) EventHandled {
if c := m.FirstChild; c != nil {
return c.Wrapper.OnInputEvent(e, origin.Add(m.Rect.Min))
@ -205,13 +218,17 @@ func (m *ContainerEmbed) Layout(t *theme.Theme) {
}
func (m *ContainerEmbed) Paint(t *theme.Theme, dst *image.RGBA, origin image.Point) {
m.NeedsPaint = false
m.Marks.UnmarkNeedsPaint()
origin = origin.Add(m.Rect.Min)
for c := m.FirstChild; c != nil; c = c.NextSibling {
c.Wrapper.Paint(t, dst, origin)
}
}
func (m *ContainerEmbed) OnChildMarked(child Node, newMarks Marks) {
m.Mark(newMarks)
}
func (m *ContainerEmbed) OnInputEvent(e interface{}, origin image.Point) EventHandled {
origin = origin.Add(m.Rect.Min)
var p image.Point
@ -277,20 +294,14 @@ type Embed struct {
// buffer's origin.
Rect image.Rectangle
// NeedsPaint is whether previous Paint calls for this node (and,
// implicitly, its descendents) may no longer depict its current state.
NeedsPaint bool
// Marks are a bitfield of node state, such as whether it needs measure,
// layout or paint.
Marks Marks
}
func (m *Embed) Wrappee() *Embed { return m }
func (m *Embed) MarkNeedsPaint() {
for ; m != nil; m = m.Parent {
m.NeedsPaint = true
}
}
// TODO: should insert and remove call MarkNeedsPaint? MarkNeedsLayout?
// TODO: should insert and remove call Mark(MarkNeedsMeasureLayout | MarkNeedsPaint)?
func (m *Embed) insert(c, nextSibling Node) {
n := c.Wrappee()
@ -349,3 +360,33 @@ func (m *Embed) remove(c Node) {
n.PrevSibling = nil
n.NextSibling = nil
}
func (m *Embed) Mark(marks Marks) {
oldMarks := m.Marks
m.Marks |= marks
changedMarks := m.Marks ^ oldMarks
if changedMarks != 0 && m.Parent != nil {
m.Parent.Wrapper.OnChildMarked(m.Wrapper, changedMarks)
}
}
// Marks are a bitfield of node state, such as whether it needs measure, layout
// or paint.
type Marks uint32
const (
// MarkNeedsMeasureLayout marks this node as needing Measure and Layout
// calls.
MarkNeedsMeasureLayout = Marks(1 << 0)
// TODO: use this.
// MarkNeedsPaint marks this node as needing a Paint call.
MarkNeedsPaint = Marks(1 << 1)
// TODO: have separate notions of 'base' and 'top' paint passes.
)
func (m Marks) NeedsMeasureLayout() bool { return m&MarkNeedsMeasureLayout != 0 }
func (m Marks) NeedsPaint() bool { return m&MarkNeedsPaint != 0 }
func (m *Marks) UnmarkNeedsMeasureLayout() { *m &^= MarkNeedsMeasureLayout }
func (m *Marks) UnmarkNeedsPaint() { *m &^= MarkNeedsPaint }

Просмотреть файл

@ -65,7 +65,7 @@ func (w *Text) Layout(t *theme.Theme) {
}
func (w *Text) Paint(t *theme.Theme, dst *image.RGBA, origin image.Point) {
w.NeedsPaint = false
w.Marks.UnmarkNeedsPaint()
dst = dst.SubImage(w.Rect.Add(origin)).(*image.RGBA)
if dst.Bounds().Empty() {
return

Просмотреть файл

@ -30,7 +30,7 @@ func NewUniform(c color.Color) *Uniform {
}
func (w *Uniform) Paint(t *theme.Theme, dst *image.RGBA, origin image.Point) {
w.NeedsPaint = false
w.Marks.UnmarkNeedsPaint()
if w.Uniform.C == nil {
return
}

Просмотреть файл

@ -149,13 +149,13 @@ func RunWindow(s screen.Screen, root node.Node, opts *RunWindowOptions) error {
root.Measure(t)
root.Wrappee().Rect = e.Bounds()
root.Layout(t)
// TODO: call MarkNeedsPaint?
// TODO: call Mark(node.MarkNeedsPaint)?
case error:
return e
}
if !paintPending && root.Wrappee().NeedsPaint {
if !paintPending && root.Wrappee().Marks.NeedsPaint() {
paintPending = true
w.Send(paint.Event{})
}