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

     1  package ui
     2  
     3  import (
     4  	"image"
     5  
     6  	"github.com/jmigpin/editor/util/imageutil"
     7  	"github.com/jmigpin/editor/util/uiutil/event"
     8  	"github.com/jmigpin/editor/util/uiutil/widget"
     9  )
    10  
    11  type RowSquare struct {
    12  	widget.ENode
    13  	Size  image.Point
    14  	row   *Row
    15  	state RowState
    16  }
    17  
    18  func NewRowSquare(row *Row) *RowSquare {
    19  	sq := &RowSquare{row: row, Size: image.Point{5, 5}}
    20  	sq.Cursor = event.CloseCursor
    21  	return sq
    22  }
    23  func (sq *RowSquare) Measure(hint image.Point) image.Point {
    24  	return imageutil.MinPoint(sq.Size, hint)
    25  }
    26  
    27  func (sq *RowSquare) Paint() {
    28  	img := sq.row.ui.Image()
    29  
    30  	// background
    31  	bg := sq.TreeThemePaletteColor("rowsquare")
    32  	if sq.state.hasAny(RowStateEdited) {
    33  		bg = sq.TreeThemePaletteColor("rs_edited")
    34  	}
    35  	if sq.state.hasAny(RowStateNotExist) {
    36  		bg = sq.TreeThemePaletteColor("rs_not_exist")
    37  	}
    38  	if sq.state.hasAny(RowStateExecuting) {
    39  		bg = sq.TreeThemePaletteColor("rs_executing")
    40  	}
    41  	imageutil.FillRectangle(img, sq.Bounds, bg)
    42  
    43  	// mini-squares
    44  	if sq.state.hasAny(RowStateActive) {
    45  		r := sq.miniSq(0)
    46  		c := sq.TreeThemePaletteColor("rs_active")
    47  		imageutil.FillRectangle(img, r, c)
    48  	}
    49  	if sq.state.hasAny(RowStateFsDiffer) {
    50  		r := sq.miniSq(1)
    51  		c := sq.TreeThemePaletteColor("rs_disk_changes")
    52  		imageutil.FillRectangle(img, r, c)
    53  	}
    54  	if sq.state.hasAny(RowStateDuplicate) {
    55  		r := sq.miniSq(2)
    56  		c := sq.TreeThemePaletteColor("rs_duplicate")
    57  		imageutil.FillRectangle(img, r, c)
    58  	}
    59  	if sq.state.hasAny(RowStateDuplicateHighlight) {
    60  		r := sq.miniSq(2)
    61  		c := sq.TreeThemePaletteColor("rs_duplicate_highlight")
    62  		imageutil.FillRectangle(img, r, c)
    63  	}
    64  	if sq.state.hasAny(RowStateAnnotations) {
    65  		r := sq.miniSq(3)
    66  		c := sq.TreeThemePaletteColor("rs_annotations")
    67  		imageutil.FillRectangle(img, r, c)
    68  	}
    69  	if sq.state.hasAny(RowStateAnnotationsEdited) {
    70  		r := sq.miniSq(3)
    71  		c := sq.TreeThemePaletteColor("rs_annotations_edited")
    72  		imageutil.FillRectangle(img, r, c)
    73  	}
    74  }
    75  func (sq *RowSquare) miniSq(i int) image.Rectangle {
    76  	// mini squares
    77  	// [0,1]
    78  	// [2,3]
    79  
    80  	// mini square rectangle
    81  	maxXI, maxYI := 1, 1
    82  	sideX, sideY := sq.Size.X/(maxXI+1), sq.Size.Y/(maxYI+1)
    83  	x, y := i%2, i/2
    84  	r := image.Rect(0, 0, sideX, sideY)
    85  	r = r.Add(image.Point{x * sideX, y * sideY})
    86  
    87  	// avoid rounding errors
    88  	if x == maxXI {
    89  		r.Max.X = sq.Size.X
    90  	}
    91  	if y == maxYI {
    92  		r.Max.Y = sq.Size.Y
    93  	}
    94  
    95  	// mini square position
    96  	r2 := r.Add(sq.Bounds.Min).Intersect(sq.Bounds)
    97  
    98  	return r2
    99  }
   100  
   101  func (sq *RowSquare) SetState(s RowState, v bool) {
   102  	u := sq.state.hasAny(s)
   103  	if u != v {
   104  		if v {
   105  			sq.state.add(s)
   106  		} else {
   107  			sq.state.remove(s)
   108  		}
   109  		sq.MarkNeedsPaint()
   110  	}
   111  }
   112  func (sq *RowSquare) HasState(s RowState) bool {
   113  	return sq.state.hasAny(s)
   114  }
   115  func (sq *RowSquare) OnInputEvent(ev interface{}, p image.Point) event.Handled {
   116  	switch t := ev.(type) {
   117  
   118  	// use drag events from row separator (allows dragging using rowsquare)
   119  	case *event.MouseDragMove:
   120  		sq.Cursor = event.MoveCursor
   121  		sq.row.sep.OnInputEvent(ev, p)
   122  
   123  	// when exiting a drag, make sure the cursor is back to the default
   124  	case *event.MouseDragEnd:
   125  		sq.Cursor = event.CloseCursor
   126  
   127  	// handle scroll events from row separator (allows mouse-wheel ops from rowsquare)
   128  	case *event.MouseDown:
   129  		sq.row.sep.OnInputEvent(ev, p)
   130  
   131  	case *event.MouseClick:
   132  		switch t.Button {
   133  		case event.ButtonLeft, event.ButtonMiddle, event.ButtonRight:
   134  			sq.row.Close()
   135  		}
   136  	}
   137  	return true
   138  }
   139  
   140  //----------
   141  
   142  type RowState uint16
   143  
   144  func (m *RowState) add(u RowState)         { *m |= u }
   145  func (m *RowState) remove(u RowState)      { *m &^= u }
   146  func (m *RowState) hasAny(u RowState) bool { return (*m)&u > 0 }
   147  
   148  const (
   149  	RowStateActive RowState = 1 << iota
   150  	RowStateExecuting
   151  	RowStateEdited
   152  	RowStateFsDiffer
   153  	RowStateNotExist
   154  	RowStateDuplicate
   155  	RowStateDuplicateHighlight
   156  	RowStateAnnotations
   157  	RowStateAnnotationsEdited
   158  )