blob: 5324227f303758a39598c44b370affab43d6cd57 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/vr/elements/linear_layout.h"
namespace vr {
namespace {
// NB: this function makes no attempt to account for rotated elements.
float GetExtent(const UiElement& element, bool horizontal) {
gfx::Point3F p = horizontal ? gfx::Point3F(element.size().width(), 0, 0)
: gfx::Point3F(0, element.size().height(), 0);
gfx::Point3F o;
element.LocalTransform().TransformPoint(&p);
element.LocalTransform().TransformPoint(&o);
return (p - o).Length();
}
} // namespace
LinearLayout::LinearLayout(Direction direction) : direction_(direction) {}
LinearLayout::~LinearLayout() {}
bool LinearLayout::SizeAndLayOutChildren() {
bool changed = UiElement::SizeAndLayOutChildren();
if (layout_length_ == 0.0f)
return changed;
// We need to adjust one of the elements' size to ensure a fixed total layout
// width. Find that element, set its size, and lay it out again.
UiElement* element_to_resize = nullptr;
for (auto& child : children()) {
if (child->resizable_by_layout()) {
element_to_resize = child.get();
break;
}
}
DCHECK_NE(element_to_resize, nullptr);
changed |= AdjustResizableElement(element_to_resize);
changed |= element_to_resize->SizeAndLayOut();
return changed;
}
void LinearLayout::LayOutContributingChildren() {
float x_factor = 0.f;
float y_factor = 0.f;
switch (direction_) {
case kUp:
y_factor = 1.f;
break;
case kDown:
y_factor = -1.f;
break;
case kLeft:
x_factor = -1.f;
break;
case kRight:
x_factor = 1.f;
break;
}
float total_extent;
float minor_extent;
GetTotalExtent(nullptr, &total_extent, &minor_extent);
bool horizontal = Horizontal();
float cumulative_offset = -0.5 * total_extent;
for (auto& child : children()) {
if (!child->IsVisible() || !child->requires_layout())
continue;
float child_extent = GetExtent(*child, horizontal);
float child_minor_extent = GetExtent(*child, !horizontal);
float offset = cumulative_offset + 0.5 * child_extent;
float x_align_offset = 0.0f;
float y_align_offset = 0.0f;
if (Horizontal()) {
DCHECK_NE(RIGHT, child->x_anchoring());
DCHECK_NE(LEFT, child->x_anchoring());
if (child->y_anchoring() == TOP || child->y_anchoring() == BOTTOM) {
y_align_offset = 0.5f * (minor_extent - child_minor_extent);
if (child->y_anchoring() == BOTTOM)
y_align_offset *= -1.0f;
}
} else {
DCHECK_NE(TOP, child->y_anchoring());
DCHECK_NE(BOTTOM, child->y_anchoring());
if (child->x_anchoring() == RIGHT || child->x_anchoring() == LEFT) {
x_align_offset = 0.5f * (minor_extent - child_minor_extent);
if (child->x_anchoring() == LEFT)
x_align_offset *= -1.0f;
}
}
child->SetLayoutOffset(offset * x_factor + x_align_offset,
offset * y_factor + y_align_offset);
cumulative_offset += child_extent + margin_;
}
SetSize(horizontal ? total_extent : minor_extent,
!horizontal ? total_extent : minor_extent);
}
bool LinearLayout::Horizontal() const {
return direction_ == LinearLayout::kLeft ||
direction_ == LinearLayout::kRight;
}
void LinearLayout::GetTotalExtent(const UiElement* element_to_exclude,
float* major_extent,
float* minor_extent) const {
*major_extent = -margin_;
*minor_extent = 0.f;
bool horizontal = Horizontal();
for (auto& child : children()) {
if (child->IsVisible() && child->requires_layout()) {
*major_extent += margin_;
if (child.get() != element_to_exclude) {
*major_extent += GetExtent(*child, horizontal);
*minor_extent = std::max(*minor_extent, GetExtent(*child, !horizontal));
}
}
}
}
bool LinearLayout::AdjustResizableElement(UiElement* element_to_resize) {
// Figure out how much space is available for the variable element.
float minimum_total = 0;
float minor = 0;
GetTotalExtent(element_to_resize, &minimum_total, &minor);
float extent = layout_length_ - minimum_total;
extent = std::max(extent, 0.f);
auto new_size = element_to_resize->size();
if (Horizontal())
new_size.set_width(extent);
else
new_size.set_height(extent);
if (element_to_resize->size() == new_size)
return false;
element_to_resize->SetSize(new_size.width(), new_size.height());
return true;
}
} // namespace vr