Files
textmate/Shared/include/oak/box_layout.h
Allan Odgaard 9894969e67 Initial commit
2012-08-09 16:25:56 +02:00

250 lines
6.9 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#ifndef BOX_LAYOUT_H_JU6T39XY
#define BOX_LAYOUT_H_JU6T39XY
#include "oak.h"
namespace oak
{
template <typename T>
struct box_layout_t
{
box_layout_t () : last_leaf(0) { }
size_t add (T const& newValue) { return add(newValue, place_below, 0); }
size_t add_left (T const& newValue, size_t relativeTo = 0) { return add(newValue, place_left, relativeTo); }
size_t add_right (T const& newValue, size_t relativeTo = 0) { return add(newValue, place_right, relativeTo); }
size_t add_above (T const& newValue, size_t relativeTo = 0) { return add(newValue, place_above, relativeTo); }
size_t add_below (T const& newValue, size_t relativeTo = 0) { return add(newValue, place_below, relativeTo); }
void remove (size_t leaf)
{
container = container.identifier == leaf ? box_t() : remove(container, leaf);
leafs.erase(leafs.find(leaf));
}
void layout (size_t x0, size_t x1, size_t y0, size_t y1)
{
layout(container, x0, x1, y0, y1);
}
private:
enum placement_t { place_left, place_above, place_right, place_below };
enum orientation_t { rows, columns };
struct box_t
{
box_t (size_t identifier = 0) : identifier(identifier) { }
size_t identifier;
orientation_t orientation;
std::vector<box_t> children;
};
size_t last_leaf;
std::map<size_t, T> leafs;
box_t container;
box_t add (box_t& box, size_t newLeaf, placement_t placement, size_t relativeTo)
{
if(box.identifier == relativeTo)
{
box_t wrapper;
wrapper.orientation = (placement == place_left || placement == place_right) ? columns : rows;
wrapper.children.push_back(box);
wrapper.children.insert(placement == place_left || placement == place_above ? wrapper.children.begin() : wrapper.children.end(), newLeaf);
return wrapper;
}
else
{
iterate(child, box.children)
*child = add(*child, newLeaf, placement, relativeTo);
}
return box;
}
size_t add (T const& newValue, placement_t placement, size_t relativeTo)
{
leafs.insert(std::make_pair(++last_leaf, newValue));
if(relativeTo)
container = add(container, last_leaf, placement, relativeTo);
else if(container.children.empty())
container = last_leaf;
else
{
box_t wrapper;
wrapper.orientation = (placement == place_left || placement == place_right) ? columns : rows;
wrapper.children.push_back(container);
wrapper.children.insert(placement == place_left || placement == place_above ? wrapper.children.begin() : wrapper.children.end(), last_leaf);
container = wrapper;
}
return last_leaf;
}
box_t remove (box_t& box, size_t leaf)
{
std::vector<box_t> newChildren;
iterate(child, box.children)
{
if(child->identifier != leaf)
newChildren.push_back(remove(*child, leaf));
else fprintf(stderr, "found and removed %zu\n", leaf);
}
box.children.swap(newChildren);
switch(box.children.size())
{
case 0: return box;
case 1: return box.children.back();
default: return box;
}
}
// ==================
// = Layout Helpers =
// ==================
size_t number_of (box_t const& box, orientation_t unit)
{
if(box.identifier)
return 1;
else if(box.orientation != unit)
return 1;
size_t res = 0;
iterate(child, box.children)
res += number_of(*child, unit);
return res;
}
size_t weight (box_t const& box, orientation_t unit)
{
size_t res = 0;
if(box.identifier)
{
res = (unit == rows ? max_height(leafs[box.identifier]) : max_width(leafs[box.identifier])) == SIZE_T_MAX ? 1 : 0;
}
else if(box.orientation != unit)
{
iterate(child, box.children)
res = std::max(weight(*child, unit), res);
}
else if(box.orientation == unit)
{
iterate(child, box.children)
res += weight(*child, unit);
}
return res;
}
size_t min_size (box_t const& box, orientation_t unit)
{
size_t res = 0;
if(box.identifier)
{
res = unit == rows ? min_height(leafs[box.identifier]) : min_width(leafs[box.identifier]);
}
else if(box.orientation != unit)
{
iterate(child, box.children)
res = std::max(min_size(*child, unit), res);
}
else if(box.orientation == unit)
{
iterate(child, box.children)
res += min_size(*child, unit);
}
return res;
}
size_t max_size (box_t const& box, orientation_t unit)
{
size_t res = 0;
if(box.identifier)
{
res = unit == rows ? max_height(leafs[box.identifier]) : max_width(leafs[box.identifier]);
}
else if(box.orientation != unit)
{
res = SIZE_T_MAX;
iterate(child, box.children)
res = std::min(max_size(*child, unit), res);
}
else if(box.orientation == unit)
{
iterate(child, box.children)
{
if(max_size(*child, unit) == SIZE_T_MAX)
return SIZE_T_MAX;
res += max_size(*child, unit);
}
}
return res;
}
void layout (box_t& box, size_t x0, size_t x1, size_t y0, size_t y1)
{
if(box.identifier)
{
set_frame(leafs[box.identifier], x0, x1, y0, y1);
}
else
{
size_t totalSize = box.orientation == rows ? y1 - y0 : x1 - x0;
size_t minSize = 0, maxSize = 0, totalDelta = 0;
iterate(child, box.children)
{
size_t boxWeight = weight(*child, box.orientation);
size_t boxMin = min_size(*child, box.orientation);
size_t boxMax = max_size(*child, box.orientation) == SIZE_T_MAX ? totalSize : max_size(*child, box.orientation);
minSize += boxMin;
maxSize += boxMax;
totalDelta += boxWeight * (boxMax - boxMin);
}
size_t available = totalSize - minSize;
size_t delta = 0, reserved = 0;
iterate(child, box.children)
{
size_t from = reserved + (totalDelta ? available * delta / totalDelta : 0);
size_t boxWeight = weight(*child, box.orientation);
size_t boxMin = min_size(*child, box.orientation);
size_t boxMax = max_size(*child, box.orientation) == SIZE_T_MAX ? totalSize : max_size(*child, box.orientation);
delta += boxWeight * (boxMax - boxMin);
reserved += boxMin;
size_t to = reserved + (totalDelta ? available * delta / totalDelta : 0);
if(box.orientation == rows)
layout(*child, x0, x1, y0 + from, y0 + to);
else layout(*child, x0 + from, x0 + to, y0, y1);
}
}
}
// =========
// = Other =
// =========
void print (box_t const& box, std::string const& indent = "")
{
if(box.identifier)
{
fprintf(stderr, "%s* %s (%zu)\n", indent.c_str(), description(leafs[box.identifier]).c_str(), box.identifier);
}
else
{
fprintf(stderr, "%s* rows/columns %zu×%zu, weights %zu×%zu, min/max sizes: %zu-%zu / %zu-%zu\n", indent.c_str(), number_of(box, rows), number_of(box, columns), weight(box, rows), weight(box, columns), min_size(box, rows), max_size(box, rows), min_size(box, columns), max_size(box, columns));
iterate(child, box.children)
print(*child, indent + " ");
}
}
public:
void print () { print(container); }
};
} /* oak */
#endif /* end of include guard: BOX_LAYOUT_H_JU6T39XY */