erm/vendor/github.com/lxn/walk/flowlayout.go
2021-07-30 23:29:20 +01:00

283 lines
6.8 KiB
Go

// Copyright 2018 The Walk Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build windows
package walk
import (
"github.com/lxn/win"
)
type FlowLayout struct {
LayoutBase
hwnd2StretchFactor map[win.HWND]int
}
func NewFlowLayout() *FlowLayout {
l := &FlowLayout{
LayoutBase: LayoutBase{
margins96dpi: Margins{9, 9, 9, 9},
spacing96dpi: 6,
},
hwnd2StretchFactor: make(map[win.HWND]int),
}
l.layout = l
return l
}
func (l *FlowLayout) StretchFactor(widget Widget) int {
if factor, ok := l.hwnd2StretchFactor[widget.Handle()]; ok {
return factor
}
return 1
}
func (l *FlowLayout) SetStretchFactor(widget Widget, factor int) error {
if factor != l.StretchFactor(widget) {
if l.container == nil {
return newError("container required")
}
handle := widget.Handle()
if !l.container.Children().containsHandle(handle) {
return newError("unknown widget")
}
if factor < 1 {
return newError("factor must be >= 1")
}
l.hwnd2StretchFactor[handle] = factor
l.container.RequestLayout()
}
return nil
}
func (l *FlowLayout) CreateLayoutItem(ctx *LayoutContext) ContainerLayoutItem {
li := &flowLayoutItem{
size2MinSize: make(map[Size]Size),
hwnd2StretchFactor: make(map[win.HWND]int),
}
for hwnd, sf := range l.hwnd2StretchFactor {
li.hwnd2StretchFactor[hwnd] = sf
}
return li
}
type flowLayoutItem struct {
ContainerLayoutItemBase
size2MinSize map[Size]Size // in native pixels
hwnd2StretchFactor map[win.HWND]int
}
type flowLayoutSection struct {
items []flowLayoutSectionItem
primarySpaceLeft int // in native pixels
secondaryMinSize int // in native pixels
}
type flowLayoutSectionItem struct {
item LayoutItem
minSize Size // in native pixels
}
func (*flowLayoutItem) LayoutFlags() LayoutFlags {
return ShrinkableHorz | ShrinkableVert | GrowableHorz | GrowableVert | GreedyHorz | GreedyVert
}
func (li *flowLayoutItem) MinSize() Size {
return li.MinSizeForSize(li.geometry.ClientSize)
}
func (li *flowLayoutItem) HeightForWidth(width int) int {
return li.MinSizeForSize(Size{width, li.geometry.ClientSize.Height}).Height
}
func (li *flowLayoutItem) MinSizeForSize(size Size) Size {
if min, ok := li.size2MinSize[size]; ok {
return min
}
spacing := IntFrom96DPI(li.spacing96dpi, li.ctx.dpi)
margins := MarginsFrom96DPI(li.margins96dpi, li.ctx.dpi)
bounds := Rectangle{Width: size.Width}
sections := li.sectionsForPrimarySize(size.Width)
var s Size
var maxPrimary int
for i, section := range sections {
var items []LayoutItem
var sectionMinWidth int
for _, sectionItem := range section.items {
items = append(items, sectionItem.item)
sectionMinWidth += sectionItem.minSize.Width
}
sectionMinWidth += (len(section.items) - 1) * spacing
maxPrimary = maxi(maxPrimary, sectionMinWidth)
bounds.Height = section.secondaryMinSize
margins96dpi := li.margins96dpi
if i > 0 {
margins96dpi.VNear = 0
}
if i < len(sections)-1 {
margins96dpi.VFar = 0
}
layoutItems := boxLayoutItems(li, items, Horizontal, li.alignment, bounds, margins96dpi, li.spacing96dpi, li.hwnd2StretchFactor)
var maxSecondary int
for _, item := range layoutItems {
if hfw, ok := item.Item.(HeightForWidther); ok && hfw.HasHeightForWidth() {
item.Bounds.Height = hfw.HeightForWidth(item.Bounds.Width)
} else {
min := li.MinSizeEffectiveForChild(item.Item)
item.Bounds.Height = min.Height
}
maxSecondary = maxi(maxSecondary, item.Bounds.Height)
}
s.Height += maxSecondary
bounds.Y += maxSecondary + spacing
}
s.Width = maxPrimary
s.Width += margins.HNear + margins.HFar
s.Height += margins.VNear + margins.VFar + (len(sections)-1)*spacing
if s.Width > 0 && s.Height > 0 {
li.size2MinSize[size] = s
}
return s
}
func (li *flowLayoutItem) PerformLayout() []LayoutResultItem {
spacing := IntFrom96DPI(li.spacing96dpi, li.ctx.dpi)
bounds := Rectangle{Width: li.geometry.ClientSize.Width, Height: li.geometry.ClientSize.Height}
sections := li.sectionsForPrimarySize(bounds.Width)
var resultItems []LayoutResultItem
for i, section := range sections {
var items []LayoutItem
for _, sectionItem := range section.items {
items = append(items, sectionItem.item)
}
bounds.Height = section.secondaryMinSize
margins96dpi := li.margins96dpi
if i > 0 {
margins96dpi.VNear = 0
}
if i < len(sections)-1 {
margins96dpi.VFar = 0
}
layoutItems := boxLayoutItems(li, items, Horizontal, li.alignment, bounds, margins96dpi, li.spacing96dpi, li.hwnd2StretchFactor)
margins := MarginsFrom96DPI(margins96dpi, li.ctx.dpi)
var maxSecondary int
for _, item := range layoutItems {
if hfw, ok := item.Item.(HeightForWidther); ok && hfw.HasHeightForWidth() {
item.Bounds.Height = hfw.HeightForWidth(item.Bounds.Width)
} else {
item.Bounds.Height = li.MinSizeEffectiveForChild(item.Item).Height
}
maxSecondary = maxi(maxSecondary, item.Bounds.Height)
}
bounds.Height = maxSecondary + margins.VNear + margins.VFar
resultItems = append(resultItems, boxLayoutItems(li, items, Horizontal, li.alignment, bounds, margins96dpi, li.spacing96dpi, li.hwnd2StretchFactor)...)
bounds.Y += bounds.Height + spacing
}
return resultItems
}
// sectionsForPrimarySize calculates sections for primary width in native pixels.
func (li *flowLayoutItem) sectionsForPrimarySize(primarySize int) []flowLayoutSection {
margins := MarginsFrom96DPI(li.margins96dpi, li.ctx.dpi)
spacing := IntFrom96DPI(li.spacing96dpi, li.ctx.dpi)
var sections []flowLayoutSection
section := flowLayoutSection{
primarySpaceLeft: primarySize - margins.HNear - margins.HFar,
}
addSection := func() {
sections = append(sections, section)
section.items = nil
section.primarySpaceLeft = primarySize - margins.HNear - margins.HFar
section.secondaryMinSize = 0
}
for _, item := range li.children {
var sectionItem flowLayoutSectionItem
sectionItem.item = item
if !shouldLayoutItem(item) {
continue
}
sectionItem.minSize = li.MinSizeEffectiveForChild(item)
addItem := func() {
section.items = append(section.items, sectionItem)
if len(section.items) > 1 {
section.primarySpaceLeft -= spacing
}
section.primarySpaceLeft -= sectionItem.minSize.Width
section.secondaryMinSize = maxi(section.secondaryMinSize, sectionItem.minSize.Height)
}
if section.primarySpaceLeft < sectionItem.minSize.Width && len(section.items) == 0 {
addItem()
addSection()
} else if section.primarySpaceLeft < spacing+sectionItem.minSize.Width && len(section.items) > 0 {
addSection()
addItem()
} else {
addItem()
}
}
if len(section.items) > 0 {
addSection()
}
if len(sections) > 0 {
sections[0].secondaryMinSize += margins.VNear
sections[len(sections)-1].secondaryMinSize += margins.VFar
}
return sections
}