Skip to content

Commit

Permalink
Adding stacking layouts - largely copied from Chaco graph layout code.
Browse files Browse the repository at this point in the history
TODO - update chaco code to use these classes.
  • Loading branch information
cwebster committed Jul 2, 2010
1 parent 4e20c80 commit 75fb3ac
Show file tree
Hide file tree
Showing 2 changed files with 260 additions and 0 deletions.
106 changes: 106 additions & 0 deletions enthought/enable/stacked_container.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
""" Containers which lay out their components horizontally or vertically
"""

from enthought.traits.api import Enum, Float

from container import Container
from stacked_layout import stacked_preferred_size, stack_layout

class StackedContainer(Container):
""" Base class for stacked containers
"""

# The dimension along which to stack components that are added to
# this container.
stack_dimension = Enum("h", "v")

# The "other" dimension, i.e., the dual of the stack dimension.
other_dimension = Enum("v", "h")

# The index into obj.position and obj.bounds that corresponds to
# **stack_dimension**. This is a class-level and not an instance-level
# attribute. It must be 0 or 1.
stack_index = 0

# The amount of space to put between components.
spacing = Float(0.0)

def get_preferred_size(self, components=None):
return stacked_preferred_size(self, components)


class HStackedContainer(StackedContainer):
"""
A container that stacks components horizontally.
"""

# Overrides StackedPlotContainer.
stack_dimension = "h"
# Overrides StackedPlotContainer.
other_dimension = "v"
# Overrides StackedPlotContainer.
stack_index = 0

# VPlotContainer attributes

# The horizontal alignment of objects that don't span the full width.
halign = Enum("bottom", "top", "center")

# The order in which components in the plot container are laid out.
stack_order = Enum("left_to_right", "right_to_left")

def _do_layout(self):
""" Actually performs a layout (called by do_layout()).
"""
if self.stack_order == "left_to_right":
components = self.components
else:
components = self.components[::-1]
if self.halign == "bottom":
align = "min"
elif self.halign == "center":
align = "center"
else:
align = "max"

#import pdb; pdb.set_trace()
return stack_layout(self, components, align)

class VStackedContainer(StackedContainer):
"""
A container that stacks components vertically.
"""

# Overrides StackedPlotContainer.
stack_dimension = "v"
# Overrides StackedPlotContainer.
other_dimension = "h"
# Overrides StackedPlotContainer.
stack_index = 1

# VPlotContainer attributes

# The horizontal alignment of objects that don't span the full width.
halign = Enum("left", "right", "center")

# The order in which components in the plot container are laid out.
stack_order = Enum("bottom_to_top", "top_to_bottom")

def _do_layout(self):
""" Actually performs a layout (called by do_layout()).
"""
if self.stack_order == "bottom_to_top":
components = self.components
else:
components = self.components[::-1]
if self.halign == "left":
align = "min"
elif self.halign == "center":
align = "center"
else:
align = "max"

#import pdb; pdb.set_trace()
return stack_layout(self, components, align)

154 changes: 154 additions & 0 deletions enthought/enable/stacked_layout.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
""" Routines for stacked layout of components in a container
"""

# TODO: stolen from Chaco PlotContainers, should change their classes to use

def stacked_preferred_size(container, components=None):
""" Returns the size (width,height) that is preferred for this component.
Overrides Component.
"""
if container.fixed_preferred_size is not None:
container._cached_preferred_size = container.fixed_preferred_size
return container.fixed_preferred_size

#if container.resizable == "":
# container._cached_preferred_size = container.outer_bounds[:]
# return container.outer_bounds

if components is None:
components = container.components

ndx = container.stack_index
other_ndx = 1 - ndx

no_visible_components = True
total_size = 0
max_other_size = 0
for component in components:
if not container._should_layout(component):
continue

no_visible_components = False

pref_size = component.get_preferred_size()
total_size += pref_size[ndx] + container.spacing
#print container, component, total_size
if pref_size[other_ndx] > max_other_size:
max_other_size = pref_size[other_ndx]

if total_size >= container.spacing:
total_size -= container.spacing

if (container.stack_dimension not in container.resizable) and \
(container.stack_dimension not in container.fit_components):
total_size = container.bounds[ndx]
elif no_visible_components or (total_size == 0):
total_size = container.default_size[ndx]

if (container.other_dimension not in container.resizable) and \
(container.other_dimension not in container.fit_components):
max_other_size = container.bounds[other_ndx]
elif no_visible_components or (max_other_size == 0):
max_other_size = container.default_size[other_ndx]

if ndx == 0:
container._cached_preferred_size = (total_size + container.hpadding,
max_other_size + container.vpadding)
else:
container._cached_preferred_size = (max_other_size + container.hpadding,
total_size + container.vpadding)

return container._cached_preferred_size


def stack_layout(container, components, align):
""" Helper method that does the actual work of layout.
"""

size = list(container.bounds)
if container.fit_components != "":
container.get_preferred_size()
if "h" in container.fit_components:
size[0] = container._cached_preferred_size[0] - container.hpadding
if "v" in container.fit_components:
size[1] = container._cached_preferred_size[1] - container.vpadding

ndx = container.stack_index
other_ndx = 1 - ndx
other_dim = container.other_dimension

# Assign sizes of non-resizable components, and compute the total size
# used by them (along the stack dimension).
total_fixed_size = 0
resizable_components = []
size_prefs = {}
total_resizable_size = 0

for component in components:
if not container._should_layout(component):
continue
if container.stack_dimension not in component.resizable:
total_fixed_size += component.outer_bounds[ndx]
else:
preferred_size = component.get_preferred_size()
size_prefs[component] = preferred_size
total_resizable_size += preferred_size[ndx]
resizable_components.append(component)

new_bounds_dict = {}

# Assign sizes of all the resizable components along the stack dimension
if resizable_components:
space = container.spacing * (len(container.components) - 1)
avail_size = size[ndx] - total_fixed_size - space
if total_resizable_size > 0:
scale = avail_size / float(total_resizable_size)
for component in resizable_components:
tmp = list(component.outer_bounds)
tmp[ndx] = int(size_prefs[component][ndx] * scale)
new_bounds_dict[component] = tmp
else:
each_size = int(avail_size / len(resizable_components))
for component in resizable_components:
tmp = list(component.outer_bounds)
tmp[ndx] = each_size
new_bounds_dict[component] = tmp

# Loop over all the components, assigning position and computing the
# size in the other dimension and its position.
cur_pos = 0
for component in components:
if not container._should_layout(component):
continue

position = list(component.outer_position)
position[ndx] = cur_pos

bounds = new_bounds_dict.get(component, list(component.outer_bounds))
cur_pos += bounds[ndx] + container.spacing

if (bounds[other_ndx] > size[other_ndx]) or \
(other_dim in component.resizable):
# If the component is resizable in the other dimension or it exceeds the
# container bounds, set it to the maximum size of the container

#component.set_outer_position(other_ndx, 0)
#component.set_outer_bounds(other_ndx, size[other_ndx])
position[other_ndx] = 0
bounds[other_ndx] = size[other_ndx]
else:
#component.set_outer_position(other_ndx, 0)
#old_coord = component.outer_position[other_ndx]
position[other_ndx] = 0
if align == "min":
pass
elif align == "max":
position[other_ndx] = size[other_ndx] - bounds[other_ndx]
elif align == "center":
position[other_ndx] = (size[other_ndx] - bounds[other_ndx]) / 2.0

component.outer_position = position
component.outer_bounds = bounds
component.do_layout()
return

0 comments on commit 75fb3ac

Please sign in to comment.