package layouts import ( "image" "git.milar.in/milarin/gui" ) // BorderLayout ia a gui.Layout which places its children onto a given gui.Side type BorderLayout struct { gui.ViewTmpl views map[Slot]gui.View horizontalLayout *LayoutResult verticalLayout *LayoutResult viewDims map[Slot]gui.Dimension } var _ gui.Layout = &BorderLayout{} func NewBorderLayout() *BorderLayout { return &BorderLayout{ views: map[Slot]gui.View{}, viewDims: map[Slot]gui.Dimension{}, } } func (g *BorderLayout) Views() []gui.View { s := make([]gui.View, 0, len(g.views)) for _, view := range g.views { s = append(s, view) } return s } func (g *BorderLayout) SetView(v gui.View, slot Slot) { g.views[slot] = v } func (g *BorderLayout) View(slot Slot) gui.View { return g.views[slot] } func (g *BorderLayout) Draw(img *gui.Image, ctx gui.AppContext) { g.ViewTmpl.Draw(img, ctx) if g.verticalLayout == nil { g.Layout(ctx) } verticalLayout := g.verticalLayout if g.horizontalLayout == nil { g.Layout(ctx) } horizontalLayout := g.horizontalLayout remainingVerticalSpacePerView := (img.Bounds().Dy() - verticalLayout.Sum.Height) if verticalLayout.VerticalNegativeCount > 0 { remainingVerticalSpacePerView /= verticalLayout.VerticalNegativeCount } remainingHorizontalSpacePerView := (img.Bounds().Dx() - horizontalLayout.Sum.Width) if horizontalLayout.HorizontalNegativeCount > 0 { remainingHorizontalSpacePerView /= horizontalLayout.HorizontalNegativeCount } fitsVertically := img.Bounds().Dy() >= verticalLayout.Sum.Height fitsHorizontally := img.Bounds().Dx() >= horizontalLayout.Sum.Width var topHeight int var bottomHeight int var leftWidth int var rightWidth int if view, ok := g.views[Top]; ok { _, topHeight = view.Layout(ctx) if fitsVertically { topHeight = iff(topHeight < 0, remainingVerticalSpacePerView, topHeight) } else { topHeight = int(float64(img.Bounds().Dy()) * float64(topHeight) / float64(verticalLayout.Sum.Height)) } g.viewDims[Top] = gui.D(0, 0, img.Bounds().Dx(), topHeight) view.Draw(img.SubImage(image.Rect(0, 0, img.Bounds().Dx(), topHeight)).(*gui.Image), ctx) } if view, ok := g.views[Bottom]; ok { _, bottomHeight = view.Layout(ctx) if fitsVertically { bottomHeight = iff(bottomHeight < 0, remainingVerticalSpacePerView, bottomHeight) } else { bottomHeight = int(float64(img.Bounds().Dy()) * float64(bottomHeight) / float64(verticalLayout.Sum.Height)) } g.viewDims[Bottom] = gui.D(0, img.Bounds().Dy()-bottomHeight, img.Bounds().Dx(), bottomHeight) view.Draw(img.SubImage(image.Rect(0, img.Bounds().Dy()-bottomHeight, img.Bounds().Dx(), img.Bounds().Dy())).(*gui.Image), ctx) } if view, ok := g.views[Left]; ok { leftWidth, _ = view.Layout(ctx) if fitsHorizontally { leftWidth = iff(leftWidth < 0, remainingHorizontalSpacePerView, leftWidth) } else { leftWidth = int(float64(img.Bounds().Dx()) * float64(leftWidth) / float64(horizontalLayout.Sum.Width)) } g.viewDims[Left] = gui.D(0, topHeight, leftWidth, img.Bounds().Dy()-topHeight-bottomHeight) view.Draw(img.SubImage(image.Rect(0, topHeight, leftWidth, img.Bounds().Dy()-bottomHeight)).(*gui.Image), ctx) } if view, ok := g.views[Right]; ok { rightWidth, _ = view.Layout(ctx) if fitsHorizontally { rightWidth = iff(rightWidth < 0, remainingHorizontalSpacePerView, rightWidth) } else { rightWidth = int(float64(img.Bounds().Dx()) * float64(rightWidth) / float64(horizontalLayout.Sum.Width)) } g.viewDims[Right] = gui.D(img.Bounds().Dx()-rightWidth, topHeight, rightWidth, img.Bounds().Dy()-topHeight-bottomHeight) view.Draw(img.SubImage(image.Rect(img.Bounds().Dx()-rightWidth, topHeight, img.Bounds().Dx(), img.Bounds().Dy()-bottomHeight)).(*gui.Image), ctx) } if view, ok := g.views[Center]; ok { g.viewDims[Center] = gui.D(leftWidth, topHeight, img.Bounds().Dx()-leftWidth-rightWidth, img.Bounds().Dy()-topHeight-bottomHeight) view.Draw(img.SubImage(image.Rect(leftWidth, topHeight, img.Bounds().Dx()-rightWidth, img.Bounds().Dy()-bottomHeight)).(*gui.Image), ctx) } g.verticalLayout = nil g.horizontalLayout = nil } func (g *BorderLayout) Layout(ctx gui.AppContext) (prefWidth, prefHeight int) { g.verticalLayout = CalculateLayoutResult(ctx, []gui.View{g.View(Top), g.View(Center), g.View(Bottom)}) g.horizontalLayout = CalculateLayoutResult(ctx, []gui.View{g.View(Left), g.View(Center), g.View(Right)}) return -1, -1 } func (g *BorderLayout) OnMouseClick(event gui.MouseEvent) (consumed bool) { for slot, dim := range g.viewDims { if event.Position.In(dim) { if g.views[slot].OnMouseClick(event.SubtractPos(dim.Point)) { return true } } } return false } func (g *BorderLayout) OnMouseMove(event gui.MouseEvent) (consumed bool) { for slot, dim := range g.viewDims { if event.Position.In(dim) { if g.views[slot].OnMouseMove(event.SubtractPos(dim.Point)) { return true } } } return false } func (g *BorderLayout) OnMouseScroll(event gui.MouseEvent) (consumed bool) { for slot, dim := range g.viewDims { if event.Position.In(dim) { if g.views[slot].OnMouseScroll(event.SubtractPos(dim.Point)) { return true } } } return false } type Slot string const ( Top Slot = "top" Bottom Slot = "bottom" Left Slot = "left" Right Slot = "right" Center Slot = "center" )