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 }