github.com/jmigpin/editor@v1.6.0/util/uiutil/widget/node.go (about)

     1  package widget
     2  
     3  import (
     4  	"container/list"
     5  	"fmt"
     6  	"image"
     7  	"image/color"
     8  	"strings"
     9  
    10  	"github.com/jmigpin/editor/util/fontutil"
    11  	"github.com/jmigpin/editor/util/imageutil"
    12  	"github.com/jmigpin/editor/util/uiutil/event"
    13  )
    14  
    15  type Node interface {
    16  	fullNode() // ensure that EmbNode can't be directly assigned to a Node
    17  
    18  	Embed() *EmbedNode
    19  
    20  	InsertBefore(n Node, mark *EmbedNode)
    21  	Append(n ...Node)
    22  	Remove(child Node)
    23  
    24  	Measure(hint image.Point) image.Point
    25  
    26  	LayoutMarked()
    27  	LayoutTree()
    28  	Layout() // set childs bounds, don't call childs layout
    29  	ChildsLayoutTree()
    30  
    31  	PaintMarked() image.Rectangle
    32  	PaintTree() bool
    33  	PaintBase() // pre-paint step, useful for widgets with a pre-paint stage
    34  	Paint()
    35  	ChildsPaintTree()
    36  
    37  	OnThemeChange()
    38  	OnChildMarked(child Node, newMarks Marks)
    39  	OnInputEvent(ev interface{}, p image.Point) event.Handled
    40  }
    41  
    42  //----------
    43  
    44  // Doesn't allow embed to be assigned to a Node directly, which prevents a range of programming mistakes. This is the node other widgets should inherit from.
    45  type ENode struct {
    46  	EmbedNode
    47  }
    48  
    49  func (ENode) fullNode() {}
    50  
    51  //----------
    52  
    53  type EmbedNode struct {
    54  	Bounds  image.Rectangle
    55  	Cursor  event.Cursor
    56  	Wrapper Node
    57  	Parent  *EmbedNode
    58  
    59  	marks  Marks
    60  	childs list.List
    61  	elem   *list.Element
    62  
    63  	theme Theme
    64  }
    65  
    66  //----------
    67  
    68  func (en *EmbedNode) Embed() *EmbedNode {
    69  	return en
    70  }
    71  
    72  // Only the root node should need to set the wrapper explicitly.
    73  func (en *EmbedNode) SetWrapperForRoot(n Node) {
    74  	en.Wrapper = n
    75  }
    76  
    77  //----------
    78  
    79  // If a node wants its InsertBefore implementation to be used, the wrapper must be set.
    80  func (en *EmbedNode) Append(nodes ...Node) {
    81  	for _, n := range nodes {
    82  		if en.Wrapper != nil {
    83  			en.Wrapper.InsertBefore(n, nil)
    84  		} else {
    85  			en.InsertBefore(n, nil)
    86  		}
    87  	}
    88  }
    89  
    90  func (en *EmbedNode) InsertBefore(child Node, next *EmbedNode) {
    91  	childe := child.Embed()
    92  
    93  	if childe == en {
    94  		panic("inserting into itself")
    95  	}
    96  	if childe.Parent != nil {
    97  		panic("element already has a parent")
    98  	}
    99  
   100  	// insert in list and get element
   101  	var elem *list.Element
   102  	if next == nil {
   103  		elem = en.childs.PushBack(childe)
   104  	} else {
   105  		// ensure next element is a child of this node
   106  		if next.Parent != en {
   107  			panic("next is not a child of this node")
   108  		}
   109  
   110  		elem = en.childs.InsertBefore(childe, next.elem)
   111  	}
   112  	if elem == nil {
   113  		panic("element not inserted")
   114  	}
   115  
   116  	childe.elem = elem
   117  	childe.Parent = en
   118  	childe.Wrapper = child // auto set the wrapper
   119  
   120  	en.MarkNeedsLayoutAndPaint()
   121  
   122  	childe.themeChangeCallback()
   123  }
   124  
   125  //----------
   126  
   127  func (en *EmbedNode) Remove(child Node) {
   128  	childe := child.Embed()
   129  	if childe.Parent != en {
   130  		panic("not a child of this node")
   131  	}
   132  	en.childs.Remove(childe.elem)
   133  	childe.elem = nil
   134  	childe.Parent = nil
   135  
   136  	en.MarkNeedsLayoutAndPaint()
   137  }
   138  
   139  //----------
   140  
   141  // Doesn't use Remove/Insert. So implementing nodes overriding those will not see their functions used.
   142  func (en *EmbedNode) Swap(u Node) {
   143  	eu := u.Embed()
   144  	if en.Parent != eu.Parent {
   145  		panic("nodes don't have the same parent")
   146  	}
   147  	l := &en.Parent.childs
   148  	e1 := en.elem
   149  	e2 := eu.elem
   150  	if e1.Next() == e2 {
   151  		l.MoveAfter(e1, e2)
   152  	} else if e2.Next() == e1 {
   153  		l.MoveAfter(e2, e1)
   154  	} else {
   155  		prev := e1.Prev()
   156  		l.MoveAfter(e1, e2)
   157  		if prev == nil {
   158  			l.MoveToFront(e2)
   159  		} else {
   160  			l.MoveAfter(e2, prev)
   161  		}
   162  	}
   163  }
   164  
   165  //----------
   166  
   167  func (en *EmbedNode) ChildsLen() int {
   168  	return en.childs.Len()
   169  }
   170  
   171  //----------
   172  
   173  func elemEmbed(e *list.Element) *EmbedNode {
   174  	if e == nil {
   175  		return nil
   176  	}
   177  	return e.Value.(*EmbedNode)
   178  }
   179  func elemWrapper(e *list.Element) Node {
   180  	if e == nil {
   181  		return nil
   182  	}
   183  	return e.Value.(*EmbedNode).Wrapper
   184  }
   185  
   186  //----------
   187  
   188  func (en *EmbedNode) FirstChild() *EmbedNode {
   189  	return elemEmbed(en.childs.Front())
   190  }
   191  func (en *EmbedNode) LastChild() *EmbedNode {
   192  	return elemEmbed(en.childs.Back())
   193  }
   194  func (en *EmbedNode) NextSibling() *EmbedNode {
   195  	return elemEmbed(en.elem.Next())
   196  }
   197  func (en *EmbedNode) PrevSibling() *EmbedNode {
   198  	return elemEmbed(en.elem.Prev())
   199  }
   200  
   201  //----------
   202  
   203  func (en *EmbedNode) FirstChildWrapper() Node {
   204  	return elemWrapper(en.childs.Front())
   205  }
   206  func (en *EmbedNode) LastChildWrapper() Node {
   207  	return elemWrapper(en.childs.Back())
   208  }
   209  func (en *EmbedNode) NextSiblingWrapper() Node {
   210  	return elemWrapper(en.elem.Next())
   211  }
   212  func (en *EmbedNode) PrevSiblingWrapper() Node {
   213  	return elemWrapper(en.elem.Prev())
   214  }
   215  
   216  //----------
   217  
   218  func (en *EmbedNode) Iterate(f func(*EmbedNode) bool) {
   219  	for e := en.childs.Front(); e != nil; e = e.Next() {
   220  		if !f(elemEmbed(e)) {
   221  			break
   222  		}
   223  	}
   224  }
   225  func (en *EmbedNode) IterateReverse(f func(*EmbedNode) bool) {
   226  	for e := en.childs.Back(); e != nil; e = e.Prev() {
   227  		if !f(elemEmbed(e)) {
   228  			break
   229  		}
   230  	}
   231  }
   232  func (en *EmbedNode) IterateWrappers(f func(Node) bool) {
   233  	for e := en.childs.Front(); e != nil; e = e.Next() {
   234  		if !f(elemWrapper(e)) {
   235  			break
   236  		}
   237  	}
   238  }
   239  func (en *EmbedNode) IterateWrappersReverse(f func(Node) bool) {
   240  	for e := en.childs.Back(); e != nil; e = e.Prev() {
   241  		if !f(elemWrapper(e)) {
   242  			break
   243  		}
   244  	}
   245  }
   246  
   247  //----------
   248  
   249  // Iterate2 family functions: iterate all without break possibility.
   250  
   251  func (en *EmbedNode) Iterate2(f func(*EmbedNode)) {
   252  	for e := en.childs.Front(); e != nil; e = e.Next() {
   253  		f(elemEmbed(e))
   254  	}
   255  }
   256  func (en *EmbedNode) IterateReverse2(f func(*EmbedNode)) {
   257  	for e := en.childs.Back(); e != nil; e = e.Prev() {
   258  		f(elemEmbed(e))
   259  	}
   260  }
   261  func (en *EmbedNode) IterateWrappers2(f func(Node)) {
   262  	for e := en.childs.Front(); e != nil; e = e.Next() {
   263  		f(elemWrapper(e))
   264  	}
   265  }
   266  func (en *EmbedNode) IterateWrappersReverse2(f func(Node)) {
   267  	for e := en.childs.Back(); e != nil; e = e.Prev() {
   268  		f(elemWrapper(e))
   269  	}
   270  }
   271  
   272  //----------
   273  
   274  func (en *EmbedNode) ChildsWrappers() []Node {
   275  	w := []Node{}
   276  	en.IterateWrappers2(func(c Node) {
   277  		w = append(w, c)
   278  	})
   279  	return w
   280  }
   281  
   282  //----------
   283  
   284  func (en *EmbedNode) HasAnyMarks(m Marks) bool {
   285  	return en.marks.HasAny(m)
   286  }
   287  
   288  func (en *EmbedNode) AddMarks(m Marks) {
   289  	en.markUp(m, nil, 0)
   290  }
   291  
   292  func (en *EmbedNode) RemoveMarks(m Marks) {
   293  	// direcly non-removable marks
   294  	u := MarkNeedsPaint | MarkNeedsLayout |
   295  		MarkChildNeedsPaint | MarkChildNeedsLayout
   296  	if m.HasAny(u) {
   297  		panic(fmt.Sprintf("mark not directly removable: %v", u))
   298  	}
   299  	en.marks.Remove(m)
   300  }
   301  
   302  //----------
   303  
   304  func (en *EmbedNode) markUp(m Marks, child Node, childChangedMarks Marks) {
   305  	old := en.marks
   306  	en.marks |= m
   307  	changed := en.marks ^ old
   308  
   309  	// this node is a parent, run callback as soon as it gets marked (now)
   310  	if en.Wrapper != nil && child != nil && childChangedMarks != 0 {
   311  		en.Wrapper.OnChildMarked(child, childChangedMarks)
   312  	}
   313  
   314  	if en.Parent != nil && changed != 0 {
   315  		// setup marks to add to parent
   316  		var u Marks
   317  		if changed.HasAny(MarkNeedsPaint | MarkChildNeedsPaint) {
   318  			u.Add(MarkChildNeedsPaint)
   319  		}
   320  		if changed.HasAny(MarkNeedsLayout | MarkChildNeedsLayout) {
   321  			u.Add(MarkChildNeedsLayout)
   322  		}
   323  
   324  		// mark parent
   325  		en.Parent.markUp(u, en.Wrapper, changed)
   326  	}
   327  }
   328  
   329  func (en *EmbedNode) OnChildMarked(child Node, newMarks Marks) {
   330  }
   331  
   332  //----------
   333  
   334  func (en *EmbedNode) MarkNeedsLayout() {
   335  	en.AddMarks(MarkNeedsLayout)
   336  }
   337  func (en *EmbedNode) MarkNeedsPaint() {
   338  	en.AddMarks(MarkNeedsPaint)
   339  }
   340  func (en *EmbedNode) MarkNeedsLayoutAndPaint() {
   341  	en.AddMarks(MarkNeedsLayout | MarkNeedsPaint)
   342  }
   343  
   344  //----------
   345  
   346  func (en *EmbedNode) TreeNeedsPaint() bool {
   347  	return en.HasAnyMarks(MarkNeedsPaint | MarkChildNeedsPaint)
   348  }
   349  
   350  func (en *EmbedNode) TreeNeedsLayout() bool {
   351  	return en.HasAnyMarks(MarkNeedsLayout | MarkChildNeedsLayout)
   352  }
   353  
   354  //----------
   355  
   356  func (en *EmbedNode) Measure(hint image.Point) image.Point {
   357  	var max image.Point
   358  	en.IterateWrappers2(func(c Node) {
   359  		m := c.Measure(hint)
   360  		max = imageutil.MaxPoint(max, m)
   361  	})
   362  	return max
   363  }
   364  
   365  //----------
   366  
   367  func (en *EmbedNode) LayoutMarked() {
   368  	if en.HasAnyMarks(MarkNeedsLayout) {
   369  		en.Wrapper.LayoutTree()
   370  	} else if en.HasAnyMarks(MarkChildNeedsLayout) {
   371  		en.marks.Remove(MarkChildNeedsLayout)
   372  		en.IterateWrappers2(func(c Node) {
   373  			c.LayoutMarked()
   374  		})
   375  	}
   376  }
   377  
   378  //var depth int
   379  
   380  func (en *EmbedNode) LayoutTree() {
   381  	//fmt.Printf("%*s layouttree %T %v\n", depth*4, "", en.Wrapper, en.Bounds)
   382  	//depth++
   383  	//defer func() { depth-- }()
   384  
   385  	en.marks.Remove(MarkNeedsLayout | MarkChildNeedsLayout)
   386  
   387  	// keep/set default bounds before layouting childs
   388  	cbm := map[*EmbedNode]image.Rectangle{}
   389  	en.Iterate2(func(c *EmbedNode) {
   390  		cbm[c] = c.Bounds
   391  		c.Bounds = en.Bounds // parent bounds
   392  
   393  		// set to empty if not visible
   394  		if c.HasAnyMarks(MarkForceZeroBounds) {
   395  			c.Bounds = image.Rectangle{}
   396  		}
   397  	})
   398  
   399  	en.Wrapper.Layout()
   400  	en.Wrapper.ChildsLayoutTree()
   401  
   402  	// auto detect if it needs paint if bounds change
   403  	en.Iterate2(func(c *EmbedNode) {
   404  		if cb, ok := cbm[c]; ok && c.Bounds != cb {
   405  			c.MarkNeedsPaint()
   406  		}
   407  	})
   408  }
   409  
   410  func (en *EmbedNode) Layout() {
   411  }
   412  
   413  func (en *EmbedNode) ChildsLayoutTree() {
   414  	en.IterateWrappers2(func(c Node) {
   415  		c.LayoutTree()
   416  	})
   417  }
   418  
   419  //----------
   420  
   421  func (en *EmbedNode) PaintMarked() image.Rectangle {
   422  	u := image.Rectangle{}
   423  	if en.HasAnyMarks(MarkNeedsPaint) {
   424  		if en.Wrapper.PaintTree() {
   425  			u = u.Union(en.Bounds)
   426  		}
   427  	} else if en.HasAnyMarks(MarkChildNeedsPaint) {
   428  		en.marks.Remove(MarkChildNeedsPaint)
   429  		en.IterateWrappers2(func(c Node) {
   430  			r := c.PaintMarked()
   431  			u = u.Union(r)
   432  		})
   433  	}
   434  	return u
   435  }
   436  
   437  func (en *EmbedNode) PaintTree() bool {
   438  	en.marks.Remove(MarkNeedsPaint | MarkChildNeedsPaint)
   439  
   440  	if en.HasAnyMarks(MarkNotPaintable | MarkForceZeroBounds) {
   441  		return false
   442  	}
   443  
   444  	en.Wrapper.PaintBase()
   445  	en.Wrapper.Paint()
   446  	en.Wrapper.ChildsPaintTree()
   447  	return true
   448  }
   449  
   450  func (en *EmbedNode) PaintBase() {
   451  }
   452  
   453  func (en *EmbedNode) Paint() {
   454  }
   455  
   456  func (en *EmbedNode) ChildsPaintTree() {
   457  	en.IterateWrappers2(func(c Node) {
   458  		c.PaintTree()
   459  	})
   460  }
   461  
   462  //----------
   463  
   464  func (en *EmbedNode) OnInputEvent(ev interface{}, p image.Point) event.Handled {
   465  	return false
   466  }
   467  
   468  //----------
   469  
   470  func (en *EmbedNode) SetTheme(t Theme) {
   471  	defer en.themeChangeCallback()
   472  	defer en.MarkNeedsPaint()  // possible palette change/update
   473  	defer en.MarkNeedsLayout() // possible font change
   474  
   475  	en.theme = t
   476  }
   477  
   478  func (en *EmbedNode) Theme() *Theme {
   479  	return &en.theme
   480  }
   481  
   482  //----------
   483  
   484  func (en *EmbedNode) ThemePalette() Palette {
   485  	return en.theme.Palette
   486  }
   487  
   488  func (en *EmbedNode) SetThemePalette(p Palette) {
   489  	defer en.themeChangeCallback()
   490  	defer en.MarkNeedsPaint()
   491  
   492  	en.theme.SetPalette(p)
   493  }
   494  
   495  func (en *EmbedNode) SetThemePaletteColor(name string, c color.Color) {
   496  	defer en.themeChangeCallback()
   497  	defer en.MarkNeedsPaint()
   498  
   499  	en.theme.SetPaletteColor(name, c)
   500  }
   501  
   502  func (en *EmbedNode) SetThemePaletteNamePrefix(prefix string) {
   503  	defer en.themeChangeCallback()
   504  	defer en.MarkNeedsPaint()
   505  
   506  	en.theme.SetPaletteNamePrefix(prefix)
   507  }
   508  
   509  //----------
   510  
   511  func (en *EmbedNode) TreeThemePaletteColor(name string) color.Color {
   512  	if c, ok := en.treeThemePaletteColor2(name); ok {
   513  		return c
   514  	}
   515  	// last resort: a color that is not white/black to help debug
   516  	return cint(0xff0000)
   517  }
   518  
   519  func (en *EmbedNode) treeThemePaletteColor2(name string) (color.Color, bool) {
   520  	if !strings.HasPrefix(name, en.theme.PaletteNamePrefix) {
   521  		s := en.theme.PaletteNamePrefix + name
   522  		if c, ok := en.treeThemePaletteColor2(s); ok {
   523  			return c, true
   524  		}
   525  	}
   526  	if c, ok := en.theme.Palette[name]; ok {
   527  		return c, true
   528  	}
   529  	if en.Parent != nil {
   530  		if c, ok := en.Parent.treeThemePaletteColor2(name); ok {
   531  			return c, true
   532  		}
   533  	}
   534  	// at root tree (parent is nil) and not found, try default palette
   535  	if c, ok := DefaultPalette[name]; ok {
   536  		return c, true
   537  	}
   538  	return nil, false
   539  }
   540  
   541  //----------
   542  
   543  func (en *EmbedNode) SetThemeFontFace(ff *fontutil.FontFace) {
   544  	defer en.themeChangeCallback()
   545  	defer en.MarkNeedsLayout()
   546  
   547  	en.theme.SetFontFace(ff)
   548  }
   549  
   550  func (en *EmbedNode) TreeThemeFontFace() *fontutil.FontFace {
   551  	for n := en; n != nil; n = n.Parent {
   552  		if n.theme.FontFace != nil {
   553  			return n.theme.FontFace
   554  		}
   555  	}
   556  	return fontutil.DefaultFontFace()
   557  }
   558  
   559  //----------
   560  
   561  func (en *EmbedNode) themeChangeCallback() {
   562  	if en.Wrapper != nil {
   563  		en.Wrapper.OnThemeChange()
   564  	}
   565  	en.Iterate2(func(c *EmbedNode) {
   566  		c.themeChangeCallback()
   567  	})
   568  }
   569  
   570  func (en *EmbedNode) OnThemeChange() {
   571  }
   572  
   573  //----------
   574  
   575  type Marks uint16
   576  
   577  func (m *Marks) Add(u Marks)        { *m |= u }
   578  func (m *Marks) Remove(u Marks)     { *m &^= u }
   579  func (m Marks) Mask(u Marks) Marks  { return m & u }
   580  func (m Marks) HasAny(u Marks) bool { return m.Mask(u) > 0 }
   581  
   582  //func (m *Marks) Modify(u Marks, v bool) {
   583  //	if v {
   584  //		m.Add(u)
   585  //	} else {
   586  //		m.Remove(u)
   587  //	}
   588  //}
   589  //func (m Marks) Changes(u Marks) Marks {
   590  //	old := m
   591  //	m |= u
   592  //	return m ^ old
   593  //}
   594  
   595  //----------
   596  
   597  const (
   598  	MarkNeedsPaint Marks = 1 << iota
   599  	MarkNeedsLayout
   600  
   601  	MarkChildNeedsPaint
   602  	MarkChildNeedsLayout
   603  
   604  	MarkPointerInside // mouseEnter/mouseLeave events
   605  	MarkNotDraggable  // won't emit mouseDrag events
   606  
   607  	MarkForceZeroBounds // sets bounds to zero (aka not visible)
   608  
   609  	MarkInBoundsHandlesEvent // helps with layer nodes keep events
   610  
   611  	// For transparent widgets that cross two or more other widgets (ex: a non visible separator handle). Improves on detecting if others need paint.
   612  	MarkNotPaintable
   613  )