github.com/jmigpin/editor@v1.6.0/ui/column.go (about)

     1  package ui
     2  
     3  import (
     4  	"image"
     5  
     6  	"github.com/jmigpin/editor/util/evreg"
     7  	"github.com/jmigpin/editor/util/uiutil/widget"
     8  )
     9  
    10  type Column struct {
    11  	*widget.BoxLayout
    12  	RowsLayout *widget.SplBg // exported to access sp values
    13  	Cols       *Columns
    14  	EvReg      evreg.Register
    15  
    16  	sep       *ColSeparator
    17  	colSquare *ColumnSquare
    18  	ui        *UI
    19  }
    20  
    21  func NewColumn(cols *Columns) *Column {
    22  	col := &Column{Cols: cols, ui: cols.Root.UI}
    23  	col.BoxLayout = widget.NewBoxLayout()
    24  
    25  	// separator
    26  	col.sep = NewColSeparator(col)
    27  	col.Append(col.sep)
    28  	col.SetChildFill(col.sep, false, true)
    29  
    30  	// when where are no rows, or the first row is pushed aside
    31  	noRows0 := widget.NewFreeLayout()
    32  	noRows := WrapInTopShadowOrSeparator(cols.Root.UI, noRows0)
    33  	{
    34  		noRows0.SetThemePaletteNamePrefix("column_norows_")
    35  		rect := widget.NewRectangle(col.ui)
    36  		rect.Size = image.Point{10000, 10000}
    37  		col.colSquare = NewColumnSquare(col)
    38  		noRows0.Append(rect, col.colSquare)
    39  	}
    40  
    41  	// rows layout
    42  	col.RowsLayout = widget.NewSplBg(noRows)
    43  	col.RowsLayout.Spl.YAxis = true
    44  	col.Append(col.RowsLayout)
    45  
    46  	return col
    47  }
    48  
    49  func (col *Column) Close() {
    50  	for _, r := range col.Rows() {
    51  		r.Close()
    52  	}
    53  	col.Cols.removeColumn(col)
    54  	col.Cols = nil
    55  	col.sep.Close()
    56  	col.EvReg.RunCallbacks(ColumnCloseEventId, &ColumnCloseEvent{col})
    57  }
    58  
    59  func (col *Column) NewRowBefore(next *Row) *Row {
    60  	row := NewRow(col)
    61  	col.insertRowBefore(row, next)
    62  	return row
    63  }
    64  
    65  func (col *Column) insertRowBefore(row, next *Row) {
    66  	var nexte *widget.EmbedNode
    67  	if next != nil {
    68  		nexte = next.Embed()
    69  	}
    70  
    71  	row.Col = col
    72  	col.RowsLayout.Spl.InsertBefore(row, nexte)
    73  
    74  	// resizing before laying out (previous row still has the old bounds)
    75  	col.ui.resizeRowToGoodSize(row)
    76  
    77  	// ensure up-to-date values now (ex: bounds, drawer.getpoint)
    78  	col.LayoutMarked()
    79  }
    80  
    81  func (col *Column) removeRow(row *Row) {
    82  	col.RowsLayout.Spl.Remove(row)
    83  }
    84  
    85  func (col *Column) Layout() {
    86  	tf := col.TreeThemeFontFace()
    87  	col.RowsLayout.Spl.MinimumChildSize = UIThemeUtil.RowMinimumHeight(tf)
    88  	col.colSquare.Size = UIThemeUtil.RowSquareSize(tf)
    89  
    90  	col.BoxLayout.Layout()
    91  }
    92  
    93  //----------
    94  
    95  func (col *Column) FirstChildRow() *Row {
    96  	u := col.RowsLayout.Spl.FirstChildWrapper()
    97  	if u == nil {
    98  		return nil
    99  	}
   100  	return u.(*Row)
   101  }
   102  func (col *Column) LastChildRow() *Row {
   103  	u := col.RowsLayout.Spl.LastChildWrapper()
   104  	if u == nil {
   105  		return nil
   106  	}
   107  	return u.(*Row)
   108  }
   109  
   110  //----------
   111  
   112  func (col *Column) Rows() []*Row {
   113  	u := make([]*Row, 0, col.RowsLayout.Spl.ChildsLen())
   114  	col.RowsLayout.Spl.IterateWrappers2(func(c widget.Node) {
   115  		u = append(u, c.(*Row))
   116  	})
   117  	return u
   118  }
   119  
   120  //----------
   121  
   122  func (col *Column) PointNextRow(p *image.Point) (*Row, bool) {
   123  	for _, r := range col.Rows() {
   124  		if p.Y < r.Bounds.Min.Y {
   125  			return r, true
   126  		}
   127  		if p.In(r.Bounds) {
   128  			return r.NextRow(), true
   129  		}
   130  	}
   131  	return nil, false
   132  }
   133  
   134  func (col *Column) PointNextRowExtra(p *image.Point) (*Row, bool) {
   135  	next, ok := col.PointNextRow(p)
   136  	if ok {
   137  		return next, true
   138  	}
   139  
   140  	first := col.FirstChildRow()
   141  	if first == nil {
   142  		return nil, true
   143  	}
   144  	last := col.LastChildRow()
   145  	if p.Y < first.Embed().Bounds.Min.Y {
   146  		return first, true
   147  	} else if p.Y > last.Embed().Bounds.Max.Y {
   148  		return nil, true
   149  	} else {
   150  		for _, r := range col.Rows() {
   151  			y0, y1 := r.Bounds.Min.Y, r.Bounds.Max.Y
   152  			if y0 <= p.Y && p.Y < y1 {
   153  				return r.NextRow(), true
   154  			}
   155  		}
   156  	}
   157  
   158  	return nil, false
   159  }
   160  
   161  //----------
   162  
   163  func (col *Column) resizeToPointWithSwap(p *image.Point) {
   164  	bounds := col.Cols.Root.Bounds
   165  	dx := float64(bounds.Dx())
   166  	perc := float64(p.Sub(bounds.Min).X) / dx
   167  
   168  	col.Cols.ColsLayout.Spl.ResizeWithMove(col, perc)
   169  }
   170  
   171  func (col *Column) resizeWithMoveJump(left bool, p *image.Point) {
   172  	jump := 20
   173  	if left {
   174  		jump *= -1
   175  	}
   176  
   177  	p2 := *p
   178  	p2.X += jump
   179  	col.resizeWithMoveToPoint(&p2)
   180  
   181  	// layout for accurate col.bounds to warp pointer
   182  	col.Cols.ColsLayout.Spl.Layout()
   183  
   184  	p3 := image.Point{col.Bounds.Min.X, p.Y}
   185  	col.ui.WarpPointer(p3)
   186  }
   187  
   188  func (col *Column) resizeWithMoveToPoint(p *image.Point) {
   189  	bounds := col.Cols.Root.Bounds
   190  	dx := float64(bounds.Dx())
   191  	perc := float64(p.Sub(bounds.Min).X) / dx
   192  
   193  	col.Cols.ColsLayout.Spl.ResizeWithMove(col, perc)
   194  }
   195  
   196  //----------
   197  
   198  const (
   199  	ColumnCloseEventId = iota
   200  )
   201  
   202  type ColumnCloseEvent struct {
   203  	Col *Column
   204  }