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 }