package layouts import ( "image" "git.milar.in/milarin/gui" ) // FlowLayout ia a gui.Layout which places its children in a linear layout type FlowLayout struct { gui.ViewTmpl views []gui.View lastLayoutPhase *LayoutResult viewDims map[gui.View]gui.Dimension // Orientation defines in which direction the children will be placed Orientation gui.Orientation } var _ gui.Layout = &FlowLayout{} func NewFlowLayout(orientation gui.Orientation) *FlowLayout { return &FlowLayout{ views: make([]gui.View, 0), viewDims: map[gui.View]gui.Dimension{}, Orientation: orientation, } } func (g *FlowLayout) Views() []gui.View { return g.views } func (g *FlowLayout) AppendViews(v ...gui.View) { g.views = append(g.views, v...) } func (g *FlowLayout) PrependViews(v ...gui.View) { g.views = append(v, g.views...) } func (g *FlowLayout) InsertView(v gui.View, index int) { g.views = append(g.views[:index], append([]gui.View{v}, g.views[index:]...)...) } func (g *FlowLayout) removeView(v gui.View) { for index, view := range g.Views() { if v == view { delete(g.viewDims, view) g.views = append(g.views[:index], g.views[index+1:]...) return } } } func (g *FlowLayout) RemoveViews(v ...gui.View) { views := append(make([]gui.View, 0, len(v)), v...) for _, view := range views { g.removeView(view) } } func (g *FlowLayout) Draw(img *gui.Image, ctx gui.AppContext) { g.ViewTmpl.Draw(img, ctx) if g.lastLayoutPhase == nil { g.Layout(ctx) } layout := g.lastLayoutPhase if g.Orientation == gui.Horizontal { remainingSpacePerView := img.Bounds().Dx() - layout.Sum.Width if remainingSpacePerView < 0 { remainingSpacePerView = 0 } if layout.HorizontalNegativeCount > 0 { remainingSpacePerView /= layout.HorizontalNegativeCount } x := 0 for _, view := range g.views { size := layout.Sizes[view] size.Height = iff(size.Height < 0, img.Bounds().Dy(), size.Height) if size.Width < 0 { size.Width = iff(layout.Sum.Width > img.Bounds().Dx(), 0, remainingSpacePerView) } g.viewDims[view] = gui.D(x, 0, size.Width, size.Height) view.Draw(img.SubImage(image.Rect(x, 0, x+size.Width, size.Height)).(*gui.Image), ctx) x += size.Width if x >= img.Bounds().Dx() { break } } } else if g.Orientation == gui.Vertical { remainingSpacePerView := img.Bounds().Dy() - layout.Sum.Height if remainingSpacePerView < 0 { remainingSpacePerView = 0 } if layout.VerticalNegativeCount > 0 { remainingSpacePerView /= layout.VerticalNegativeCount } y := 0 for _, view := range g.views { size := layout.Sizes[view] size.Width = iff(size.Width < 0, img.Bounds().Dx(), size.Width) if size.Height < 0 { size.Height = iff(layout.Sum.Height > img.Bounds().Dy(), 0, remainingSpacePerView) } g.viewDims[view] = gui.D(0, y, size.Width, size.Height) view.Draw(img.SubImage(image.Rect(0, y, size.Width, y+size.Height)).(*gui.Image), ctx) y += size.Height if y >= img.Bounds().Dy() { break } } } g.lastLayoutPhase = nil } func (g *FlowLayout) Layout(ctx gui.AppContext) (prefWidth, prefHeight int) { layout := CalculateLayoutResult(ctx, g.Views()) g.lastLayoutPhase = layout if g.Orientation == gui.Horizontal { prefWidth = iff(layout.HorizontalNegativeCount == 0, layout.Sum.Width, -1) prefHeight = iff(layout.VerticalNegativeCount == 0, layout.Max.Height, -1) } else if g.Orientation == gui.Vertical { prefWidth = iff(layout.HorizontalNegativeCount == 0, layout.Max.Width, -1) prefHeight = iff(layout.VerticalNegativeCount == 0, layout.Sum.Height, -1) } layout.Pref = gui.Size{Width: prefWidth, Height: prefHeight} return } func (g *FlowLayout) OnMouseClick(event gui.MouseEvent) (consumed bool) { for view, dim := range g.viewDims { if event.Position.In(dim) { if view.OnMouseClick(event.SubtractPos(dim.Point)) { return true } } } return false } func (g *FlowLayout) OnMouseMove(event gui.MouseEvent) (consumed bool) { for view, dim := range g.viewDims { if event.Position.In(dim) { if view.OnMouseMove(event.SubtractPos(dim.Point)) { return true } } } return false } func (g *FlowLayout) OnMouseScroll(event gui.MouseEvent) (consumed bool) { for view, dim := range g.viewDims { if event.Position.In(dim) { if view.OnMouseScroll(event.SubtractPos(dim.Point)) { return true } } } return false }