| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 |
- // 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/math32"
- "github.com/g3n/engine/window"
- )
- // Splitter is a GUI element that splits two panels and can be adjusted
- type Splitter struct {
- Panel // Embedded panel
- P0 Panel // Left/Top panel
- P1 Panel // Right/Bottom panel
- styles *SplitterStyles // pointer to current styles
- spacer Panel // spacer panel
- horiz bool // horizontal or vertical splitter
- pos float32 // relative position of the center of the spacer panel (0 to 1)
- posLast float32 // last position in pixels of the mouse cursor when dragging
- pressed bool // mouse button is pressed and dragging
- mouseOver bool // mouse is over the spacer panel
- }
- // SplitterStyle contains the styling of a Splitter
- type SplitterStyle struct {
- SpacerBorderColor math32.Color4
- SpacerColor math32.Color4
- SpacerSize float32
- }
- // SplitterStyles contains a SplitterStyle for each valid GUI state
- type SplitterStyles struct {
- Normal SplitterStyle
- Over SplitterStyle
- Drag SplitterStyle
- }
- // NewHSplitter creates and returns a pointer to a new horizontal splitter
- // widget with the specified initial dimensions
- func NewHSplitter(width, height float32) *Splitter {
- return newSplitter(true, width, height)
- }
- // NewVSplitter creates and returns a pointer to a new vertical splitter
- // widget with the specified initial dimensions
- func NewVSplitter(width, height float32) *Splitter {
- return newSplitter(false, width, height)
- }
- // newSpliter creates and returns a pointer of a new splitter with
- // the specified orientation and initial dimensions.
- func newSplitter(horiz bool, width, height float32) *Splitter {
- s := new(Splitter)
- s.horiz = horiz
- s.styles = &StyleDefault().Splitter
- s.Panel.Initialize(s, width, height)
- // Initialize left/top panel
- s.P0.Initialize(&s.P0, 0, 0)
- s.Panel.Add(&s.P0)
- // Initialize right/bottom panel
- s.P1.Initialize(&s.P1, 0, 0)
- s.Panel.Add(&s.P1)
- // Initialize spacer panel
- s.spacer.Initialize(&s.spacer, 0, 0)
- s.Panel.Add(&s.spacer)
- if horiz {
- s.spacer.SetBorders(0, 1, 0, 1)
- s.pos = 0.5
- } else {
- s.spacer.SetBorders(1, 0, 1, 0)
- s.pos = 0.5
- }
- s.Subscribe(OnResize, s.onResize)
- s.spacer.Subscribe(OnMouseDown, s.onMouse)
- s.spacer.Subscribe(OnMouseUp, s.onMouse)
- s.spacer.Subscribe(OnCursor, s.onCursor)
- s.spacer.Subscribe(OnCursorEnter, s.onCursor)
- s.spacer.Subscribe(OnCursorLeave, s.onCursor)
- s.update()
- s.recalc()
- return s
- }
- // SetSplit sets the position of the splitter bar.
- // It accepts a value from 0.0 to 1.0
- func (s *Splitter) SetSplit(pos float32) {
- s.setSplit(pos)
- s.recalc()
- }
- // Split returns the current position of the splitter bar.
- // It returns a value from 0.0 to 1.0
- func (s *Splitter) Split() float32 {
- return s.pos
- }
- // onResize receives subscribed resize events for the whole splitter panel
- func (s *Splitter) onResize(evname string, ev interface{}) {
- s.recalc()
- }
- // onMouse receives subscribed mouse events over the spacer panel
- func (s *Splitter) onMouse(evname string, ev interface{}) {
- mev := ev.(*window.MouseEvent)
- if mev.Button != window.MouseButtonLeft {
- return
- }
- switch evname {
- case OnMouseDown:
- s.pressed = true
- if s.horiz {
- s.posLast = mev.Xpos
- } else {
- s.posLast = mev.Ypos
- }
- Manager().SetCursorFocus(&s.spacer)
- case OnMouseUp:
- s.pressed = false
- window.Get().SetCursor(window.ArrowCursor)
- Manager().SetCursorFocus(nil)
- }
- }
- // onCursor receives subscribed cursor events over the spacer panel
- func (s *Splitter) onCursor(evname string, ev interface{}) {
- if evname == OnCursorEnter {
- if s.horiz {
- window.Get().SetCursor(window.HResizeCursor)
- } else {
- window.Get().SetCursor(window.VResizeCursor)
- }
- s.mouseOver = true
- s.update()
- } else if evname == OnCursorLeave {
- window.Get().SetCursor(window.ArrowCursor)
- s.mouseOver = false
- s.update()
- } else if evname == OnCursor {
- if !s.pressed {
- return
- }
- cev := ev.(*window.CursorEvent)
- var delta float32
- pos := s.pos
- if s.horiz {
- delta = cev.Xpos - s.posLast
- s.posLast = cev.Xpos
- pos += delta / s.ContentWidth()
- } else {
- delta = cev.Ypos - s.posLast
- s.posLast = cev.Ypos
- pos += delta / s.ContentHeight()
- }
- s.setSplit(pos)
- s.recalc()
- }
- }
- // setSplit sets the validated and clamped split position from the received value.
- func (s *Splitter) setSplit(pos float32) {
- if pos < 0 {
- s.pos = 0
- } else if pos > 1 {
- s.pos = 1
- } else {
- s.pos = pos
- }
- }
- // update updates the splitter visual state
- func (s *Splitter) update() {
- if s.pressed {
- s.applyStyle(&s.styles.Drag)
- return
- }
- if s.mouseOver {
- s.applyStyle(&s.styles.Over)
- return
- }
- s.applyStyle(&s.styles.Normal)
- }
- // applyStyle applies the specified splitter style
- func (s *Splitter) applyStyle(ss *SplitterStyle) {
- s.spacer.SetBordersColor4(&ss.SpacerBorderColor)
- s.spacer.SetColor4(&ss.SpacerColor)
- if s.horiz {
- s.spacer.SetWidth(ss.SpacerSize)
- } else {
- s.spacer.SetHeight(ss.SpacerSize)
- }
- }
- // recalc relcalculates the position and sizes of the internal panels
- func (s *Splitter) recalc() {
- width := s.ContentWidth()
- height := s.ContentHeight()
- if s.horiz {
- // Calculate x position for spacer panel
- spx := width*s.pos - s.spacer.Width()/2
- if spx < 0 {
- spx = 0
- } else if spx > width-s.spacer.Width() {
- spx = width - s.spacer.Width()
- }
- // Left panel
- s.P0.SetPosition(0, 0)
- s.P0.SetSize(spx, height)
- // Spacer panel
- s.spacer.SetPosition(spx, 0)
- s.spacer.SetHeight(height)
- // Right panel
- s.P1.SetPosition(spx+s.spacer.Width(), 0)
- s.P1.SetSize(width-spx-s.spacer.Width(), height)
- } else {
- // Calculate y position for spacer panel
- spy := height*s.pos - s.spacer.Height()/2
- if spy < 0 {
- spy = 0
- } else if spy > height-s.spacer.Height() {
- spy = height - s.spacer.Height()
- }
- // Top panel
- s.P0.SetPosition(0, 0)
- s.P0.SetSize(width, spy)
- // Spacer panel
- s.spacer.SetPosition(0, spy)
- s.spacer.SetWidth(width)
- // Bottom panel
- s.P1.SetPosition(0, spy+s.spacer.Height())
- s.P1.SetSize(width, height-spy-s.spacer.Height())
- }
- }
|