diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index 38fb9d3fb6..f6e530edc2 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -61,20 +61,26 @@ void DevMenu::CreatePopupContents(UI::ViewGroup *parent) { I18NCategory *dev = GetI18NCategory("Developer"); I18NCategory *sy = GetI18NCategory("System"); + ScrollView *scroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, 1.0f)); + LinearLayout *items = new LinearLayout(ORIENT_VERTICAL); + #if !defined(MOBILE_DEVICE) - parent->Add(new Choice(dev->T("Log View")))->OnClick.Handle(this, &DevMenu::OnLogView); + items->Add(new Choice(dev->T("Log View")))->OnClick.Handle(this, &DevMenu::OnLogView); #endif - parent->Add(new Choice(dev->T("Logging Channels")))->OnClick.Handle(this, &DevMenu::OnLogConfig); - parent->Add(new Choice(sy->T("Developer Tools")))->OnClick.Handle(this, &DevMenu::OnDeveloperTools); - parent->Add(new Choice(dev->T("Jit Compare")))->OnClick.Handle(this, &DevMenu::OnJitCompare); - parent->Add(new Choice(dev->T("Shader Viewer")))->OnClick.Handle(this, &DevMenu::OnShaderView); - parent->Add(new Choice(dev->T("Toggle Freeze")))->OnClick.Handle(this, &DevMenu::OnFreezeFrame); - parent->Add(new Choice(dev->T("Dump Frame GPU Commands")))->OnClick.Handle(this, &DevMenu::OnDumpFrame); - parent->Add(new Choice(dev->T("Toggle Audio Debug")))->OnClick.Handle(this, &DevMenu::OnToggleAudioDebug); + items->Add(new Choice(dev->T("Logging Channels")))->OnClick.Handle(this, &DevMenu::OnLogConfig); + items->Add(new Choice(sy->T("Developer Tools")))->OnClick.Handle(this, &DevMenu::OnDeveloperTools); + items->Add(new Choice(dev->T("Jit Compare")))->OnClick.Handle(this, &DevMenu::OnJitCompare); + items->Add(new Choice(dev->T("Shader Viewer")))->OnClick.Handle(this, &DevMenu::OnShaderView); + items->Add(new Choice(dev->T("Toggle Freeze")))->OnClick.Handle(this, &DevMenu::OnFreezeFrame); + items->Add(new Choice(dev->T("Dump Frame GPU Commands")))->OnClick.Handle(this, &DevMenu::OnDumpFrame); + items->Add(new Choice(dev->T("Toggle Audio Debug")))->OnClick.Handle(this, &DevMenu::OnToggleAudioDebug); #ifdef USE_PROFILER - parent->Add(new CheckBox(&g_Config.bShowFrameProfiler, dev->T("Frame Profiler"), "")); + items->Add(new CheckBox(&g_Config.bShowFrameProfiler, dev->T("Frame Profiler"), "")); #endif + scroll->Add(items); + parent->Add(scroll); + RingbufferLogListener *ring = LogManager::GetInstance()->GetRingbufferListener(); if (ring) { ring->SetEnable(true); diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index 755d4e8bef..056c15b684 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -706,7 +706,7 @@ void MainScreen::CreateViews() { Margins actionMenuMargins(0, 10, 10, 0); - TabHolder *leftColumn = new TabHolder(ORIENT_HORIZONTAL, 64); + TabHolder *leftColumn = new TabHolder(ORIENT_HORIZONTAL, 64, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); tabHolder_ = leftColumn; leftColumn->SetClip(true); @@ -813,12 +813,12 @@ void MainScreen::CreateViews() { if (vertical) { root_ = new LinearLayout(ORIENT_VERTICAL); rightColumn->ReplaceLayoutParams(new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); - leftColumn->ReplaceLayoutParams(new LinearLayoutParams(1.0)); + leftColumn->ReplaceLayoutParams(new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, 1.0)); root_->Add(rightColumn); root_->Add(leftColumn); } else { root_ = new LinearLayout(ORIENT_HORIZONTAL); - leftColumn->ReplaceLayoutParams(new LinearLayoutParams(1.0)); + leftColumn->ReplaceLayoutParams(new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, 1.0)); rightColumn->ReplaceLayoutParams(new LinearLayoutParams(300, FILL_PARENT, actionMenuMargins)); root_->Add(leftColumn); root_->Add(rightColumn); @@ -1110,7 +1110,7 @@ void UmdReplaceScreen::CreateViews() { I18NCategory *mm = GetI18NCategory("MainMenu"); I18NCategory *di = GetI18NCategory("Dialog"); - TabHolder *leftColumn = new TabHolder(ORIENT_HORIZONTAL, 64, new LinearLayoutParams(1.0)); + TabHolder *leftColumn = new TabHolder(ORIENT_HORIZONTAL, 64, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, 1.0)); leftColumn->SetClip(true); ViewGroup *rightColumn = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(270, FILL_PARENT, actionMenuMargins)); diff --git a/ext/native/ui/ui_screen.cpp b/ext/native/ui/ui_screen.cpp index 48a55ccaa8..79cc80f09d 100644 --- a/ext/native/ui/ui_screen.cpp +++ b/ext/native/ui/ui_screen.cpp @@ -195,7 +195,9 @@ void PopupScreen::CreateViews() { UIContext &dc = *screenManager()->getUIContext(); - root_ = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT)); + AnchorLayout *anchor = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT)); + anchor->Overflow(false); + root_ = anchor; float yres = screenManager()->getUIContext()->GetBounds().h; diff --git a/ext/native/ui/view.cpp b/ext/native/ui/view.cpp index 15d92bcb78..4ec1c965f3 100644 --- a/ext/native/ui/view.cpp +++ b/ext/native/ui/view.cpp @@ -95,8 +95,10 @@ bool IsFocusMovementEnabled() { void MeasureBySpec(Size sz, float contentWidth, MeasureSpec spec, float *measured) { *measured = sz; if (sz == WRAP_CONTENT) { - if (spec.type == UNSPECIFIED || spec.type == AT_MOST) + if (spec.type == UNSPECIFIED) *measured = contentWidth; + else if (spec.type == AT_MOST) + *measured = contentWidth < spec.size ? contentWidth : spec.size; else if (spec.type == EXACTLY) *measured = spec.size; } else if (sz == FILL_PARENT) { diff --git a/ext/native/ui/view.h b/ext/native/ui/view.h index 62926338d0..952e3d9c29 100644 --- a/ext/native/ui/view.h +++ b/ext/native/ui/view.h @@ -292,6 +292,26 @@ public: // Fake RTTI bool Is(LayoutParamsType type) const { return type_ == type; } + template + T *As() { + if (Is(T::StaticType())) { + return static_cast(this); + } + return nullptr; + } + + template + const T *As() const { + if (Is(T::StaticType())) { + return static_cast(this); + } + return nullptr; + } + + static LayoutParamsType StaticType() { + return LP_PLAIN; + } + private: LayoutParamsType type_; }; diff --git a/ext/native/ui/viewgroup.cpp b/ext/native/ui/viewgroup.cpp index f8ece69a2d..21f37cb8c9 100644 --- a/ext/native/ui/viewgroup.cpp +++ b/ext/native/ui/viewgroup.cpp @@ -395,9 +395,7 @@ void LinearLayout::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec v continue; numVisible++; - const LayoutParams *layoutParams = views_[i]->GetLayoutParams(); - const LinearLayoutParams *linLayoutParams = static_cast(layoutParams); - if (!linLayoutParams->Is(LP_LINEAR)) linLayoutParams = 0; + const LinearLayoutParams *linLayoutParams = views_[i]->GetLayoutParams()->As(); Margins margins = defaultMargins_; @@ -446,14 +444,20 @@ void LinearLayout::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec v MeasureBySpec(layoutParams_->width, weightZeroSum, horiz, &measuredWidth_); MeasureBySpec(layoutParams_->height, maxOther, vert, &measuredHeight_); - float unit = (measuredWidth_ - weightZeroSum) / weightSum; + // If we've got stretch, allow growing to fill the parent. + float allowedWidth = measuredWidth_; + if (horiz.type == AT_MOST && measuredWidth_ < horiz.size) { + allowedWidth = horiz.size; + } + + float unit = (allowedWidth - weightZeroSum) / weightSum; + float usedWidth = 0.0f; + // Redistribute the stretchy ones! and remeasure the children! for (size_t i = 0; i < views_.size(); i++) { if (views_[i]->GetVisibility() == V_GONE) continue; - const LayoutParams *layoutParams = views_[i]->GetLayoutParams(); - const LinearLayoutParams *linLayoutParams = static_cast(layoutParams); - if (!linLayoutParams->Is(LP_LINEAR)) linLayoutParams = 0; + const LinearLayoutParams *linLayoutParams = views_[i]->GetLayoutParams()->As(); if (linLayoutParams && linLayoutParams->weight > 0.0f) { Margins margins = defaultMargins_; @@ -463,23 +467,36 @@ void LinearLayout::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec v MeasureSpec v = vert; if (v.type == UNSPECIFIED && measuredHeight_ != 0.0) v = MeasureSpec(AT_MOST, measuredHeight_); - views_[i]->Measure(dc, MeasureSpec(EXACTLY, unit * linLayoutParams->weight - marginSum), v - (float)(margins.top + margins.bottom)); + MeasureSpec h(AT_MOST, unit * linLayoutParams->weight - marginSum); + if (horiz.type == EXACTLY) { + h.type = EXACTLY; + } + views_[i]->Measure(dc, h, v - (float)(margins.top + margins.bottom)); + usedWidth += views_[i]->GetMeasuredWidth(); } } + + if (horiz.type == AT_MOST && measuredWidth_ < horiz.size) { + measuredWidth_ += usedWidth; + } } else { - //MeasureBySpec(layoutParams_->height, vert.type == UNSPECIFIED ? sum : weightZeroSum, vert, &measuredHeight_); MeasureBySpec(layoutParams_->height, weightZeroSum, vert, &measuredHeight_); MeasureBySpec(layoutParams_->width, maxOther, horiz, &measuredWidth_); - float unit = (measuredHeight_ - weightZeroSum) / weightSum; + // If we've got stretch, allow growing to fill the parent. + float allowedHeight = measuredHeight_; + if (vert.type == AT_MOST && measuredHeight_ < vert.size) { + allowedHeight = vert.size; + } + + float unit = (allowedHeight - weightZeroSum) / weightSum; + float usedHeight = 0.0f; // Redistribute the stretchy ones! and remeasure the children! for (size_t i = 0; i < views_.size(); i++) { if (views_[i]->GetVisibility() == V_GONE) continue; - const LayoutParams *layoutParams = views_[i]->GetLayoutParams(); - const LinearLayoutParams *linLayoutParams = static_cast(layoutParams); - if (!linLayoutParams->Is(LP_LINEAR)) linLayoutParams = 0; + const LinearLayoutParams *linLayoutParams = views_[i]->GetLayoutParams()->As(); if (linLayoutParams && linLayoutParams->weight > 0.0f) { Margins margins = defaultMargins_; @@ -489,9 +506,18 @@ void LinearLayout::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec v MeasureSpec h = horiz; if (h.type == UNSPECIFIED && measuredWidth_ != 0.0) h = MeasureSpec(AT_MOST, measuredWidth_); - views_[i]->Measure(dc, h - (float)(margins.left + margins.right), MeasureSpec(EXACTLY, unit * linLayoutParams->weight - marginSum)); + MeasureSpec v(AT_MOST, unit * linLayoutParams->weight - marginSum); + if (vert.type == EXACTLY) { + v.type = EXACTLY; + } + views_[i]->Measure(dc, h - (float)(margins.left + margins.right), v); + usedHeight += views_[i]->GetMeasuredHeight(); } } + + if (vert.type == AT_MOST && measuredWidth_ < vert.size) { + measuredHeight_ += usedHeight; + } } } @@ -516,9 +542,7 @@ void LinearLayout::Layout() { if (views_[i]->GetVisibility() == V_GONE) continue; - const LayoutParams *layoutParams = views_[i]->GetLayoutParams(); - const LinearLayoutParams *linLayoutParams = static_cast(layoutParams); - if (!linLayoutParams->Is(LP_LINEAR)) linLayoutParams = 0; + const LinearLayoutParams *linLayoutParams = views_[i]->GetLayoutParams()->As(); Gravity gravity = G_TOPLEFT; Margins margins = defaultMargins_; @@ -583,10 +607,7 @@ void ScrollView::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec ver // Respect margins Margins margins; if (views_.size()) { - const LinearLayoutParams *linLayoutParams = static_cast(views_[0]->GetLayoutParams()); - if (!linLayoutParams->Is(LP_LINEAR)) { - linLayoutParams = 0; - } + const LinearLayoutParams *linLayoutParams = views_[0]->GetLayoutParams()->As(); if (linLayoutParams) { margins = linLayoutParams->margins; } @@ -604,8 +625,17 @@ void ScrollView::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec ver views_[0]->Measure(dc, MeasureSpec(AT_MOST, measuredWidth_ - (margins.left + margins.right)), MeasureSpec(UNSPECIFIED)); MeasureBySpec(layoutParams_->width, views_[0]->GetMeasuredWidth(), horiz, &measuredWidth_); } - if (orientation_ == ORIENT_VERTICAL && vert.type != EXACTLY && measuredHeight_ < views_[0]->GetBounds().h) - measuredHeight_ = views_[0]->GetBounds().h; + if (orientation_ == ORIENT_VERTICAL && vert.type != EXACTLY) { + if (measuredHeight_ < views_[0]->GetMeasuredHeight()) { + measuredHeight_ = views_[0]->GetMeasuredHeight(); + } + if (measuredHeight_ < views_[0]->GetBounds().h) { + measuredHeight_ = views_[0]->GetBounds().h; + } + if (vert.type == AT_MOST && measuredHeight_ > vert.size) { + measuredHeight_ = vert.size; + } + } } } @@ -616,8 +646,7 @@ void ScrollView::Layout() { // Respect margins Margins margins; - const LinearLayoutParams *linLayoutParams = static_cast(views_[0]->GetLayoutParams()); - if (!linLayoutParams->Is(LP_LINEAR)) linLayoutParams = 0; + const LinearLayoutParams *linLayoutParams = views_[0]->GetLayoutParams()->As(); if (linLayoutParams) { margins = linLayoutParams->margins; } @@ -859,22 +888,34 @@ void AnchorLayout::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec v MeasureSpec specW(UNSPECIFIED, 0.0f); MeasureSpec specH(UNSPECIFIED, 0.0f); - const AnchorLayoutParams *params = static_cast(views_[i]->GetLayoutParams()); - if (!params->Is(LP_ANCHOR)) params = 0; + if (!overflow_) { + if (horiz.type != UNSPECIFIED) { + specW = MeasureSpec(AT_MOST, horiz.size); + } + if (vert.type != UNSPECIFIED) { + specH = MeasureSpec(AT_MOST, vert.size); + } + } + + const AnchorLayoutParams *params = views_[i]->GetLayoutParams()->As(); if (params) { width = params->width; height = params->height; if (!params->center) { - if (params->left >= 0 && params->right >= 0) { + if (params->left >= 0 && params->right >= 0) { width = measuredWidth_ - params->left - params->right; } - if (params->top >= 0 && params->bottom >= 0) { + if (params->top >= 0 && params->bottom >= 0) { height = measuredHeight_ - params->top - params->bottom; } } - specW = width < 0 ? MeasureSpec(UNSPECIFIED) : MeasureSpec(EXACTLY, width); - specH = height < 0 ? MeasureSpec(UNSPECIFIED) : MeasureSpec(EXACTLY, height); + if (width >= 0) { + specW = MeasureSpec(EXACTLY, width); + } + if (height >= 0) { + specH = MeasureSpec(EXACTLY, height); + } } views_[i]->Measure(dc, specW, specH); @@ -883,8 +924,7 @@ void AnchorLayout::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec v void AnchorLayout::Layout() { for (size_t i = 0; i < views_.size(); i++) { - const AnchorLayoutParams *params = static_cast(views_[i]->GetLayoutParams()); - if (!params->Is(LP_ANCHOR)) params = 0; + const AnchorLayoutParams *params = views_[i]->GetLayoutParams()->As(); Bounds vBounds; vBounds.w = views_[i]->GetMeasuredWidth(); diff --git a/ext/native/ui/viewgroup.h b/ext/native/ui/viewgroup.h index d45cf2cadb..833c937ef4 100644 --- a/ext/native/ui/viewgroup.h +++ b/ext/native/ui/viewgroup.h @@ -111,14 +111,24 @@ public: // Set to NONE to not attach this edge to the container. float left, top, right, bottom; bool center; // If set, only two "sides" can be set, and they refer to the center, not the edge, of the view being layouted. + + static LayoutParamsType StaticType() { + return LP_ANCHOR; + } }; class AnchorLayout : public ViewGroup { public: - AnchorLayout(LayoutParams *layoutParams = 0) : ViewGroup(layoutParams) {} + AnchorLayout(LayoutParams *layoutParams = 0) : ViewGroup(layoutParams), overflow_(true) {} void Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert) override; void Layout() override; + void Overflow(bool allow) { + overflow_ = allow; + } std::string Describe() const override { return "AnchorLayout: " + View::Describe(); } + +private: + bool overflow_; }; class LinearLayoutParams : public LayoutParams { @@ -146,6 +156,10 @@ public: bool HasMargins() const { return hasMargins_; } + static LayoutParamsType StaticType() { + return LP_LINEAR; + } + private: bool hasMargins_; };