-
Notifications
You must be signed in to change notification settings - Fork 44
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adding stacking layouts - largely copied from Chaco graph layout code.
TODO - update chaco code to use these classes.
- Loading branch information
cwebster
committed
Jul 2, 2010
1 parent
4e20c80
commit 75fb3ac
Showing
2 changed files
with
260 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |