SplitLayout.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. package giu
  2. import (
  3. "image/color"
  4. "github.com/AllenDang/imgui-go"
  5. )
  6. // SplitDirection represents a direction (vertical/horizontal) of splitting layout.
  7. type SplitDirection uint8
  8. const (
  9. // DirectionHorizontal is a horizontal line.
  10. DirectionHorizontal SplitDirection = 1 << iota
  11. // DirectionVertical is a vertical line.
  12. DirectionVertical
  13. )
  14. var _ Disposable = &splitLayoutState{}
  15. type splitLayoutState struct {
  16. delta float32
  17. sashPos float32
  18. }
  19. // Dispose implements disposable interface.
  20. func (s *splitLayoutState) Dispose() {
  21. // noop
  22. }
  23. // SplitLayoutWidget creates two childs with a line between them.
  24. // This line can be moved by the user to adjust child sizes.
  25. type SplitLayoutWidget struct {
  26. id string
  27. direction SplitDirection
  28. layout1 Widget
  29. layout2 Widget
  30. originItemSpacingX float32
  31. originItemSpacingY float32
  32. originFramePaddingX float32
  33. originFramePaddingY float32
  34. sashPos float32
  35. border bool
  36. }
  37. // SplitLayout creates split layout widget.
  38. func SplitLayout(direction SplitDirection, sashPos float32, layout1, layout2 Widget) *SplitLayoutWidget {
  39. return &SplitLayoutWidget{
  40. direction: direction,
  41. sashPos: sashPos,
  42. layout1: layout1,
  43. layout2: layout2,
  44. border: true,
  45. id: GenAutoID("SplitLayout"),
  46. }
  47. }
  48. // Border sets if childs should have borders.
  49. func (s *SplitLayoutWidget) Border(b bool) *SplitLayoutWidget {
  50. s.border = b
  51. return s
  52. }
  53. // ID allows to manually set splitter's id.
  54. func (s *SplitLayoutWidget) ID(id string) *SplitLayoutWidget {
  55. s.id = id
  56. return s
  57. }
  58. // Build implements widget interface.
  59. func (s *SplitLayoutWidget) Build() {
  60. splitLayoutState := s.getState()
  61. s.originItemSpacingX, s.originItemSpacingY = GetItemInnerSpacing()
  62. s.originFramePaddingX, s.originFramePaddingY = GetFramePadding()
  63. var layout Layout
  64. splitLayoutState.sashPos += splitLayoutState.delta
  65. if splitLayoutState.sashPos < 1 {
  66. splitLayoutState.sashPos = 1
  67. }
  68. switch s.direction {
  69. case DirectionHorizontal:
  70. availableW, _ := GetAvailableRegion()
  71. if splitLayoutState.sashPos >= availableW {
  72. splitLayoutState.sashPos = availableW
  73. }
  74. layout = Layout{
  75. Row(
  76. s.buildChild(splitLayoutState.sashPos, 0, s.layout1),
  77. VSplitter(&(splitLayoutState.delta)).Size(s.originItemSpacingX, 0),
  78. s.buildChild(Auto, Auto, s.layout2),
  79. ),
  80. }
  81. case DirectionVertical:
  82. _, availableH := GetAvailableRegion()
  83. if splitLayoutState.sashPos >= availableH {
  84. splitLayoutState.sashPos = availableH
  85. }
  86. layout = Layout{
  87. Column(
  88. s.buildChild(Auto, splitLayoutState.sashPos, s.layout1),
  89. HSplitter(&(splitLayoutState.delta)).Size(0, s.originItemSpacingY),
  90. s.buildChild(Auto, Auto, s.layout2),
  91. ),
  92. }
  93. }
  94. PushItemSpacing(0, 0)
  95. layout.Build()
  96. PopStyle()
  97. }
  98. func (s *SplitLayoutWidget) restoreItemSpacing(layout Widget) Layout {
  99. return Layout{
  100. Custom(func() {
  101. PushItemSpacing(s.originItemSpacingX, s.originItemSpacingY)
  102. PushFramePadding(s.originFramePaddingX, s.originFramePaddingY)
  103. // Restore Child bg color
  104. bgColor := imgui.CurrentStyle().GetColor(imgui.StyleColorChildBg)
  105. PushStyleColor(StyleColorChildBg, Vec4ToRGBA(bgColor))
  106. }),
  107. layout,
  108. Custom(func() {
  109. PopStyleColor()
  110. PopStyleV(2)
  111. }),
  112. }
  113. }
  114. // Build Child panel. If layout is a SplitLayout, set the frame padding to zero.
  115. func (s *SplitLayoutWidget) buildChild(width, height float32, layout Widget) Widget {
  116. return Layout{
  117. Custom(func() {
  118. _, isSplitLayoutWidget := layout.(*SplitLayoutWidget)
  119. hasFramePadding := isSplitLayoutWidget || !s.border
  120. hasBorder := !isSplitLayoutWidget && s.border
  121. if hasFramePadding {
  122. PushFramePadding(0, 0)
  123. }
  124. PushStyleColor(StyleColorChildBg, color.RGBA{R: 0, G: 0, B: 0, A: 0})
  125. Child().
  126. Border(hasBorder).
  127. Size(width, height).
  128. Layout(s.restoreItemSpacing(layout)).
  129. Build()
  130. PopStyleColor()
  131. if hasFramePadding {
  132. PopStyle()
  133. }
  134. }),
  135. }
  136. }
  137. func (s *SplitLayoutWidget) getState() (state *splitLayoutState) {
  138. if st := Context.GetState(s.id); st == nil {
  139. state = &splitLayoutState{delta: 0.0, sashPos: s.sashPos}
  140. Context.SetState(s.id, state)
  141. } else {
  142. var isOk bool
  143. state, isOk = st.(*splitLayoutState)
  144. Assert(isOk, "SplitLayoutWidget", "Build", "got unexpected type of widget's state")
  145. }
  146. return state
  147. }