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

     1  package ui
     2  
     3  import (
     4  	"image"
     5  
     6  	"github.com/jmigpin/editor/util/uiutil"
     7  )
     8  
     9  type UI struct {
    10  	*uiutil.BasicUI
    11  	Root    *Root
    12  	OnError func(error)
    13  }
    14  
    15  func NewUI(winName string) (*UI, error) {
    16  	ui := &UI{}
    17  
    18  	ui.Root = NewRoot(ui)
    19  
    20  	bui, err := uiutil.NewBasicUI(winName, ui.Root)
    21  	if err != nil {
    22  		return nil, err
    23  	}
    24  	ui.BasicUI = bui
    25  
    26  	// set theme before root init
    27  	c1 := &ColorThemeCycler
    28  	c1.Set(c1.CurName, ui.Root)
    29  	c2 := &FontThemeCycler
    30  	c2.Set(c2.CurName, ui.Root)
    31  
    32  	// build ui - needs ui.BasicUI to be set
    33  	ui.Root.Init()
    34  
    35  	return ui, nil
    36  }
    37  
    38  //----------
    39  
    40  func (ui *UI) WarpPointerToRectanglePad(r image.Rectangle) {
    41  	p, err := ui.QueryPointer()
    42  	if err != nil {
    43  		return
    44  	}
    45  
    46  	pad := 5
    47  
    48  	set := func(v *int, min, max int) {
    49  		if max-min < pad*2 {
    50  			*v = min + (max-min)/2
    51  		} else {
    52  			if *v < min+pad {
    53  				*v = min + pad
    54  			} else if *v > max-pad {
    55  				*v = max - pad
    56  			}
    57  		}
    58  	}
    59  
    60  	set(&p.X, r.Min.X, r.Max.X)
    61  	set(&p.Y, r.Min.Y, r.Max.Y)
    62  
    63  	ui.WarpPointer(p)
    64  }
    65  
    66  //----------
    67  
    68  func (ui *UI) resizeRowToGoodSize(row *Row) {
    69  	if row.PrevSibling() == nil {
    70  		return
    71  	}
    72  	prevRow := row.PrevSiblingWrapper().(*Row)
    73  	b := ui.rowInsertionBounds(prevRow)
    74  	col := row.Col
    75  	colDy := col.Bounds.Dy()
    76  	perc := float64(b.Min.Y-col.Bounds.Min.Y) / float64(colDy)
    77  	col.RowsLayout.Spl.Resize(row, perc)
    78  }
    79  
    80  //----------
    81  
    82  func (ui *UI) GoodRowPos() *RowPos {
    83  	var best struct {
    84  		area    int
    85  		col     *Column
    86  		nextRow *Row
    87  	}
    88  
    89  	// default position if nothing better is found
    90  	best.col = ui.Root.Cols.FirstChildColumn()
    91  
    92  	for _, c := range ui.Root.Cols.Columns() {
    93  		rows := c.Rows()
    94  
    95  		// space before first row
    96  		s := c.Bounds.Size()
    97  		if len(rows) > 0 {
    98  			s.Y = rows[0].Bounds.Min.Y - c.Bounds.Min.Y
    99  		}
   100  		a := s.X * s.Y
   101  		if a > best.area {
   102  			best.area = a
   103  			best.col = c
   104  			best.nextRow = nil
   105  			if len(rows) > 0 {
   106  				best.nextRow = rows[0]
   107  			}
   108  		}
   109  
   110  		// space between rows
   111  		for _, r := range rows {
   112  			b := ui.rowInsertionBounds(r)
   113  			s := b.Size()
   114  			a := s.X * s.Y
   115  			if a > best.area {
   116  				best.area = a
   117  				best.col = c
   118  				best.nextRow = r.NextRow()
   119  			}
   120  		}
   121  	}
   122  
   123  	return NewRowPos(best.col, best.nextRow)
   124  }
   125  
   126  //----------
   127  
   128  func (ui *UI) rowInsertionBounds(prevRow *Row) image.Rectangle {
   129  	ta := prevRow.TextArea
   130  	if r2, ok := ui.boundsAfterVisibleCursor(ta); ok {
   131  		return *r2
   132  	} else if r3, ok := ui.boundsOfTwoThirds(ta); ok {
   133  		return *r3
   134  	} else {
   135  		b := prevRow.Bounds
   136  		b.Max = b.Max.Sub(b.Size().Div(2)) // half size from StartPercentLayout
   137  		return b
   138  	}
   139  }
   140  
   141  func (ui *UI) boundsAfterVisibleCursor(ta *TextArea) (*image.Rectangle, bool) {
   142  	ci := ta.CursorIndex()
   143  	if !ta.IndexVisible(ci) {
   144  		return nil, false
   145  	}
   146  	p := ta.GetPoint(ci)
   147  	lh := ta.LineHeight()
   148  	r := ta.Bounds
   149  	r.Min.Y = p.Y + lh
   150  	r = ta.Bounds.Intersect(r)
   151  	if r.Dy() < lh*2 {
   152  		return nil, false
   153  	}
   154  	return &r, true
   155  }
   156  
   157  func (ui *UI) boundsOfTwoThirds(ta *TextArea) (*image.Rectangle, bool) {
   158  	lh := ta.LineHeight()
   159  	if ta.Bounds.Size().Y < lh {
   160  		return nil, false
   161  	}
   162  	b := ta.Bounds
   163  	b.Min.Y = b.Max.Y - (ta.Bounds.Dy() * 2 / 3)
   164  	r := ta.Bounds.Intersect(b)
   165  	return &r, true
   166  }
   167  
   168  //----------
   169  
   170  func (ui *UI) Error(err error) {
   171  	if ui.OnError != nil {
   172  		ui.OnError(err)
   173  	}
   174  }
   175  
   176  //----------
   177  
   178  type RowPos struct {
   179  	Column  *Column
   180  	NextRow *Row
   181  }
   182  
   183  func NewRowPos(col *Column, nextRow *Row) *RowPos {
   184  	return &RowPos{col, nextRow}
   185  }