A lot of NewUI work: layout and focus fixes, "TabHolder"

This commit is contained in:
Henrik Rydgard 2013-06-08 22:41:17 +02:00
parent 8e5890a7e2
commit 166acebeff
11 changed files with 221 additions and 57 deletions

View File

@ -37,7 +37,7 @@ TouchInput GestureDetector::Update(const TouchInput &touch, const Bounds &bounds
active_ |= GESTURE_DRAG_VERTICAL;
// Kill the drag
TouchInput inp2 = touch;
inp2.flags = TOUCH_UP;
inp2.flags = TOUCH_UP | TOUCH_CANCEL;
return inp2;
}
} else {

View File

@ -98,6 +98,7 @@ enum {
TOUCH_MOVE = 1,
TOUCH_DOWN = 2,
TOUCH_UP = 4,
TOUCH_CANCEL = 8, // Sent by scrollviews to their children when they detect a scroll
};
// Used for asynchronous touch input.

View File

@ -24,6 +24,9 @@ struct Point {
// Resolved bounds on screen after layout.
struct Bounds {
Bounds() {}
Bounds(float x_, float y_, float w_, float h_) : x(x_), y(y_), w(w_), h(h_) {}
bool Contains(float px, float py) const {
return (px >= x && py >= y && px < x + w && py < y + h);
}

View File

@ -1,12 +1,12 @@
#include "ui.h"
#include "ui_context.h"
#include "ui/ui.h"
#include "ui/view.h"
#include "ui/ui_context.h"
#include "gfx/texture.h"
#include "gfx_es2/draw_buffer.h"
#include "gfx_es2/glsl_program.h"
#include "gfx_es2/gl_state.h"
void UIContext::Begin()
{
void UIContext::Begin() {
glstate.blend.enable();
glstate.blendFunc.set(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glstate.cullFace.disable();
@ -24,8 +24,7 @@ void UIContext::Begin()
uidrawbufferTop_->Begin();*/
}
void UIContext::BeginNoTex()
{
void UIContext::BeginNoTex() {
glstate.blend.enable();
glstate.blendFunc.set(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glstate.cullFace.disable();
@ -44,28 +43,23 @@ void UIContext::BeginNoTex()
}
void UIContext::RebindTexture() const
{
void UIContext::RebindTexture() const {
if (uitexture_)
uitexture_->Bind(0);
}
void UIContext::Flush()
{
if (uidrawbuffer_)
{
void UIContext::Flush() {
if (uidrawbuffer_) {
uidrawbuffer_->End();
uidrawbuffer_->Flush();
}
if (uidrawbufferTop_)
{
if (uidrawbufferTop_) {
uidrawbufferTop_->End();
uidrawbufferTop_->Flush();
}
}
void UIContext::End()
{
void UIContext::End() {
UIEnd();
Flush();
}
@ -98,3 +92,17 @@ void UIContext::ActivateTopScissor() {
glstate.scissorTest.disable();
}
}
void UIContext::FillRect(const UI::Drawable &drawable, const Bounds &bounds) {
switch (drawable.type) {
case UI::DRAW_SOLID_COLOR:
uidrawbuffer_->DrawImageStretch(theme->whiteImage, bounds.x, bounds.y, bounds.x2(), bounds.y2(), drawable.color);
break;
case UI::DRAW_4GRID:
uidrawbuffer_->DrawImage4Grid(drawable.image, bounds.x, bounds.y, bounds.x2(), bounds.y2(), drawable.color);
break;
case UI::DRAW_STRETCH_IMAGE:
uidrawbuffer_->DrawImageStretch(drawable.image, bounds.x, bounds.y, bounds.x2(), bounds.y2(), drawable.color);
break;
}
}

View File

@ -14,6 +14,7 @@ class DrawBuffer;
// Kind of ugly connection to UI.
namespace UI {
struct Drawable;
struct Theme;
}
@ -49,6 +50,12 @@ public:
const UI::Theme *theme;
// Utility methods
void FillRect(const UI::Drawable &drawable, const Bounds &bounds);
private:
// TODO: Collect these into a UIContext
const GLSLProgram *uishader_;

View File

@ -3,12 +3,17 @@
#include "ui/ui_context.h"
#include "ui/screen.h"
UIScreen::UIScreen()
: Screen(), root_(0), recreateViews_(false) {
}
void UIScreen::update(InputState &input) {
if (!root_) {
CreateViews();
}
if (orientationChanged_) {
if (recreateViews_) {
delete root_;
root_ = 0;
CreateViews();
@ -18,17 +23,25 @@ void UIScreen::update(InputState &input) {
}
void UIScreen::render() {
UI::LayoutViewHierarchy(*screenManager()->getUIContext(), root_);
if (root_) {
UI::LayoutViewHierarchy(*screenManager()->getUIContext(), root_);
screenManager()->getUIContext()->Begin();
DrawBackground();
root_->Draw(*screenManager()->getUIContext());
screenManager()->getUIContext()->End();
screenManager()->getUIContext()->Flush();
screenManager()->getUIContext()->Begin();
DrawBackground();
root_->Draw(*screenManager()->getUIContext());
screenManager()->getUIContext()->End();
screenManager()->getUIContext()->Flush();
} else {
ELOG("Tried to render without a view root");
}
}
void UIScreen::touch(const TouchInput &touch) {
root_->Touch(touch);
if (root_) {
root_->Touch(touch);
} else {
ELOG("Tried to touch without a view root");
}
}
UI::EventReturn UIScreen::OnBack(UI::EventParams &e) {

View File

@ -5,7 +5,7 @@
class UIScreen : public Screen {
public:
UIScreen() : Screen(), root_(0), orientationChanged_(false) {}
UIScreen();
~UIScreen() { delete root_; }
virtual void update(InputState &input);
@ -19,6 +19,10 @@ protected:
virtual void CreateViews() = 0;
virtual void DrawBackground() {}
void RecreateViews() { recreateViews_ = true; }
UI::ViewGroup *root_;
bool orientationChanged_;
private:
bool recreateViews_;
};

View File

@ -2,6 +2,7 @@
#include "base/mutex.h"
#include "input/input_state.h"
#include "gfx_es2/draw_buffer.h"
#include "gfx/texture.h"
#include "gfx/texture_atlas.h"
#include "ui/ui.h"
#include "ui/view.h"
@ -117,7 +118,7 @@ void Clickable::Touch(const TouchInput &input) {
down_ = bounds_.Contains(input.x, input.y);
}
if (input.flags & TOUCH_UP) {
if (dragging_ && bounds_.Contains(input.x, input.y)) {
if ((input.flags & TOUCH_CANCEL) == 0 && dragging_ && bounds_.Contains(input.x, input.y)) {
Click();
}
downCountDown_ = 0;
@ -159,15 +160,18 @@ ClickableItem::ClickableItem(LayoutParams *layoutParams) : Clickable(layoutParam
}
void ClickableItem::Draw(UIContext &dc) {
Style style = dc.theme->itemStyle;
if (down_) {
dc.Draw()->DrawImageStretch(dc.theme->whiteImage, bounds_.x, bounds_.y, bounds_.x2(), bounds_.y2(), dc.theme->itemDownStyle.bgColor);
style = dc.theme->itemDownStyle;
} else if (HasFocus()) {
dc.Draw()->DrawImageStretch(dc.theme->whiteImage, bounds_.x, bounds_.y, bounds_.x2(), bounds_.y2(), dc.theme->itemFocusedStyle.bgColor);
style = dc.theme->itemFocusedStyle;
}
dc.FillRect(style.background, bounds_);
}
void Choice::Draw(UIContext &dc) {
ClickableItem::Draw(dc);
int paddingX = 4;
int paddingY = 4;
dc.Draw()->DrawText(dc.theme->uiFont, text_.c_str(), bounds_.x + paddingX, bounds_.centerY(), 0xFFFFFFFF, ALIGN_VCENTER);
@ -175,16 +179,17 @@ void Choice::Draw(UIContext &dc) {
}
void InfoItem::Draw(UIContext &dc) {
Item::Draw(dc);
int paddingX = 4;
int paddingY = 4;
dc.Draw()->DrawText(dc.theme->uiFont, text_.c_str(), bounds_.x + paddingX, bounds_.centerY(), 0xFFFFFFFF, ALIGN_VCENTER);
dc.Draw()->DrawText(dc.theme->uiFont, text_.c_str(), bounds_.x2() - paddingX, bounds_.centerY(), 0xFFFFFFFF, ALIGN_VCENTER | ALIGN_RIGHT);
dc.Draw()->DrawImageStretch(dc.theme->whiteImage, bounds_.x, bounds_.y, bounds_.x2(), bounds_.y + 2, dc.theme->itemDownStyle.bgColor);
// dc.Draw()->DrawImageStretch(dc.theme->whiteImage, bounds_.x, bounds_.y, bounds_.x2(), bounds_.y + 2, dc.theme->itemDownStyle.bgColor);
}
void ItemHeader::Draw(UIContext &dc) {
dc.Draw()->DrawText(dc.theme->uiFontSmaller, text_.c_str(), bounds_.x + 4, bounds_.y, 0xFF707070, ALIGN_LEFT);
dc.Draw()->DrawImageStretch(dc.theme->whiteImage, bounds_.x, bounds_.y2()-2, bounds_.x2(), bounds_.y2(), dc.theme->itemDownStyle.bgColor);
dc.Draw()->DrawImageStretch(dc.theme->whiteImage, bounds_.x, bounds_.y2()-2, bounds_.x2(), bounds_.y2(), 0xFFFFFFFF);
}
void CheckBox::Draw(UIContext &dc) {
@ -195,7 +200,7 @@ void CheckBox::Draw(UIContext &dc) {
int image = *toggle_ ? dc.theme->checkOn : dc.theme->checkOff;
dc.Draw()->DrawText(dc.theme->uiFont, text_.c_str(), bounds_.x + paddingX, bounds_.centerY(), 0xFFFFFFFF, ALIGN_VCENTER);
dc.Draw()->DrawImage(image, bounds_.x2() - 4, bounds_.centerY(), 0xFFFFFFFF, ALIGN_RIGHT | ALIGN_VCENTER);
dc.Draw()->DrawImage(image, bounds_.x2() - 4, bounds_.centerY(), 1.0f, 0xFFFFFFFF, ALIGN_RIGHT | ALIGN_VCENTER);
}
void Button::GetContentDimensions(const UIContext &dc, float &w, float &h) const {
@ -208,7 +213,8 @@ void Button::Draw(UIContext &dc) {
if (down_) style = dc.theme->buttonDownStyle;
if (!enabled_) style = dc.theme->buttonDisabledStyle;
dc.Draw()->DrawImage4Grid(style.image, bounds_.x, bounds_.y, bounds_.x2(), bounds_.y2(), style.bgColor);
// dc.Draw()->DrawImage4Grid(style.image, bounds_.x, bounds_.y, bounds_.x2(), bounds_.y2(), style.bgColor);
dc.FillRect(style.background, bounds_);
dc.Draw()->DrawText(dc.theme->uiFont, text_.c_str(), bounds_.centerX(), bounds_.centerY(), style.fgColor, ALIGN_CENTER);
}
@ -224,6 +230,28 @@ void ImageView::Draw(UIContext &dc) {
dc.Draw()->DrawImage(atlasImage_, bounds_.x, bounds_.y, bounds_.w, bounds_.h, 0xFFFFFFFF);
}
void TextureView::GetContentDimensions(const UIContext &dc, float &w, float &h) const {
// TODO: involve sizemode
if (texture_) {
w = (float)texture_->Width();
h = (float)texture_->Height();
} else {
w = 16;
h = 16;
}
}
void TextureView::Draw(UIContext &dc) {
// TODO: involve sizemode
if (texture_) {
dc.Flush();
texture_->Bind(0);
dc.Draw()->Rect(bounds_.x, bounds_.y, bounds_.w, bounds_.h, color_);
dc.Flush();
dc.RebindTexture();
}
}
void TextView::GetContentDimensions(const UIContext &dc, float &w, float &h) const {
dc.Draw()->SetFontScale(textScale_, textScale_);
dc.Draw()->MeasureText(dc.theme->uiFont, text_.c_str(), &w, &h);

View File

@ -24,6 +24,7 @@ struct TouchInput;
struct InputState;
class DrawBuffer;
class Texture;
class UIContext;
// I don't generally like namespaces but I think we do need one for UI, so many potentially-clashing names.
@ -42,6 +43,7 @@ enum DrawableType {
DRAW_NOTHING,
DRAW_SOLID_COLOR,
DRAW_4GRID,
DRAW_STRETCH_IMAGE,
};
enum Visibility {
@ -51,17 +53,20 @@ enum Visibility {
};
struct Drawable {
Drawable() : type(DRAW_NOTHING) {}
Drawable() : type(DRAW_NOTHING), image(-1), color(0xFFFFFFFF) {}
explicit Drawable(uint32_t col) : type(DRAW_SOLID_COLOR), image(-1), color(col) {}
Drawable(DrawableType t, int img, uint32_t col = 0xFFFFFFFF) : type(t), image(img), color(col) {}
DrawableType type;
uint32_t data;
uint32_t image;
uint32_t color;
};
struct Style {
Style() : fgColor(0xFFFFFFFF), bgColor(0xFF303030), image(-1) {}
Style() : fgColor(0xFFFFFFFF), background(0xFF303030), image(-1) {}
uint32_t fgColor;
uint32_t bgColor;
Drawable background;
int image; // where applicable.
};
@ -79,6 +84,7 @@ struct Theme {
Style buttonDownStyle;
Style buttonDisabledStyle;
Style itemStyle;
Style itemDownStyle;
Style itemFocusedStyle;
};
@ -195,7 +201,9 @@ private:
struct Margins {
Margins() : top(0), bottom(0), left(0), right(0) {}
explicit Margins(uint8_t all) : top(all), bottom(all), left(all), right(all) {}
explicit Margins(uint8_t horiz, uint8_t vert) : top(vert), bottom(vert), left(horiz), right(horiz) {}
Margins(uint8_t horiz, uint8_t vert) : top(vert), bottom(vert), left(horiz), right(horiz) {}
Margins(uint8_t l, uint8_t t, uint8_t r, uint8_t b) : top(t), bottom(b), left(l), right(r) {}
uint8_t top;
uint8_t bottom;
uint8_t left;
@ -250,6 +258,7 @@ public:
// Called when the layout is done.
void SetBounds(Bounds bounds) { bounds_ = bounds; }
virtual const LayoutParams *GetLayoutParams() const { return layoutParams_.get(); }
virtual void ReplaceLayoutParams(LayoutParams *newLayoutParams) { layoutParams_.reset(newLayoutParams); }
const Bounds &GetBounds() const { return bounds_; }
virtual bool SetFocus() {
@ -272,10 +281,14 @@ public:
void SetVisibility(Visibility visibility) { visibility_ = visibility; }
Visibility GetVisibility() const { return visibility_; }
const std::string &Tag() const { return tag_; }
void SetTag(const std::string &str) { tag_ = str; }
protected:
// Inputs to layout
scoped_ptr<LayoutParams> layoutParams_;
std::string tag_;
bool enabled_;
Visibility visibility_;
@ -425,7 +438,7 @@ private:
class CheckBox : public ClickableItem {
public:
CheckBox(bool *toggle, const std::string &text, const std::string &smallText = "", LayoutParams *layoutParams = 0)
: ClickableItem(layoutParams), text_(text), smallText_(smallText) {
: ClickableItem(layoutParams), toggle_(toggle), text_(text), smallText_(smallText) {
OnClick.Handle(this, &CheckBox::OnClicked);
}
@ -488,6 +501,24 @@ private:
ImageSizeMode sizeMode_;
};
class TextureView : public InertView {
public:
TextureView(Texture *texture, ImageSizeMode sizeMode, LayoutParams *layoutParams = 0)
: InertView(layoutParams), texture_(texture), sizeMode_(sizeMode) {}
virtual void GetContentDimensions(const UIContext &dc, float &w, float &h) const;
virtual void Draw(UIContext &dc);
void SetTexture(Texture *texture) { texture_ = texture; }
void SetColor(uint32_t color) { color_ = color; }
private:
Texture *texture_;
uint32_t color_;
ImageSizeMode sizeMode_;
};
class ProgressBar : public InertView {
public:
ProgressBar(LayoutParams *layoutParams = 0)

View File

@ -3,9 +3,12 @@
#include "ui/ui_context.h"
#include "ui/view.h"
#include "ui/viewgroup.h"
#include "gfx_es2/draw_buffer.h"
namespace UI {
const float ITEM_HEIGHT = 64.f;
void ApplyGravity(const Bounds outer, const Margins &margins, float w, float h, int gravity, Bounds &inner) {
inner.w = w - (margins.left + margins.right);
inner.h = h - (margins.right + margins.left);
@ -79,6 +82,8 @@ float GetDirectionScore(View *origin, View *destination, FocusDirection directio
return 0.0f;
if (destination->GetEnabled() == false)
return 0.0f;
if (destination->GetVisibility() != V_VISIBLE)
return 0.0f;
float dx = destination->GetBounds().centerX() - origin->GetBounds().centerX();
float dy = destination->GetBounds().centerY() - origin->GetBounds().centerY();
@ -111,6 +116,11 @@ float GetDirectionScore(View *origin, View *destination, FocusDirection directio
NeighborResult ViewGroup::FindNeighbor(View *view, FocusDirection direction, NeighborResult result) {
if (!GetEnabled())
return result;
if (GetVisibility() != V_VISIBLE)
return result;
// First, find the position of the view in the list.
size_t num = -1;
for (size_t i = 0; i < views_.size(); i++) {
@ -162,7 +172,7 @@ NeighborResult ViewGroup::FindNeighbor(View *view, FocusDirection direction, Nei
// Boost neighbors with the same parent
if (num != -1) {
result.score += 100.0f;
//result.score += 100.0f;
}
return result;
@ -196,11 +206,11 @@ void MoveFocus(ViewGroup *root, FocusDirection direction) {
}
void LinearLayout::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert) {
if (views_.empty()) {
MeasureBySpec(layoutParams_->width, 0.0f, horiz, &measuredWidth_);
MeasureBySpec(layoutParams_->height, 0.0f, vert, &measuredHeight_);
MeasureBySpec(layoutParams_->width, 0.0f, horiz, &measuredWidth_);
MeasureBySpec(layoutParams_->height, 0.0f, vert, &measuredHeight_);
if (views_.empty())
return;
}
float sum = 0.0f;
float maxOther = 0.0f;
@ -226,9 +236,13 @@ void LinearLayout::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec v
}
if (orientation_ == ORIENT_HORIZONTAL) {
views_[i]->Measure(dc, MeasureSpec(UNSPECIFIED), vert - (float)(margins.top + margins.bottom));
MeasureSpec v = vert;
if (v.type == UNSPECIFIED) v = MeasureSpec(AT_MOST, measuredHeight_);
views_[i]->Measure(dc, MeasureSpec(UNSPECIFIED, measuredWidth_), vert - (float)(margins.top + margins.bottom));
} else if (orientation_ == ORIENT_VERTICAL) {
views_[i]->Measure(dc, horiz - (float)(margins.left + margins.right), MeasureSpec(UNSPECIFIED));
MeasureSpec h = horiz;
if (h.type == UNSPECIFIED) h = MeasureSpec(AT_MOST, measuredWidth_);
views_[i]->Measure(dc, h - (float)(margins.left + margins.right), MeasureSpec(UNSPECIFIED, measuredHeight_));
}
float amount;
@ -265,8 +279,8 @@ void LinearLayout::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec v
const LayoutParams *layoutParams = views_[i]->GetLayoutParams();
const LinearLayoutParams *linLayoutParams = dynamic_cast<const LinearLayoutParams *>(layoutParams);
if (linLayoutParams && linLayoutParams->weight > 0.0)
views_[i]->Measure(dc, MeasureSpec(EXACTLY, unit * linLayoutParams->weight), vert);
if (linLayoutParams && linLayoutParams->weight > 0.0f)
views_[i]->Measure(dc, MeasureSpec(EXACTLY, unit * linLayoutParams->weight), MeasureSpec(EXACTLY, measuredHeight_));
}
} else {
MeasureBySpec(layoutParams_->height, weightZeroSum, vert, &measuredHeight_);
@ -279,8 +293,8 @@ void LinearLayout::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec v
const LayoutParams *layoutParams = views_[i]->GetLayoutParams();
const LinearLayoutParams *linLayoutParams = dynamic_cast<const LinearLayoutParams *>(layoutParams);
if (linLayoutParams && linLayoutParams->weight)
views_[i]->Measure(dc, horiz, MeasureSpec(EXACTLY, unit * linLayoutParams->weight));
if (linLayoutParams && linLayoutParams->weight > 0.0f)
views_[i]->Measure(dc, MeasureSpec(EXACTLY, measuredWidth_), MeasureSpec(EXACTLY, unit * linLayoutParams->weight));
}
}
}
@ -367,9 +381,9 @@ void ScrollView::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec ver
MeasureBySpec(layoutParams_->height, 0.0f, vert, &measuredHeight_);
if (orientation_ == ORIENT_HORIZONTAL) {
views_[0]->Measure(dc, MeasureSpec(UNSPECIFIED), vert - (margins.top + margins.bottom));
views_[0]->Measure(dc, MeasureSpec(UNSPECIFIED), MeasureSpec(AT_MOST, measuredHeight_ - (margins.top + margins.bottom)));
} else {
views_[0]->Measure(dc, horiz - (margins.left + margins.right), MeasureSpec(UNSPECIFIED));
views_[0]->Measure(dc, MeasureSpec(AT_MOST, measuredWidth_ - (margins.left + margins.right)), MeasureSpec(UNSPECIFIED));
}
}
@ -491,7 +505,7 @@ void GridLayout::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec ver
// Okay, got the width we are supposed to adjust to. Now we can calculate the number of columns.
int numColumns = (measuredWidth_ - settings_.spacing) / (settings_.columnWidth + settings_.spacing);
int numRows = (views_.size() + (numColumns - 1)) / numColumns;
int numRows = (int)(views_.size() + (numColumns - 1)) / numColumns;
float estimatedHeight = settings_.rowHeight * numRows;
@ -500,7 +514,7 @@ void GridLayout::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec ver
void AnchorLayout::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert) {
MeasureBySpec(layoutParams_->width, 0.0f, horiz, &measuredWidth_);
MeasureBySpec(layoutParams_->height, 0.0f, horiz, &measuredHeight_);
MeasureBySpec(layoutParams_->height, 0.0f, vert, &measuredHeight_);
for (size_t i = 0; i < views_.size(); i++) {
Size width = WRAP_CONTENT;
@ -582,6 +596,17 @@ void GridLayout::Layout() {
}
}
EventReturn TabHolder::OnTabClick(EventParams &e) {
tabs_[currentTab_]->SetVisibility(V_GONE);
for (int i = 0; i < tabChoices_.size(); i++) {
if (e.v == tabChoices_[i]) {
currentTab_ = i;
}
}
tabs_[currentTab_]->SetVisibility(V_VISIBLE);
return EVENT_DONE;
}
void LayoutViewHierarchy(const UIContext &dc, ViewGroup *root) {
Bounds rootBounds;
rootBounds.x = 0;
@ -599,7 +624,6 @@ void LayoutViewHierarchy(const UIContext &dc, ViewGroup *root) {
root->Layout();
}
void UpdateViewHierarchy(const InputState &input_state, ViewGroup *root) {
if (input_state.pad_buttons_down & (PAD_BUTTON_LEFT | PAD_BUTTON_RIGHT | PAD_BUTTON_UP | PAD_BUTTON_DOWN))
{

View File

@ -2,6 +2,7 @@
#include "base/logging.h"
#include "ui/view.h"
#include "math/geom2d.h"
#include "input/gesture_detector.h"
namespace UI {
@ -93,6 +94,8 @@ public:
: LayoutParams(w, h), weight(wgt), gravity(grav), hasMargins_(false) {}
LinearLayoutParams(Size w, Size h, float wgt, Gravity grav, const Margins &mgn)
: LayoutParams(w, h), weight(wgt), gravity(grav), margins(mgn), hasMargins_(true) {}
LinearLayoutParams(Size w, Size h, const Margins &mgn)
: LayoutParams(w, h), weight(0.0f), gravity(G_TOPLEFT), margins(mgn), hasMargins_(true) {}
LinearLayoutParams(const Margins &mgn)
: LayoutParams(WRAP_CONTENT, WRAP_CONTENT), weight(0.0f), gravity(G_TOPLEFT), margins(mgn), hasMargins_(true) {}
@ -187,6 +190,48 @@ class ViewPager : public ScrollView {
public:
};
class TabHolder : public LinearLayout {
public:
TabHolder(Orientation orientation, float stripSize, LayoutParams *layoutParams = 0)
: LinearLayout(ORIENT_HORIZONTAL, layoutParams),
orientation_(orientation), stripSize_(stripSize), currentTab_(0) {
tabStrip_ = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(stripSize, WRAP_CONTENT));
Add(tabStrip_);
}
template <class T>
T *AddTab(const std::string &title, T *tabContents) {
tabContents->ReplaceLayoutParams(new LinearLayoutParams(1.0f));
tabTitles_.push_back(title);
tabs_.push_back(tabContents);
Choice *choice = new Choice(title);
tabStrip_->Add(choice)->OnClick.Handle(this, &TabHolder::OnTabClick);
tabChoices_.push_back(choice);
Add(tabContents);
if (tabs_.size() > 1)
tabContents->SetVisibility(V_GONE);
return tabContents;
}
void SetCurrentTab(int tab) {
tabs_[currentTab_]->SetVisibility(V_GONE);
currentTab_ = tab;
tabs_[currentTab_]->SetVisibility(V_VISIBLE);
}
private:
EventReturn OnTabClick(EventParams &e);
ViewGroup *tabStrip_;
Orientation orientation_;
int currentTab_;
float stripSize_;
std::vector<std::string> tabTitles_;
std::vector<Choice *> tabChoices_;
std::vector<View *> tabs_;
};
void LayoutViewHierarchy(const UIContext &dc, ViewGroup *root);
void UpdateViewHierarchy(const InputState &input_state, ViewGroup *root);