github.com/jmigpin/editor@v1.6.0/util/uiutil/widget/scrollarea.go (about)

     1  package widget
     2  
     3  import (
     4  	"image"
     5  
     6  	"github.com/jmigpin/editor/util/imageutil"
     7  	"github.com/jmigpin/editor/util/uiutil/event"
     8  )
     9  
    10  type ScrollArea struct {
    11  	ENode
    12  	ScrollWidth int
    13  	LeftScroll  bool
    14  	YBar        *ScrollBar
    15  	XBar        *ScrollBar
    16  
    17  	scrollable ScrollableNode
    18  	ctx        ImageContext
    19  }
    20  
    21  func NewScrollArea(ctx ImageContext, scrollable ScrollableNode, xbar, ybar bool) *ScrollArea {
    22  	sa := &ScrollArea{
    23  		ScrollWidth: 10,
    24  		LeftScroll:  true,
    25  		scrollable:  scrollable,
    26  		ctx:         ctx,
    27  	}
    28  	sa.SetBars(xbar, ybar)
    29  	sa.Append(sa.scrollable)
    30  	return sa
    31  }
    32  
    33  //----------
    34  
    35  func (sa *ScrollArea) SetBars(xbar, ybar bool) {
    36  	if xbar && sa.XBar == nil {
    37  		sa.XBar = NewScrollBar(sa.ctx, sa)
    38  		sa.XBar.Horizontal = true
    39  		sa.Append(sa.XBar)
    40  
    41  	}
    42  	if !xbar && sa.XBar != nil {
    43  		sa.Remove(sa.XBar)
    44  		sa.XBar = nil
    45  	}
    46  	if ybar && sa.YBar == nil {
    47  		sa.YBar = NewScrollBar(sa.ctx, sa)
    48  		sa.Append(sa.YBar)
    49  	}
    50  	if !ybar && sa.YBar != nil {
    51  		sa.Remove(sa.YBar)
    52  		sa.YBar = nil
    53  	}
    54  	sa.scrollable.SetScrollable(xbar, ybar)
    55  }
    56  
    57  //----------
    58  
    59  func (sa *ScrollArea) scrollPageUp()   { sa.scrollPage(true) }
    60  func (sa *ScrollArea) scrollPageDown() { sa.scrollPage(false) }
    61  func (sa *ScrollArea) scrollPage(up bool) {
    62  	if sa.YBar != nil {
    63  		sa.YBar.scrollPage(up)
    64  	}
    65  }
    66  
    67  func (sa *ScrollArea) scrollJumpUp()   { sa.scrollJump(true) }
    68  func (sa *ScrollArea) scrollJumpDown() { sa.scrollJump(false) }
    69  func (sa *ScrollArea) scrollJump(up bool) {
    70  	if sa.YBar != nil {
    71  		sa.YBar.scrollWheel(up)
    72  	}
    73  }
    74  
    75  //----------
    76  
    77  func (sa *ScrollArea) OnChildMarked(child Node, newMarks Marks) {
    78  	if child == sa.scrollable {
    79  		if newMarks.HasAny(MarkNeedsLayout) {
    80  			sa.MarkNeedsLayout()
    81  		}
    82  	}
    83  }
    84  
    85  //----------
    86  
    87  func (sa *ScrollArea) Measure(hint image.Point) image.Point {
    88  	// space to reduce due to scrollbars
    89  	var space image.Point
    90  	if sa.YBar != nil {
    91  		space.X = sa.ScrollWidth
    92  	}
    93  	if sa.XBar != nil {
    94  		space.Y = sa.ScrollWidth
    95  	}
    96  
    97  	h := hint.Sub(space)
    98  	h = imageutil.MaxPoint(h, image.Point{0, 0})
    99  
   100  	//m := sa.ENode.Measure(h)
   101  	m := sa.scrollable.Measure(h)
   102  
   103  	m = m.Add(space)
   104  	m = imageutil.MinPoint(m, hint)
   105  
   106  	return m
   107  }
   108  
   109  func (sa *ScrollArea) Layout() {
   110  	b := sa.Bounds
   111  	if sa.YBar != nil {
   112  		r := b
   113  		if sa.LeftScroll {
   114  			r.Max.X = r.Min.X + sa.ScrollWidth
   115  			b.Min.X = r.Max.X
   116  		} else {
   117  			r.Min.X = r.Max.X - sa.ScrollWidth
   118  			b.Max.X = r.Min.X
   119  		}
   120  		r = r.Intersect(sa.Bounds)
   121  		sa.YBar.Bounds = r
   122  	}
   123  	if sa.XBar != nil {
   124  		r := b
   125  		r.Min.Y = r.Max.Y - sa.ScrollWidth
   126  		b.Max.Y = r.Min.Y
   127  		r = r.Intersect(sa.Bounds)
   128  		sa.XBar.Bounds = r
   129  	}
   130  
   131  	// scrollable bounds
   132  	sa.scrollable.Embed().Bounds = b.Intersect(sa.Bounds)
   133  	// ensure scrollable layout is done before the scrollbar since it might be calculated before the scrollable due to child order. The scrollable needs to be aware of its updated bounds to correctly return the viewsize.
   134  	sa.scrollable.Layout()
   135  }
   136  
   137  //----------
   138  
   139  func (sa *ScrollArea) OnInputEvent(ev0 interface{}, p image.Point) event.Handled {
   140  	switch evt := ev0.(type) {
   141  	case *event.KeyDown:
   142  		switch evt.KeySym {
   143  		case event.KSymPageUp:
   144  			sa.scrollPageUp()
   145  		case event.KSymPageDown:
   146  			sa.scrollPageDown()
   147  		default:
   148  			// allow scrollable to receive keydown input
   149  			if !p.In(sa.scrollable.Embed().Bounds) {
   150  				sa.scrollable.OnInputEvent(ev0, p)
   151  			}
   152  		}
   153  	case *event.MouseDown:
   154  		// scrolling with the wheel on the content area
   155  		if p.In(sa.scrollable.Embed().Bounds) {
   156  			m := evt.Mods.ClearLocks()
   157  			if !m.HasAny(event.ModCtrl) {
   158  				switch {
   159  				case evt.Button == event.ButtonWheelUp:
   160  					sa.scrollJumpUp()
   161  				case evt.Button == event.ButtonWheelDown:
   162  					sa.scrollJumpDown()
   163  				}
   164  			}
   165  		}
   166  	}
   167  	return false
   168  }