grid-box.zc
来自「实现树形结构」· ZC 代码 · 共 638 行
ZC
638 行
//[of]:description
//[c]Grid layout manager
//[c]
//[cf]
//[of]:license
//[c]Code Browser - a folding text editor for programmers
//[c]Copyright (C) 2003-07 Marc Kerbiquet
//[c]
//[c]This program is free software; you can redistribute it and/or modify
//[c]it under the terms of the GNU General Public License as published by
//[c]the Free Software Foundation; either version 2 of the License, or
//[c](at your option) any later version.
//[c]
//[c]This program is distributed in the hope that it will be useful,
//[c]but WITHOUT ANY WARRANTY; without even the implied warranty of
//[c]MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//[c]GNU General Public License for more details.
//[c]
//[c]You should have received a copy of the GNU General Public License
//[c]along with this program; if not, write to the Free Software
//[c]Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//[cf]
//[of]:imports
import "base/types"
import "base/memory-allocator"
//[c]
import "graphics/geometry"
import "user/box"
import "user/container-box"
import "user/blank-box"
//[cf]
//[c]
//[of]:grid cell
//[of]:description
//[c]A grid cell describes the location and constraints of
//[c]a widget in the grid box container.
//[cf]
//[of]:type
public struct grid cell
// next chains all grid cells for a grid.
next: grid cell
// the box that occupy this cell
box: box
// the next attibutes define the rectangle in grid units
left: int
top: int
width: int
height: int
end
//[cf]
//[of]:instance creation
//[of]:new grid cell (next, box, left, top, width, height)
//[c]
public func new grid cell (
next: grid cell,
box: box,
left: int,
top: int,
width: int,
height: int)
def c = allocate memory (sizeof local grid cell): grid cell
initialize (c, next, box, left, top, width, height)
return c
end
//[cf]
//[of]:delete
public func delete (cell: grid cell)
free memory (cell)
end
//[cf]
//[cf]
//[of]:initialize - release
//[of]:initialize (...)
//[c]Initializes a cell
//[c]
public func initialize(
m: grid cell,
next: grid cell,
box: box,
left: int,
top: int,
width: int,
height: int)
next (m) = next
box (m) = box
left (m) = left
top (m) = top
width (m) = width
height (m) = height
end
//[cf]
//[cf]
//[of]:private
//[of]:each
//[c]Enumerates all cells (given the first cell of a linked list)
//[c]
equ each (cell: grid cell)
def e = cell
while not nil (e)
yield (e)
e = next (e)
end
end
//[cf]
//[of]:intersects (rectangle)
//[c]Returns true if the cell intersects with r
//[c]
equ intersects (cell: grid cell, r: rectangle) =
overlaps (
r,
rectangle (
left (cell),
top (cell),
width (cell),
height (cell)))
//[cf]
//[of]:intersects any (rectangle)
//[c]Returns true if any of the cells instersects with the rectangle
//[c]
func intersects any (cell: grid cell, r: rectangle)
each (cell) ? e
if intersects (e, r)
return true
end
end
return false
end
//[cf]
//[cf]
//[cf]
//[of]:grid box
//[of]:type
public struct grid box : local container box
columns: int // number of columns
rows: int // number of rows
elements: grid cell // cells
hband: list of bands // horizontal bands
vband: list of bands // vertical bands
hbuf: [] int // buffer to store offsets
vbuf: [] int // buffer to store offsets
end
//[cf]
//[of]:instance creation
//[of]:new grid box (parent box)
public func new grid box (parent: box)
equ s = sizeof local grid box
def b = allocate memory (s): grid box
initialize (b, parent)
return b
end
//[cf]
//[cf]
//[of]:configuring layout
//[of]:configure (h, v, grid cell)
//[c]Configures the grid
//[c]
//[c] horizontal and vertical are int arrays terminated by -1.
//[c] - 0 means non stretchable columns
//[c] - 1 means stretchable columns
//[c] - -1 is the end of the list
//[c]
//[c]
func len (m: [] int)
def index = 0
while m[index]<>-1; index+=1; end
return index
end
//[c]
public func configure (m: grid box, h: [] int, v: [] int, e: grid cell)
def columns = len (h)
def rows = len (v)
hband (m) = new bands (columns)
vband (m) = new bands (rows)
hbuf (m) = new offsets (columns+1)
vbuf (m) = new offsets (rows+1)
columns (m) = columns
rows (m) = rows
elements (m) = e
def band = hband (m)
repeat
def c = h[]
if c==-1; break; end
band[].stretch = c
++h
++band
end
band = vband (m)
repeat
def c = v[]
if c==-1; break; end
band[].stretch = c
++v
++band
end
end
//[cf]
//[of]:configure (h, v, grid cell, extern, intern)
//[c]Configures the grid with margins
//[c]
//[c] horizontal and vertical are int arrays terminated by -1.
//[c] - 0 means non stretchable columns
//[c] - 1 means stretchable columns
//[c] - -1 is the end of the list
//[c]
public func configure (
m: grid box,
h: [] int,
v: [] int,
e: grid cell,
extern: int,
intern: int)
def offset = (extern == 0) -> 0, 1
def columns = 2 * (len (h) + offset) - 1
def rows = 2 * (len (v) + offset) - 1
hband (m) = new bands (columns)
vband (m) = new bands (rows)
hbuf (m) = new offsets (columns+1)
vbuf (m) = new offsets (rows+1)
columns (m) = columns
rows (m) = rows
each (e) ? el
left (el) = 2 * left (el) + offset
width (el) = 2 * width (el) - 1
top (el) = 2 * top (el) + offset
height (el) = 2 * height (el) - 1
end
def band = hband (m)
def sep = (offset == 1)
repeat
def c = h++[]
if c==-1; break; end
if sep
band[].stretch = 0
++band
end
sep = true
band[].stretch = c
++band
end
if offset == 1
band[].stretch = 0
end
band = vband (m)
sep = (offset == 1)
repeat
def c = v++[]
if c==-1; break; end
if sep
band[].stretch = 0
++band
end
sep = true
band[].stretch = c
++band
end
if offset == 1
band[].stretch = 0
end
// external margins
if extern > 0
e = new grid cell (e, new blank box (m, extern), 0, 0, columns, 1)
e = new grid cell (e, new blank box (m, extern), 0, rows-1, columns, 1)
e = new grid cell (e, new blank box (m, extern), 0, 1, 1, rows-2)
e = new grid cell (e, new blank box (m, extern), columns-1, 1, 1, rows-2)
end
// create internal margins
def y = offset
def wg = columns - offset
def hg = rows - offset
while y < hg
def x = offset
while x < wg
def r := rectangle (x, y, 0, 1)
while x < wg
++ w (r)
// interesect with a cell
if intersects any (e, r)
-- w (r)
break
end
++ x
end
if w (r) > 0
e = new grid cell (e, new blank box (m, intern), x (r), y (r), w (r), h (r))
end
++x
end
++y
end
elements (m) = e
end
//[cf]
//[cf]
//[c]
//[of]:restricted
//[of]:initialize (parent)
public func initialize (m: grid box, parent: box)
initialize (super (m), parent)
class (m) = grid box class
elements (m) = nil
hband (m) = nil
vband (m) = nil
hbuf (m) = nil
vbuf (m) = nil
end
//[cf]
//[c]
//[of]:actual compute min size
//[c]Computes min size
//[c]
public func actual compute min size (m: grid box)
// aliases
def h = hband (m)
def v = vband (m)
// reset the minimum widthes
def c = 0
def cmax = columns (m)
while c<cmax
h[c++].min = 0
end
// reset the minimum heights
def r = 0
def rmax = rows (m)
while r<rmax
v[r++].min = 0
end
// pass 1 - compute with single length boxes
each element (m) ? e
if width (e) == 1
h[e.left].min = max (h[e.left].min, min width (box (e)))
end
if height (e) == 1
v[e.top].min = max (v[e.top].min, min height (box (e)))
end
end
// pass 2 - compute with multiple lengthes boxes
each element (m) ? e
if width (e) > 1
compute minimum (h, left (e), left (e) + width (e), min width (box (e)))
end
if height (e) > 1
compute minimum (v, top (e), top (e) + height (e), min height (box (e)))
end
end
// compute the global min size
def width = 0
def height = 0
c = 0
r = 0
while c < cmax; width += h[c++].min; end
while r < rmax; height += v[r++].min; end
set (min size (m), width, height)
end
//[cf]
//[of]:actual adjust
//[c]Adjust
//[c]Min sizes may have changed: the layout must be recomputed
//[c]
public func actual adjust (m: grid box)
compute layout (m)
end
//[cf]
//[of]:actual release
public func actual release (m: grid box)
free memory (hband (m):mem)
free memory (vband (m):mem)
free memory (hbuf (m):mem)
free memory (vbuf (m):mem)
def cell = elements (m)
while not nil (cell)
def next = next (cell)
delete (cell)
cell = next
end
actual release (super (m))
end
//[cf]
//[c]
//[of]:handle size
//[c]The grid has been resized
//[c]
public func actual on size (m: grid box)
compute layout (m)
end
//[cf]
//[cf]
//[of]:private
//[of]:compute layout
//[c]Computes the layout: move all children
//[c]
func compute layout (m: grid box)
def a = client rect (m)
def left = x (a)
def top = y (a)
def x = hbuf (m)
def y = vbuf (m)
def cmax = columns (m)
def rmax = rows (m)
// compute lengths
compute length (hband (m), 0, cmax, w (a))
compute length (vband (m), 0, rmax, h (a))
// compute x
x[0] = 0
def c = 0
def band = hband (m)
while c < cmax
x[c+1] = x[c] + band[c].length
c += 1
end
// compute y
y[0] = 0
def r = 0
band = vband (m)
while r < rmax
y[r+1] = y[r] + band[r].length
r += 1
end
// move boxes
each element (m) ? e
def x1 = x[e.left]
def x2 = x[e.left + e.width]
def y1 = y[e.top]
def y2 = y[e.top + e.height]
move(
box (e),
rectangle from boundaries (x1+left, y1+top, x2+left, y2+top))
end
end
//[cf]
//[of]:each element
equ each element (m: grid box)
def e = elements (m)
while not nil (e)
yield (e)
e = next (e)
end
end
//[cf]
//[c]
//[c]list of bands:
//[of]:type
//[c]Grid Box Band
//[c]
//[c] A band defines a constraint for a single row or a
//[c] single column in the grid.
//[c]
struct grid box band
// stretch
// the stretch attribute is set at initialization time
// 0 = don't stretch
// 1 = stretch (catch a part of the extra length)
stretch: int
// minimum length of the segment
// the minimum is supplied by the inner box
min: int
// length of the segment
length: int
end
//[c]
//[c]List of Bands
//[c]
typedef list of bands = [] local grid box band
//[cf]
//[of]:new bands (n)
//[c]
equ new bands (n: int) =
allocate memory (sizeof local grid box band * n:dword): list of bands
//[cf]
//[of]:compute length (list of bands, s1, s2, total)
//[c]Computes length
//[c]
//[c] The sum of lengths in the [s1,s2[ range must
//[c] be at leat equal to total. The difference is shared
//[c] between each band.
//[c]
func compute length (band: list of bands, s1: int, s2: int, total: int)
def stretchable = 0
def s = s1
while s<s2
band[s].length = band[s].min
total -= band[s].min
stretchable += band[s].stretch
s += 1
end
if total>0
def current = 0
def last = 0
s = s1
while s<s2
if band[s].stretch<>0
def n = (++current * total) / stretchable
band[s].length += n - last
last = n
end
s += 1
end
end
end
//[cf]
//[of]:compute minimum (list of bands, s1, s2, total)
//[c]Computes Minimum
//[c]
//[c] The sum of minima in the [s1,s2[ range must
//[c] be at leat equal to total. The difference is shared
//[c] between each band.
//[c]
func compute minimum (band: list of bands, s1: int, s2: int, total: int)
def stretchable = 0
def s = s1
while s<s2
total -= band[s].min
stretchable += band[s].stretch
s += 1
end
if total>0
def current = 0
def last = 0
s = s1
while s<s2
if band[s].stretch<>0
def n = (++current * total) / stretchable
band[s].min += n - last
last = n
end
s += 1
end
end
end
//[cf]
//[c]
//[c]list of offsets:
//[of]:new offsets (n)
//[c]
equ new offsets (n: int) =
allocate memory (sizeof int * n:dword): [] int
//[cf]
//[cf]
//[cf]
//[of]:grid box class
//[of]:type
public struct grid box class : local container box class
// empty
end
//[cf]
//[of]:grid box class
public func grid box class
def c = the grid box class
if ~ initialized
initialized = true
c . copy (container box class)
c . release = ^actual release (grid box)
c . on size = ^actual on size (grid box)
c . adjust = ^actual adjust (grid box)
c . compute min size = ^actual compute min size (grid box)
end
return c
end
private def initialized = false
private def the grid box class: local grid box class
//[cf]
//[cf]
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?