瀏覽代碼

added new scroller, renamed old one to itemscroller

danaugrs 7 年之前
父節點
當前提交
eeed70ebe7
共有 7 個文件被更改,包括 1215 次插入540 次删除
  1. 643 0
      gui/itemscroller.go
  2. 12 12
      gui/list.go
  3. 16 7
      gui/panel.go
  4. 28 29
      gui/scrollbar.go
  5. 479 474
      gui/scroller.go
  6. 2 1
      gui/style.go
  7. 35 17
      gui/style_light.go

+ 643 - 0
gui/itemscroller.go

@@ -0,0 +1,643 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gui
+
+import (
+	"github.com/g3n/engine/window"
+	"math"
+)
+
+// ItemScroller is the GUI element that allows scrolling of IPanels
+type ItemScroller struct {
+	Panel                              // Embedded panel
+	vert           bool                // vertical/horizontal scroller flag
+	styles         *ItemScrollerStyles // pointer to current styles
+	items          []IPanel            // list of panels in the scroller
+	hscroll        *ScrollBar          // horizontal scroll bar
+	vscroll        *ScrollBar          // vertical scroll bar
+	maxAutoWidth   float32             // maximum auto width (if 0, auto width disabled)
+	maxAutoHeight  float32             // maximum auto height (if 0, auto width disabled)
+	first          int                 // first visible item position
+	adjustItem     bool                // adjust item to width or height
+	focus          bool                // has keyboard focus
+	cursorOver     bool                // mouse is over the list
+	autoButtonSize bool                // scroll button size is adjusted relative to content/view
+	scrollBarEvent bool
+}
+
+// ItemScrollerStyle contains the styling of a ItemScroller
+type ItemScrollerStyle BasicStyle
+
+// ItemScrollerStyles contains a ItemScrollerStyle for each valid GUI state
+type ItemScrollerStyles struct {
+	Normal   ItemScrollerStyle
+	Over     ItemScrollerStyle
+	Focus    ItemScrollerStyle
+	Disabled ItemScrollerStyle
+}
+
+// NewVScroller creates and returns a pointer to a new vertical scroller panel
+// with the specified dimensions.
+func NewVScroller(width, height float32) *ItemScroller {
+
+	return newScroller(true, width, height)
+}
+
+// NewHScroller creates and returns a pointer to a new horizontal scroller panel
+// with the specified dimensions.
+func NewHScroller(width, height float32) *ItemScroller {
+
+	return newScroller(false, width, height)
+}
+
+// newScroller creates and returns a pointer to a new ItemScroller panel
+// with the specified layout orientation and initial dimensions
+func newScroller(vert bool, width, height float32) *ItemScroller {
+
+	s := new(ItemScroller)
+	s.initialize(vert, width, height)
+	return s
+}
+
+// Clear removes and disposes of all the scroller children
+func (s *ItemScroller) Clear() {
+
+	s.Panel.DisposeChildren(true)
+	s.first = 0
+	s.hscroll = nil
+	s.vscroll = nil
+	s.items = s.items[0:0]
+	s.update()
+	s.recalc()
+}
+
+// Len return the number of items in the scroller
+func (s *ItemScroller) Len() int {
+
+	return len(s.items)
+}
+
+// Add appends the specified item to the end of the scroller
+func (s *ItemScroller) Add(item IPanel) {
+
+	s.InsertAt(len(s.items), item)
+}
+
+// InsertAt inserts an item at the specified position
+func (s *ItemScroller) InsertAt(pos int, item IPanel) {
+
+	// Validates position
+	if pos < 0 || pos > len(s.items) {
+		panic("ItemScroller.InsertAt(): Invalid position")
+	}
+	item.GetPanel().SetVisible(false)
+
+	// Insert item in the items array
+	s.items = append(s.items, nil)
+	copy(s.items[pos+1:], s.items[pos:])
+	s.items[pos] = item
+
+	// Insert item in the scroller
+	s.Panel.Add(item)
+	s.autoSize()
+	s.recalc()
+
+	// Scroll bar should be on the foreground,
+	// in relation of all the other child panels.
+	if s.vscroll != nil {
+		s.Panel.SetTopChild(s.vscroll)
+	}
+	if s.hscroll != nil {
+		s.Panel.SetTopChild(s.hscroll)
+	}
+}
+
+// RemoveAt removes item from the specified position
+func (s *ItemScroller) RemoveAt(pos int) IPanel {
+
+	// Validates position
+	if pos < 0 || pos >= len(s.items) {
+		panic("ItemScroller.RemoveAt(): Invalid position")
+	}
+
+	// Remove event listener
+	item := s.items[pos]
+
+	// Remove item from the items array
+	copy(s.items[pos:], s.items[pos+1:])
+	s.items[len(s.items)-1] = nil
+	s.items = s.items[:len(s.items)-1]
+
+	// Remove item from the scroller children
+	s.Panel.Remove(item)
+	s.autoSize()
+	s.recalc()
+	return item
+}
+
+// Remove removes the specified item from the ItemScroller
+func (s *ItemScroller) Remove(item IPanel) {
+
+	for p, curr := range s.items {
+		if curr == item {
+			s.RemoveAt(p)
+			return
+		}
+	}
+}
+
+// ItemAt returns the item at the specified position.
+// Returns nil if the position is invalid.
+func (s *ItemScroller) ItemAt(pos int) IPanel {
+
+	if pos < 0 || pos >= len(s.items) {
+		return nil
+	}
+	return s.items[pos]
+}
+
+// ItemPosition returns the position of the specified item in
+// the scroller of -1 if not found
+func (s *ItemScroller) ItemPosition(item IPanel) int {
+
+	for pos := 0; pos < len(s.items); pos++ {
+		if s.items[pos] == item {
+			return pos
+		}
+	}
+	return -1
+}
+
+// First returns the position of the first visible item
+func (s *ItemScroller) First() int {
+
+	return s.first
+}
+
+// SetFirst set the position of first visible if possible
+func (s *ItemScroller) SetFirst(pos int) {
+
+	if pos >= 0 && pos <= s.maxFirst() {
+		s.first = pos
+		s.recalc()
+	}
+}
+
+// ScrollDown scrolls the list down one item if possible
+func (s *ItemScroller) ScrollDown() {
+
+	max := s.maxFirst()
+	if s.first >= max {
+		return
+	}
+	s.first++
+	s.recalc()
+}
+
+// ScrollUp scrolls the list up one item if possible
+func (s *ItemScroller) ScrollUp() {
+
+	if s.first == 0 {
+		return
+	}
+	s.first--
+	s.recalc()
+}
+
+// ItemVisible returns indication if the item at the specified
+// position is completely visible or not
+func (s *ItemScroller) ItemVisible(pos int) bool {
+
+	if pos < s.first {
+		return false
+	}
+
+	// Vertical scroller
+	if s.vert {
+		var height float32
+		for i := s.first; i < len(s.items); i++ {
+			item := s.items[pos]
+			height += item.GetPanel().height
+			if height > s.height {
+				return false
+			}
+			if pos == i {
+				return true
+			}
+		}
+		return false
+	}
+
+	// Horizontal scroller
+	var width float32
+	for i := s.first; i < len(s.items); i++ {
+		item := s.items[pos]
+		width += item.GetPanel().width
+		if width > s.width {
+			return false
+		}
+		if pos == i {
+			return true
+		}
+	}
+	return false
+}
+
+// SetStyles set the scroller styles overriding the default style
+func (s *ItemScroller) SetStyles(ss *ItemScrollerStyles) {
+
+	s.styles = ss
+	s.update()
+}
+
+// ApplyStyle applies the specified style to the ItemScroller
+func (s *ItemScroller) ApplyStyle(style int) {
+
+	switch style {
+	case StyleOver:
+		s.applyStyle(&s.styles.Over)
+	case StyleFocus:
+		s.applyStyle(&s.styles.Focus)
+	case StyleNormal:
+		s.applyStyle(&s.styles.Normal)
+	case StyleDef:
+		s.update()
+	}
+}
+
+// SetAutoWidth sets the maximum automatic width
+func (s *ItemScroller) SetAutoWidth(maxWidth float32) {
+
+	s.maxAutoWidth = maxWidth
+}
+
+// SetAutoHeight sets the maximum automatic height
+func (s *ItemScroller) SetAutoHeight(maxHeight float32) {
+
+	s.maxAutoHeight = maxHeight
+}
+
+// SetAutoButtonSize specified whether the scrollbutton size should be adjusted relative to the size of the content/view
+func (s *ItemScroller) SetAutoButtonSize(autoButtonSize bool) {
+
+	s.autoButtonSize = autoButtonSize
+}
+
+// initialize initializes this scroller and is normally used by other types which contains a scroller
+func (s *ItemScroller) initialize(vert bool, width, height float32) {
+
+	s.vert = vert
+	s.autoButtonSize = true
+	s.Panel.Initialize(width, height)
+	s.styles = &StyleDefault().ItemScroller
+
+	s.Panel.Subscribe(OnCursorEnter, s.onCursor)
+	s.Panel.Subscribe(OnCursorLeave, s.onCursor)
+	s.Panel.Subscribe(OnScroll, s.onScroll)
+	s.Panel.Subscribe(OnResize, s.onResize)
+
+	s.update()
+	s.recalc()
+}
+
+// onCursor receives subscribed cursor events over the panel
+func (s *ItemScroller) onCursor(evname string, ev interface{}) {
+
+	switch evname {
+	case OnCursorEnter:
+		s.root.SetScrollFocus(s)
+		s.cursorOver = true
+		s.update()
+	case OnCursorLeave:
+		s.root.SetScrollFocus(nil)
+		s.cursorOver = false
+		s.update()
+	}
+	s.root.StopPropagation(Stop3D)
+}
+
+// onScroll receives subscriber mouse scroll events when this scroller has
+// the scroll focus (set by OnMouseEnter)
+func (s *ItemScroller) onScroll(evname string, ev interface{}) {
+
+	sev := ev.(*window.ScrollEvent)
+	if sev.Yoffset > 0 {
+		s.ScrollUp()
+	} else if sev.Yoffset < 0 {
+		s.ScrollDown()
+	}
+	s.root.StopPropagation(Stop3D)
+}
+
+// onResize receives resize events
+func (s *ItemScroller) onResize(evname string, ev interface{}) {
+
+	s.recalc()
+}
+
+// autoSize resizes the scroller if necessary
+func (s *ItemScroller) autoSize() {
+
+	if s.maxAutoWidth == 0 && s.maxAutoHeight == 0 {
+		return
+	}
+
+	var width float32
+	var height float32
+	for _, item := range s.items {
+		panel := item.GetPanel()
+		if panel.Width() > width {
+			width = panel.Width()
+		}
+		height += panel.TotalHeight()
+	}
+
+	// If auto maximum width enabled
+	if s.maxAutoWidth > 0 {
+		if width <= s.maxAutoWidth {
+			s.SetContentWidth(width)
+		}
+	}
+	// If auto maximum height enabled
+	if s.maxAutoHeight > 0 {
+		if height <= s.maxAutoHeight {
+			s.SetContentHeight(height)
+		}
+	}
+}
+
+// recalc recalculates the positions and visibilities of all the items
+func (s *ItemScroller) recalc() {
+
+	if s.vert {
+		s.vRecalc()
+	} else {
+		s.hRecalc()
+	}
+}
+
+// vRecalc recalculates for the vertical scroller
+func (s *ItemScroller) vRecalc() {
+
+	// Checks if scroll bar should be visible or not
+	scroll := false
+	if s.first > 0 {
+		scroll = true
+	} else {
+		var posY float32
+		for _, item := range s.items[s.first:] {
+			posY += item.TotalHeight()
+			if posY > s.height {
+				scroll = true
+				break
+			}
+		}
+	}
+	s.setVScrollBar(scroll)
+
+	// Compute size of scroll button
+	if scroll && s.autoButtonSize {
+		var totalHeight float32
+		for _, item := range s.items {
+			// TODO OPTIMIZATION
+			// Break when the view/content proportion becomes smaller than the minimum button size
+			totalHeight += item.TotalHeight()
+		}
+		s.vscroll.SetButtonSize(s.height * s.height/totalHeight)
+	}
+
+	// Items width
+	width := s.ContentWidth()
+	if scroll {
+		width -= s.vscroll.Width()
+	}
+
+	var posY float32
+	// Sets positions of all items
+	for pos, ipan := range s.items {
+		item := ipan.GetPanel()
+		if pos < s.first {
+			item.SetVisible(false)
+			continue
+		}
+		// If item is after last visible, sets not visible
+		if posY > s.height {
+			item.SetVisible(false)
+			continue
+		}
+		// Sets item position
+		item.SetVisible(true)
+		item.SetPosition(0, posY)
+		if s.adjustItem {
+			item.SetWidth(width)
+		}
+		posY += ipan.TotalHeight()
+	}
+
+	// Set scroll bar value if recalc was not due by scroll event
+	if scroll && !s.scrollBarEvent {
+		s.vscroll.SetValue(float32(s.first) / float32(s.maxFirst()))
+	}
+	s.scrollBarEvent = false
+}
+
+// hRecalc recalculates for the horizontal scroller
+func (s *ItemScroller) hRecalc() {
+
+	// Checks if scroll bar should be visible or not
+	scroll := false
+	if s.first > 0 {
+		scroll = true
+	} else {
+		var posX float32
+		for _, item := range s.items[s.first:] {
+			posX += item.GetPanel().Width()
+			if posX > s.width {
+				scroll = true
+				break
+			}
+		}
+	}
+	s.setHScrollBar(scroll)
+
+	// Compute size of scroll button
+	if scroll && s.autoButtonSize {
+		var totalWidth float32
+		for _, item := range s.items {
+			// TODO OPTIMIZATION
+			// Break when the view/content proportion becomes smaller than the minimum button size
+			totalWidth += item.GetPanel().Width()
+		}
+		s.hscroll.SetButtonSize(s.width * s.width/totalWidth)
+	}
+
+	// Items height
+	height := s.ContentHeight()
+	if scroll {
+		height -= s.hscroll.Height()
+	}
+
+	var posX float32
+	// Sets positions of all items
+	for pos, ipan := range s.items {
+		item := ipan.GetPanel()
+		// If item is before first visible, sets not visible
+		if pos < s.first {
+			item.SetVisible(false)
+			continue
+		}
+		// If item is after last visible, sets not visible
+		if posX > s.width {
+			item.SetVisible(false)
+			continue
+		}
+		// Sets item position
+		item.SetVisible(true)
+		item.SetPosition(posX, 0)
+		if s.adjustItem {
+			item.SetHeight(height)
+		}
+		posX += item.Width()
+	}
+
+	// Set scroll bar value if recalc was not due by scroll event
+	if scroll && !s.scrollBarEvent {
+		s.hscroll.SetValue(float32(s.first) / float32(s.maxFirst()))
+	}
+	s.scrollBarEvent = false
+}
+
+// maxFirst returns the maximum position of the first visible item
+func (s *ItemScroller) maxFirst() int {
+
+	// Vertical scroller
+	if s.vert {
+		var height float32
+		pos := len(s.items) - 1
+		if pos < 0 {
+			return 0
+		}
+		for {
+			item := s.items[pos]
+			height += item.GetPanel().Height()
+			if height > s.Height() {
+				break
+			}
+			pos--
+			if pos < 0 {
+				break
+			}
+		}
+		return pos + 1
+	}
+
+	// Horizontal scroller
+	var width float32
+	pos := len(s.items) - 1
+	if pos < 0 {
+		return 0
+	}
+	for {
+		item := s.items[pos]
+		width += item.GetPanel().Width()
+		if width > s.Width() {
+			break
+		}
+		pos--
+		if pos < 0 {
+			break
+		}
+	}
+	return pos + 1
+}
+
+// setVScrollBar sets the visibility state of the vertical scrollbar
+func (s *ItemScroller) setVScrollBar(state bool) {
+
+	// Visible
+	if state {
+		var scrollWidth float32 = 20
+		if s.vscroll == nil {
+			s.vscroll = NewVScrollBar(0, 0)
+			s.vscroll.SetBorders(0, 0, 0, 1)
+			s.vscroll.Subscribe(OnChange, s.onScrollBarEvent)
+			s.Panel.Add(s.vscroll)
+		}
+		s.vscroll.SetSize(scrollWidth, s.ContentHeight())
+		s.vscroll.SetPositionX(s.ContentWidth() - scrollWidth)
+		s.vscroll.SetPositionY(0)
+		s.vscroll.recalc()
+		s.vscroll.SetVisible(true)
+		// Not visible
+	} else {
+		if s.vscroll != nil {
+			s.vscroll.SetVisible(false)
+		}
+	}
+}
+
+// setHScrollBar sets the visibility state of the horizontal scrollbar
+func (s *ItemScroller) setHScrollBar(state bool) {
+
+	// Visible
+	if state {
+		var scrollHeight float32 = 20
+		if s.hscroll == nil {
+			s.hscroll = NewHScrollBar(0, 0)
+			s.hscroll.SetBorders(1, 0, 0, 0)
+			s.hscroll.Subscribe(OnChange, s.onScrollBarEvent)
+			s.Panel.Add(s.hscroll)
+		}
+		s.hscroll.SetSize(s.ContentWidth(), scrollHeight)
+		s.hscroll.SetPositionX(0)
+		s.hscroll.SetPositionY(s.ContentHeight() - scrollHeight)
+		s.hscroll.recalc()
+		s.hscroll.SetVisible(true)
+		// Not visible
+	} else {
+		if s.hscroll != nil {
+			s.hscroll.SetVisible(false)
+		}
+	}
+}
+
+// onScrollEvent is called when the list scrollbar value changes
+func (s *ItemScroller) onScrollBarEvent(evname string, ev interface{}) {
+
+	var pos float64
+	if s.vert {
+		pos = s.vscroll.Value()
+	} else {
+		pos = s.hscroll.Value()
+	}
+
+	first := int(math.Floor((float64(s.maxFirst()) * pos) + 0.5))
+	if first == s.first {
+		return
+	}
+	s.scrollBarEvent = true
+	s.first = first
+	s.recalc()
+}
+
+// update updates the visual state the list and its items
+func (s *ItemScroller) update() {
+
+	if s.cursorOver {
+		s.applyStyle(&s.styles.Over)
+		return
+	}
+	if s.focus {
+		s.applyStyle(&s.styles.Focus)
+		return
+	}
+	s.applyStyle(&s.styles.Normal)
+}
+
+// applyStyle sets the specified style
+func (s *ItemScroller) applyStyle(st *ItemScrollerStyle) {
+
+	s.Panel.ApplyStyle(&st.PanelStyle)
+}

+ 12 - 12
gui/list.go

@@ -10,7 +10,7 @@ import (
 
 // List represents a list GUI element
 type List struct {
-	Scroller             // Embedded scroller
+	ItemScroller         // Embedded scroller
 	styles   *ListStyles // Pointer to styles
 	single   bool        // Single selection flag (default is true)
 	focus    bool        // has keyboard focus
@@ -31,7 +31,7 @@ type ListItem struct {
 
 // ListStyles
 type ListStyles struct {
-	Scroller *ScrollerStyles
+	Scroller *ItemScrollerStyles
 	Item     *ListItemStyles
 }
 
@@ -78,12 +78,12 @@ func (li *List) initialize(vert bool, width, height float32) {
 	li.styles = &StyleDefault().List
 	li.single = true
 
-	li.Scroller.initialize(vert, width, height)
-	li.Scroller.SetStyles(li.styles.Scroller)
-	li.Scroller.adjustItem = true
-	li.Scroller.Subscribe(OnMouseDown, li.onMouseEvent)
-	li.Scroller.Subscribe(OnKeyDown, li.onKeyEvent)
-	li.Scroller.Subscribe(OnKeyRepeat, li.onKeyEvent)
+	li.ItemScroller.initialize(vert, width, height)
+	li.ItemScroller.SetStyles(li.styles.Scroller)
+	li.ItemScroller.adjustItem = true
+	li.ItemScroller.Subscribe(OnMouseDown, li.onMouseEvent)
+	li.ItemScroller.Subscribe(OnKeyDown, li.onKeyEvent)
+	li.ItemScroller.Subscribe(OnKeyRepeat, li.onKeyEvent)
 
 	if vert {
 		li.keyNext = window.KeyDown
@@ -112,7 +112,7 @@ func (li *List) Single() bool {
 func (li *List) SetStyles(s *ListStyles) {
 
 	li.styles = s
-	li.Scroller.SetStyles(li.styles.Scroller)
+	li.ItemScroller.SetStyles(li.styles.Scroller)
 	li.update()
 }
 
@@ -127,7 +127,7 @@ func (li *List) Add(item IPanel) {
 func (li *List) InsertAt(pos int, item IPanel) {
 
 	litem := newListItem(li, item)
-	li.Scroller.InsertAt(pos, litem)
+	li.ItemScroller.InsertAt(pos, litem)
 	litem.Panel.Subscribe(OnMouseDown, litem.onMouse)
 	litem.Panel.Subscribe(OnCursorEnter, litem.onCursor)
 }
@@ -136,7 +136,7 @@ func (li *List) InsertAt(pos int, item IPanel) {
 func (li *List) RemoveAt(pos int) IPanel {
 
 	// Remove the list item from the internal scroller
-	pan := li.Scroller.RemoveAt(pos)
+	pan := li.ItemScroller.RemoveAt(pos)
 	litem := pan.(*ListItem)
 
 	// Remove item from the list item children and disposes of the list item panel
@@ -160,7 +160,7 @@ func (li *List) Remove(item IPanel) {
 // ItemAt returns the list item at the specified position
 func (li *List) ItemAt(pos int) IPanel {
 
-	item := li.Scroller.ItemAt(pos)
+	item := li.ItemScroller.ItemAt(pos)
 	if item == nil {
 		return nil
 	}

+ 16 - 7
gui/panel.go

@@ -46,7 +46,11 @@ type IPanel interface {
 	SetRoot(*Root)
 	LostKeyFocus()
 	TotalHeight() float32
+	TotalWidth() float32
 	SetLayout(ILayout)
+	SetPosition(x, y float32)
+	SetPositionX(x float32)
+	SetPositionY(y float32)
 }
 
 // Panel is 2D rectangular graphic which by default has a quad (2 triangles) geometry.
@@ -219,7 +223,14 @@ func (p *Panel) LostKeyFocus() {
 // height of this panel considering visible not bounded children
 func (p *Panel) TotalHeight() float32 {
 
-	return p.Height()
+	return p.height
+}
+
+// TotalWidth satisfies the IPanel interface and returns the total
+// width of this panel considering visible not bounded children
+func (p *Panel) TotalWidth() float32 {
+
+	return p.width
 }
 
 // Material returns a pointer for this panel core.Material
@@ -470,14 +481,14 @@ func (p *Panel) SetContentSize(width, height float32) {
 }
 
 // SetContentWidth sets this panel content width to the specified dimension in pixels.
-// The external size of the panel may increase or decrease to acomodate the new width
+// The external size of the panel may increase or decrease to accommodate the new width
 func (p *Panel) SetContentWidth(width float32) {
 
 	p.SetContentSize(width, p.content.Height)
 }
 
 // SetContentHeight sets this panel content height to the specified dimension in pixels.
-// The external size of the panel may increase or decrease to acomodate the new width
+// The external size of the panel may increase or decrease to accommodate the new width
 func (p *Panel) SetContentHeight(height float32) {
 
 	p.SetContentSize(p.content.Width, height)
@@ -585,10 +596,8 @@ func (p *Panel) ContainsPosition(x, y float32) bool {
 // Unlike "ContainsPosition" is does not consider the panel margins.
 func (p *Panel) InsideBorders(x, y float32) bool {
 
-	if x < (p.pospix.X+p.marginSizes.Left) || x >= (p.pospix.X+p.width-p.marginSizes.Right) {
-		return false
-	}
-	if y < (p.pospix.Y+p.marginSizes.Top) || y >= (p.pospix.Y+p.height-p.marginSizes.Bottom) {
+	if 	x < (p.pospix.X + p.marginSizes.Left) || x >= (p.pospix.X + p.width - p.marginSizes.Right) ||
+		y < (p.pospix.Y + p.marginSizes.Top) ||	y >= (p.pospix.Y + p.height - p.marginSizes.Bottom) {
 		return false
 	}
 	return true

+ 28 - 29
gui/scrollbar.go

@@ -23,21 +23,21 @@ import (
 **/
 
 type ScrollBar struct {
-	Panel                        // Embedded panel
-	styles      *ScrollBarStyles
-	vertical    bool             // type of scrollbar
-	button      scrollBarButton  // scrollbar button
-	cursorOver  bool
+	Panel                       // Embedded panel
+	styles     *ScrollBarStyles // styles of the scrollbar
+	vertical   bool             // type of scrollbar
+	button     scrollBarButton  // scrollbar button
+	cursorOver bool
 }
 
 type scrollBarButton struct {
-	Panel               // Embedded panel
-	sb       *ScrollBar // pointer to parent scroll bar
-	pressed  bool       // mouse button pressed flag
-	mouseX   float32    // last mouse click x position
-	mouseY   float32    // last mouse click y position
-	Size     float32    // button size
-	MinSize  float32    // minimum button size
+	Panel              // Embedded panel
+	sb      *ScrollBar // pointer to parent scroll bar
+	pressed bool       // mouse button pressed flag
+	mouseX  float32    // last mouse click x position
+	mouseY  float32    // last mouse click y position
+	Size    float32    // button size
+	MinSize float32    // minimum button size
 }
 
 type ScrollBarStyles struct {
@@ -48,14 +48,9 @@ type ScrollBarStyles struct {
 
 type ScrollBarStyle struct {
 	PanelStyle
-	Button       ScrollBarButtonStyle
-}
-
-type ScrollBarButtonStyle struct {
-	Borders      RectBounds
-	BordersColor math32.Color4
-	Color        math32.Color4
-	Size         float32 	   // This is the default/minimum button size
+	Button       PanelStyle
+	ButtonLength float32 // This is the default/minimum button length
+	// TODO ScrollSpeed ?
 }
 
 // NewVScrollBar creates and returns a pointer to a new vertical scroll bar
@@ -95,7 +90,7 @@ func (sb *ScrollBar) initialize(width, height float32, vertical bool) {
 	sb.button.Panel.Subscribe(OnMouseUp, sb.button.onMouse)
 	sb.button.Panel.Subscribe(OnCursor, sb.button.onCursor)
 	sb.button.SetMargins(1, 1, 1, 1)
-	sb.button.Size = sb.styles.Normal.Button.Size
+	sb.button.Size = sb.styles.Normal.ButtonLength
 	sb.button.sb = sb
 	sb.Add(&sb.button)
 
@@ -120,9 +115,17 @@ func (sb *ScrollBar) SetButtonSize(size float32) {
 func (sb *ScrollBar) Value() float64 {
 
 	if sb.vertical {
-		return float64(sb.button.Position().Y) / (float64(sb.content.Height) - float64(sb.button.height))
+		den := float64(sb.content.Height) - float64(sb.button.height)
+		if den == 0 {
+			return 0
+		}
+		return float64(sb.button.Position().Y) / den
 	} else {
-		return float64(sb.button.Position().X) / (float64(sb.content.Width) - float64(sb.button.width))
+		den := float64(sb.content.Width) - float64(sb.button.width)
+		if den == 0 {
+			return 0
+		}
+		return float64(sb.button.Position().X) / den
 	}
 }
 
@@ -190,12 +193,8 @@ func (sb *ScrollBar) update() {
 func (sb *ScrollBar) applyStyle(sbs *ScrollBarStyle) {
 
 	sb.Panel.ApplyStyle(&sbs.PanelStyle)
-
-	sb.button.SetBordersFrom(&sbs.Button.Borders)
-	sb.button.SetBordersColor4(&sbs.Button.BordersColor)
-	sb.button.SetColor4(&sbs.Button.Color)
-
-	sb.button.MinSize = sbs.Button.Size
+	sb.button.ApplyStyle(&sbs.Button)
+	sb.button.MinSize = sbs.ButtonLength
 }
 
 // onMouse receives subscribed mouse events for the scroll bar button

文件差異過大導致無法顯示
+ 479 - 474
gui/scroller.go


+ 2 - 1
gui/style.go

@@ -19,7 +19,8 @@ type Style struct {
 	Slider        SliderStyles
 	Splitter      SplitterStyles
 	Window        WindowStyles
-	Scroller      ScrollerStyles
+	ItemScroller  ItemScrollerStyles
+	Scroller      ScrollerStyle
 	List          ListStyles
 	DropDown      DropDownStyles
 	Folder        FolderStyles

+ 35 - 17
gui/style_light.go

@@ -114,11 +114,11 @@ func NewLightStyle() *Style {
 	s.ScrollBar.Normal.Border = oneBounds
 	s.ScrollBar.Normal.BorderColor = borderColor
 	s.ScrollBar.Normal.BgColor = math32.Color4{0.8, 0.8, 0.8, 1}
-	s.ScrollBar.Normal.Button = ScrollBarButtonStyle{
-		Borders:      oneBounds,
-		BordersColor: borderColor,
-		Color:        math32.Color4{0.5, 0.5, 0.5, 1},
-		Size:         30,
+	s.ScrollBar.Normal.ButtonLength = 32
+	s.ScrollBar.Normal.Button = PanelStyle{
+		Border:      oneBounds,
+		BorderColor: borderColor,
+		BgColor:     math32.Color4{0.5, 0.5, 0.5, 1},
 	}
 	s.ScrollBar.Over = s.ScrollBar.Normal
 	s.ScrollBar.Disabled = s.ScrollBar.Normal
@@ -162,21 +162,39 @@ func NewLightStyle() *Style {
 	s.Window.Focus = s.Window.Normal
 	s.Window.Disabled = s.Window.Normal
 
-	// Scroller styles
-	s.Scroller = ScrollerStyles{}
-	s.Scroller.Normal = ScrollerStyle{}
-	s.Scroller.Normal.Border = oneBounds
-	s.Scroller.Normal.BorderColor = borderColor
-	s.Scroller.Normal.BgColor = bgColor
-	s.Scroller.Normal.FgColor = fgColor
-	s.Scroller.Over = s.Scroller.Normal
-	s.Scroller.Over.BgColor = bgColorOver
-	s.Scroller.Focus = s.Scroller.Over
-	s.Scroller.Disabled = s.Scroller.Normal
+	// ItemScroller styles
+	s.Scroller = ScrollerStyle{}
+	s.Scroller.VerticalScrollbar = ScrollerScrollbarStyle{}
+	s.Scroller.VerticalScrollbar.ScrollBarStyle = s.ScrollBar.Normal
+	s.Scroller.VerticalScrollbar.Broadness = 16
+	s.Scroller.VerticalScrollbar.Position = ScrollbarRight
+	s.Scroller.VerticalScrollbar.OverlapContent = false
+	s.Scroller.VerticalScrollbar.AutoSizeButton = true
+	s.Scroller.HorizontalScrollbar = s.Scroller.VerticalScrollbar
+	s.Scroller.HorizontalScrollbar.Position = ScrollbarBottom
+	s.Scroller.ScrollbarInterlocking = ScrollbarInterlockingNone
+	s.Scroller.CornerCovered = true
+	s.Scroller.CornerPanel = PanelStyle{}
+	s.Scroller.CornerPanel.BgColor = math32.Color4Name("silver")
+	s.Scroller.Border = oneBounds
+	s.Scroller.BorderColor = borderColor
+	s.Scroller.BgColor = bgColor
+
+	// ItemScroller styles
+	s.ItemScroller = ItemScrollerStyles{}
+	s.ItemScroller.Normal = ItemScrollerStyle{}
+	s.ItemScroller.Normal.Border = oneBounds
+	s.ItemScroller.Normal.BorderColor = borderColor
+	s.ItemScroller.Normal.BgColor = bgColor
+	s.ItemScroller.Normal.FgColor = fgColor
+	s.ItemScroller.Over = s.ItemScroller.Normal
+	s.ItemScroller.Over.BgColor = bgColorOver
+	s.ItemScroller.Focus = s.ItemScroller.Over
+	s.ItemScroller.Disabled = s.ItemScroller.Normal
 
 	// List styles
 	s.List = ListStyles{}
-	s.List.Scroller = &s.Scroller
+	s.List.Scroller = &s.ItemScroller
 	s.List.Item = &ListItemStyles{}
 	s.List.Item.Normal = ListItemStyle{}
 	s.List.Item.Normal.Border = RectBounds{0, 0, 1, 0}