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

     1  package ui
     2  
     3  import (
     4  	"image"
     5  
     6  	"github.com/jmigpin/editor/util/drawutil/drawer4"
     7  	"github.com/jmigpin/editor/util/evreg"
     8  	"github.com/jmigpin/editor/util/uiutil/event"
     9  	"github.com/jmigpin/editor/util/uiutil/widget"
    10  )
    11  
    12  type Row struct {
    13  	*widget.BoxLayout
    14  	Toolbar  *RowToolbar
    15  	TextArea *TextArea
    16  	Col      *Column
    17  	EvReg    evreg.Register
    18  
    19  	ScrollArea *widget.ScrollArea
    20  	sep        *RowSeparator
    21  	ui         *UI
    22  }
    23  
    24  func NewRow(col *Column) *Row {
    25  	row := &Row{Col: col, ui: col.Cols.Root.UI}
    26  	row.BoxLayout = widget.NewBoxLayout()
    27  	row.YAxis = true
    28  
    29  	// row separator from other rows
    30  	row.sep = NewRowSeparator(row)
    31  	row.Append(row.sep)
    32  	row.SetChildFill(row.sep, true, false)
    33  
    34  	// toolbar
    35  	row.Toolbar = NewRowToolbar(row)
    36  	row.Append(row.Toolbar)
    37  	row.SetChildFlex(row.Toolbar, true, false)
    38  
    39  	// scrollarea with textarea
    40  	{
    41  		row.TextArea = NewTextArea(row.ui)
    42  		row.TextArea.SupportClickInsideSelection = true
    43  		row.TextArea.EnableCursorWordHighlight(true)
    44  		row.TextArea.EnableParenthesisMatch(true)
    45  		if d, ok := row.TextArea.Drawer.(*drawer4.Drawer); ok {
    46  			d.Opt.QuickMeasure = true // performance
    47  		}
    48  
    49  		row.ScrollArea = widget.NewScrollArea(row.ui, row.TextArea, false, true)
    50  		row.ScrollArea.LeftScroll = ScrollBarLeft
    51  
    52  		container := WrapInTopShadowOrSeparator(row.ui, row.ScrollArea)
    53  		row.Append(container)
    54  		row.SetChildFlex(container, true, true)
    55  	}
    56  
    57  	return row
    58  }
    59  
    60  //----------
    61  
    62  func (row *Row) Close() {
    63  	row.Col.removeRow(row)
    64  	row.Col = nil
    65  	row.sep.Close()
    66  	row.EvReg.RunCallbacks(RowCloseEventId, &RowCloseEvent{row})
    67  }
    68  
    69  //----------
    70  
    71  func (row *Row) OnChildMarked(child widget.Node, newMarks widget.Marks) {
    72  	// dynamic toolbar
    73  	if row.Toolbar != nil && row.Toolbar.HasAnyMarks(widget.MarkNeedsLayout) {
    74  		row.MarkNeedsLayout()
    75  	}
    76  }
    77  
    78  //----------
    79  
    80  func (row *Row) Layout() {
    81  	ff := row.Toolbar.TreeThemeFontFace()
    82  	row.ScrollArea.ScrollWidth = UIThemeUtil.GetScrollBarWidth(ff)
    83  	row.BoxLayout.Layout()
    84  }
    85  
    86  //----------
    87  
    88  func (row *Row) OnInputEvent(ev0 interface{}, p image.Point) event.Handled {
    89  	ev2 := &RowInputEvent{row, ev0}
    90  	row.EvReg.RunCallbacks(RowInputEventId, ev2)
    91  	return false
    92  }
    93  
    94  //----------
    95  
    96  func (row *Row) NextRow() *Row {
    97  	u := row.NextSiblingWrapper()
    98  	if u == nil {
    99  		return nil
   100  	}
   101  	return u.(*Row)
   102  }
   103  
   104  //----------
   105  
   106  func (row *Row) Maximize() {
   107  	col := row.Col
   108  	col.RowsLayout.Spl.MaximizeNode(row)
   109  }
   110  
   111  //----------
   112  
   113  func (row *Row) resizeWithMoveToPoint(p *image.Point) {
   114  	col, ok := row.Col.Cols.PointColumnExtra(p)
   115  	if !ok {
   116  		return
   117  	}
   118  
   119  	// move to another column
   120  	if col != row.Col {
   121  		next, ok := col.PointNextRowExtra(p)
   122  		if !ok {
   123  			next = nil
   124  		}
   125  		row.Col.removeRow(row)
   126  		col.insertRowBefore(row, next)
   127  	}
   128  
   129  	bounds := row.Col.Bounds
   130  	dy := float64(bounds.Dy())
   131  	perc := float64(p.Sub(bounds.Min).Y) / dy
   132  
   133  	row.Col.RowsLayout.Spl.ResizeWithMove(row, perc)
   134  }
   135  
   136  //----------
   137  
   138  func (row *Row) resizeWithPushJump(up bool, p *image.Point) {
   139  	jump := 40
   140  	if up {
   141  		jump *= -1
   142  	}
   143  
   144  	pad := p.Sub(row.Bounds.Min)
   145  
   146  	p2 := row.Bounds.Min
   147  	p2.Y += jump
   148  	row.resizeWithPushToPoint(&p2)
   149  
   150  	// layout for accurate bounds, to warp pointer
   151  	row.Col.RowsLayout.Spl.Layout()
   152  
   153  	p3 := row.Bounds.Min.Add(pad)
   154  	p3.Y = row.Bounds.Min.Y // accurate y
   155  	row.ui.WarpPointer(p3)
   156  }
   157  
   158  func (row *Row) resizeWithPushToPoint(p *image.Point) {
   159  	col := row.Col
   160  	dy := float64(col.Bounds.Dy())
   161  	perc := float64(p.Sub(col.Bounds.Min).Y) / dy
   162  
   163  	col.RowsLayout.Spl.SetPercentWithPush(row, perc)
   164  }
   165  
   166  //----------
   167  
   168  func (row *Row) EnsureTextAreaMinimumHeight() {
   169  	ta := row.TextArea
   170  
   171  	taMin := ta.LineHeight() * 3
   172  	if ta.Bounds.Dy() >= taMin {
   173  		return
   174  	}
   175  
   176  	hint := image.Point{row.Bounds.Dx(), row.Col.Bounds.Dy()}
   177  	tbm := row.Toolbar.Measure(hint)
   178  	minH := tbm.Y + taMin + 2 // pad to cover borders used
   179  	perc := float64(minH) / float64(row.Col.Bounds.Dy())
   180  
   181  	row.Col.RowsLayout.Spl.SetSizePercentWithPush(row, perc)
   182  }
   183  
   184  func (row *Row) EnsureOneToolbarLineYVisible() {
   185  	minH := row.TextArea.LineHeight()
   186  	rowY := row.Bounds.Dy()
   187  	if rowY >= minH {
   188  		return
   189  	}
   190  	perc := float64(minH) / float64(row.Col.Bounds.Dy())
   191  	row.Col.RowsLayout.Spl.SetSizePercentWithPush(row, perc)
   192  }
   193  
   194  //----------
   195  
   196  func (row *Row) SetState(s RowState, v bool) {
   197  	row.Toolbar.Square.SetState(s, v)
   198  }
   199  func (row *Row) HasState(s RowState) bool {
   200  	return row.Toolbar.Square.HasState(s)
   201  }
   202  
   203  //----------
   204  
   205  func (row *Row) PosBelow() *RowPos {
   206  	return NewRowPos(row.Col, row.NextRow())
   207  }
   208  
   209  //----------
   210  
   211  const (
   212  	RowInputEventId = iota
   213  	RowCloseEventId
   214  )
   215  
   216  type RowInputEvent struct {
   217  	Row   *Row
   218  	Event interface{}
   219  }
   220  type RowCloseEvent struct {
   221  	Row *Row
   222  }