Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions adobe/eve.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,15 @@ class eve_t

std::pair<int, int> adjust(evaluate_options_t options, int width, int height);

/*!
\brief Print the current state of the view proxy forest to stderr.

The name of each widget will be the type_info of underlying placeable.
Parameters for each widget include view proxy attribute values for
bounds (top, left, width, height), horizontal/vertical placement, and alignment.
*/
void print_debug(std::ostream& os);

private:
friend struct implementation::view_proxy_t;

Expand Down
117 changes: 117 additions & 0 deletions source/eve.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ class eve_t::implementation_t : private extents_slices_t {
void set_visible(iterator, bool);
void set_layout_attributes(iterator, const layout_attributes_t&);

void print_debug(std::ostream& os);
private:
void solve(slice_select_t select);
void layout(slice_select_t select, int optional_length);
Expand Down Expand Up @@ -290,6 +291,10 @@ std::pair<int, int> eve_t::adjust(evaluate_options_t options, int width, int hei
return object_m->adjust(options, width, height);
}

void eve_t::print_debug(std::ostream& os) {
object_m->print_debug(os);
}

eve_t::iterator eve_t::add_placeable(iterator parent, const layout_attributes_t& initial,
bool is_container_type, // is the element a container?
poly_placeable_t& placeable, // signals to call for the element
Expand Down Expand Up @@ -344,13 +349,125 @@ eve_t::iterator eve_t::implementation_t::add_placeable(iterator parent,

/**************************************************************************************************/

namespace {

constexpr const char* to_string(eve_t::placement_t x) {
switch (x) {
case eve_t::place_leaf: return "leaf";
case eve_t::place_column: return "column";
case eve_t::place_row: return "row";
case eve_t::place_overlay: return "overlay";
}
// If you get here, it means you somehow have a placement_t whose value is invalid.
ADOBE_ASSERT(false);
return "invalid";
}

constexpr const char* to_string(layout_attributes_alignment_t::alignment_t x) {
switch (x) {
case layout_attributes_alignment_t::align_forward: return "forward";
case layout_attributes_alignment_t::align_reverse: return "reverse";
case layout_attributes_alignment_t::align_center: return "center";
case layout_attributes_alignment_t::align_proportional: return "proportional";
case layout_attributes_alignment_t::align_forward_fill: return "forward_fill";
case layout_attributes_alignment_t::align_reverse_fill: return "reverse_fill";
case layout_attributes_alignment_t::align_default: return "default";
}
// If you get here, it means you somehow have an alignment_t whose value is invalid.
ADOBE_ASSERT(false);
return "invalid";
}

///
/// This routine ensures the `os` is set to a default state such that output
/// to it will be in a consistent format. The stream's state will be restored
/// when the routine returns, even if an exception is thrown.
///
/// \param os The stream to be formatted.
/// \param f A function to be called with the formatted stream.
///
template <class F>
void with_default_formatting(std::ostream& os, F&& f) {
struct state_saver {
state_saver(std::ostream& os) : _os(os) {
_old.copyfmt(_os); // save the original state
_os.copyfmt(std::ios{nullptr}); // default formatting - picks up global locale
_os.imbue(std::locale::classic()); // "C" locale with UTF-8 support
}

~state_saver() {
_os.copyfmt(_old); // restore the original state
}

std::ios _old{nullptr};
std::ostream& _os;
} saver(os);

f(os);
}

} // namespace

/**************************************************************************************************/

void eve_t::implementation_t::set_visible(iterator c, bool visible) { c->visible_m = visible; }

void eve_t::implementation_t::set_layout_attributes(iterator c,
const layout_attributes_t& geometry) {
c->geometry_m = geometry;
}

void eve_t::implementation_t::print_debug(std::ostream& output_stream) {
with_default_formatting(output_stream, [&](std::ostream& os) {
iterator iter = proxies_m.begin();
iterator last = proxies_m.end();
std::size_t depth(0);

for (; iter != last; ++iter) {
if (iter.edge() == forest_trailing_edge) {
--depth;
if (has_children(iter)) {
os << std::string(depth * 4, ' ') << "}\n";
}
continue;
}

const implementation::view_proxy_t& proxy = *iter;
const auto& g = proxy.geometry_m;
const auto& h = proxy.place_m.horizontal();
const auto& v = proxy.place_m.vertical();

os << std::string(depth * 4, ' ');
os << proxy.placeable_m.type_info().name();
os << "(";
os << "left: " << h.position_m;
os << ", ";
os << "top: " << v.position_m;
os << ", ";
os << "width: " << h.length_m;
os << ", ";
os << "height: " << v.length_m;
os << ", ";
os << "horizontal: " << to_string(g.horizontal().alignment_m);
os << ", ";
os << "vertical: " << to_string(g.vertical().alignment_m);
os << ", ";
os << "placement: " << to_string(g.placement_m);
os << ")";

if (has_children(iter)) {
os << " {";
} else {
os << ";";
}

os << "\n";

++depth;
}
});
}

/**************************************************************************************************/

void eve_t::implementation_t::solve(slice_select_t select) {
Expand Down
38 changes: 38 additions & 0 deletions test/eve_smoke/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,42 @@ adobe::eve_callback_suite_t::position_t assemble(adobe::name_t name,
return adobe::eve_callback_suite_t::position_t();
}

/**************************************************************************************************/
//
/// Ensure eve_t::print_debug() behaves as expected.
//
void test_print_debug() {
std::stringstream result;
const auto fail_test = [&result]() {
std::cerr << "test_print_debug() failed\n" << result.str() << "\n";
throw std::runtime_error("test_print_debug() failed");
};

adobe::eve_t eve;
eve.print_debug(result);
if (!result.str().empty()) {
fail_test();
}

struct my_leaf {
void measure(adobe::extents_t&) {}
void place(const adobe::place_data_t&) {}
};

adobe::poly_placeable_t placeable_leaf{my_leaf()};

eve.add_placeable(adobe::eve_t::iterator(), adobe::layout_attributes_t(), false, placeable_leaf, false);

eve.print_debug(result);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check that the string matches something known.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about this, too, but the name of the type is implementation dependent, so will change from compiler to compiler. Do you have a suggestion on how that could be mitigated?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hopefully this workaround will suffice?


// The exact string cannot be checked for because the name of the type
// will vary from compiler to compiler. Even so, we can ensure the attribute
// value are correct and in the right order.
if (result.str().find("(left: 0, top: 0, width: 0, height: 0, horizontal: default, vertical: default, placement: leaf);") == std::string::npos) {
fail_test();
}
}

/**************************************************************************************************/

//
Expand Down Expand Up @@ -128,6 +164,8 @@ int main(int argc, char* argv[]) {
//

testParse(file_path);

test_print_debug();
} catch (const std::exception& error) {
//
// Oops, something didn't work out.
Expand Down
Loading