From 166acebeffb5b90efc0e40704943731aedee9969 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sat, 8 Jun 2013 22:41:17 +0200 Subject: [PATCH] A lot of NewUI work: layout and focus fixes, "TabHolder" --- input/gesture_detector.cpp | 2 +- input/input_state.h | 1 + math/geom2d.h | 3 ++ ui/ui_context.cpp | 40 ++++++++++++++++----------- ui/ui_context.h | 7 +++++ ui/ui_screen.cpp | 29 ++++++++++++++------ ui/ui_screen.h | 8 ++++-- ui/view.cpp | 42 +++++++++++++++++++++++----- ui/view.h | 45 +++++++++++++++++++++++++----- ui/viewgroup.cpp | 56 +++++++++++++++++++++++++++----------- ui/viewgroup.h | 45 ++++++++++++++++++++++++++++++ 11 files changed, 221 insertions(+), 57 deletions(-) diff --git a/input/gesture_detector.cpp b/input/gesture_detector.cpp index 852b8fae60..585e077adb 100644 --- a/input/gesture_detector.cpp +++ b/input/gesture_detector.cpp @@ -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 { diff --git a/input/input_state.h b/input/input_state.h index 11903f651e..5be7b48280 100644 --- a/input/input_state.h +++ b/input/input_state.h @@ -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. diff --git a/math/geom2d.h b/math/geom2d.h index fd8357de40..4e0749746d 100644 --- a/math/geom2d.h +++ b/math/geom2d.h @@ -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); } diff --git a/ui/ui_context.cpp b/ui/ui_context.cpp index 9a92458971..8751109c21 100644 --- a/ui/ui_context.cpp +++ b/ui/ui_context.cpp @@ -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(); } @@ -97,4 +91,18 @@ void UIContext::ActivateTopScissor() { } else { 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; + } } \ No newline at end of file diff --git a/ui/ui_context.h b/ui/ui_context.h index 5552290f98..ed9e17aa40 100644 --- a/ui/ui_context.h +++ b/ui/ui_context.h @@ -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_; diff --git a/ui/ui_screen.cpp b/ui/ui_screen.cpp index 09fe04791b..5a0389ed3a 100644 --- a/ui/ui_screen.cpp +++ b/ui/ui_screen.cpp @@ -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) { diff --git a/ui/ui_screen.h b/ui/ui_screen.h index e8111d3727..bcf2ab2297 100644 --- a/ui/ui_screen.h +++ b/ui/ui_screen.h @@ -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_; }; diff --git a/ui/view.cpp b/ui/view.cpp index 2a73699012..1f57254942 100644 --- a/ui/view.cpp +++ b/ui/view.cpp @@ -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); diff --git a/ui/view.h b/ui/view.h index 9bee17ace5..eef7e3958e 100644 --- a/ui/view.h +++ b/ui/view.h @@ -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_; - + + 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) diff --git a/ui/viewgroup.cpp b/ui/viewgroup.cpp index c47d572638..eda02f61d0 100644 --- a/ui/viewgroup.cpp +++ b/ui/viewgroup.cpp @@ -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(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(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)) { diff --git a/ui/viewgroup.h b/ui/viewgroup.h index 1ad1a4a067..011e8a27ba 100644 --- a/ui/viewgroup.h +++ b/ui/viewgroup.h @@ -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 + 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 tabTitles_; + std::vector tabChoices_; + std::vector tabs_; +}; + void LayoutViewHierarchy(const UIContext &dc, ViewGroup *root); void UpdateViewHierarchy(const InputState &input_state, ViewGroup *root);