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 + -
显示快捷键?