1 Star 0 Fork 0

程序员锡哥/walk

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
gridlayout.go 14.69 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690
// Copyright 2011 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 (
"sort"
)
import (
"github.com/lxn/win"
)
type gridLayoutCell struct {
row int
column int
widget Widget
}
type gridLayoutSection struct {
greedyNonSpacerCount int
greedySpacerCount int
}
type gridLayoutWidgetInfo struct {
cell *gridLayoutCell
spanHorz int
spanVert int
minSize Size
minSizeHint Size
}
type GridLayout struct {
container Container
margins Margins
spacing int
resetNeeded bool
rowStretchFactors []int
columnStretchFactors []int
widget2Info map[Widget]*gridLayoutWidgetInfo
cells [][]gridLayoutCell
}
func NewGridLayout() *GridLayout {
l := &GridLayout{
widget2Info: make(map[Widget]*gridLayoutWidgetInfo),
}
return l
}
func (l *GridLayout) Container() Container {
return l.container
}
func (l *GridLayout) SetContainer(value Container) {
if value != l.container {
if l.container != nil {
l.container.SetLayout(nil)
}
l.container = value
if value != nil && value.Layout() != Layout(l) {
value.SetLayout(l)
l.Update(true)
}
}
}
func (l *GridLayout) Margins() Margins {
return l.margins
}
func (l *GridLayout) SetMargins(value Margins) error {
if value.HNear < 0 || value.VNear < 0 || value.HFar < 0 || value.VFar < 0 {
return newError("margins must be positive")
}
l.margins = value
return nil
}
func (l *GridLayout) Spacing() int {
return l.spacing
}
func (l *GridLayout) SetSpacing(value int) error {
if value != l.spacing {
if value < 0 {
return newError("spacing cannot be negative")
}
l.spacing = value
l.Update(false)
}
return nil
}
func (l *GridLayout) sufficientStretchFactors(stretchFactors []int, required int) []int {
oldLen := len(stretchFactors)
if oldLen < required {
if cap(stretchFactors) < required {
temp := make([]int, required, maxi(required, len(stretchFactors)*2))
copy(temp, stretchFactors)
stretchFactors = temp
} else {
stretchFactors = stretchFactors[:required]
}
for i := oldLen; i < len(stretchFactors); i++ {
stretchFactors[i] = 1
}
}
return stretchFactors
}
func (l *GridLayout) ensureSufficientSize(rows, columns int) {
l.rowStretchFactors = l.sufficientStretchFactors(l.rowStretchFactors, rows)
l.columnStretchFactors = l.sufficientStretchFactors(l.columnStretchFactors, columns)
if len(l.cells) < len(l.rowStretchFactors) {
if cap(l.cells) < cap(l.rowStretchFactors) {
temp := make([][]gridLayoutCell, len(l.rowStretchFactors), cap(l.rowStretchFactors))
copy(temp, l.cells)
l.cells = temp
} else {
l.cells = l.cells[:len(l.rowStretchFactors)]
}
}
for i := 0; i < len(l.cells); i++ {
if len(l.cells[i]) < len(l.columnStretchFactors) {
if cap(l.cells[i]) < cap(l.columnStretchFactors) {
temp := make([]gridLayoutCell, len(l.columnStretchFactors))
copy(temp, l.cells[i])
l.cells[i] = temp
} else {
l.cells[i] = l.cells[i][:len(l.columnStretchFactors)]
}
}
}
// FIXME: Not sure if this works.
for widget, info := range l.widget2Info {
l.widget2Info[widget].cell = &l.cells[info.cell.row][info.cell.column]
}
}
func (l *GridLayout) RowStretchFactor(row int) int {
if row < 0 {
// FIXME: Should we rather return an error?
return -1
}
if row >= len(l.rowStretchFactors) {
return 1
}
return l.rowStretchFactors[row]
}
func (l *GridLayout) SetRowStretchFactor(row, factor int) error {
if row < 0 {
return newError("row must be >= 0")
}
if factor != l.RowStretchFactor(row) {
if l.container == nil {
return newError("container required")
}
if factor < 1 {
return newError("factor must be >= 1")
}
l.ensureSufficientSize(row+1, len(l.columnStretchFactors))
l.rowStretchFactors[row] = factor
l.Update(false)
}
return nil
}
func (l *GridLayout) ColumnStretchFactor(column int) int {
if column < 0 {
// FIXME: Should we rather return an error?
return -1
}
if column >= len(l.columnStretchFactors) {
return 1
}
return l.columnStretchFactors[column]
}
func (l *GridLayout) SetColumnStretchFactor(column, factor int) error {
if column < 0 {
return newError("column must be >= 0")
}
if factor != l.ColumnStretchFactor(column) {
if l.container == nil {
return newError("container required")
}
if factor < 1 {
return newError("factor must be >= 1")
}
l.ensureSufficientSize(len(l.rowStretchFactors), column+1)
l.columnStretchFactors[column] = factor
l.Update(false)
}
return nil
}
func rangeFromGridLayoutWidgetInfo(info *gridLayoutWidgetInfo) Rectangle {
return Rectangle{
X: info.cell.column,
Y: info.cell.row,
Width: info.spanHorz,
Height: info.spanVert,
}
}
func (l *GridLayout) setWidgetOnCells(widget Widget, r Rectangle) {
for row := r.Y; row < r.Y+r.Height; row++ {
for col := r.X; col < r.X+r.Width; col++ {
l.cells[row][col].widget = widget
}
}
}
func (l *GridLayout) Range(widget Widget) (r Rectangle, ok bool) {
if widget == nil {
return Rectangle{}, false
}
info := l.widget2Info[widget]
if info == nil ||
l.container == nil ||
!l.container.Children().containsHandle(widget.Handle()) {
return Rectangle{}, false
}
return rangeFromGridLayoutWidgetInfo(info), true
}
func (l *GridLayout) SetRange(widget Widget, r Rectangle) error {
if widget == nil {
return newError("widget required")
}
if l.container == nil {
return newError("container required")
}
if !l.container.Children().containsHandle(widget.Handle()) {
return newError("widget must be child of container")
}
if r.X < 0 || r.Y < 0 {
return newError("range.X and range.Y must be >= 0")
}
if r.Width < 0 || r.Height < 0 {
return newError("range.Width and range.Height must be > 1")
}
info := l.widget2Info[widget]
if info == nil {
info = new(gridLayoutWidgetInfo)
} else {
l.setWidgetOnCells(nil, rangeFromGridLayoutWidgetInfo(info))
}
l.ensureSufficientSize(r.Y+r.Height, r.X+r.Width)
cell := &l.cells[r.Y][r.X]
cell.row = r.Y
cell.column = r.X
if info.cell == nil {
// We have to do this _after_ calling ensureSufficientSize().
l.widget2Info[widget] = info
}
info.cell = cell
info.spanHorz = r.Width
info.spanVert = r.Height
l.setWidgetOnCells(widget, r)
return nil
}
func (l *GridLayout) cleanup() {
// Make sure only children of our container occupy the precious cells.
children := l.container.Children()
for widget, info := range l.widget2Info {
if !children.containsHandle(widget.Handle()) {
l.setWidgetOnCells(nil, rangeFromGridLayoutWidgetInfo(info))
delete(l.widget2Info, widget)
}
}
}
func (l *GridLayout) stretchFactorsTotal(stretchFactors []int) int {
total := 0
for _, v := range stretchFactors {
total += maxi(1, v)
}
return total
}
func (l *GridLayout) LayoutFlags() LayoutFlags {
if l.container == nil {
return 0
}
var flags LayoutFlags
children := l.container.Children()
count := children.Len()
if count == 0 {
return ShrinkableHorz | ShrinkableVert | GrowableHorz | GrowableVert
} else {
for i := 0; i < count; i++ {
widget := children.At(i)
if shouldLayoutWidget(widget) {
flags |= widget.LayoutFlags()
}
}
}
return flags
}
func (l *GridLayout) MinSize() Size {
if l.container == nil || len(l.cells) == 0 {
return Size{}
}
widths := make([]int, len(l.cells[0]))
heights := make([]int, len(l.cells))
widget2MinSize := make(map[Widget]Size)
for widget, _ := range l.widget2Info {
if !shouldLayoutWidget(widget) {
continue
}
widget2MinSize[widget] = minSizeEffective(widget)
}
for row := 0; row < len(heights); row++ {
for col := 0; col < len(widths); col++ {
widget := l.cells[row][col].widget
if !shouldLayoutWidget(widget) {
continue
}
min := widget2MinSize[widget]
info := l.widget2Info[widget]
if info.spanHorz == 1 {
widths[col] = maxi(widths[col], min.Width)
}
if info.spanVert == 1 {
heights[row] = maxi(heights[row], min.Height)
}
}
}
width := l.margins.HNear + l.margins.HFar
height := l.margins.VNear + l.margins.VFar
for i, w := range widths {
if w > 0 {
if i > 0 {
width += l.spacing
}
width += w
}
}
for i, h := range heights {
if h > 0 {
if i > 0 {
height += l.spacing
}
height += h
}
}
return Size{width, height}
}
type gridLayoutSectionInfo struct {
index int
minSize int
maxSize int
stretch int
hasGreedyNonSpacer bool
hasGreedySpacer bool
}
type gridLayoutSectionInfoList []gridLayoutSectionInfo
func (l gridLayoutSectionInfoList) Len() int {
return len(l)
}
func (l gridLayoutSectionInfoList) Less(i, j int) bool {
if l[i].hasGreedyNonSpacer == l[j].hasGreedyNonSpacer {
if l[i].hasGreedySpacer == l[j].hasGreedySpacer {
minDiff := l[i].minSize - l[j].minSize
if minDiff == 0 {
return l[i].maxSize/l[i].stretch < l[j].maxSize/l[j].stretch
}
return minDiff > 0
}
return l[i].hasGreedySpacer
}
return l[i].hasGreedyNonSpacer
}
func (l gridLayoutSectionInfoList) Swap(i, j int) {
l[i], l[j] = l[j], l[i]
}
func (l *GridLayout) Update(reset bool) error {
if l.container == nil {
return nil
}
if reset {
l.resetNeeded = true
}
if l.container.Suspended() {
return nil
}
if l.resetNeeded {
l.resetNeeded = false
l.cleanup()
}
widths := l.sectionSizes(Horizontal)
heights := l.sectionSizes(Vertical)
hdwp := win.BeginDeferWindowPos(int32(l.container.Children().Len()))
if hdwp == 0 {
return lastError("BeginDeferWindowPos")
}
for widget, info := range l.widget2Info {
x := l.margins.HNear
for i := 0; i < info.cell.column; i++ {
x += widths[i] + l.spacing
}
y := l.margins.VNear
for i := 0; i < info.cell.row; i++ {
y += heights[i] + l.spacing
}
w := 0
for i := info.cell.column; i < info.cell.column+info.spanHorz; i++ {
w += widths[i]
if i > info.cell.column {
w += l.spacing
}
}
h := 0
for i := info.cell.row; i < info.cell.row+info.spanVert; i++ {
h += heights[i]
if i > info.cell.row {
h += l.spacing
}
}
if lf := widget.LayoutFlags(); lf&GrowableHorz == 0 || lf&GrowableVert == 0 {
hint := widget.SizeHint()
if lf&GrowableHorz == 0 {
w = hint.Width
}
if lf&GrowableVert == 0 {
h = hint.Height
}
}
if hdwp = win.DeferWindowPos(
hdwp,
widget.Handle(),
0,
int32(x),
int32(y),
int32(w),
int32(h),
win.SWP_NOACTIVATE|win.SWP_NOOWNERZORDER|win.SWP_NOZORDER); hdwp == 0 {
return lastError("DeferWindowPos")
}
}
if !win.EndDeferWindowPos(hdwp) {
return lastError("EndDeferWindowPos")
}
return nil
}
func (l *GridLayout) sectionSizes(orientation Orientation) []int {
var stretchFactors []int
if orientation == Horizontal {
stretchFactors = l.columnStretchFactors
} else {
stretchFactors = l.rowStretchFactors
}
var sectionCountWithGreedyNonSpacer int
var sectionCountWithGreedySpacer int
var stretchFactorsTotal [3]int
var minSizesRemaining int
minSizes := make([]int, len(stretchFactors))
maxSizes := make([]int, len(stretchFactors))
sizes := make([]int, len(stretchFactors))
sortedSections := gridLayoutSectionInfoList(make([]gridLayoutSectionInfo, len(stretchFactors)))
for i := 0; i < len(stretchFactors); i++ {
var otherAxisCount int
if orientation == Horizontal {
otherAxisCount = len(l.rowStretchFactors)
} else {
otherAxisCount = len(l.columnStretchFactors)
}
for j := 0; j < otherAxisCount; j++ {
var widget Widget
if orientation == Horizontal {
widget = l.cells[j][i].widget
} else {
widget = l.cells[i][j].widget
}
if !shouldLayoutWidget(widget) {
continue
}
info := l.widget2Info[widget]
flags := widget.LayoutFlags()
min := widget.MinSize()
max := widget.MaxSize()
minHint := widget.MinSizeHint()
pref := widget.SizeHint()
if orientation == Horizontal {
if info.spanHorz == 1 {
minSizes[i] = maxi(minSizes[i], maxi(min.Width, minHint.Width))
}
if max.Width > 0 {
maxSizes[i] = maxi(maxSizes[i], max.Width)
} else if pref.Width > 0 && flags&GrowableHorz == 0 {
maxSizes[i] = maxi(maxSizes[i], pref.Width)
} else {
maxSizes[i] = 32768
}
if info.spanHorz == 1 && flags&GreedyHorz > 0 {
if _, isSpacer := widget.(*Spacer); isSpacer {
sortedSections[i].hasGreedySpacer = true
} else {
sortedSections[i].hasGreedyNonSpacer = true
}
}
} else {
if info.spanVert == 1 {
minSizes[i] = maxi(minSizes[i], maxi(min.Height, minHint.Height))
}
if max.Height > 0 {
maxSizes[i] = maxi(maxSizes[i], max.Height)
} else if pref.Height > 0 && flags&GrowableVert == 0 {
maxSizes[i] = maxi(maxSizes[i], pref.Height)
} else {
maxSizes[i] = 32768
}
if info.spanVert == 1 && flags&GreedyVert > 0 {
if _, isSpacer := widget.(*Spacer); isSpacer {
sortedSections[i].hasGreedySpacer = true
} else {
sortedSections[i].hasGreedyNonSpacer = true
}
}
}
}
sortedSections[i].index = i
sortedSections[i].minSize = minSizes[i]
sortedSections[i].maxSize = maxSizes[i]
sortedSections[i].stretch = maxi(1, stretchFactors[i])
minSizesRemaining += minSizes[i]
if sortedSections[i].hasGreedyNonSpacer {
sectionCountWithGreedyNonSpacer++
stretchFactorsTotal[0] += stretchFactors[i]
} else if sortedSections[i].hasGreedySpacer {
sectionCountWithGreedySpacer++
stretchFactorsTotal[1] += stretchFactors[i]
} else {
stretchFactorsTotal[2] += stretchFactors[i]
}
}
sort.Stable(sortedSections)
cb := l.container.ClientBounds()
var space int
if orientation == Horizontal {
space = cb.Width - l.margins.HNear - l.margins.HFar
} else {
space = cb.Height - l.margins.VNear - l.margins.VFar
}
spacingRemaining := l.spacing * (len(stretchFactors) - 1)
offsets := [3]int{0, sectionCountWithGreedyNonSpacer, sectionCountWithGreedyNonSpacer + sectionCountWithGreedySpacer}
counts := [3]int{sectionCountWithGreedyNonSpacer, sectionCountWithGreedySpacer, len(stretchFactors) - sectionCountWithGreedyNonSpacer - sectionCountWithGreedySpacer}
for i := 0; i < 3; i++ {
stretchFactorsRemaining := stretchFactorsTotal[i]
for j := 0; j < counts[i]; j++ {
info := sortedSections[offsets[i]+j]
k := info.index
stretch := stretchFactors[k]
min := info.minSize
max := info.maxSize
size := min
if min < max {
excessSpace := float64(space - minSizesRemaining - spacingRemaining)
size += int(excessSpace * float64(stretch) / float64(stretchFactorsRemaining))
if size < min {
size = min
} else if size > max {
size = max
}
}
sizes[k] = size
minSizesRemaining -= min
stretchFactorsRemaining -= stretch
space -= (size + l.spacing)
spacingRemaining -= l.spacing
}
}
return sizes
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/msgy/walk.git
git@gitee.com:msgy/walk.git
msgy
walk
walk
master

搜索帮助

0d507c66 1850385 C8b1a773 1850385